feat: add --transfer-spec flag for two-step transfer workflow#2077
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a two-step transfer mode to Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as "ocm transfer cv (CLI)"
participant SpecLoader as "loadTransferSpec()"
participant ArgBuilder as "buildGraphDefinitionFromArgs()"
participant GraphBuilder as "BuildAndCheck / Transformers"
participant Engine as "graph.Process()"
User->>CLI: invoke command
alt --transfer-spec provided
CLI->>SpecLoader: read file/stdin, unmarshal YAML
SpecLoader-->>CLI: TransformationGraphDefinition
else build from args
CLI->>ArgBuilder: validate args & flags, build TGD
ArgBuilder-->>CLI: TransformationGraphDefinition
end
CLI->>GraphBuilder: Build and check transformers from TGD
GraphBuilder-->>CLI: runtime graph
CLI->>CLI: optional dry-run render (stdout/yaml)
CLI->>Engine: execute graph.Process(ctx)
Engine-->>CLI: progress/events and completion
CLI-->>User: exit status / final log
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
cli/cmd/transfer/component-version/cmd_test.go (1)
85-274: Good coverage overall; please add one stdin case.
loadTransferSpec()has a separatepath == "-"branch, but the new tests only exercise file-backed specs. One small--transfer-spec -round-trip would protect the documented pipe workflow from regressing.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/transfer/component-version/cmd_test.go` around lines 85 - 274, Add a test exercising the stdin ("-") branch of loadTransferSpec by round-tripping a generated spec through stdin: generate the spec via the existing dry-run flow into a buffer, then invoke test.OCM with args "transfer component-version --transfer-spec -", supplying the buffer as the process stdin (and appropriate output/log readers), and assert the command succeeds and the transfer behaves as in the other transfer-spec tests; this ensures loadTransferSpec's path == "-" branch is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cli/cmd/transfer/component-version/cmd.go`:
- Around line 121-135: The transferArgs function currently allows
--transfer-spec while silently ignoring graph-building flags; update
transferArgs(cmd *cobra.Command, args []string) to detect when specPath != ""
and then check whether any of the graph-building flags (--recursive,
--copy-resources, --upload-as) were supplied (use
cmd.Flags().Changed("recursive"), cmd.Flags().Changed("copy-resources"), and
cmd.Flags().Changed("upload-as") or read their values and check Changed) and
return an error if any are present, with a clear message that those flags are
not allowed with --transfer-spec; keep the existing positional-argument
rejection behavior and ensure you reference FlagTransferSpec and the flag names
in the error text for clarity.
---
Nitpick comments:
In `@cli/cmd/transfer/component-version/cmd_test.go`:
- Around line 85-274: Add a test exercising the stdin ("-") branch of
loadTransferSpec by round-tripping a generated spec through stdin: generate the
spec via the existing dry-run flow into a buffer, then invoke test.OCM with args
"transfer component-version --transfer-spec -", supplying the buffer as the
process stdin (and appropriate output/log readers), and assert the command
succeeds and the transfer behaves as in the other transfer-spec tests; this
ensures loadTransferSpec's path == "-" branch is covered.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9293dd44-d7b1-4ea6-bb67-65f387086110
📒 Files selected for processing (3)
cli/cmd/transfer/component-version/cmd.gocli/cmd/transfer/component-version/cmd_test.gocli/integration/transfer_with_transfer_spec_test.go
|
@coderabbitai review again please |
|
🧠 Learnings used✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
cli/cmd/transfer/component-version/cmd.go (1)
236-259: Consider validating the loaded spec has required content.
loadTransferSpecunmarshals YAML intoTransformationGraphDefinitionbut doesn't validate the result. Based on context snippet 2 (builder.go:29-87),BuildAndCheckwill eventually process the spec, but an emptyTransformationsslice or nilEnvironmentwon't produce a clear error message for the user.If the user provides an empty file or malformed spec without transformations, the error surfaces later in
BuildAndCheckwith a less actionable message.💡 Optional: Add basic validation after unmarshaling
tgd := &transformv1alpha1.TransformationGraphDefinition{} if err := yaml.Unmarshal(data, tgd); err != nil { return nil, fmt.Errorf("parsing transfer spec: %w", err) } + + if len(tgd.Transformations) == 0 { + return nil, fmt.Errorf("transfer spec contains no transformations") + } return tgd, nil🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/transfer/component-version/cmd.go` around lines 236 - 259, loadTransferSpec currently unmarshals YAML into a transformv1alpha1.TransformationGraphDefinition but doesn't validate required fields, so empty or missing data (e.g., no Transformations or nil Environment) only fails later in BuildAndCheck with poor context; after yaml.Unmarshal in loadTransferSpec validate that tgd is non-nil, that len(tgd.Transformations) > 0 and that tgd.Environment (or other required top-level fields used by BuildAndCheck) is present/initialized, and return a clear fmt.Errorf like "invalid transfer spec: missing Transformations" (or "missing Environment") so users get immediate actionable errors referencing loadTransferSpec rather than surfacing later in BuildAndCheck.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@cli/cmd/transfer/component-version/cmd.go`:
- Around line 236-259: loadTransferSpec currently unmarshals YAML into a
transformv1alpha1.TransformationGraphDefinition but doesn't validate required
fields, so empty or missing data (e.g., no Transformations or nil Environment)
only fails later in BuildAndCheck with poor context; after yaml.Unmarshal in
loadTransferSpec validate that tgd is non-nil, that len(tgd.Transformations) > 0
and that tgd.Environment (or other required top-level fields used by
BuildAndCheck) is present/initialized, and return a clear fmt.Errorf like
"invalid transfer spec: missing Transformations" (or "missing Environment") so
users get immediate actionable errors referencing loadTransferSpec rather than
surfacing later in BuildAndCheck.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 243f474f-2d45-4f2c-a922-612b56436262
📒 Files selected for processing (4)
cli/cmd/transfer/component-version/cmd.gocli/cmd/transfer/component-version/cmd_test.gocli/docs/reference/ocm_transfer_component-version.mdcli/integration/transfer_with_transfer_spec_test.go
d0a9fce to
19d5b91
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
cli/integration/transfer_with_transfer_spec_test.go (2)
88-91: Consider increasing transfer timeout to reduce CI flakiness.A fixed 30s budget is tight for registry/container-based integration tests. A slightly larger timeout (or shared integration timeout constant) would make this more reliable.
Suggested adjustment
- ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 2*time.Minute)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/integration/transfer_with_transfer_spec_test.go` around lines 88 - 91, The 30s timeout passed to context.WithTimeout around transferCMD.ExecuteContext is too tight for container/registry integration tests; increase the timeout (e.g., 2m or a shared integration timeout constant) or introduce a shared constant like integrationTimeout and use context.WithTimeout(t.Context(), integrationTimeout) so the test has a larger, configurable budget to reduce CI flakiness when calling transferCMD.ExecuteContext.
42-42: Use narrower file permissions for temp artifacts.
os.ModePermis broader than needed and can trigger security lint noise. Prefer explicit file modes for test files.Suggested adjustment
- r.NoError(os.WriteFile(constructorPath, []byte(constructorContent), os.ModePerm)) + r.NoError(os.WriteFile(constructorPath, []byte(constructorContent), 0o600)) - r.NoError(os.WriteFile(specFile, spec, os.ModePerm)) + r.NoError(os.WriteFile(specFile, spec, 0o600)) - r.NoError(os.WriteFile(specFile, []byte(modifiedSpec), os.ModePerm)) + r.NoError(os.WriteFile(specFile, []byte(modifiedSpec), 0o600))Also applies to: 137-137, 182-182
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/integration/transfer_with_transfer_spec_test.go` at line 42, Replace the permissive os.ModePerm usage when writing temp test files with a narrower explicit mode (e.g., 0o600) to avoid security lint noise; update the os.WriteFile calls (see the r.NoError(os.WriteFile(constructorPath, []byte(constructorContent), os.ModePerm)) call and the similar os.WriteFile invocations around the other noted lines) to use the explicit file mode constant instead of os.ModePerm.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cli/cmd/transfer/component-version/cmd_test.go`:
- Around line 143-218: Add two tests in
cli/cmd/transfer/component-version/cmd_test.go: one positive
TestTransferComponentVersionWithTransferSpecStdin that creates a spec via
dryRunTransferSpec/setupSourceRef, invokes test.OCM with args "transfer
component-version --transfer-spec -", supplies the spec on stdin via
test.WithInput(bytes.NewBufferString(spec)) and asserts no error and that the
component exists in the target (use openCTFRepo and GetComponentVersion to
verify); and one negative
TestTransferComponentVersionWithTransferSpecStdinInvalid that supplies invalid
YAML on stdin, runs the same command, and asserts an error and appropriate error
text. Reuse helpers like setupSourceRef, dryRunTransferSpec,
executeTransferSpec/writeSpecFile patterns and test.OCM options (test.WithArgs,
test.WithInput, test.WithOutput, test.WithErrorOutput) to match existing tests.
---
Nitpick comments:
In `@cli/integration/transfer_with_transfer_spec_test.go`:
- Around line 88-91: The 30s timeout passed to context.WithTimeout around
transferCMD.ExecuteContext is too tight for container/registry integration
tests; increase the timeout (e.g., 2m or a shared integration timeout constant)
or introduce a shared constant like integrationTimeout and use
context.WithTimeout(t.Context(), integrationTimeout) so the test has a larger,
configurable budget to reduce CI flakiness when calling
transferCMD.ExecuteContext.
- Line 42: Replace the permissive os.ModePerm usage when writing temp test files
with a narrower explicit mode (e.g., 0o600) to avoid security lint noise; update
the os.WriteFile calls (see the r.NoError(os.WriteFile(constructorPath,
[]byte(constructorContent), os.ModePerm)) call and the similar os.WriteFile
invocations around the other noted lines) to use the explicit file mode constant
instead of os.ModePerm.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2b643777-7e08-43e5-9498-28370620ebd3
📒 Files selected for processing (2)
cli/cmd/transfer/component-version/cmd_test.gocli/integration/transfer_with_transfer_spec_test.go
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com> # Conflicts: # cli/cmd/transfer/component-version/cmd.go
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
cf614d4 to
7b956fb
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cli/cmd/transfer/component-version/cmd.go`:
- Around line 184-194: The current error-handling calls renderTGD(tgd, output)
then unconditionally defers reader.Close() and reads from reader even if
renderTGD returned an error (nil reader), which can panic and mask the original
err from BuildAndCheck; fix by calling renderTGD, check rerr first, and only
defer Close and call io.ReadAll when reader != nil (or when rerr == nil),
otherwise include rerr (and skip reading/closing) when composing the returned
errors from the enclosing function (refer to renderTGD, reader, rerr and the
surrounding errors.Join call).
- Around line 245-247: The code currently uses yaml.Unmarshal(data, tgd) which
silently ignores unknown/misspelled fields; replace this with strict
unmarshalling so unknown keys cause an error: either call
yaml.UnmarshalStrict(data, tgd) if your yaml package supports it, or use a
yaml.Decoder with KnownFields(true) (e.g.
yaml.NewDecoder(bytes.NewReader(data)); dec.KnownFields(true); dec.Decode(&tgd))
to fail on unknown fields; update the error path that returns
fmt.Errorf("parsing transfer spec: %w", err) to surface those strict-unmarshal
errors for the TransformationGraphDefinition load path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 74c96a57-7f0a-42b5-9d48-596fdaa976c3
📒 Files selected for processing (4)
cli/cmd/transfer/component-version/cmd.gocli/cmd/transfer/component-version/cmd_test.gocli/docs/reference/ocm_transfer_component-version.mdcli/integration/transfer_with_transfer_spec_test.go
✅ Files skipped from review due to trivial changes (1)
- cli/cmd/transfer/component-version/cmd_test.go
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
cli/cmd/transfer/component-version/cmd.go (1)
184-196:⚠️ Potential issue | 🟠 Major
renderTGD()still needs a hard error check beforeio.ReadAll().The nil-guard on
Close()fixed only half of the earlier issue. IfrenderTGD()returns(nil, rerr), Line 191 still reads from a nil reader, and anyio.ReadAll()failure is still dropped, so the originalBuildAndCheck()error can still get masked.🔧 Suggested change
if err != nil { reader, rerr := renderTGD(tgd, output) - defer func() { - if reader != nil { - _ = reader.Close() - } - }() - raw, _ := io.ReadAll(reader) - return errors.Join( - err, - rerr, - fmt.Errorf("%s", raw), - ) + if rerr != nil { + return errors.Join(err, rerr) + } + defer func() { + if cerr := reader.Close(); cerr != nil { + slog.WarnContext(ctx, "closing transformation graph reader failed", "error", cerr) + } + }() + raw, readErr := io.ReadAll(reader) + if readErr != nil { + return errors.Join(err, readErr) + } + if len(raw) == 0 { + return err + } + return errors.Join(err, fmt.Errorf("%s", raw)) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/transfer/component-version/cmd.go` around lines 184 - 196, The error handling around renderTGD() is incomplete: ensure you check rerr and that reader is non-nil before calling io.ReadAll(), and capture any error from io.ReadAll() instead of discarding it; specifically, after calling renderTGD(tgd, output) check if rerr != nil and return or include it, only set up the deferred Close() when reader != nil, then call raw, readErr := io.ReadAll(reader) and include readErr in the errors.Join alongside err and rerr (or return early if reader is nil and rerr is set) so the original BuildAndCheck error isn't masked and ReadAll failures are propagated.
🧹 Nitpick comments (1)
cli/cmd/transfer/component-version/cmd.go (1)
231-252: Reject blank / empty transfer specs before returning a zero-value graph.
yaml.Unmarshalaccepts an empty file or stdin intoTransformationGraphDefinition{}. Since the bindings type leaves both top-level fields zero-valued by default, this path currently depends on downstream behavior to catch a truncated spec. A small upfront validation here would make the new hand-edited workflow much easier to diagnose.🔧 Suggested change
func loadTransferSpec(path string, stdin io.Reader) (*transformv1alpha1.TransformationGraphDefinition, error) { var data []byte var err error if path == "-" { data, err = io.ReadAll(stdin) if err != nil { return nil, fmt.Errorf("reading transfer spec from stdin: %w", err) } } else { data, err = os.ReadFile(path) if err != nil { return nil, fmt.Errorf("reading transfer spec file %q: %w", path, err) } } + if len(bytes.TrimSpace(data)) == 0 { + return nil, fmt.Errorf("transfer spec is empty") + } tgd := &transformv1alpha1.TransformationGraphDefinition{} if err := yaml.Unmarshal(data, tgd); err != nil { return nil, fmt.Errorf("parsing transfer spec: %w", err) } + if len(tgd.Transformations) == 0 { + return nil, fmt.Errorf("transfer spec must contain at least one transformation") + } return tgd, nil }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/transfer/component-version/cmd.go` around lines 231 - 252, In loadTransferSpec, reject empty or blank specs and zero-value parsed graphs before returning: after reading data, validate that bytes.TrimSpace(data) is non-empty and after yaml.Unmarshal into transformv1alpha1.TransformationGraphDefinition (tgd) check that at least one required top-level field on tgd is set (i.e., not the zero-value) — if not, return a descriptive error like "empty transfer spec" so callers don't get a zero-value TransformationGraphDefinition; update loadTransferSpec to perform these checks and return errors instead of returning a zeroed tgd.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@cli/cmd/transfer/component-version/cmd.go`:
- Around line 184-196: The error handling around renderTGD() is incomplete:
ensure you check rerr and that reader is non-nil before calling io.ReadAll(),
and capture any error from io.ReadAll() instead of discarding it; specifically,
after calling renderTGD(tgd, output) check if rerr != nil and return or include
it, only set up the deferred Close() when reader != nil, then call raw, readErr
:= io.ReadAll(reader) and include readErr in the errors.Join alongside err and
rerr (or return early if reader is nil and rerr is set) so the original
BuildAndCheck error isn't masked and ReadAll failures are propagated.
---
Nitpick comments:
In `@cli/cmd/transfer/component-version/cmd.go`:
- Around line 231-252: In loadTransferSpec, reject empty or blank specs and
zero-value parsed graphs before returning: after reading data, validate that
bytes.TrimSpace(data) is non-empty and after yaml.Unmarshal into
transformv1alpha1.TransformationGraphDefinition (tgd) check that at least one
required top-level field on tgd is set (i.e., not the zero-value) — if not,
return a descriptive error like "empty transfer spec" so callers don't get a
zero-value TransformationGraphDefinition; update loadTransferSpec to perform
these checks and return errors instead of returning a zeroed tgd.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 58cb98f1-8018-40d4-9971-bba259f6d57b
📒 Files selected for processing (1)
cli/cmd/transfer/component-version/cmd.go
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
…bruns/open-component-model into feat/930_existing_transfer_spec
jakobmoellerdev
left a comment
There was a problem hiding this comment.
awesome job, just a nit
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
1348e95 to
b8b219f
Compare
What this PR does / why we need it
Adds a
--transfer-specflag toocm transfer component-versionthat enables a two-step transfer workflow:--dry-run -o yamland save it to a file--transfer-spec <file>This gives users full control over the TransformationGraphDefinition before execution.
Changes:
--transfer-specflag accepting a file path (or-for stdin)--transfer-specis set, exactly 2 otherwise (source, destination)loadTransferSpec()function for reading and deserializing YAML/JSON specsbuildGraphDefinitionFromArgs()extracted for cleaner code structureWhich issue(s) this PR fixes
Contributes open-component-model/ocm-project#930
Testing
How to test the changes
Verification
ocm