Skip to content

feat: add resource crd subcommand#26

Open
fernandezcuesta wants to merge 1 commit into
crossplane:mainfrom
fernandezcuesta:feat/add-crd-download
Open

feat: add resource crd subcommand#26
fernandezcuesta wants to merge 1 commit into
crossplane:mainfrom
fernandezcuesta:feat/add-crd-download

Conversation

@fernandezcuesta
Copy link
Copy Markdown

@fernandezcuesta fernandezcuesta commented May 22, 2026

Description of your changes

Add a crossplane xpkg crd subcommand to retrieve the CRD (and optionally, JSON schemas) of all dependencies (providers, functions).

Fixes #15

I have:

Happy to do a PR for the docs, but I see the docs are not update with the last CLI changes?

  • [ ] Added backport release-x.y labels to auto-backport this PR.

Need help with this checklist? See the cheat sheet.

Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com>
@fernandezcuesta fernandezcuesta marked this pull request as ready for review May 22, 2026 07:29
@fernandezcuesta fernandezcuesta requested review from a team, jcogilvie and tampakrap as code owners May 22, 2026 07:29
@fernandezcuesta fernandezcuesta requested review from bobh66 and removed request for a team May 22, 2026 07:29
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a new xpkg crd subcommand that exports CustomResourceDefinitions from package dependencies. The command loads extensions, uses the validate.Manager to resolve schemas, and writes output either as YAML files or as draft-07 JSON Schema documents converted from OpenAPI specifications, with full test coverage and integrated into the xpkg command structure.

Changes

CRD Export Subcommand

Layer / File(s) Summary
Manager CRDs accessor
cmd/crossplane/validate/manager.go
Exposes collected CRDs from the Manager via public CRDs() method for command access.
CRD subcommand core implementation
cmd/crossplane/xpkg/crd.go
Defines crdCmd type with output/cache/schema-mode flags; implements Help, AfterApply, and Run lifecycle; includes writeCRDs for YAML output, writeJSONSchemas for JSON Schema output, and openAPIToJSONSchema for OpenAPI-to-JSON conversion.
CRD subcommand tests
cmd/crossplane/xpkg/crd_test.go
Provides testCRD fixture and three unit tests: TestOpenAPIToJSONSchema validates schema conversion; TestWriteCRDs validates YAML file writing; TestWriteJSONSchemas validates JSON schema file generation and metadata injection.
xpkg command integration
cmd/crossplane/xpkg/xpkg.go
Registers new CRD subcommand field in exported Cmd struct; refactors embed import to parenthesized block syntax.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • jcogilvie
  • bobh66
  • haarchri
🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a new crd subcommand to the xpkg command.
Description check ✅ Passed The description is directly related to the changeset, explaining the purpose of the new subcommand and referencing the specific issue it addresses.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Breaking Changes ✅ Passed PR only adds new public APIs (Manager.CRDs() and CRD field to Cmd struct) without removing fields, renaming flags, or removing behavior. All changes are backwards-compatible.
Feature Gate Requirement ✅ Passed PR adds a new CLI subcommand (crossplane xpkg crd) with no changes to apis/** or core platform behaviors. As a CLI-only feature, feature gates are not applicable.

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


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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
cmd/crossplane/validate/manager.go (1)

80-83: ⚡ Quick win

Avoid exposing mutable manager state from CRDs().

Could we return a copied slice here so callers can’t accidentally mutate Manager’s internal state?

Proposed change
 func (m *Manager) CRDs() []*extv1.CustomResourceDefinition {
-	return m.crds
+	out := make([]*extv1.CustomResourceDefinition, len(m.crds))
+	copy(out, m.crds)
+	return out
 }
🤖 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/validate/manager.go` around lines 80 - 83, The CRDs() accessor
currently returns the internal slice m.crds allowing external mutation; change
CRDs() to return a shallow copy of the slice (e.g., allocate a new slice with
the same length and copy m.crds into it) so callers cannot mutate Manager's
internal slice while still returning the same []*extv1.CustomResourceDefinition
elements.
cmd/crossplane/xpkg/crd_test.go (1)

147-147: ⚡ Quick win

Use cmpopts.EquateErrors() for error comparisons in these table tests.

Nice coverage overall. Could we align these three assertions with the repo’s test pattern for error equality?

Proposed change
 import (
 	"bytes"
 	"encoding/json"
 	"testing"
 
 	"github.com/alecthomas/kong"
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/spf13/afero"
 	extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	"github.com/crossplane/crossplane-runtime/v2/pkg/test"
 )
...
-			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
+			if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" {
...
-			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
+			if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" {
...
-			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
+			if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" {

As per coding guidelines **/*_test.go: "use cmp.Diff with cmpopts.EquateErrors() for error testing."

Also applies to: 245-245, 330-330

🤖 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/crd_test.go` at line 147, Replace the use of
cmp.EquateErrors() in the cmp.Diff error assertions with cmpopts.EquateErrors();
specifically update the cmp.Diff(...) calls that compare tc.want.err and err to
use cmpopts.EquateErrors() and ensure the cmpopts package is imported
(github.com/google/go-cmp/cmp/cmpopts) so the table-test error comparisons
follow the repo test pattern.
🤖 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.

Inline comments:
In `@cmd/crossplane/xpkg/crd.go`:
- Around line 114-117: The expansion of c.CacheDir when it starts with "~/"
currently ignores the error from os.UserHomeDir(), which can produce an invalid
path; update the code that handles c.CacheDir (the branch checking
strings.HasPrefix(c.CacheDir, "~/")) to check the returned error from
os.UserHomeDir(), and on error return/fail fast with a clear error message that
includes the underlying error and guidance to provide an absolute --cache-dir.
Ensure the change surfaces the error to the caller (rather than silently using
an empty homeDir) so callers of the function receive the failure.

---

Nitpick comments:
In `@cmd/crossplane/validate/manager.go`:
- Around line 80-83: The CRDs() accessor currently returns the internal slice
m.crds allowing external mutation; change CRDs() to return a shallow copy of the
slice (e.g., allocate a new slice with the same length and copy m.crds into it)
so callers cannot mutate Manager's internal slice while still returning the same
[]*extv1.CustomResourceDefinition elements.

In `@cmd/crossplane/xpkg/crd_test.go`:
- Line 147: Replace the use of cmp.EquateErrors() in the cmp.Diff error
assertions with cmpopts.EquateErrors(); specifically update the cmp.Diff(...)
calls that compare tc.want.err and err to use cmpopts.EquateErrors() and ensure
the cmpopts package is imported (github.com/google/go-cmp/cmp/cmpopts) so the
table-test error comparisons follow the repo test pattern.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b85eaa87-430f-46a4-ab19-94788ce42e99

📥 Commits

Reviewing files that changed from the base of the PR and between 5a1ea69 and 73e1dbd.

📒 Files selected for processing (4)
  • cmd/crossplane/validate/manager.go
  • cmd/crossplane/xpkg/crd.go
  • cmd/crossplane/xpkg/crd_test.go
  • cmd/crossplane/xpkg/xpkg.go

Comment on lines +114 to +117
} else if strings.HasPrefix(c.CacheDir, "~/") {
homeDir, _ := os.UserHomeDir()
c.CacheDir = filepath.Join(homeDir, c.CacheDir[2:])
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="cmd/crossplane/xpkg/crd.go"

# Show the surrounding lines for the ~/ cache-dir expansion
nl -ba "$FILE" | sed -n '90,150p'

# Show imports to confirm which errors package is used in this file
echo "---- imports ----"
sed -n '1,80p' "$FILE"

# Search for UserHomeDir usage in this file/repo for consistency
rg -n "UserHomeDir\(" -S "$FILE" || true
rg -n "cannot resolve home directory|cache-dir to an absolute path" -S "$FILE" || true

# Check if errors.Wrap/Wrapf from crossplane-runtime is used around return-early patterns
rg -n "errors\.Wrapf?\(" "$FILE" | head -n 50

Repository: crossplane/cli

Length of output: 101


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="cmd/crossplane/xpkg/crd.go"

# Print lines 90-150 with line numbers via awk (since nl isn't available)
echo "---- snippet (lines 90-150) ----"
awk -v start=90 -v end=150 'NR>=start && NR<=end {printf "%4d:%s\n", NR, $0}' "$FILE"

echo "---- imports (lines 1-80) ----"
sed -n '1,80p' "$FILE"

echo "---- UserHomeDir usage in this file ----"
rg -n "UserHomeDir\(" "$FILE" || true

echo "---- errors.Wrap/Wrapf usage in this file ----"
rg -n "errors\.Wrapf?\(" "$FILE" || true

echo "---- any mention of cache-dir / CacheDir in this file ----"
rg -n "cache-dir|CacheDir" "$FILE" || true

Repository: crossplane/cli

Length of output: 7670


Handle os.UserHomeDir() failure when expanding ~/ in --cache-dir — currently the error is ignored, so a bad/empty home dir can produce a broken cache path without user feedback. Should crossplane xpkg crd fail fast here with guidance to pass an absolute --cache-dir?

Proposed change
 	} else if strings.HasPrefix(c.CacheDir, "~/") {
-		homeDir, _ := os.UserHomeDir()
+		homeDir, err := os.UserHomeDir()
+		if err != nil {
+			return errors.Wrap(err, "cannot resolve home directory; set --cache-dir to an absolute path")
+		}
 		c.CacheDir = filepath.Join(homeDir, c.CacheDir[2:])
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if strings.HasPrefix(c.CacheDir, "~/") {
homeDir, _ := os.UserHomeDir()
c.CacheDir = filepath.Join(homeDir, c.CacheDir[2:])
}
} else if strings.HasPrefix(c.CacheDir, "~/") {
homeDir, err := os.UserHomeDir()
if err != nil {
return errors.Wrap(err, "cannot resolve home directory; set --cache-dir to an absolute path")
}
c.CacheDir = filepath.Join(homeDir, c.CacheDir[2:])
}
🤖 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/crd.go` around lines 114 - 117, The expansion of
c.CacheDir when it starts with "~/" currently ignores the error from
os.UserHomeDir(), which can produce an invalid path; update the code that
handles c.CacheDir (the branch checking strings.HasPrefix(c.CacheDir, "~/")) to
check the returned error from os.UserHomeDir(), and on error return/fail fast
with a clear error message that includes the underlying error and guidance to
provide an absolute --cache-dir. Ensure the change surfaces the error to the
caller (rather than silently using an empty homeDir) so callers of the function
receive the failure.

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.

Command to download dependencies CRDs

1 participant