fix(bundler): wire PreManifestFiles through flux deployer with terminal-aware dependsOn#924
Conversation
|
🌿 Preview your docs: https://nvidia-preview-fix-flux-pre-manifests-923.docs.buildwithfern.com/aicr |
84cbbbd to
86363b9
Compare
This comment was marked as resolved.
This comment was marked as resolved.
mchmarny
left a comment
There was a problem hiding this comment.
Solid fix that closes the gap PR #921 left for the flux deployer, plus a real win on the pre-existing terminal-aware dependsOn bug that Codex caught. Test coverage for the main paths is thorough, docs are updated in sync, and the collision guard pattern is the right shape.
Two medium notes worth considering before merge: the -post half of the localformat-parity collision check is still missing in flux (latent pre-existing gap, but the new docstring makes the omission visible), and the vendored-chart + pre-manifests path is exercised by the code but not by tests. Plus one nit on error-wrapping inconsistency in bundler.go. None are blockers.
CI looks healthy — tests / E2E and analyze are still running but every completed check is green.
86363b9 to
b8e9f50
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
pkg/bundler/deployer/flux/flux.go (2)
625-651: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winExtract
HelmReleaseinto a named constant.This literal is repeated in the same helper and is already tripping
goconst.As per coding guidelines "Extract hardcoded resource names from templates as named constants to keep code and templates in sync".♻️ Suggested change
const ( fileChart = "Chart.yaml" fileConfigMap = "configmap-values.yaml" fileHelmRelease = "helmrelease.yaml" fileKustomization = "kustomization.yaml" fileReadme = "README.md" + componentSummaryTypeHelmRelease = "HelmRelease" )- Type: "HelmRelease", + Type: componentSummaryTypeHelmRelease, ... - Type: "HelmRelease", + Type: componentSummaryTypeHelmRelease, ... - Type: "HelmRelease", + Type: componentSummaryTypeHelmRelease,🤖 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/bundler/deployer/flux/flux.go` around lines 625 - 651, Multiple occurrences of the literal "HelmRelease" are used when constructing ComponentSummary entries (see the summaries append blocks around preName, ref.Name, and ref.Name+"-post"); extract this hardcoded resource type into a named constant (e.g., const componentTypeHelmRelease = "HelmRelease") and replace all literal occurrences in the helper where ComponentSummary{ Type: "HelmRelease", ... } is set (including the isMixed branch and the pre/post blocks) to use the new constant to satisfy goconst and keep templates in sync.
643-651:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse the actual post-release emission rule here.
The generator emits
<name>-postfor any non-manifest-only component with post-manifests, but this README path only adds that row when bothref.Chartandref.Sourceare set. Git/path-backed components with post-manifests will therefore render an incomplete chain in the README.🩹 Suggested fix
- isMixed := ref.Chart != "" && ref.Source != "" && len(manifests[ref.Name]) > 0 - if isMixed { + hasPostRelease := terminalReleaseNameFor(ref, manifests) == ref.Name+"-post" + if hasPostRelease { summaries = append(summaries, ComponentSummary{ Name: ref.Name + "-post", Type: "HelmRelease", Namespace: ref.Namespace, DependsOnStr: ref.Name, }) }🤖 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/bundler/deployer/flux/flux.go` around lines 643 - 651, The README generation currently only emits a post-release row when both ref.Chart and ref.Source are set; change the isMixed check used when building the ComponentSummary (symbol: isMixed, used to append Name: ref.Name + "-post") to detect any non-manifest-only component with post-manifests by testing that manifests[ref.Name] has entries AND at least one of ref.Chart or ref.Source is present (i.e., use logical OR instead of AND), so git/path-backed components with post-manifests also produce the "<name>-post" HelmRelease summary.
🤖 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/deployer/flux/flux_test.go`:
- Around line 1900-1906: The test currently checks the collision error by string
matching on err.Error(), which is brittle; change the assertion to unwrap and
inspect the structured error returned by g.Generate(ctx, outputDir) by using
errors.As into a *errors.StructuredError (or equivalent type) and assert se.Code
== errors.ErrCodeInvalidRequest (using errors.As/Is per project conventions);
after confirming the error code, keep the existing secondary check that the
error message contains the offending pair text (`would inject "foo"-pre`) to
ensure the message names the colliding pair. Ensure you reference the error
value returned by g.Generate and the StructuredError type and
ErrCodeInvalidRequest constant in your replacement assertions.
---
Outside diff comments:
In `@pkg/bundler/deployer/flux/flux.go`:
- Around line 625-651: Multiple occurrences of the literal "HelmRelease" are
used when constructing ComponentSummary entries (see the summaries append blocks
around preName, ref.Name, and ref.Name+"-post"); extract this hardcoded resource
type into a named constant (e.g., const componentTypeHelmRelease =
"HelmRelease") and replace all literal occurrences in the helper where
ComponentSummary{ Type: "HelmRelease", ... } is set (including the isMixed
branch and the pre/post blocks) to use the new constant to satisfy goconst and
keep templates in sync.
- Around line 643-651: The README generation currently only emits a post-release
row when both ref.Chart and ref.Source are set; change the isMixed check used
when building the ComponentSummary (symbol: isMixed, used to append Name:
ref.Name + "-post") to detect any non-manifest-only component with
post-manifests by testing that manifests[ref.Name] has entries AND at least one
of ref.Chart or ref.Source is present (i.e., use logical OR instead of AND), so
git/path-backed components with post-manifests also produce the "<name>-post"
HelmRelease summary.
🪄 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: ddcc6c2c-1d58-48cc-9ce8-004aef851d6c
📒 Files selected for processing (5)
docs/user/cli-reference.mdpkg/bundler/bundler.gopkg/bundler/deployer/flux/doc.gopkg/bundler/deployer/flux/flux.gopkg/bundler/deployer/flux/flux_test.go
b8e9f50 to
2693e41
Compare
|
@mchmarny — all 6 inline comments addressed at
Bonus refactor: collision detection extracted into |
2693e41 to
7194d8f
Compare
|
Adding a 7th item I missed in the table above — CodeRabbit also posted a nit on
Branch is now at |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
pkg/bundler/deployer/flux/flux.go (2)
643-668: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winExtract
HelmReleaseinto a named constant.This hunk repeats the same literal three times, and the current lint run is already flagging it. Pulling it into a constant keeps CI green and avoids resource-type strings drifting across the Flux renderer.
Suggested fix
const ( fileChart = "Chart.yaml" fileConfigMap = "configmap-values.yaml" fileHelmRelease = "helmrelease.yaml" fileKustomization = "kustomization.yaml" fileReadme = "README.md" + resourceTypeHelmRelease = "HelmRelease" )summaries = append(summaries, ComponentSummary{ Name: preName, - Type: "HelmRelease", + Type: resourceTypeHelmRelease, Namespace: ref.Namespace, DependsOnStr: previousTerminal, }) @@ summaries = append(summaries, ComponentSummary{ Name: ref.Name, - Type: "HelmRelease", + Type: resourceTypeHelmRelease, Version: version, Namespace: ref.Namespace, DependsOnStr: primaryDependsOn, }) @@ summaries = append(summaries, ComponentSummary{ Name: ref.Name + "-post", - Type: "HelmRelease", + Type: resourceTypeHelmRelease, Namespace: ref.Namespace, DependsOnStr: ref.Name, })As per coding guidelines, "Extract hardcoded resource names from templates as named constants to keep code and templates in sync."
🤖 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/bundler/deployer/flux/flux.go` around lines 643 - 668, The literal "HelmRelease" is repeated in the ComponentSummary construction (see occurrences inside the summaries append blocks where ComponentSummary is used for preName, ref.Name, and ref.Name+"-post"); extract that resource-type string into a named constant (e.g., const ResourceTypeHelmRelease = "HelmRelease") and replace the three hardcoded occurrences with the constant, ensuring you update any nearby uses that expect the same literal (references: ComponentSummary struct initializations, variables preName, primaryDependsOn, isMixed and the summaries append calls).
660-663:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse the same mixed-component predicate as generation.
Line 662 requires both
ref.Chartandref.Source, but the generator logic around this helper treats any chart-or-source component with post manifests as emitting<name>-post. That means the README can still omit the post release for source-only/chart-only components even though Flux generates it.Suggested fix
- isMixed := ref.Chart != "" && ref.Source != "" && len(manifests[ref.Name]) > 0 + hasChartOrSource := ref.Chart != "" || ref.Source != "" + isMixed := hasChartOrSource && len(manifests[ref.Name]) > 0🤖 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/bundler/deployer/flux/flux.go` around lines 660 - 663, The mixed-component predicate is too strict: change the isMixed check in flux.go from requiring both ref.Chart and ref.Source to match the generator logic that treats any chart-or-source component with post manifests as mixed; i.e., set isMixed when (ref.Chart != "" || ref.Source != "") && len(manifests[ref.Name]) > 0 so components with either Chart or Source plus manifests will emit the "<name>-post" HelmRelease (update the isMixed variable and any checks that rely on 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 `@pkg/bundler/deployer/flux/flux.go`:
- Around line 643-668: The literal "HelmRelease" is repeated in the
ComponentSummary construction (see occurrences inside the summaries append
blocks where ComponentSummary is used for preName, ref.Name, and
ref.Name+"-post"); extract that resource-type string into a named constant
(e.g., const ResourceTypeHelmRelease = "HelmRelease") and replace the three
hardcoded occurrences with the constant, ensuring you update any nearby uses
that expect the same literal (references: ComponentSummary struct
initializations, variables preName, primaryDependsOn, isMixed and the summaries
append calls).
- Around line 660-663: The mixed-component predicate is too strict: change the
isMixed check in flux.go from requiring both ref.Chart and ref.Source to match
the generator logic that treats any chart-or-source component with post
manifests as mixed; i.e., set isMixed when (ref.Chart != "" || ref.Source != "")
&& len(manifests[ref.Name]) > 0 so components with either Chart or Source plus
manifests will emit the "<name>-post" HelmRelease (update the isMixed variable
and any checks that rely on it).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 4d8e1736-166d-40e6-aa53-dd7b52ec6385
📒 Files selected for processing (5)
docs/user/cli-reference.mdpkg/bundler/bundler.gopkg/bundler/deployer/flux/doc.gopkg/bundler/deployer/flux/flux.gopkg/bundler/deployer/flux/flux_test.go
…al-aware dependsOn The flux deployer never consumed `ComponentPreManifests`, so the synthesized GKE critical-priority `ResourceQuota` from PR NVIDIA#921 was silently dropped on `--deployer flux` bundles and the original symptom from NVIDIA#915 reproduced. The same gap also blocked the os-talos mixin from working on flux. Add `ComponentPreManifests` to `flux.Generator`, wire it through `bundler.buildDeployer`, and emit a `<name>-pre` HelmRelease ahead of the primary when pre-manifests exist. Rewire the primary's `dependsOn` to point at `<name>-pre` so the chain is `previous → <name>-pre → <name> → <name>-post → next`. Also fix a pre-existing ordering bug: the next component used to depend on the previous component's primary name, not its terminal (`<prev>-post` for mixed components). New `terminalReleaseNameFor` helper resolves the correct tail; `buildPrimaryDependsOn` and the README renderer both use it. Without this fix, Flux could reconcile the next component in parallel with the previous component's post manifests. Add a `<name>-pre` collision guard mirroring the rule in `pkg/bundler/deployer/localformat/writer.go`. Tests: pre-only, pre+post with terminal-aware next-component dependsOn (regression guard), collision rejection, and a refreshed `TestBuildPrimaryDependsOn` covering mixed, manifest-only, and chart-only previous components. Docs: `pkg/bundler/deployer/flux/doc.go` describes the chain and the example tree now includes `<name>-pre/`; `docs/user/cli-reference.md` updates the Flux Deployment Order bullet. Fixes: NVIDIA#923
7194d8f to
a7665fc
Compare
…from Tech Stack Three small fixes to the contributor docs, all triggered by a real lint miss on PR NVIDIA#924 where a local `golangci-lint` v2.10.1 silently passed a `goconst` finding that CI's v2.12.2 caught: 1. **Drop the hard-coded `v2.11.3` from CLAUDE.md and AGENTS.md Tech Stack.** Renovate auto-bumps `.settings.yaml` via its `# renovate:` annotation, but the bare prose mention in CLAUDE.md never moves — so the doc drifts every minor bump. Replacing with "pinned versions in `.settings.yaml`" makes the pointer self-healing. 2. **Document `make tools-update` in DEVELOPMENT.md and CONTRIBUTING.md.** The make target exists and is the only way to *upgrade* an existing local install to match `.settings.yaml`, but it was undocumented — CONTRIBUTING and DEVELOPMENT only mentioned `tools-setup` (first-time install) and `tools-check` (verify). DEVELOPMENT's troubleshooting row even pointed contributors at the wrong target (`tools-setup` for an upgrade scenario). 3. **Add a callout that local lint can silently miss issues if the toolchain is behind CI**, with `make tools-update` as the fix. This is the specific pitfall PR NVIDIA#924 hit; the existing docs implied `make qualify` passing locally is sufficient, which is only true when `.settings.yaml` and the local toolchain agree. Doc-only — no code or YAML changes. Verified with `make check-agents-sync`, `make check-docs-mdx`, and `make check-docs-filenames` (all pass). Full `make qualify` skipped per CLAUDE.local.md's doc-only verification rule.
Summary
Wire
ComponentPreManifeststhrough the flux deployer so the synthesized GKE critical-priorityResourceQuotafrom #921 (and any futurePreManifestFilesconsumer such as the os-talos mixin) actually reaches--deployer fluxbundles. Also fix a pre-existing chain-ordering bug where the next component depended on the previous component's primary name instead of its terminal release.Motivation / Context
PR #921 fixed #915 (GKE
ResourceQuotaadmission blockssystem-*-criticalpods outsidekube-system) for thehelm,helmfile,argocd, andargocd-helmdeployers by synthesizing a pre-phaseResourceQuotaviacollectComponentPreManifests. The PR description claimed flux benefits too, butflux.Generatornever had aComponentPreManifestsfield and theDeployerFluxswitch case never calledcollectComponentPreManifests— so the synthesized quota was computed and silently dropped. A GKE bundle generated with--deployer fluxstill hit the original symptom.The same gap also blocked the
os-talosmixin (#856) from working on flux, since both consumers ride onComponentRef.PreManifestFiles.While addressing the issue, Codex review surfaced a second, pre-existing bug: every component's
dependsOnresolved to the previous component's primary name, even when that previous component had-postmanifests. Flux could reconcile the next component in parallel with the previous component's post manifests, defeating the chain's purpose. Codex verified this against a local GKE Flux bundle:gke-nccl-tcpxo.dependsOn = gpu-operatorinstead ofgpu-operator-post.Fixes: #923
Related: #915, #921, #856
Type of Change
Component(s) Affected
pkg/bundler,pkg/component/*)docs/,examples/)Implementation Notes
flux.Generatorfield — addComponentPreManifests map[string]map[string][]byte, mirroring the field already present onhelm,helmfile,argocd, andargocd-helmgenerators.bundler.buildDeployer'sDeployerFluxcase now callscollectComponentPreManifestsalongsidecollectComponentManifestsand threads both into the flux generator.<name>-preemission — whenComponentPreManifests[ref.Name]is non-empty,generateComponentResourcesemits a<name>-preHelmRelease via the existinggenerateManifestHelmCharthelper (same path used for-post) and rewires the primary'sdependsOnto<name>-pre. Chain becomesprevious → <name>-pre → <name> → <name>-post → next.dependsOn— replace freebuildDependsOn(sortedRefs, index)with methodg.buildPrimaryDependsOn, backed by a new free functionterminalReleaseNameFor(ref, postManifests)that returns<name>-postfor mixed components and<name>otherwise. The README renderer (buildComponentSummaries) uses the same helper, so the rendered README matches the actual chain.Generate()entry, a recipe declaring bothfoo(with pre-manifests) and a separatefoo-preis rejected at bundle time. Mirrors the rule inpkg/bundler/deployer/localformat/writer.go.localformat, so the synthesis lives directly influx.go. Same applies tovalidatePreManifestNamespace; flux doesn't yet have an os-talos-shaped consumer, so the namespace-drift guard isn't ported. Can be added later if a flux user needs it.Testing
make qualify # all 21 chainsaw tests pass, including cli-bundle-fluxNew / updated tests in
pkg/bundler/deployer/flux/flux_test.go:TestBuildPrimaryDependsOn(4 sub-cases) — exercises terminal resolution for mixed, manifest-only, and chart-only previous components. ReplacesTestBuildDependsOnsincebuildDependsOnis gone.TestGenerate_WithPreManifests— pre-only component emits<name>-pre/with Chart.yaml + templates + HelmRelease, pre HelmRelease depends on previous component, primary depends on<name>-pre(not previous), root kustomization references the pre folder.TestGenerate_PreAndPostManifests— full pre + post + downstream-next shape. Assertspreis head of chain (no dependsOn), primary depends on<name>-pre, post depends on<name>, and the next component depends on<name>-post(regression guard for Codex's finding).TestGenerate_PreManifestsCollision— recipe declaring bothfoo(with pre-manifests) andfoo-preis rejected with an error naming the offending pair.Coverage:
pkg/bundler/deployer/flux83.5% → 83.9% (+0.4%).Risk Assessment
dependsOnonly changes output for mixed previous components, which previously had the next release racing the post anyway — a latent correctness improvement, not a behavior change for the common case). Well-tested, easy to revert.Rollout notes: For flux bundles where the previous component is mixed (chart + post-manifests), the next component's
dependsOnwill now resolve to<prev>-postrather than<prev>. This is the documented chain — operators tracking reconcile order should see the expected sequencing rather than parallel reconcile of the next component with the previous component's post manifests. No CLI flags, no API surface changes.Checklist
make testwith-race)make lint)git commit -S)