Skip to content

feat: add --transfer-spec flag for two-step transfer workflow#2077

Merged
matthiasbruns merged 12 commits into
open-component-model:mainfrom
matthiasbruns:feat/930_existing_transfer_spec
Mar 27, 2026
Merged

feat: add --transfer-spec flag for two-step transfer workflow#2077
matthiasbruns merged 12 commits into
open-component-model:mainfrom
matthiasbruns:feat/930_existing_transfer_spec

Conversation

@matthiasbruns

@matthiasbruns matthiasbruns commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it

Adds a --transfer-spec flag to ocm transfer component-version that enables a two-step transfer workflow:

  1. Generate the transfer spec via --dry-run -o yaml and save it to a file
  2. Edit the spec (e.g., change the target registry, remove transformations)
  3. Execute the modified spec via --transfer-spec <file>

This gives users full control over the TransformationGraphDefinition before execution.

Changes:

  • New --transfer-spec flag accepting a file path (or - for stdin)
  • Custom argument validator: 0 args when --transfer-spec is set, exactly 2 otherwise (source, destination)
  • loadTransferSpec() function for reading and deserializing YAML/JSON specs
  • buildGraphDefinitionFromArgs() extracted for cleaner code structure
  • Updated command description and examples documenting the workflow

Which issue(s) this PR fixes

Contributes open-component-model/ocm-project#930

Testing

How to test the changes
# Step 1: Generate a transfer spec
ocm transfer cv --dry-run -o yaml ctf::./my-archive//ocm.software/mycomponent:1.0.0 ghcr.io/my-org/ocm > spec.yaml

# Step 2: Edit spec.yaml (e.g. change target registry)

# Step 3: Execute the modified spec
ocm transfer cv --transfer-spec spec.yaml
Verification
  • I have tested the changes locally by running ocm
#!/bin/zsh
#
# Test script for the --transfer-spec feature (issue #930).
# Demonstrates the two-step transfer workflow:
#   1. Generate a transfer spec via --dry-run
#   2. Edit the spec (change target registry)
#   3. Execute the modified spec via --transfer-spec
#
# Prerequisites:
#   - Authenticated to both REGISTRY and REGISTRY2 (e.g. via docker login / gh auth)
#   - Go installed (uses go run)

set -euo pipefail

alias OCM='go run ../../main.go'
REGISTRY="ghcr.io/matthiasbruns/ocm-tutorials"
REGISTRY2="ghcr.io/matthiasbruns/ocm-tutorials-2"

pause() {
  echo "\n>>> Next: $1"
  echo "--- Press Enter to continue ---"
  read
}

# ── Step 1: Create a source CTF archive ──────────────────────────────────────

cat <<EOF > constructor.yaml
components:
- name: ocm.software/transfer-spec-test
  version: 1.0.0
  provider:
    name: ocm.software
  resources:
  - name: test-resource
    version: 1.0.0
    type: plainText
    input:
      type: utf8
      text: "Hello from transfer-spec test!"
EOF

CTF_DIR=$(mktemp -d)
echo "Using temporary directory for CTF: $CTF_DIR"

OCM add cv --repository ctf::$CTF_DIR --constructor constructor.yaml

SOURCE_REF="ctf::$CTF_DIR//ocm.software/transfer-spec-test:1.0.0"

pause "Generate transfer spec via --dry-run (targeting $REGISTRY)"

# ── Step 2: Generate transfer spec targeting REGISTRY ────────────────────────

SPEC_FILE=$(mktemp).yaml
echo "Writing transfer spec to: $SPEC_FILE"

OCM transfer component-version $SOURCE_REF $REGISTRY \
  --dry-run -o yaml \
  --copy-resources \
  > $SPEC_FILE

echo "\n=== Generated Transfer Spec ==="
cat $SPEC_FILE
echo "=== End of Transfer Spec ===\n"

pause "Modify the spec to target $REGISTRY2 instead of $REGISTRY"

# ── Step 3: Edit the spec to change the target registry ──────────────────────

MODIFIED_SPEC_FILE=$(mktemp).yaml
sed "s|${REGISTRY}|${REGISTRY2}|g" $SPEC_FILE > $MODIFIED_SPEC_FILE

echo "\n=== Modified Transfer Spec (diff) ==="
diff $SPEC_FILE $MODIFIED_SPEC_FILE || true
echo "=== End of Diff ===\n"

pause "Execute the modified spec (transfer to $REGISTRY2)"

# ── Step 4: Execute the modified transfer spec ───────────────────────────────

OCM transfer component-version --transfer-spec $MODIFIED_SPEC_FILE --loglevel debug

echo "\nTransfer completed successfully!"

pause "Verify: describe component in $REGISTRY2"

# ── Step 5: Verify the component was transferred to REGISTRY2 ────────────────

OCM describe component-version https://$REGISTRY2//ocm.software/transfer-spec-test:1.0.0

# ── Cleanup ──────────────────────────────────────────────────────────────────

rm -rf $CTF_DIR constructor.yaml $SPEC_FILE $MODIFIED_SPEC_FILE

echo "\nAll done! The component was transferred to $REGISTRY2 using a modified transfer spec."

@matthiasbruns matthiasbruns requested a review from a team as a code owner March 26, 2026 12:28
@github-actions github-actions Bot added the kind/feature new feature, enhancement, improvement, extension label Mar 26, 2026
@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a two-step transfer mode to ocm transfer component-version via a new --transfer-spec flag that loads a persisted TransformationGraphDefinition (file or stdin). Command validation and graph construction now accept either a loaded spec or an args-built spec and execute a unified downstream flow.

Changes

Cohort / File(s) Summary
Command Implementation
cli/cmd/transfer/component-version/cmd.go
Adds --transfer-spec flag (file path or - for stdin); replaces fixed positional-arg validation with conditional logic; introduces loadTransferSpec(...) and buildGraphDefinitionFromArgs(...); unifies transformer build/check, dry-run rendering, progress tracking, and graph.Process execution from a single TransformationGraphDefinition.
Command Tests
cli/cmd/transfer/component-version/cmd_test.go
Adds in-memory CTF helpers and five CLI tests validating: end-to-end transfer via spec, dry-run/spec determinism, disallowed positional args with --transfer-spec, spec mutation retargeting, and missing-spec-file error handling.
Integration Tests
cli/integration/transfer_with_transfer_spec_test.go
Adds two integration tests that generate a dry-run spec and execute --transfer-spec transfers (CTF→OCI and mutated-target OCI), verifying presence/absence of component versions and resources in target registries.
Documentation
cli/docs/reference/ocm_transfer_component-version.md
Documents the --transfer-spec two-step workflow with examples, notes that positional reference/target and graph-building flags are ignored when using a spec, and updates option/help text formatting.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jakobmoellerdev
  • fabianburth

Poem

🐰
I hopped through specs both new and tried,
I nibbled YAML where changes hide.
Draft, edit, save — then set it free,
The transfer hops where it should be.
A tiny hop, a happy me.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a --transfer-spec flag to enable a two-step transfer workflow, which is the primary focus of all changeset modifications.
Description check ✅ Passed The description provides comprehensive context explaining the new --transfer-spec feature, its purpose, usage workflow, implementation details, and testing approach, all directly related to the changeset.
Linked Issues check ✅ Passed The PR successfully implements the core requirements: adds --transfer-spec flag for two-step workflow, implements file/stdin loading, includes argument validation, adds unit and integration tests, and updates documentation per issue #930 done criteria.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the --transfer-spec feature: CLI flag addition, argument validation refactoring, helper functions, comprehensive tests, and documentation updates align with issue #930 objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the size/l Large label Mar 26, 2026
@matthiasbruns matthiasbruns marked this pull request as draft March 26, 2026 12:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 separate path == "-" 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

📥 Commits

Reviewing files that changed from the base of the PR and between b084c3f and f10c256.

📒 Files selected for processing (3)
  • cli/cmd/transfer/component-version/cmd.go
  • cli/cmd/transfer/component-version/cmd_test.go
  • cli/integration/transfer_with_transfer_spec_test.go

Comment thread cli/cmd/transfer/component-version/cmd.go
@matthiasbruns

Copy link
Copy Markdown
Contributor Author

@coderabbitai review again please

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

@matthiasbruns Sure! I'll re-review the changes in PR #2077 right away.


🧠 Learnings used
Learnt from: morri-son
Repo: open-component-model/open-component-model PR: 1900
File: .github/workflows/kubernetes-controller.yml:431-436
Timestamp: 2026-03-05T18:57:29.740Z
Learning: In `.github/workflows/kubernetes-controller.yml` (open-component-model/open-component-model PR `#1900`), the reusable workflow `.github/workflows/publish-ocm-component-version.yml` is intentionally absent from this PR. It is being introduced in a separate PR (referenced as PR `#1901`). Do not flag the missing file as a blocking issue in reviews of PR `#1900`.
✅ Actions performed

Full review triggered.

@matthiasbruns matthiasbruns linked an issue Mar 26, 2026 that may be closed by this pull request
7 tasks

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
cli/cmd/transfer/component-version/cmd.go (1)

236-259: Consider validating the loaded spec has required content.

loadTransferSpec unmarshals YAML into TransformationGraphDefinition but doesn't validate the result. Based on context snippet 2 (builder.go:29-87), BuildAndCheck will eventually process the spec, but an empty Transformations slice or nil Environment won'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 BuildAndCheck with 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

📥 Commits

Reviewing files that changed from the base of the PR and between b084c3f and 5401344.

📒 Files selected for processing (4)
  • cli/cmd/transfer/component-version/cmd.go
  • cli/cmd/transfer/component-version/cmd_test.go
  • cli/docs/reference/ocm_transfer_component-version.md
  • cli/integration/transfer_with_transfer_spec_test.go

@matthiasbruns matthiasbruns marked this pull request as ready for review March 26, 2026 13:17
@matthiasbruns matthiasbruns marked this pull request as draft March 26, 2026 13:17
@matthiasbruns matthiasbruns force-pushed the feat/930_existing_transfer_spec branch from d0a9fce to 19d5b91 Compare March 26, 2026 13:52
@matthiasbruns matthiasbruns marked this pull request as ready for review March 26, 2026 14:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.ModePerm is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5401344 and cf614d4.

📒 Files selected for processing (2)
  • cli/cmd/transfer/component-version/cmd_test.go
  • cli/integration/transfer_with_transfer_spec_test.go

Comment thread cli/cmd/transfer/component-version/cmd_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>
@matthiasbruns matthiasbruns force-pushed the feat/930_existing_transfer_spec branch from cf614d4 to 7b956fb Compare March 26, 2026 16:26

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between cf614d4 and 7b956fb.

📒 Files selected for processing (4)
  • cli/cmd/transfer/component-version/cmd.go
  • cli/cmd/transfer/component-version/cmd_test.go
  • cli/docs/reference/ocm_transfer_component-version.md
  • cli/integration/transfer_with_transfer_spec_test.go
✅ Files skipped from review due to trivial changes (1)
  • cli/cmd/transfer/component-version/cmd_test.go

Comment thread cli/cmd/transfer/component-version/cmd.go Outdated
Comment thread cli/cmd/transfer/component-version/cmd.go
On-behalf-of: SAP <matthias.bruns@sap.com>
Signed-off-by: Matthias Bruns <git@matthiasbruns.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
cli/cmd/transfer/component-version/cmd.go (1)

184-196: ⚠️ Potential issue | 🟠 Major

renderTGD() still needs a hard error check before io.ReadAll().

The nil-guard on Close() fixed only half of the earlier issue. If renderTGD() returns (nil, rerr), Line 191 still reads from a nil reader, and any io.ReadAll() failure is still dropped, so the original BuildAndCheck() 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.Unmarshal accepts an empty file or stdin into TransformationGraphDefinition{}. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7b956fb and cf38fde.

📒 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
Comment thread cli/cmd/transfer/component-version/cmd.go Outdated

@jakobmoellerdev jakobmoellerdev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome job, just a nit

matthiasbruns and others added 2 commits March 27, 2026 08:07
On-behalf-of: SAP <matthias.bruns@sap.com>
Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
@matthiasbruns matthiasbruns force-pushed the feat/930_existing_transfer_spec branch from 1348e95 to b8b219f Compare March 27, 2026 08:05
@matthiasbruns matthiasbruns enabled auto-merge (squash) March 27, 2026 10:36
@matthiasbruns matthiasbruns merged commit c6de48f into open-component-model:main Mar 27, 2026
24 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Apr 15, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature new feature, enhancement, improvement, extension size/l Large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ocm transfer cv with existing transfer spec

3 participants