Skip to content

feat: version aliasing#2049

Merged
fabianburth merged 2 commits into
open-component-model:mainfrom
chrisbleyerSAP:main
Apr 10, 2026
Merged

feat: version aliasing#2049
fabianburth merged 2 commits into
open-component-model:mainfrom
chrisbleyerSAP:main

Conversation

@chrisbleyerSAP

@chrisbleyerSAP chrisbleyerSAP commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the ability to create aliases (additional tags) for existing component versions, similar to OCI tag behavior (e.g., tagging the same image as both v1.0.0 and latest).

Also see open-component-model/ocm-project#720.

Changes

CTF Module (bindings/go/ctf)

  • index/v1/index.go: Modified AddArtifact to allow multiple entries with the same digest but different tags (OCI Image Layout-like behavior)
  • index/v1/index_test.go: Added tests for multi-tag scenarios, cross-repository isolation, and encode/decode roundtrip persistence

OCI Module (bindings/go/oci)

  • interface.go: New AliasComponentVersionRepository interface with AddComponentVersionAlias method
  • repository.go: Implementation of AddComponentVersionAlias that tags existing manifests/indexes with new aliases
  • repository_test.go: tests covering aliasing, tag chaining, alias movement, and version immutability
  • ctf/store.go: Removed dead code (addOrUpdateArtifactMetadataInIndex was operating on a clone)
  • ctf/store_test.go: Updated test expectations for multi-tag behavior

Compatibility Considerations

CTF Index Format

  • Old CTF read by new code: Fully compatible
  • New CTF read by old code: Read operations work correctly
  • New CTF written by old code: Old AddArtifact would overwrite tags on the same digest (potential data loss if mixing library versions writing to the same CTF)

Key Design Decisions

  • Aliases are stored only in CTF's artifact-index.json / OCI registry tags
  • Manifest/index blobs are never modified - AnnotationVersion always contains the semantic version from the component descriptor
  • The aliasing feature only calls store.Tag(), ensuring content-addressable integrity

Merge Order

  1. Merge CTF first: The multi-tag support is self-contained and backwards compatible for reads. The OCI module requires this change, but CTF works independently.
  2. Merge OCI second: Depends on the new CTF behavior for the aliasing feature to work correctly.

@github-actions github-actions Bot added the kind/feature new feature, enhancement, improvement, extension label Mar 23, 2026
@coderabbitai

coderabbitai Bot commented Mar 23, 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 mutable OCI-style aliases for component versions, centralizes component-version descriptor validation in a new validate package, removes store-side retag reconciliation (now relying on index.AddArtifact), and adds repository implementation and tests for alias management and resolution.

Changes

Cohort / File(s) Summary
Store tag handling
bindings/go/oci/ctf/store.go, bindings/go/oci/ctf/store_test.go, bindings/go/ctf/index/v1/index.go
Removed pre-add tag-clearing; store.go now calls idx.AddArtifact(meta) directly. Test updated to expect multiple tags per digest. Index docs clarify AddArtifact does no GC and preserves cleared-tag entries.
Alias API
bindings/go/oci/interface.go
Added AliasComponentVersionRepository and embedded it into ComponentVersionRepository, exposing AddComponentVersionAlias(...).
Repository implementation & tests
bindings/go/oci/repository.go, bindings/go/oci/repository_test.go
Implemented Repository.AddComponentVersionAlias (semver-name rejection, resolve→validate→tag) and extensive end-to-end tests for alias creation, moving, chaining, idempotency, semver rejection, and index-backed versions.
Validation package
bindings/go/oci/internal/validate/validate.go, bindings/go/oci/internal/validate/validate_test.go
New validate package with ErrInvalidComponentVersion and ComponentVersionDescriptor(...) to fetch/decode manifests/indexes and extract/verify component-version annotations; includes unit tests and a mock fetcher.
Lister integration
bindings/go/oci/internal/lister/component/component_versions.go
Replaced inline manifest/index parsing with validate.ComponentVersionDescriptor call; invalid descriptors are soft-skipped by joining lister.ErrSkip.
Module bumps
bindings/go/oci/go.mod, bindings/go/oci/integration/go.mod
Bumped dependency ocm.software/open-component-model/bindings/go/ctf from v0.3.0v0.4.0.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Repository
    participant Store
    participant Validator

    Client->>Repository: AddComponentVersionAlias(component, versionOrAlias, alias)
    Repository->>Repository: reject if alias matches semverRegex
    Repository->>Store: getStore(versionOrAlias) / Resolve(ref)
    Store-->>Repository: descriptor (or ErrNotFound)
    Repository->>Validator: ComponentVersionDescriptor(ctx, Store, descriptor, component, ref)
    Validator->>Store: Fetch(descriptor)
    Store-->>Validator: payload reader
    Validator->>Validator: decode manifest/index, extract annotation, validate component
    Validator-->>Repository: parsed version (or ErrInvalidComponentVersion)
    Repository->>Store: Tag(descriptor, alias)
    Store-->>Repository: success
    Repository-->>Client: success / error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jakobmoellerdev
  • fabianburth
  • morri-son

Poem

🐰 A little rabbit hops with care and cheer,

Tags can gather now — no need to fear,
Validator sniffs the manifest true,
Repository ties the old and new,
Hooray — aliases hop from here to there! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'feat: version aliasing' is concise, clear, and directly describes the main feature being added—the ability to create aliases for component versions.
Description check ✅ Passed The pull request description is comprehensive and directly related to the changeset, providing summary, detailed changes across modules, compatibility considerations, design decisions, and merge order guidance.

✏️ 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/m Medium label Mar 23, 2026

@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.

This looks very promising already. just smaller implementation related comments, great job in concept!

Comment thread bindings/go/ctf/index/v1/index.go
Comment thread bindings/go/oci/repository.go Outdated
Comment thread bindings/go/oci/repository.go Outdated
Comment thread bindings/go/oci/repository.go Outdated
Comment thread bindings/go/oci/repository.go Outdated
@github-actions github-actions Bot added the size/l Large label Mar 27, 2026
@chrisbleyerSAP

Copy link
Copy Markdown
Contributor Author

--- Changes with latest commit ---

AddArtifact rework (ctf/index/v1/index.go)

The previous implementation always appended a new entry for same-digest-different-tag, causing untagged entries to accumulate unboundedly on retag cycles. Reworked to three explicit cases:

  1. Exact duplicate (repo+tag+digest) → skip
  2. Retag (same tag, different digest) → clear old tag
  3. Tag an existing untagged entry with matching digest → update in-place instead of appending
    This prevents index growth when tags move between digests.

AddComponentVersionAlias fix (oci/repository.go)

Previously fetched the manifest from the descriptor and, when the top-level was an OCI image index, tagged index.Manifests[0] (the inner manifest) instead of the index itself. This created structural inconsistency between the original version and the alias. Now tags base directly and delegates validation to validate.ComponentVersionDescriptor.

Extracted shared validation (oci/internal/lister/component/component_versions.go)

ReferenceTagVersionResolver duplicated the manifest/index fetching and annotation checking logic that AddComponentVersionAlias also needed. Replaced with a call to the shared validate.ComponentVersionDescriptor function.

Test coverage (repository_test.go, index_test.go)

Test What it covers
ListComponentVersions_WithAliases Aliases don't produce duplicate entries in version listings
OverwritesExistingVersion Documents that aliasing over a real version tag is destructive
GetLocalResource via alias Resource retrieval works through aliases on plain manifest path
OCIImageIndex Aliasing works when component versions are stored behind an OCI image index
Additional index_test.go cases AddArtifact rework: retag-from-untagged, multiple untagged artifacts

@jakobmoellerdev should we prevent the behavior document with the TestRepository_AddComponentVersionAlias_OverwritesExistingVersion test in repository_test.go?

Comment thread bindings/go/oci/interface.go Outdated
Comment thread bindings/go/oci/interface.go Outdated
Comment thread bindings/go/oci/repository.go Outdated
Comment thread bindings/go/oci/internal/validate/validate.go
jakobmoellerdev pushed a commit that referenced this pull request Apr 1, 2026
## Summary
This PR extracts and upstreams the CTF index changes from the broader
version aliasing feature
#2049.
It's part of this feature request
open-component-model/ocm-project#720.

### Changes
- **CTF index (`bindings/go/ctf/index/v1`)**: Reworked `AddArtifact` to
match OCI Image Layout semantics — multiple entries with the same digest
but different tags can now coexist. Previously, adding a new tag to an
existing digest would overwrite the old tag.
  - Exact duplicates (same repo + tag + digest) are skipped
  - Retagging (same tag, different digest) clears the old tag
  - Tagging an untagged entry updates it in place
- Added comprehensive tests covering multi-tag, deduplication,
cross-repo isolation, and encode/decode round-tripping
 
### Context
This is the foundational change for **version aliasing** (see draft PR:
#2049,
which allows multiple OCI tags to reference the same component version.
The remaining OCI repository, CTF store, and validation changes will
follow in a separate PR once this lands and a new version of the CTF
module is released.

Signed-off-by: Christoph Bleyer <christoph.bleyer@sap.com>
@chrisbleyerSAP chrisbleyerSAP marked this pull request as ready for review April 2, 2026 07:24
@chrisbleyerSAP chrisbleyerSAP requested a review from a team as a code owner April 2, 2026 07:24

@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: 4

🧹 Nitpick comments (1)
bindings/go/oci/repository_test.go (1)

1601-1614: Add one more not-found case for missing target version on an existing component.

Right now only the missing-component path is covered; adding missing-version coverage would better pin expected ErrNotFound behavior.

➕ Suggested test extension
 func TestRepository_AddComponentVersionAlias_NonExistent(t *testing.T) {
 	r := require.New(t)
 	ctx := t.Context()

 	fs, err := filesystem.NewFS(t.TempDir(), os.O_RDWR)
 	r.NoError(err)
 	store := ocictf.NewFromCTF(ctf.NewFileSystemCTF(fs))
 	repo := Repository(t, ocictf.WithCTF(store))

 	// Must fail for non-existent component
 	err = repo.AddComponentVersionAlias(ctx, "non-existent", "1.0.0", "latest")
 	r.Error(err)
 	r.ErrorIs(err, repository.ErrNotFound)
+
+	// Must also fail for non-existent version in an existing component
+	componentName := "ocm.software/test-component"
+	r.NoError(repo.AddComponentVersion(ctx, &descriptor.Descriptor{
+		Meta: descriptor.Meta{Version: "v2"},
+		Component: descriptor.Component{
+			Provider: descriptor.Provider{Name: "test-provider"},
+			ComponentMeta: descriptor.ComponentMeta{
+				ObjectMeta: descriptor.ObjectMeta{Name: componentName, Version: "1.0.0"},
+			},
+		},
+	}))
+	err = repo.AddComponentVersionAlias(ctx, componentName, "9.9.9", "latest")
+	r.Error(err)
+	r.ErrorIs(err, repository.ErrNotFound)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindings/go/oci/repository_test.go` around lines 1601 - 1614, Extend
TestRepository_AddComponentVersionAlias_NonExistent to also assert ErrNotFound
when the component exists but the target version does not: create the component
first (e.g., via Repository helper plus repo.AddComponentVersion or the existing
API used elsewhere in this test file to add a version), then call
repo.AddComponentVersionAlias(ctx, "<existing-component>",
"<non-existent-version>", "latest") and assert r.Error(err) and r.ErrorIs(err,
repository.ErrNotFound); keep the original missing-component assertion intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bindings/go/oci/interface.go`:
- Around line 19-23: The ComponentVersionRepository interface currently embeds
AliasComponentVersionRepository which forces all downstream implementations to
implement aliasing; remove AliasComponentVersionRepository from the embedded
list in the ComponentVersionRepository declaration so aliasing remains opt-in
(keep embedding of repository.ComponentVersionRepository,
repository.HealthCheckable and ResourceDigestProcessor intact), update any local
implementations/mocks in this package that assumed the wider shape, and if
callers need alias behavior they should type-assert to
AliasComponentVersionRepository where required; optionally add a short comment
above ComponentVersionRepository explaining aliasing is intentionally opt-in via
type assertion.

In `@bindings/go/oci/internal/lister/component/component_versions.go`:
- Around line 76-80: The tag-based lookup currently emits duplicate canonical
versions because alias tags resolve to the same canonical version (via
validate.ComponentVersionDescriptor in component_versions.go) but
ListComponentVersions (the resolver that iterates tags in lister.go) appends
every tag result without deduplication; update the ListComponentVersions flow to
track canonical version identifiers (use the canonical value returned by
validate.ComponentVersionDescriptor — e.g., the resolved version
string/descriptor) and skip adding a result if that canonical ID was already
emitted (apply this when LookupPolicyTagOnly or tag-fallback logic is used),
ensuring only unique canonical versions are returned.

In `@bindings/go/oci/internal/validate/validate.go`:
- Around line 90-93: The parse error from
annotations.ParseComponentVersionAnnotation should be wrapped with the sentinel
validate.ErrInvalidComponentVersion so ListComponentVersions can treat it as
non-fatal; replace the current return that wraps the original error with one
that wraps validate.ErrInvalidComponentVersion (e.g. using fmt.Errorf with %w)
while preserving the underlying parse error message, updating the error return
in the block where candidate, version, err :=
annotations.ParseComponentVersionAnnotation(annotation) is handled.

In `@bindings/go/oci/repository.go`:
- Around line 869-894: The current flow (involving versionRegex, repo.getStore,
store.Resolve, validate.ComponentVersionDescriptor, and store.Tag) allows
creating an alias that would overwrite an existing declared version name; fix
this by checking whether the requested alias already resolves to an existing
component version before calling store.Tag: call store.Resolve(ctx,
aliasReference) (or Resolve with alias as reference) and if it returns a valid
base (i.e., not errdef.ErrNotFound) compare that resolved base/reference to the
base you intend to tag; if they differ, return a descriptive error rejecting the
alias creation (do not call store.Tag); if Resolve returns not found, proceed to
validate.ComponentVersionDescriptor and then store.Tag as before. Ensure you use
the same error wrapping style as other errors.

---

Nitpick comments:
In `@bindings/go/oci/repository_test.go`:
- Around line 1601-1614: Extend
TestRepository_AddComponentVersionAlias_NonExistent to also assert ErrNotFound
when the component exists but the target version does not: create the component
first (e.g., via Repository helper plus repo.AddComponentVersion or the existing
API used elsewhere in this test file to add a version), then call
repo.AddComponentVersionAlias(ctx, "<existing-component>",
"<non-existent-version>", "latest") and assert r.Error(err) and r.ErrorIs(err,
repository.ErrNotFound); keep the original missing-component assertion intact.
🪄 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: c56a4c71-e411-4312-bb94-2aed6e59d830

📥 Commits

Reviewing files that changed from the base of the PR and between e120070 and 3b05592.

⛔ Files ignored due to path filters (1)
  • bindings/go/oci/go.sum is excluded by !**/*.sum
📒 Files selected for processing (9)
  • bindings/go/oci/ctf/store.go
  • bindings/go/oci/ctf/store_test.go
  • bindings/go/oci/go.mod
  • bindings/go/oci/interface.go
  • bindings/go/oci/internal/lister/component/component_versions.go
  • bindings/go/oci/internal/validate/validate.go
  • bindings/go/oci/internal/validate/validate_test.go
  • bindings/go/oci/repository.go
  • bindings/go/oci/repository_test.go

Comment thread bindings/go/oci/interface.go
Comment thread bindings/go/oci/internal/lister/component/component_versions.go
Comment thread bindings/go/oci/internal/validate/validate.go
Comment thread bindings/go/oci/repository.go
@jakobmoellerdev

Copy link
Copy Markdown
Member

@jakobmoellerdev should we prevent the behavior document with the TestRepository_AddComponentVersionAlias_OverwritesExistingVersion test in repository_test.go?

I think this requires some thinking. If we have a CTF with a component version 1.0.0 and digest abc, we are relying on this behavior right now so we dont keep abc in the index when there is a new version def. In OCI we have garbage collection for this.

I believe we need to drop the old manifest in case the tag moves from one digest to another. WDYT?

frewilhelm added a commit that referenced this pull request Apr 8, 2026
<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it


e2b46f9
introduced a change in
`ocm.software/open-component-model/bindings/go/ctf v0.4.0` that [breaks
tests](https://github.com/open-component-model/open-component-model/actions/runs/24078003573/job/70231212198?pr=2152)
in `bindings/go/oci/ctf`. A
[fix](#2049)
is already in progress but it includes a bigger implementation that is
not ready to merge yet. This is why, this PR reverts the version bump
for that module for now.

#### Which issue(s) this PR fixes

Fixes #2152 

#### Testing

Run `task test` and `task test/integration`

---------

Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
Co-authored-by: ocmbot[bot] <125909804+ocmbot[bot]@users.noreply.github.com>
morri-son pushed a commit that referenced this pull request Apr 9, 2026
…no ctf) (#2194)

<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it

#2192 wants to bump. `bindings/go/ctf` again. However, this bump breaks
other modules until the respective PR (#2049) is merged. That is why
this PR reverts the version bump for this module. We might need to do
this until the other PR is merged.

Fixes #2192

---------

Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
Co-authored-by: ocmbot[bot] <125909804+ocmbot[bot]@users.noreply.github.com>
@chrisbleyerSAP

chrisbleyerSAP commented Apr 9, 2026

Copy link
Copy Markdown
Contributor Author

@jakobmoellerdev should we prevent the behavior document with the TestRepository_AddComponentVersionAlias_OverwritesExistingVersion test in repository_test.go?

I think this requires some thinking. If we have a CTF with a component version 1.0.0 and digest abc, we are relying on this behavior right now so we dont keep abc in the index when there is a new version def. In OCI we have garbage collection for this.

I believe we need to drop the old manifest in case the tag moves between digests. WDYT?

@jakobmoellerdev Completely valid point! That would create a lot of orphaned entries over time as tags move through different digests... Especially a latest tag being retagged frequently will create lots of untagged entries, causing index bloat...

Since this needs to be fixed on the ctf layer I have adressed this in a seperate PR: #2230. Let me know what you think about this there.

@netlify

netlify Bot commented Apr 9, 2026

Copy link
Copy Markdown

Deploy Preview for ocm-website ready!

Name Link
🔨 Latest commit b846226
🔍 Latest deploy log https://app.netlify.com/projects/ocm-website/deploys/69d8d507ff57980008ee6709
😎 Deploy Preview https://deploy-preview-2049--ocm-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bindings/go/oci/ctf/store.go (1)

296-342: ⚠️ Potential issue | 🟠 Major

Reject unparseable references before writing index metadata.

If looseref.ParseReference(reference) fails, meta stays zero-valued and this still appends it to the index via idx.AddArtifact(meta). With the new alias API, malformed user input can now hit this path and silently persist an empty artifact record instead of returning an error.

Suggested fix
-	if ref, err := looseref.ParseReference(reference); err == nil {
-		if err := ref.ValidateReferenceAsTag(); err == nil {
+	ref, err := looseref.ParseReference(reference)
+	if err != nil {
+		return fmt.Errorf("invalid reference %q: %w", reference, err)
+	}
+	if err := ref.ValidateReferenceAsTag(); err == nil {
 			meta = v1.ArtifactMetadata{
 				Repository: repo,
 				Tag:        ref.Tag,
 				Digest:     desc.Digest.String(),
 				MediaType:  desc.MediaType,
 			}
-		} else if err := ref.ValidateReferenceAsDigest(); err == nil {
+	} else if err := ref.ValidateReferenceAsDigest(); err == nil {
 			meta = v1.ArtifactMetadata{
 				Repository: repo,
 				Digest:     desc.Digest.String(),
 				MediaType:  desc.MediaType,
 			}
-		} else {
+	} else {
 			ref := registry.Reference{Reference: reference}
 			if err := ref.ValidateReferenceAsTag(); err == nil {
 				meta = v1.ArtifactMetadata{
 					Repository: repo,
 					Tag:        reference,
 					Digest:     desc.Digest.String(),
 					MediaType:  desc.MediaType,
 				}
 			} else if err := ref.ValidateReferenceAsDigest(); err == nil {
 				meta = v1.ArtifactMetadata{
 					Repository: repo,
 					Digest:     desc.Digest.String(),
 					MediaType:  desc.MediaType,
 				}
 			} else {
 				return fmt.Errorf("invalid raw reference %q: %w", reference, err)
 			}
-		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindings/go/oci/ctf/store.go` around lines 296 - 342, The code currently lets
an unparseable reference fall through and write a zero-valued meta via
idx.AddArtifact(meta); change the control flow around
looseref.ParseReference(reference) so that if ParseReference returns an error
you reject the reference immediately (return an error) instead of continuing;
keep the existing tag/digest checks
(ref.ValidateReferenceAsTag/ValidateReferenceAsDigest) for the parsed ref and
the fallback registry.Reference branch, but if both parsing and any validation
attempts fail return a descriptive error (wrap the original parse/validation
error with fmt.Errorf) before reaching idx.AddArtifact(meta).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bindings/go/oci/internal/validate/validate.go`:
- Around line 81-84: The current branch in validate.go returns the lookup ref
(ref) for legacy manifests when annotations.OCMComponentVersion is missing,
which lets ReferenceTagVersionResolver treat that lookup ref as a canonical
version and causes ListComponentVersions to surface aliases; change this
behavior so that when oldOCMComponentVersion is true and the manifestAnnotations
lookup for annotations.OCMComponentVersion fails, do NOT return ref as the
canonical version—either reject the aliasing by returning an explicit error (so
ReferenceTagVersionResolver/ReferenceTagVersionResolver callers cannot record a
spurious version) or return a distinct sentinel (e.g., empty version) that
upstream logic will not treat as a real version; modify the branch handling
manifestAnnotations[annotations.OCMComponentVersion] (the block that currently
returns ref, nil) to implement that rejection/neutral result and update any
callers (ReferenceTagVersionResolver, ListComponentVersions) to handle the new
error/sentinel accordingly.

---

Outside diff comments:
In `@bindings/go/oci/ctf/store.go`:
- Around line 296-342: The code currently lets an unparseable reference fall
through and write a zero-valued meta via idx.AddArtifact(meta); change the
control flow around looseref.ParseReference(reference) so that if ParseReference
returns an error you reject the reference immediately (return an error) instead
of continuing; keep the existing tag/digest checks
(ref.ValidateReferenceAsTag/ValidateReferenceAsDigest) for the parsed ref and
the fallback registry.Reference branch, but if both parsing and any validation
attempts fail return a descriptive error (wrap the original parse/validation
error with fmt.Errorf) before reaching idx.AddArtifact(meta).
🪄 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: f2fb051a-2d15-4631-80a4-d94f089e2d4b

📥 Commits

Reviewing files that changed from the base of the PR and between 9a51ab8 and 25b0faa.

⛔ Files ignored due to path filters (2)
  • bindings/go/oci/go.sum is excluded by !**/*.sum
  • bindings/go/oci/integration/go.sum is excluded by !**/*.sum
📒 Files selected for processing (9)
  • bindings/go/ctf/index/v1/index.go
  • bindings/go/oci/ctf/store.go
  • bindings/go/oci/ctf/store_test.go
  • bindings/go/oci/interface.go
  • bindings/go/oci/internal/lister/component/component_versions.go
  • bindings/go/oci/internal/validate/validate.go
  • bindings/go/oci/internal/validate/validate_test.go
  • bindings/go/oci/repository.go
  • bindings/go/oci/repository_test.go
✅ Files skipped from review due to trivial changes (1)
  • bindings/go/oci/ctf/store_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • bindings/go/ctf/index/v1/index.go

Comment thread bindings/go/oci/internal/validate/validate.go
@chrisbleyerSAP chrisbleyerSAP force-pushed the main branch 4 times, most recently from a3b687a to b826585 Compare April 10, 2026 09:37
Signed-off-by: Christoph Bleyer <christoph.bleyer@sap.com>
@fabianburth fabianburth merged commit a98538c into open-component-model:main Apr 10, 2026
26 checks passed
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 14, 2026
## Summary
This PR extracts and upstreams the CTF index changes from the broader
version aliasing feature
open-component-model#2049.
It's part of this feature request
open-component-model/ocm-project#720.

### Changes
- **CTF index (`bindings/go/ctf/index/v1`)**: Reworked `AddArtifact` to
match OCI Image Layout semantics — multiple entries with the same digest
but different tags can now coexist. Previously, adding a new tag to an
existing digest would overwrite the old tag.
  - Exact duplicates (same repo + tag + digest) are skipped
  - Retagging (same tag, different digest) clears the old tag
  - Tagging an untagged entry updates it in place
- Added comprehensive tests covering multi-tag, deduplication,
cross-repo isolation, and encode/decode round-tripping
 
### Context
This is the foundational change for **version aliasing** (see draft PR:
open-component-model#2049,
which allows multiple OCI tags to reference the same component version.
The remaining OCI repository, CTF store, and validation changes will
follow in a separate PR once this lands and a new version of the CTF
module is released.

Signed-off-by: Christoph Bleyer <christoph.bleyer@sap.com>

Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 14, 2026
…component-model#2174)

<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it


open-component-model@e2b46f9
introduced a change in
`ocm.software/open-component-model/bindings/go/ctf v0.4.0` that [breaks
tests](https://github.com/open-component-model/open-component-model/actions/runs/24078003573/job/70231212198?pr=2152)
in `bindings/go/oci/ctf`. A
[fix](open-component-model#2049)
is already in progress but it includes a bigger implementation that is
not ready to merge yet. This is why, this PR reverts the version bump
for that module for now.

#### Which issue(s) this PR fixes

Fixes open-component-model#2152 

#### Testing

Run `task test` and `task test/integration`

---------

Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
Co-authored-by: ocmbot[bot] <125909804+ocmbot[bot]@users.noreply.github.com>

Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 14, 2026
…dule dependencies) (no ctf) (open-component-model#2194)

<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it

open-component-model#2192 wants to bump. `bindings/go/ctf` again. However, this bump breaks
other modules until the respective PR (open-component-model#2049) is merged. That is why
this PR reverts the version bump for this module. We might need to do
this until the other PR is merged.

Fixes open-component-model#2192

---------

Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
Co-authored-by: ocmbot[bot] <125909804+ocmbot[bot]@users.noreply.github.com>

Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 14, 2026
###   Summary

Adds the ability to create aliases (additional tags) for existing
component versions, similar to OCI tag behavior (e.g., tagging the same
image as both v1.0.0 and latest).
  
Also see open-component-model/ocm-project#720.

 ###  Changes

  CTF Module (bindings/go/ctf)

- index/v1/index.go: Modified AddArtifact to allow multiple entries with
the same digest but different tags (OCI Image Layout-like behavior)
- index/v1/index_test.go: Added tests for multi-tag scenarios,
cross-repository isolation, and encode/decode roundtrip persistence

  OCI Module (bindings/go/oci)

- interface.go: New AliasComponentVersionRepository interface with
AddComponentVersionAlias method
- repository.go: Implementation of AddComponentVersionAlias that tags
existing manifests/indexes with new aliases
- repository_test.go: tests covering aliasing, tag chaining, alias
movement, and version immutability
- ctf/store.go: Removed dead code (addOrUpdateArtifactMetadataInIndex
was operating on a clone)
  - ctf/store_test.go: Updated test expectations for multi-tag behavior
 

  Compatibility Considerations

  CTF Index Format

  - Old CTF read by new code: Fully compatible
  - New CTF read by old code: Read operations work correctly
- New CTF written by old code: Old AddArtifact would overwrite tags on
the same digest (potential data loss if mixing library versions writing
to the same CTF)

  Key Design Decisions

- Aliases are stored only in CTF's artifact-index.json / OCI registry
tags
- Manifest/index blobs are never modified - AnnotationVersion always
contains the semantic version from the component descriptor
- The aliasing feature only calls store.Tag(), ensuring
content-addressable integrity

  Merge Order

1. Merge CTF first: The multi-tag support is self-contained and
backwards compatible for reads. The OCI module requires this change, but
CTF works independently.
2. Merge OCI second: Depends on the new CTF behavior for the aliasing
feature to work correctly.

Signed-off-by: Christoph Bleyer <christoph.bleyer@sap.com>

Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 15, 2026
## Summary
This PR extracts and upstreams the CTF index changes from the broader
version aliasing feature
open-component-model#2049.
It's part of this feature request
open-component-model/ocm-project#720.

### Changes
- **CTF index (`bindings/go/ctf/index/v1`)**: Reworked `AddArtifact` to
match OCI Image Layout semantics — multiple entries with the same digest
but different tags can now coexist. Previously, adding a new tag to an
existing digest would overwrite the old tag.
  - Exact duplicates (same repo + tag + digest) are skipped
  - Retagging (same tag, different digest) clears the old tag
  - Tagging an untagged entry updates it in place
- Added comprehensive tests covering multi-tag, deduplication,
cross-repo isolation, and encode/decode round-tripping

### Context
This is the foundational change for **version aliasing** (see draft PR:
open-component-model#2049,
which allows multiple OCI tags to reference the same component version.
The remaining OCI repository, CTF store, and validation changes will
follow in a separate PR once this lands and a new version of the CTF
module is released.

Signed-off-by: Christoph Bleyer <christoph.bleyer@sap.com>
Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 15, 2026
…component-model#2174)

<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it

open-component-model@e2b46f9
introduced a change in
`ocm.software/open-component-model/bindings/go/ctf v0.4.0` that [breaks
tests](https://github.com/open-component-model/open-component-model/actions/runs/24078003573/job/70231212198?pr=2152)
in `bindings/go/oci/ctf`. A
[fix](open-component-model#2049)
is already in progress but it includes a bigger implementation that is
not ready to merge yet. This is why, this PR reverts the version bump
for that module for now.

#### Which issue(s) this PR fixes

Fixes open-component-model#2152

#### Testing

Run `task test` and `task test/integration`

---------

Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
Co-authored-by: ocmbot[bot] <125909804+ocmbot[bot]@users.noreply.github.com>
Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 15, 2026
…dule dependencies) (no ctf) (open-component-model#2194)

<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it

open-component-model#2192 wants to bump. `bindings/go/ctf` again. However, this bump breaks
other modules until the respective PR (open-component-model#2049) is merged. That is why
this PR reverts the version bump for this module. We might need to do
this until the other PR is merged.

Fixes open-component-model#2192

---------

Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
Co-authored-by: ocmbot[bot] <125909804+ocmbot[bot]@users.noreply.github.com>
Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Apr 15, 2026
###   Summary

Adds the ability to create aliases (additional tags) for existing
component versions, similar to OCI tag behavior (e.g., tagging the same
image as both v1.0.0 and latest).

Also see open-component-model/ocm-project#720.

 ###  Changes

  CTF Module (bindings/go/ctf)

- index/v1/index.go: Modified AddArtifact to allow multiple entries with
the same digest but different tags (OCI Image Layout-like behavior)
- index/v1/index_test.go: Added tests for multi-tag scenarios,
cross-repository isolation, and encode/decode roundtrip persistence

  OCI Module (bindings/go/oci)

- interface.go: New AliasComponentVersionRepository interface with
AddComponentVersionAlias method
- repository.go: Implementation of AddComponentVersionAlias that tags
existing manifests/indexes with new aliases
- repository_test.go: tests covering aliasing, tag chaining, alias
movement, and version immutability
- ctf/store.go: Removed dead code (addOrUpdateArtifactMetadataInIndex
was operating on a clone)
  - ctf/store_test.go: Updated test expectations for multi-tag behavior

  Compatibility Considerations

  CTF Index Format

  - Old CTF read by new code: Fully compatible
  - New CTF read by old code: Read operations work correctly
- New CTF written by old code: Old AddArtifact would overwrite tags on
the same digest (potential data loss if mixing library versions writing
to the same CTF)

  Key Design Decisions

- Aliases are stored only in CTF's artifact-index.json / OCI registry
tags
- Manifest/index blobs are never modified - AnnotationVersion always
contains the semantic version from the component descriptor
- The aliasing feature only calls store.Tag(), ensuring
content-addressable integrity

  Merge Order

1. Merge CTF first: The multi-tag support is self-contained and
backwards compatible for reads. The OCI module requires this change, but
CTF works independently.
2. Merge OCI second: Depends on the new CTF behavior for the aliasing
feature to work correctly.

Signed-off-by: Christoph Bleyer <christoph.bleyer@sap.com>
Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
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.

3 participants