feat(cli): add --set-json/--set-file for list and object bundle overrides#1162
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR implements CLI support for typed component value overrides in the Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsStopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a Comment |
a940158 to
9d63bd8
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@docs/user/cli-reference.md`:
- Around line 1307-1309: The fenced code block following the "**Examples:**"
heading lacks the required blank line for markdownlint MD031; insert one blank
line between the "**Examples:**" line and the opening ```shell fence so the code
block is separated from the preceding paragraph/header (update the section that
contains the "**Examples:**" heading and its subsequent fenced block to include
the blank line).
🪄 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: ASSERTIVE
Plan: Enterprise
Run ID: 4863a429-bdd6-48b4-8f8f-6187f4baee43
📒 Files selected for processing (14)
docs/user/cli-reference.mddocs/user/component-catalog.mdpkg/bundler/bundler.gopkg/bundler/bundler_test.gopkg/bundler/config/config.gopkg/bundler/config/typed_override.gopkg/bundler/config/typed_override_test.gopkg/cli/bundle.gopkg/cli/bundle_config.gopkg/cli/bundle_typed_overrides_test.gopkg/component/overrides.gopkg/component/typed_overrides_test.gopkg/defaults/spec.gorecipes/components/agentgateway/values.yaml
4d6c66e to
541e47d
Compare
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 `@pkg/bundler/config/typed_override.go`:
- Around line 96-103: In WithValueOverridesTypedPaths, reject any
TypedComponentPath entries where tp.Path == "enabled" so non-CLI callers cannot
stash an enabled: override; locate the loop in WithValueOverridesTypedPaths (and
the TypedComponentPath type) and add a guard that fails fast (e.g., panic or
return an error from the caller path if your Option pattern supports errors)
with a clear message referencing "enabled" and the component name instead of
inserting it into c.valueOverridesTyped[tp.Component][tp.Path]; this enforces
the same rejection as pkg/cli/bundle_config.go at the bundler boundary.
In `@pkg/cli/bundle_config.go`:
- Around line 135-146: The current error handling wraps parse errors from
bundlercfg.ParseTypedOverrides(..., decodeSetFileValue) and
bundlercfg.ParseValueOverridesJSON(...) with errors.Wrap, which double-wraps
structured ErrCodeInvalidRequest errors; change those returns to use
errors.PropagateOrWrap(err, errors.ErrCodeInvalidRequest, "invalid --set-file")
and errors.PropagateOrWrap(err, errors.ErrCodeInvalidRequest, "invalid
--set-json") (or simply return the original err) so that structured parse errors
are propagated intact instead of being re-wrapped.
- Around line 182-185: The current os.Open(path) error is always wrapped as
errors.ErrCodeNotFound; change the handling to check os.IsNotExist(err) first
and only map that branch to errors.ErrCodeNotFound, and for all other errors
wrap them with an appropriate fallback code (e.g., errors.ErrCodeIO or a
suitable existing I/O/error code) using errors.Wrap; update the os.Open(path)
error path where errors.Wrap is called so it uses os.IsNotExist(err) to choose
between errors.ErrCodeNotFound and the fallback code.
🪄 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: ASSERTIVE
Plan: Enterprise
Run ID: b442c0d2-f72e-4821-a61f-3547db71112a
📒 Files selected for processing (16)
docs/user/cli-reference.mddocs/user/component-catalog.mdpkg/bundler/bundler.gopkg/bundler/bundler_test.gopkg/bundler/config/config.gopkg/bundler/config/typed_override.gopkg/bundler/config/typed_override_test.gopkg/cli/bundle.gopkg/cli/bundle_config.gopkg/cli/bundle_typed_overrides_test.gopkg/component/overrides.gopkg/component/typed_overrides_test.gopkg/defaults/spec.gopkg/serializer/copy.gopkg/serializer/copy_test.gorecipes/components/agentgateway/values.yaml
541e47d to
38a8ac5
Compare
38a8ac5 to
f7b7c43
Compare
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 `@docs/user/cli-reference.md`:
- Line 1342: The heading "Replace an object value (deep-merges into the existing
map)" is contradictory; update the heading text to reflect merge semantics
(e.g., "Update an object value (deep-merges into the existing map)" or "Merge
into an object value (deep-merges into the existing map)") so it no longer
implies replace semantics while preserving the parenthetical note; edit the
heading line in docs/user/cli-reference.md where that exact phrase appears.
In `@pkg/cli/bundle.go`:
- Around line 461-468: Update the --set-json flag help text (the
cli.StringSliceFlag with Name "set-json") to explicitly state that JSON scalars
(booleans, numbers, null) are supported in addition to lists/objects; modify the
Usage string to mention scalar acceptance, adjust the example or add a short
scalar example (e.g., --set-json mycomp:replicas=3 or mycomp:enabled=true) and
clarify that scalars replace existing values while objects deep-merge and lists
replace.
In `@pkg/component/overrides.go`:
- Around line 122-150: The mergeAnyMap implementation currently copies a src map
verbatim when the destination key isn't a map, which preserves nil children as
explicit nulls instead of treating them as "delete" markers; update mergeAnyMap
so that in the branch where you assign dst[k] = serializer.DeepCopyAnyMap(svMap)
you instead iterate svMap and for each entry set dstSub[key] =
DeepCopyAny(value) for non-nil values and delete(dstSub, key) for nil values
(creating dstSub as a new map if needed), so nil children are interpreted as
deletions rather than stored as nulls; refer to mergeAnyMap, svMap, dst and
dst[k] to locate where to change the assignment.
🪄 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: ASSERTIVE
Plan: Enterprise
Run ID: 61e87f47-e075-41ff-bbbb-39ab5172451b
📒 Files selected for processing (16)
docs/user/cli-reference.mddocs/user/component-catalog.mdpkg/bundler/bundler.gopkg/bundler/bundler_test.gopkg/bundler/config/config.gopkg/bundler/config/typed_override.gopkg/bundler/config/typed_override_test.gopkg/cli/bundle.gopkg/cli/bundle_config.gopkg/cli/bundle_typed_overrides_test.gopkg/component/overrides.gopkg/component/typed_overrides_test.gopkg/defaults/spec.gopkg/serializer/copy.gopkg/serializer/copy_test.gorecipes/components/agentgateway/values.yaml
9121ea5 to
81a5353
Compare
81a5353 to
0f75362
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@pkg/cli/bundle_config.go`:
- Around line 117-175: Move the business rules out of pkg/cli: keep
file-decoding helper (decodeSetFileValue) and raw flag parsing calls (e.g.,
cmd.StringSlice in resolveTypedOverrides) in pkg/cli but relocate precedence
handling (collecting --set-file before --set-json and merge ordering) and the
top-level enabled-path validation (logic that checks tp.Path ==
componentEnabledKey) into pkg/bundler/config (or bundler) so the bundler package
owns the contract; have resolveTypedOverrides call into a new bundlercfg
function that accepts the parsed TypedComponentPath slices (from
bundlercfg.ParseTypedOverrides and bundlercfg.ParseValueOverridesJSON) and
returns the final merged []bundlercfg.TypedComponentPath or an error, preserving
existing error propagation from ParseTypedOverrides/ParseValueOverridesJSON.
- Around line 189-211: In decodeSetFileValue, reject non-regular paths before
calling os.Open: call os.Lstat (or os.Stat) on path first and if it returns
os.ErrNotExist wrap and return errors.ErrCodeNotFound, otherwise if the file
mode is not a regular file (or IsDir) return errors.ErrCodeInvalidRequest with a
message that the --set-file path is not a regular file (or is a directory). Only
proceed to os.Open after confirming the path exists and is a regular file. Keep
existing error codes/messages for NotFound and InvalidRequest to match current
behavior.
🪄 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: ASSERTIVE
Plan: Enterprise
Run ID: 2bbcdb69-6489-48f2-889d-0d59bc3b1f19
📒 Files selected for processing (14)
docs/user/cli-reference.mddocs/user/component-catalog.mdpkg/bundler/bundler.gopkg/bundler/bundler_test.gopkg/bundler/config/config.gopkg/bundler/config/typed_override.gopkg/bundler/config/typed_override_test.gopkg/cli/bundle.gopkg/cli/bundle_config.gopkg/cli/bundle_typed_overrides_test.gopkg/component/overrides.gopkg/component/typed_overrides_test.gopkg/defaults/spec.gorecipes/components/agentgateway/values.yaml
| // resolveTypedOverrides parses the --set-json and --set-file flags into | ||
| // structured component-path overrides for list/object values that scalar | ||
| // --set cannot express (see #1161). --set-json decodes the value as JSON; | ||
| // --set-file reads the value from a file and decodes it as YAML (a JSON | ||
| // superset). Both reuse --set's "component:path" validation. | ||
| // | ||
| // The two flags are additive. When both target the same component:path, | ||
| // --set-file is collected first so an inline --set-json wins on that path, | ||
| // mirroring Helm's precedence of --set over -f value files. (urfave/cli does | ||
| // not expose the interleaved command-line order across two distinct flags, so | ||
| // this fixed precedence is the deterministic contract rather than raw argument | ||
| // order.) Within a single flag, the last entry for a given component:path | ||
| // wins. Returns nil when neither flag is set. | ||
| func resolveTypedOverrides(cmd *cli.Command) ([]bundlercfg.TypedComponentPath, error) { | ||
| var out []bundlercfg.TypedComponentPath | ||
|
|
||
| // Collect --set-file first so a later-applied --set-json on the same | ||
| // component:path takes precedence (Helm-like: --set beats -f). | ||
| // | ||
| // ParseTypedOverrides / ParseValueOverridesJSON already return structured | ||
| // ErrCodeInvalidRequest errors that name the offending spec (and, for value | ||
| // decode/format failures, the flag), so propagate them as-is rather than | ||
| // re-wrapping with the same code. | ||
| if cmd.IsSet("set-file") { | ||
| parsed, err := bundlercfg.ParseTypedOverrides(cmd.StringSlice("set-file"), "--set-file", decodeSetFileValue) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| out = append(out, parsed...) | ||
| } | ||
|
|
||
| if cmd.IsSet("set-json") { | ||
| parsed, err := bundlercfg.ParseValueOverridesJSON(cmd.StringSlice("set-json")) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| out = append(out, parsed...) | ||
| } | ||
|
|
||
| // The top-level "enabled" key is the bundler's component include/exclude | ||
| // toggle, not a Helm chart value — it is honored only via scalar --set | ||
| // (getSetEnabledOverride) and stripped before reaching chart values. | ||
| // Routing it through the typed flags would neither toggle the component nor | ||
| // strip the key: it would silently write a stray literal `enabled:` into the | ||
| // component's values and ship a component the operator believed disabled. | ||
| // Reject it loudly with a pointer to --set rather than emit a misconfigured | ||
| // bundle. A nested chart value such as `gds.enabled` is unaffected — only | ||
| // the exact toggle path is blocked. | ||
| for _, tp := range out { | ||
| if tp.Path == componentEnabledKey { | ||
| return nil, errors.New(errors.ErrCodeInvalidRequest, | ||
| fmt.Sprintf("%q is the component enable/disable toggle and must be set with --set "+ | ||
| "(e.g. --set %s:%s=false), not --set-json/--set-file", | ||
| componentEnabledKey, tp.Component, componentEnabledKey)) | ||
| } | ||
| } | ||
|
|
||
| return out, nil | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift
Move typed-override precedence and enabled validation out of pkg/cli.
This function now owns domain rules that the bundler also enforces, so CLI and non-CLI entry points can drift independently. Keep the file-decoding callback in pkg/cli, but move the precedence/validation contract into pkg/bundler/config (or the bundler) so the CLI stays as thin wiring.
Based on learnings: "In this repo, ensure packages under pkg/cli contain only CLI wiring (command setup, flag/urfave/cli plumbing, and user-interaction helpers like TTY confirmation). Avoid implementing business logic in pkg/cli."
🤖 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 `@pkg/cli/bundle_config.go` around lines 117 - 175, Move the business rules out
of pkg/cli: keep file-decoding helper (decodeSetFileValue) and raw flag parsing
calls (e.g., cmd.StringSlice in resolveTypedOverrides) in pkg/cli but relocate
precedence handling (collecting --set-file before --set-json and merge ordering)
and the top-level enabled-path validation (logic that checks tp.Path ==
componentEnabledKey) into pkg/bundler/config (or bundler) so the bundler package
owns the contract; have resolveTypedOverrides call into a new bundlercfg
function that accepts the parsed TypedComponentPath slices (from
bundlercfg.ParseTypedOverrides and bundlercfg.ParseValueOverridesJSON) and
returns the final merged []bundlercfg.TypedComponentPath or an error, preserving
existing error propagation from ParseTypedOverrides/ParseValueOverridesJSON.
There was a problem hiding this comment.
Thanks for the suggestion. We will keep the precedence and enabled-toggle handling in resolveTypedOverrides for now rather than relocate it. The actual contract already lives below the CLI boundary: the bundler independently rejects the enabled toggle on typed overrides at pkg/bundler/bundler.go (the fix from the previous round), so non-CLI callers fail closed regardless of this function. What remains in pkg/cli is (a) a fast, flag-specific UX rejection that points the operator at --set, and (b) the --set-file-before---set-json precedence, which is inherently a CLI flag-ordering concern (urfave/cli does not expose interleaved order across two distinct flags). Moving these into pkg/bundler/config is a reasonable longer-term cleanup but a heavier refactor than this PR warrants given the boundary guard already exists, so we will leave it as a possible follow-up.
There was a problem hiding this comment.
@yuanchen8911, that's a well-reasoned call. The belt-and-suspenders approach — bundler rejects enabled on typed overrides regardless of entry point, CLI adds a fast, user-friendly rejection with a pointer to --set — is a solid pattern, and you're right that the --set-file/--set-json precedence is inherently a CLI flag-ordering concern that has no meaning below the CLI boundary. Happy to leave it as a possible follow-up.
🐇 ~(っ^▿^)っ
✏️ Learnings added
Learnt from: yuanchen8911
Repo: NVIDIA/aicr PR: 1162
File: pkg/cli/bundle_config.go:117-175
Timestamp: 2026-06-03T15:57:09.826Z
Learning: In NVIDIA/aicr, `pkg/cli/bundle_config.go`'s `resolveTypedOverrides` intentionally keeps two concerns in the CLI layer: (1) a fast UX early-rejection of the `enabled` component toggle (pointing the operator to `--set`), and (2) `--set-file`-before-`--set-json` precedence ordering (because urfave/cli does not expose interleaved order across two distinct flags). The actual contract (rejecting typed overrides targeting `enabled`) is separately enforced as a boundary guard in `pkg/bundler/bundler.go`, so non-CLI callers fail closed regardless. Do not flag the CLI-layer duplication as a violation of the "thin CLI" principle; the dual-layer defense is intentional per PR `#1162`.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: njhensley
Repo: NVIDIA/aicr PR: 595
File: pkg/recipe/loader.go:56-60
Timestamp: 2026-04-16T18:20:53.204Z
Learning: In this repository’s Go code, if an error was already created/wrapped with `pkg/errors` (via `aicrerrors.New(...)` or `aicrerrors.WrapWithContext(...)`) and includes the correct structured error code (e.g., `ErrCodeInvalidRequest`, `ErrCodeTimeout`, `ErrCodeInternal`), then returning it should use the bare form `return nil, err` (or propagate the error as-is). Do not require “missing context” when the source already has proper codes; do not double-wrap it. Avoid changing the error by using `errors.Wrapf` (which can override the structured code) or `fmt.Errorf("%w", err)` in these cases, which is documented as an anti-pattern in `CLAUDE.md`.
Learnt from: yuanchen8911
Repo: NVIDIA/aicr PR: 691
File: pkg/cli/skill.go:47-75
Timestamp: 2026-04-27T04:46:14.966Z
Learning: In this repo, ensure packages under `pkg/cli` contain only CLI wiring (command setup, flag/urfave/cli plumbing, and user-interaction helpers like TTY confirmation). Avoid implementing business logic in `pkg/cli`: interfaces and generator/meta extraction logic (e.g., `skillGenerator`, `claudeCodeGenerator`/`codexGenerator`, `cliMeta`, `extractCLIMeta`) should be moved into a dedicated package such as `pkg/skill` (or an appropriate domain package) so `pkg/cli` is limited to `cli.Command` orchestration (e.g., `skillCmdFlags`/`skillCmd`).
Learnt from: mchmarny
Repo: NVIDIA/aicr PR: 728
File: pkg/recipe/criteria.go:801-820
Timestamp: 2026-05-01T23:59:30.754Z
Learning: In NVIDIA/aicr, use the idiomatic helper `errors.PropagateOrWrap(err, errCode, message)` from `pkg/errors` when handling errors in Go code. This helper should be preferred over manual `errors.As` + conditional return logic: it propagates `err` as-is if it already carries a `*StructuredError` code, and otherwise wraps `err` using the provided fallback `errCode` and `message`. Apply this pattern especially when calling functions from `pkg/serializer`, `pkg/recipe`, or other packages that may already return coded structured errors.
Learnt from: njhensley
Repo: NVIDIA/aicr PR: 844
File: pkg/fingerprint/from_measurements.go:200-220
Timestamp: 2026-05-11T21:09:20.802Z
Learning: In this repo’s Go code, do not flag or request changes for `int64`→`int` casts when the value originates from a Go `int` and remains in-process (e.g., it round-trips via `measurement.Reading.Any()` but never crosses a system boundary like serialization/network/DB). Only validate/guard numeric ranges at system boundaries; adding `math.MaxInt32/MinInt32` clamping for such internal-only casts is an anti-pattern (per CLAUDE.md), since the scenario is guaranteed by internal/framework behavior and 64-bit Linux targets keep `int` effectively 64 bits.
Learnt from: mchmarny
Repo: NVIDIA/aicr PR: 1126
File: pkg/api/doc.go:105-105
Timestamp: 2026-05-30T16:28:11.381Z
Learning: In NVIDIA/aicr, timeout deadlines are intentionally layered: `defaults.ServerHandlerTimeout` (90s) is the outer HTTP/server ceiling, while individual handlers may set tighter per-operation deadlines using `context.WithTimeout` (e.g., 30s) to bound specific I/O or sub-operations. During code review, do not flag this as a conflict or redundancy: documenting both values is correct and intended. The outer timeout prevents net/http from closing connections mid-handler, and the inner deadlines constrain only the relevant sub-operation(s).
0f75362 to
3f228a9
Compare
mchmarny
left a comment
There was a problem hiding this comment.
Approving. Clean implementation with strong test coverage (incl. a 200-run determinism guard against map-iteration nondeterminism), defense-in-depth on the enabled toggle (rejected at both CLI and bundler), aliasing properly prevented on insert + getter, and a careful stat-then-open + LimitReader flow for --set-file that handles FIFOs/directories up front. CI green across 116 checks.
Three non-blocking notes inline, all medium-or-below:
DisableSliceFlagSeparator: truesilently changes comma-handling for all existing slice flags onbundle, not just the new ones — worth a callout in the PR description for operators with CI scripts.- The new alias-merge behavior (a real bug fix) applies to scalar
--settoo, but is documented only under the typed-flag section. computeSchedulingPathPolicysuppression considers only scalar--set, not typed overrides — asymmetry is probably intentional but undocumented.
None of these block merge.
…ides `aicr bundle --set` is scalar-only, so it cannot express list/object value overrides. Pointing it at a list field such as agentgateway.allowedSourceRanges exits 0 but writes a bare string into spec.loadBalancerSourceRanges, producing a type-invalid Service that may silently stay open at 0.0.0.0/0. Add two list-aware flags that mirror Helm's: - --set-json component:path=<json> — inline JSON list/object/scalar - --set-file component:path=<file> — same, read from a JSON/YAML file Both reuse --set's component:path validation. Values are applied last (highest precedence): object values deep-merge into existing maps (matching inline componentRef overrides), while lists and scalars replace. File reads are bounded to defaults.MaxSetFileBytes via os.Open + io.LimitReader, and file I/O stays in the CLI layer (the shared config parser takes a decode callback), so no filesystem access leaks into the server-reachable config package. Docs: cli-reference gains a "List and Object Value Overrides" section; the agentgateway value comment and component-catalog now point at --set-json as the clean CLI lockdown workflow. Closes NVIDIA#1161
3f228a9 to
993240e
Compare
Summary
Adds
--set-jsonand--set-filetoaicr bundleso list and object value overrides (e.g.agentgateway.allowedSourceRanges) can be set from the CLI.--setis scalar-only and silently mangles such fields.Motivation / Context
aicr bundle --set component:path=valuewrites a bare string, so a list field likeagentgateway.allowedSourceRanges(rendered into the Service'sspec.loadBalancerSourceRanges) becomes a type-invalid scalar — the gateway can silently stay open at0.0.0.0/0. The docs even warn "Do NOT use--setfor this key." This adds the list-aware flags that mirror Helm, giving operators a clean CLI lockdown workflow without hand-editing the recipe.Fixes: #1161
Related: #1160 (agentgateway open-exposure guardrails) and its PR #1163 —
--set-jsonis the CLI mechanism operators use to scopeallowedSourceRangesafter that warning/check fires.Type of Change
Component(s) Affected
cmd/aicr,pkg/cli)pkg/bundler,pkg/component/*)docs/,examples/)Implementation Notes
--set'scomponent:pathvalidation):--set-json component:path=<json>— inline JSON list/object/scalar--set-file component:path=<file>— same value read from a JSON/YAML filepkg/component.ApplyTypedOverrides): object values deep-merge into any existing map at the path (matching inlinecomponentRefs[].overrides), while lists and scalars replace. Applied last inbuildComponentValues, so a typed override wins over scalar--seton the same path. Paths are applied shallowest-first (by segment count, then lexicographically), so when one override targets a parent object and another a key beneath it (e.g.comp:driver.env=<object>andcomp:driver.env.HTTPS_PROXY=<value>), the deeper, more-specific path deterministically wins on shared keys regardless of flag order — Go's randomized map iteration is no longer observable.map[string]map[string]anyon the bundlerConfig; the registry alias resolution (gpuoperator→gpu-operator) is shared with scalar--setvia a newcomponentOverrideKeyshelper. Overrides supplied under both a component's canonical name and a registered alias are merged (viamergeOverridesAcrossKeys), not first-match-wins, so mixed canonical/alias inputs no longer silently drop entries; the canonical name wins on a shared path. The scalar--setlookup was switched to the same merge for consistency.os.Open+io.LimitReaderagainstdefaults.MaxSetFileBytes, 1 MiB), and the--set-filepath isos.Stat-validated as a regular file before opening (rejecting directories, FIFOs, devices, and sockets up front so a special file fails fast instead of blocking inos.Open), and stay in the CLI layer — the shared config parser takes a decode callback, so no filesystem access leaks into the server-reachablepkg/bundler/config. The API server surface is intentionally unchanged.DisableSliceFlagSeparator: trueon thebundlecommand so commas in slice-flag values are literal (required for comma-heavy JSON, and removes a latent footgun where a--setvalue containing a comma was silently split). Repeat-the-flag remains the documented way to add multiple values; comma-packing was never documented.Testing
Also verified end-to-end with a locally built binary against a generated EKS/H100/dynamo recipe:
New tests:
pkg/bundler/config(JSON parse, deep-copy getter, last-wins),pkg/component(deep-merge / replace / no-aliasing / null-deletes-key, plus a 200-iteration determinism guard for overlapping parent/child paths),pkg/cli(flag wiring,--set-fileread + size cap, non-regular-path rejection (FIFO/directory), comma-containing JSON),pkg/bundler(alias resolution, a regression test asserting mixed canonical+alias overrides are merged rather than dropped, end-to-end list rendering, and a precedence regression test asserting--set-jsonwins over--seton the same path throughMake()).make scan(grype) was not run: no new dependencies were introduced (uses stdlibencoding/jsonand the already-vendoredgopkg.in/yaml.v3), so the vulnerability surface is unchanged.Risk Assessment
DisableSliceFlagSeparator: trueis set on the wholebundlecommand, not just the new flags. It makes commas literal for all repeatablebundleslice flags (--set,--set-json,--set-file,--dynamic,--*-node-selector,--*-node-toleration,--workload-selector). This is required because urfave/cli v3's per-flag separator config is not exposed (the disable is command-scoped). The documented way to supply multiple values has always been to repeat the flag — comma-packing into one flag (e.g.--set a=1,b=2) was never documented as supported in AICR — but urfave/cli does split slice values on commas by default, so any existing CI script relying on that framework default (rather than AICR docs) to pack multiple values into one flag must switch to repeating the flag. That is the only usage affected; the change also removes a latent footgun where a--setvalue containing a comma was silently split. The selector/toleration parsers (ParseNodeSelectors,ParseTolerations) do not split on comma themselves, so repeat-the-flag is unaffected. e2e (22/22) is green.Rollout notes: Backwards compatible for documented usage. The API server bundle endpoint is unchanged.
Checklist
make testwith-race)make lint)git commit -S)