feat: allow composition render subcommand read from configuration pkg file - #252
feat: allow composition render subcommand read from configuration pkg file#252fernandezcuesta wants to merge 2 commits into
Conversation
6f7a72c to
f61fb12
Compare
…kage metadata file Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com>
f61fb12 to
27f0d48
Compare
Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com>
f300a4d to
ac7dc77
Compare
📝 WalkthroughWalkthroughThe render command now loads Function dependencies from ChangesConfiguration Function Resolution
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant RenderCommand
participant ConfigurationMetadata
participant Resolver
RenderCommand->>ConfigurationMetadata: load crossplane.yaml
ConfigurationMetadata-->>RenderCommand: return validated dependencies
RenderCommand->>Resolver: resolve Function dependencies
Resolver-->>RenderCommand: return Function packages
RenderCommand->>RenderCommand: render Composition without functions file
Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning)
✅ Passed checks (4 passed)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/render/xr/cmd.go`:
- Around line 89-92: Update the Kong help text for the positional Functions
argument in the XR render command to state that it is optional when either a
project file or Configuration metadata file supplies Function dependencies;
leave the surrounding flags unchanged.
- Around line 408-410: Update the project-file check in
cmd/crossplane/render/xr/cmd.go:408-410 to fall back to Configuration only when
os.IsNotExist(err) is true; wrap and return all other os.Stat errors. Apply the
same not-found-only condition at cmd/crossplane/render/xr/cmd.go:504-506 before
returning the Functions-argument error, and add coverage for a non-not-found
error.
In `@internal/xpkg/configuration_test.go`:
- Around line 35-93: Update the ParseConfiguration table-driven test to use args
and want structs, replacing expectErr with want.err and expected configuration
fields as needed. Compare the returned error against want.err using cmp.Diff
with cmpopts.EquateErrors(), while preserving the existing valid-name assertion
through the expected result structure.
🪄 Autofix
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 Plus
Run ID: da74dba2-d730-424c-bbb5-ec0c00c92b71
📒 Files selected for processing (4)
cmd/crossplane/render/xr/cmd.gocmd/crossplane/render/xr/help/render.mdinternal/xpkg/configuration.gointernal/xpkg/configuration_test.go
| CacheDir string `env:"CROSSPLANE_XPKG_CACHE" help:"Directory for cached xpkg package contents." name:"cache-dir"` | ||
| MaxConcurrency uint `default:"8" help:"Maximum concurrency for building embedded functions."` | ||
| ProjectFile string `default:"crossplane-project.yaml" help:"Path to the project file. Optional." optional:"" predictor:"yaml_file" short:"f" type:"path"` | ||
| PkgMetaFile string `default:"crossplane.yaml" help:"Path to a package metadata file (crossplane.yaml). Used as fallback when no project file is found." name:"pkg-meta-file" optional:"" predictor:"yaml_file" type:"path"` | ||
| ProjectFile string `default:"crossplane-project.yaml" help:"Path to the project file. Optional." optional:"" predictor:"yaml_file" short:"f" type:"path"` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Update the positional argument help.
The Functions help text says that the argument is optional only in a project. It is also optional when the Configuration metadata file supplies Function dependencies. Update the Kong help text to describe both cases.
As per path instructions, “Review CLI commands for proper flag handling, help text, and error messages.”
🤖 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/render/xr/cmd.go` around lines 89 - 92, Update the Kong help
text for the positional Functions argument in the XR render command to state
that it is optional when either a project file or Configuration metadata file
supplies Function dependencies; leave the surrounding flags unchanged.
Source: Path instructions
| if _, err := os.Stat(projFilePath); err != nil { | ||
| return nil, errors.New("functions argument is required when not in a project") | ||
| return c.loadFunctionsFromConfiguration(ctx, log) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Handle non-not-found file errors before fallback.
os.Stat can return permission and I/O errors. The project-file branch treats these errors as a missing project and starts Configuration fallback. The Configuration branch then hides its own access failure with a Functions-argument error.
Only use fallback behavior when os.IsNotExist(err) is true. Return a wrapped access error for every other error. Add coverage for a non-not-found error.
cmd/crossplane/render/xr/cmd.go#L408-L410: fall back to Configuration only after a not-found project-file error.cmd/crossplane/render/xr/cmd.go#L504-L506: return the Functions-argument error only after a not-found Configuration-file error.
Proposed fix
if _, err := os.Stat(projFilePath); err != nil {
+ if !os.IsNotExist(err) {
+ return nil, errors.Wrapf(err, "cannot access project file %q", projFilePath)
+ }
return c.loadFunctionsFromConfiguration(ctx, log)
}
if _, err := os.Stat(cfgFilePath); err != nil {
+ if !os.IsNotExist(err) {
+ return nil, errors.Wrapf(err, "cannot access configuration file %q", cfgFilePath)
+ }
return nil, errors.New("functions argument is required when not in a project or configuration")
}📝 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.
| if _, err := os.Stat(projFilePath); err != nil { | |
| return nil, errors.New("functions argument is required when not in a project") | |
| return c.loadFunctionsFromConfiguration(ctx, log) | |
| } | |
| if _, err := os.Stat(projFilePath); err != nil { | |
| if !os.IsNotExist(err) { | |
| return nil, errors.Wrapf(err, "cannot access project file %q", projFilePath) | |
| } | |
| return c.loadFunctionsFromConfiguration(ctx, log) | |
| } |
| if _, err := os.Stat(projFilePath); err != nil { | |
| return nil, errors.New("functions argument is required when not in a project") | |
| return c.loadFunctionsFromConfiguration(ctx, log) | |
| } | |
| if _, err := os.Stat(cfgFilePath); err != nil { | |
| if !os.IsNotExist(err) { | |
| return nil, errors.Wrapf(err, "cannot access configuration file %q", cfgFilePath) | |
| } | |
| return nil, errors.New("functions argument is required when not in a project or configuration") | |
| } |
📍 Affects 1 file
cmd/crossplane/render/xr/cmd.go#L408-L410(this comment)cmd/crossplane/render/xr/cmd.go#L504-L506
🤖 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/render/xr/cmd.go` around lines 408 - 410, Update the
project-file check in cmd/crossplane/render/xr/cmd.go:408-410 to fall back to
Configuration only when os.IsNotExist(err) is true; wrap and return all other
os.Stat errors. Apply the same not-found-only condition at
cmd/crossplane/render/xr/cmd.go:504-506 before returning the Functions-argument
error, and add coverage for a non-not-found error.
| tests := []struct { | ||
| name string | ||
| content string | ||
| expectErr bool | ||
| }{ | ||
| { | ||
| name: "ValidConfiguration", | ||
| content: ` | ||
| apiVersion: meta.pkg.crossplane.io/v1 | ||
| kind: Configuration | ||
| metadata: | ||
| name: my-config | ||
| spec: | ||
| dependsOn: | ||
| - function: ghcr.io/example/function-a | ||
| version: "v1.0.0" | ||
| `, | ||
| }, | ||
| { | ||
| name: "WrongAPIVersion", | ||
| content: "apiVersion: wrong.api/v1\nkind: Configuration\nspec: {}", | ||
| expectErr: true, | ||
| }, | ||
| { | ||
| name: "WrongKind", | ||
| content: "apiVersion: meta.pkg.crossplane.io/v1\nkind: Provider\nspec: {}", | ||
| expectErr: true, | ||
| }, | ||
| { | ||
| name: "InvalidYAML", | ||
| content: "not: valid: yaml: [", | ||
| expectErr: true, | ||
| }, | ||
| { | ||
| name: "FileNotFound", | ||
| expectErr: true, | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| fs := afero.NewMemMapFs() | ||
| if tt.content != "" { | ||
| if err := afero.WriteFile(fs, "/crossplane.yaml", []byte(tt.content), os.ModePerm); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } | ||
|
|
||
| cfg, err := ParseConfiguration(fs, "/crossplane.yaml") | ||
| if (err != nil) != tt.expectErr { | ||
| t.Fatalf("ParseConfiguration() error = %v, expectErr %v", err, tt.expectErr) | ||
| } | ||
| if err == nil && cfg.Name != "my-config" { | ||
| t.Errorf("name = %q, want %q", cfg.Name, "my-config") | ||
| } | ||
| }) | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 3 'cmpopts\.EquateErrors|cmp\.Diff' --glob '*_test.go'
rg -n 'github.com/google/go-cmp' go.modRepository: crossplane/cli
Length of output: 152
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate file =="
fd -a 'internal/xpkg/configuration_test.go' . || true
if [ -f internal/xpkg/configuration_test.go ]; then
echo "== file outline =="
ast-grep outline internal/xpkg/configuration_test.go --view expanded || true
echo "== relevant lines =="
cat -n internal/xpkg/configuration_test.go | sed -n '1,180p'
fi
echo "== go-cmp in module files =="
rg -n 'go-cmp|cmpopts\.EquateErrors|cmp\.Diff' --glob '*_test.go' --glob 'go.mod' --glob 'go.sum' . || true
echo "== current diff stat/name =="
git diff --stat || true
git diff -- internal/xpkg/configuration_test.go 2>/dev/null | sed -n '1,220p' || trueRepository: crossplane/cli
Length of output: 40017
Use the required args and want test structure.
ParseConfiguration covers error cases with expectErr, but the test should compare want.err against the returned error with cmp.Diff and cmpopts.EquateErrors(). ResolveConfigurationFunctions already uses args/want and cmp.Diff, and it has no returned errors, so no error comparison is needed there.
🤖 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/xpkg/configuration_test.go` around lines 35 - 93, Update the
ParseConfiguration table-driven test to use args and want structs, replacing
expectErr with want.err and expected configuration fields as needed. Compare the
returned error against want.err using cmp.Diff with cmpopts.EquateErrors(),
while preserving the existing valid-name assertion through the expected result
structure.
Source: Path instructions
Description of your changes
Allow functions to be read directly from a configuration file (
crossplane.yamldependsOn).Fixes #251
I have:
./nix.sh flake checkto ensure this PR is ready for review.[ ] Linked a PR or a docs tracking issue to document this change.[ ] Addedbackport release-x.ylabels to auto-backport this PR.Need help with this checklist? See the cheat sheet.