Skip to content

feat: add resource crd subcommand#26

Open
fernandezcuesta wants to merge 2 commits into
crossplane:mainfrom
fernandezcuesta:feat/add-crd-download
Open

feat: add resource crd subcommand#26
fernandezcuesta wants to merge 2 commits 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 an xpkg crd subcommand that loads package extensions, uses validate.Manager to resolve CRDs, and writes either CRD YAML files or OpenAPI→JSON-Schema documents; it adds Manager.CRDs(), exports ToJSONSchema(...), wires the subcommand into xpkg, and provides unit tests.

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.
CRD subcommand core implementation
cmd/crossplane/xpkg/crd.go
Defines crdCmd with output/cache/schema-mode flags; implements Run flow to load extensions, create a validate.Manager, and either writeCRDs (YAML) or writeJSONSchemas (OpenAPI→JSON Schema); includes helpers storageVersion and outputPath.
CRD subcommand tests
cmd/crossplane/xpkg/crd_test.go
Adds fixture testCRD and table-driven tests TestWriteCRDs and TestWriteJSONSchemas validating file outputs and JSON-schema unmarshalling; uses an in-memory filesystem.
xpkg command integration
cmd/crossplane/xpkg/xpkg.go
Adds CRD crdCmd field to exported Cmd struct and adjusts blank embed import formatting.
Schema generator export
internal/schemas/generator/json.go
Adds exported ToJSONSchema(s any, group, version, kind string) used by CRD and OpenAPI generation; applies compatibility mutators and conditional $id / x-kubernetes-group-version-kind metadata.

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 describes the main change: adding a new CRD subcommand to the xpkg tool.
Description check ✅ Passed The description is directly related to the changeset, explaining the new subcommand and its purpose with context about the issue being fixed.
Linked Issues check ✅ Passed The implementation fully satisfies issue #15 by providing a command to download provider CRDs and optionally output JSON schemas to a specified folder.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the CRD subcommand and its supporting infrastructure as required by issue #15.
Breaking Changes ✅ Passed PR contains only additive changes: new Manager.CRDs() method, optional CRD struct field in Cmd, and unexported implementations. No public APIs removed, renamed, or made required.
Feature Gate Requirement ✅ Passed PR adds a new CLI subcommand (xpkg crd) that doesn't affect apis/** or significantly alter behavior—it's an additive tool for downloading CRDs, not an API or core feature change.

✏️ 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.

Copy link
Copy Markdown
Member

@adamwg adamwg left a comment

Choose a reason for hiding this comment

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

First off, thanks for the contribution! This is definitely a useful feature.

That said, there are a few high-level things I'd like to consider before reviewing this in detail:

  1. The functionality here feels very close to crossplane xpkg extract in that it fetches xpkgs and extracts their contents (though it does very different things with that extracted contents). I wonder whether writing CRDs and JSONSchemas should become a feature of extract rather than a separate command.
  2. We've also implemented OpenAPI -> JSONSchema conversion in internal/schemas/generator. It would be nice to re-use that code, which also handles a bunch of nuances to make the resulting JSONSchemas work super well with the YAML language server.
  3. It's on my TODO list to change crossplane validate to use the shared infrastructure from internal/xpkg, which would also make it use the shared xpkg cache used by the crossplane dependency commands. It may actually make sense to use the existing dependency.Manager in validate, removing its custom Manager entirely. I don't think there's much harm in this PR introducing another usage of the validate.Manager (we can swap it out at the same time when we update validate), but I wonder if using the dependency.Manager would simplify things a bit here and also reduce the amount of consolidation needed in the future.

Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com>
@fernandezcuesta
Copy link
Copy Markdown
Author

Hi @adamwg, thanks for taking a look.

Regarding your points:

  1. Fully agree, moved it under crossplane xpkg crd. I'm still getting familiar with the command structure. Since the input is a crossplane.yaml file, I think it doesn't fit xpkg extract.
  2. Refactor to reuse as much as possible. I made some slight changes to oapiSchemaToJSONSchema (apart from exposing it) to add $id and gvk info. Not fully needed but can be a useful addition IMO.
  3. Agree this would be a better long term solution. Since you're already planning to move validate to the shared internal/xpkg infrastructure, I think the pragmatic path is to keep validate.Manager here for now and swap both at the same time. Happy to take on either approach though.

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.

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/crd_test.go (1)

181-184: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don’t ignore filesystem errors in test assertions.

Thanks for adding these coverage paths. Right now afero.Exists / afero.ReadFile errors are discarded, which can mask real test failures; can we fail the test when those calls error?

Proposed patch
-			for _, f := range tc.want.files {
-				exists, _ := afero.Exists(fs, f)
+			for _, f := range tc.want.files {
+				exists, err := afero.Exists(fs, f)
+				if err != nil {
+					t.Fatalf("%s\nwriteCRDs(...): failed checking file %s: %v", tc.reason, f, err)
+				}
 				if !exists {
 					t.Errorf("%s\nwriteCRDs(...): expected file %s to exist", tc.reason, f)
 				}
 			}
@@
-			for _, f := range tc.want.files {
-				exists, _ := afero.Exists(fs, f)
+			for _, f := range tc.want.files {
+				exists, err := afero.Exists(fs, f)
+				if err != nil {
+					t.Fatalf("%s\nwriteJSONSchemas(...): failed checking file %s: %v", tc.reason, f, err)
+				}
 				if !exists {
 					t.Errorf("%s\nwriteJSONSchemas(...): expected file %s to exist", tc.reason, f)
 				}
 
-				data, _ := afero.ReadFile(fs, f)
+				data, err := afero.ReadFile(fs, f)
+				if err != nil {
+					t.Fatalf("%s\nwriteJSONSchemas(...): failed reading file %s: %v", tc.reason, f, err)
+				}
 				var schema jsonschema.Schema
 				if err := json.Unmarshal(data, &schema); err != nil {
 					t.Errorf("%s\nwriteJSONSchemas(...): file %s is not valid JSON Schema: %v", tc.reason, f, err)
 				}
 			}

Also applies to: 279-285

🤖 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` around lines 181 - 184, The test currently
ignores errors returned by afero.Exists and afero.ReadFile which can hide real
failures; update the assertions in the test (the block using afero.Exists(fs, f)
and the later block using afero.ReadFile(fs, f)) to check the returned error and
fail the test when err != nil (use t.Fatalf or t.Fatalf-like assertion) instead
of discarding the error, and only then assert on the existence/contents
(referencing the variables fs, f, exists and the ReadFile call) so any
filesystem error surfaces as a test failure.
🧹 Nitpick comments (2)
cmd/crossplane/xpkg/crd_test.go (1)

176-176: ⚡ Quick win

Align error assertions with the repo’s test pattern (cmpopts.EquateErrors).

Nice table setup overall. Could you switch these comparisons to cmp.Diff(..., cmpopts.EquateErrors()) to match the required _test.go convention consistently?

Proposed patch
 import (
 	"bytes"
 	"encoding/json"
 	"testing"
 
 	"github.com/alecthomas/kong"
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/invopop/jsonschema"
 	"github.com/spf13/afero"
@@
-			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
+			if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" {
 				t.Errorf("%s\nwriteCRDs(...): -want error, +got error:\n%s", tc.reason, diff)
 			}
@@
-			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
+			if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" {
 				t.Errorf("%s\nwriteJSONSchemas(...): -want error, +got error:\n%s", tc.reason, diff)
 			}

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

Also applies to: 274-274

🤖 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 176, Replace the use of
test.EquateErrors() in the cmp.Diff error assertions with cmpopts.EquateErrors()
(i.e., change cmp.Diff(tc.want.err, err, test.EquateErrors()) to
cmp.Diff(tc.want.err, err, cmpopts.EquateErrors())), and add the required import
for the cmpopts package (github.com/google/go-cmp/cmp/cmpopts) so the test
follows the repo convention; update both occurrences referenced (around the
cmp.Diff calls).
internal/schemas/generator/json.go (1)

102-112: ⚡ Quick win

Re: Preserve existing jsonschema.Schema extras when adding GVK metadata

internal/schemas/generator/json.go overwrites conv.Extras when group/version/kind are set, but with invopop/jsonschema v0.14.0 this is unlikely to drop anything: Schema.Extras is json:"-" and Schema.UnmarshalJSON just unmarshals into SchemaAlt, so unknown x-* fields from the input JSON won’t populate conv.Extras in the first place. Since this helper also doesn’t set conv.Extras anywhere else before that block, it should be safe today; merging could still be a future-proof tweak if other code starts populating conv.Extras earlier.

🤖 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 `@internal/schemas/generator/json.go` around lines 102 - 112, The code
overwrites conv.Extras when setting GVK metadata which could clobber any
existing extras; modify the block that sets conv.Extras (where conv.ID is set
and GVK map is created) to preserve existing conv.Extras by copying or merging
into the existing map instead of assigning a new map, and ensure the
"x-kubernetes-group-version-kind" key is added or replaced within conv.Extras;
refer to conv.Extras and the GVK-assignment block where conv.ID is set and merge
the new []map[string]string value into the existing conv.Extras map rather than
replacing it.
🤖 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/crd_test.go`:
- Around line 181-184: The test currently ignores errors returned by
afero.Exists and afero.ReadFile which can hide real failures; update the
assertions in the test (the block using afero.Exists(fs, f) and the later block
using afero.ReadFile(fs, f)) to check the returned error and fail the test when
err != nil (use t.Fatalf or t.Fatalf-like assertion) instead of discarding the
error, and only then assert on the existence/contents (referencing the variables
fs, f, exists and the ReadFile call) so any filesystem error surfaces as a test
failure.

---

Nitpick comments:
In `@cmd/crossplane/xpkg/crd_test.go`:
- Line 176: Replace the use of test.EquateErrors() in the cmp.Diff error
assertions with cmpopts.EquateErrors() (i.e., change cmp.Diff(tc.want.err, err,
test.EquateErrors()) to cmp.Diff(tc.want.err, err, cmpopts.EquateErrors())), and
add the required import for the cmpopts package
(github.com/google/go-cmp/cmp/cmpopts) so the test follows the repo convention;
update both occurrences referenced (around the cmp.Diff calls).

In `@internal/schemas/generator/json.go`:
- Around line 102-112: The code overwrites conv.Extras when setting GVK metadata
which could clobber any existing extras; modify the block that sets conv.Extras
(where conv.ID is set and GVK map is created) to preserve existing conv.Extras
by copying or merging into the existing map instead of assigning a new map, and
ensure the "x-kubernetes-group-version-kind" key is added or replaced within
conv.Extras; refer to conv.Extras and the GVK-assignment block where conv.ID is
set and merge the new []map[string]string value into the existing conv.Extras
map rather than replacing it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3c7779b1-ea69-4672-b009-0eb3e6f33dc4

📥 Commits

Reviewing files that changed from the base of the PR and between 73e1dbd and 97c1b1a.

📒 Files selected for processing (3)
  • cmd/crossplane/xpkg/crd.go
  • cmd/crossplane/xpkg/crd_test.go
  • internal/schemas/generator/json.go

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

2 participants