Skip to content

feat(xpkg): add --annotation flag to xpkg build and xpkg push#11

Open
chaitanyapantheor wants to merge 7 commits into
crossplane:mainfrom
chaitanyapantheor:issues/7282
Open

feat(xpkg): add --annotation flag to xpkg build and xpkg push#11
chaitanyapantheor wants to merge 7 commits into
crossplane:mainfrom
chaitanyapantheor:issues/7282

Conversation

@chaitanyapantheor

Copy link
Copy Markdown

Description of your changes

Adds a repeatable --annotation/-a KEY=VALUE flag to crossplane xpkg build
and crossplane xpkg push, allowing users to attach OCI manifest annotations
to packages at build or push time.

# Build with a static annotation
crossplane xpkg build -a org.opencontainers.image.source=https://github.com/org/repo

# Push with a dynamic (CI-injected) annotation
crossplane xpkg push -f pkg.xpkg \
  -a org.opencontainers.image.revision=$(git rev-parse HEAD) \
  xpkg.example.io/org/pkg:v1.0.0

Annotations are applied to the OCI image manifest. Malformed annotations
(missing =) return an error before any write or push occurs. Reading
annotations from crossplane.yaml is intentionally out of scope to avoid
silently propagating internal Crossplane annotations to the OCI registry.

Covered by unit tests in cmd/crossplane/xpkg/annotations_test.go.

Fixes crossplane/crossplane#7282

I have:

  • Read and followed Crossplane's [contribution process].
  • Run ./nix.sh flake check to ensure this PR is ready for review.
  • Added or updated unit tests.
  • Linked a PR or a [docs tracking issue] to [document this change].
  • Added backport release-x.y labels to auto-backport this PR.

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown

Review Change Stack

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

Parses repeatable --annotation/-a CLI entries as KEY=VALUE into a map, and applies non-empty annotations to OCI image and index manifests during xpkg push flows; includes parsing tests and small CLI/help and retry-callsite edits.

Changes

OCI Annotation CLI Support

Layer / File(s) Summary
Annotation parsing and image/index application helpers
cmd/crossplane/xpkg/annotations.go, cmd/crossplane/xpkg/annotations_test.go
parseAnnotations converts []string KEY=VALUE entries to map[string]string with validation; annotateImage and annotateIndex apply non-empty annotations using mutate.Annotations. Tests cover empty input, multiple entries, values containing =, and missing delimiters.
Push command annotation support
cmd/crossplane/xpkg/push.go
Adds --annotation/-a flag parsing in pushCmd.Run, introduces errParseAnnotations, threads parsed annotations map[string]string into pushImages, applies annotateImage after layer annotation, and uses annotateIndex when writing multi-image indexes.
Build CLI metadata edits
cmd/crossplane/xpkg/build.go
Revises user-facing help/placeholder metadata for build-related flags (no functional annotation flag added in this diff).
Batch retry wiring
cmd/crossplane/xpkg/batch.go
Updates the retry loop call to pushImages to include the new annotations parameter (passed as nil at this site).

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Parser as parseAnnotations
  participant PushCmd as pushCmd.Run
  participant MutateImage as annotateImage
  participant MutateIndex as annotateIndex
  participant Remote as remote.WriteIndex

  CLI->>Parser: provide KEY=VALUE flags
  Parser->>PushCmd: map[string]string annotations
  PushCmd->>MutateImage: annotateImage(img, annotations)
  PushCmd->>MutateIndex: annotateIndex(index, annotations)
  MutateIndex->>Remote: remote.WriteIndex(annotated index)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Thanks — do you want a unit test added for annotateImage/annotateIndex behavior beyond parse tests?


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Breaking Changes ❌ Error Evidence: cmd/crossplane/xr/patch.go was deleted and the Patch subcommand field was removed from cmd/crossplane/xr/xr.go; PR page shows Labels: “None yet” (no breaking-change label). Add the breaking-change label and document the removed crossplane xr patch behavior, or revert the CLI subcommand removal.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a repeatable annotation flag to xpkg build and push commands.
Description check ✅ Passed The description is well-written, directly related to the changeset, and includes helpful examples of how the new annotation feature works.
Linked Issues check ✅ Passed The code changes fully implement the requested feature from issue #7282: adding a --annotation/-a CLI flag to both xpkg build and xpkg push commands with proper validation and OCI manifest integration.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to implementing the annotation feature: helper functions for parsing and applying annotations, updated CLI flags, and integration into push/build workflows.
Feature Gate Requirement ✅ Passed PR adds OCI manifest annotations via new --annotation flag, but xpkg build/push commands aren’t marked experimental (no maturity/feature-gate tags) and apis/** aren’t touched.

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.

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

I've left a couple of thoughts, but overall this looks good to me and seems like a valuable feature.

Comment thread cmd/crossplane/xpkg/push.go Outdated
Package string `arg:"" help:"Where to push the package. Must be a fully qualified OCI tag, including the registry, repository, and tag." placeholder:"REGISTRY/REPOSITORY:TAG"`

// Flags. Keep sorted alphabetically.
Annotation []string `help:"An OCI manifest annotation to add to the package in key=value format. Repeatable." placeholder:"KEY=VALUE" short:"a"`

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.

I wonder if this should be OCIAnnotation / --oci-annotation or something like that to make it clear how it's different from the metadata.annotations.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point — renamed to --oci-annotation (Go field OCIAnnotation, explicit name:"oci-annotation" kong tag) in both xpkg build and xpkg push. Short flag -a is unchanged.

Comment thread cmd/crossplane/xpkg/push.go
Comment thread cmd/crossplane/xpkg/annotations.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
cmd/crossplane/xpkg/annotations.go (1)

30-40: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Consider validating that annotation keys are non-empty.

The current implementation would accept an annotation like =value (empty key), which could cause confusing errors later when pushing to the registry. Adding a simple validation would provide users with a clearer, earlier error message.

🛡️ Suggested validation
 func parseAnnotations(kvs []string) (map[string]string, error) {
 	anns := make(map[string]string, len(kvs))
 	for _, kv := range kvs {
 		k, v, ok := strings.Cut(kv, "=")
 		if !ok {
 			return nil, errors.Errorf("invalid annotation %q: must be in key=value format", kv)
 		}
+		if k == "" {
+			return nil, errors.Errorf("invalid annotation %q: key cannot be empty", kv)
+		}
 		anns[k] = v
 	}
 	return anns, nil
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/crossplane/xpkg/annotations.go` around lines 30 - 40, The
parseAnnotations function accepts "k=v" pairs but doesn't reject empty keys
(e.g., "=value"); update parseAnnotations to validate that the extracted key
(variable k from strings.Cut on each kv) is non-empty and, if empty, return a
clear formatted error (similar to the existing errors.Errorf) indicating the
annotation has an empty key; ensure this check happens after the strings.Cut ok
check and before assigning into anns so no empty-key entries are inserted.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cmd/crossplane/xpkg/annotations.go`:
- Around line 30-40: The parseAnnotations function accepts "k=v" pairs but
doesn't reject empty keys (e.g., "=value"); update parseAnnotations to validate
that the extracted key (variable k from strings.Cut on each kv) is non-empty
and, if empty, return a clear formatted error (similar to the existing
errors.Errorf) indicating the annotation has an empty key; ensure this check
happens after the strings.Cut ok check and before assigning into anns so no
empty-key entries are inserted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0477f7aa-6ce3-4b3c-953a-d9cd84c3a305

📥 Commits

Reviewing files that changed from the base of the PR and between a8a515b and c9e7a70.

📒 Files selected for processing (4)
  • cmd/crossplane/xpkg/annotations.go
  • cmd/crossplane/xpkg/annotations_test.go
  • cmd/crossplane/xpkg/build.go
  • cmd/crossplane/xpkg/push.go
✅ Files skipped from review due to trivial changes (1)
  • cmd/crossplane/xpkg/annotations_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • cmd/crossplane/xpkg/build.go
  • cmd/crossplane/xpkg/push.go

@chaitanyapantheor chaitanyapantheor force-pushed the issues/7282 branch 2 times, most recently from 75da5d2 to f40a61f Compare June 7, 2026 08:03

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

Thanks for the updates! Looks good to me, aside from the comment I've left on build - sorry for not calling it out earlier.

Comment thread cmd/crossplane/xpkg/build.go Outdated
Ignore []string `help:"comma-separated list of globs specifying files to exclude from the build, relative to --package-root." placeholder:"PATH"`
PackageFile string `help:"The file to write the package to. Defaults to a generated filename in --package-root." placeholder:"PATH" predictor:"xpkg_file" short:"o" type:"path"`
PackageRoot string `default:"." help:"The directory that contains the package's crossplane.yaml file." predictor:"directory" short:"f" type:"existingdir"`
OCIAnnotation []string `help:"An OCI manifest annotation to add to the package in key=value format. Repeatable." name:"oci-annotation" placeholder:"KEY=VALUE" short:"a"`

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.

As far as I can tell, the image annotations actually get persisted anywhere by crossplane xpkg build (they're not represented anywhere in the tarball written by tarball.Write). Assuming that's true, we should remove the flag here and keep it on push only; otherwise, a user could reasonably expect that they don't need to provide annotations to push if they've already provided them to build, which isn't true.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch — you're right that annotations applied during build aren't stored in the tarball, so the flag was giving a false impression. I've removed --oci-annotation from xpkg build entirely; it now lives only on xpkg push where it can actually take effect.

chaitanyapantheor and others added 7 commits June 13, 2026 21:35
Signed-off-by: Chaitanya Maili <chaitanya.maili@pantheon.io>
Signed-off-by: Chaitanya Maili <chaitanya.maili@pantheon.io>
Signed-off-by: Chaitanya Maili <chaitanya.maili@pantheon.io>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Chaitanya Maili <chaitanya.maili@pantheon.io>
- Rename --annotation to --oci-annotation in xpkg build and push to
  distinguish from Kubernetes metadata.annotations
- Apply OCI annotations to the image index in the multi-platform push
  path, not only to individual manifests
- Fix copyright year in annotations.go and annotations_test.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Chaitanya Maili <chaitanya.maili@pantheon.io>
Annotations like "=value" passed strings.Cut with ok=true, silently
inserting an empty string key into the map. Add an explicit check and
return a clear error when the key is empty.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Chaitanya Maili <chaitanya.maili@pantheon.io>
Annotations applied during build are not persisted in the tarball written
by tarball.Write, so keeping the flag on build could mislead users into
thinking they don't need to provide annotations to push. Move the flag
and errParseAnnotations to push only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Chaitanya Maili <chaitanya.maili@pantheon.io>

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

LGTM, thanks for the contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setting OCI Annotations on artifacts produced from crossplane xkpg build/push commands

2 participants