Skip to content

fix(bundler): write recipe.yaml for every deployer, not just helm - #2759

Merged
lockwobr merged 1 commit into
mainfrom
fix/2753-bundle-recipe
Sep 15, 2026
Merged

lockwobr merged 1 commit into
mainfrom
fix/2753-bundle-recipe

Conversation

@lockwobr

@lockwobr lockwobr commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Write the resolved recipe.yaml at the bundle root for every deployer, not only helm. Four of the five bundle formats carried no recipe at all.

Motivation / Context

pkg/bundler/bundler.go gated the recipe write on b.Config.Deployer() == config.DeployerHelm, and that was its only write site, so argocd, argocd-helm, flux and helmfile bundles shipped without one.

ADR-021 Decision 5 and #2528 both build on the premise that "every bundle embeds a deterministic recipe.yaml, so the recipe and bundle forms share one code path." That premise is what makes upgrade-check --from <bundle> cheap, and it held for one deployer out of five: an operator on Argo CD or Flux had no recipe in their bundle to re-resolve.

Fixes: #2753
Related: #2528, #2531, #2758

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • Build/CI/tooling

Component(s) Affected

  • CLI (cmd/aicr, pkg/cli)
  • API server (cmd/aicrd, pkg/server)
  • Recipe engine / data (pkg/recipe)
  • Bundlers (pkg/bundler, pkg/component/*)
  • Collectors / snapshotter (pkg/collector, pkg/snapshotter)
  • Validator (pkg/validator)
  • Core libraries (pkg/errors, pkg/k8s)
  • Docs/examples (docs/, examples/)
  • Other: ____________

Implementation Notes

One serializer path, not one per deployer. The write moved out of the deployer conditional rather than being added to each deployer, so all five go through the same serializer.MarshalYAMLDeterministic call. Verified: the emitted recipe.yaml is byte-identical across all five (same SHA256). This matters because the file feeds checksums.txt, which is the subject of the bundle attestation — re-marshaling per deployer would have made two bundles built from one recipe disagree on bytes while looking correct in a file listing.

Checksums and attestation need no change. The write already happened before checksum.WriteChecksums, so the new file lands in the manifest and the attestation subject automatically. Confirmed recipe.yaml is present in checksums.txt for all five deployers.

The new file is inert everywhere it could have been active:

  • flux — kustomization.yaml uses an explicit resources: list, so the recipe is never applied to a cluster.
  • argocd-helm — the bundle root is a Helm chart. helm lint passes, helm template renders unchanged, and helm package includes recipe.yaml in the tarball as a data file outside templates/, so it never becomes a cluster object. checksums.txt already rode along the same way, so this adds no new category of file to a published chart — and for OCI-published argocd-helm bundles it means the recipe travels with the artifact, which is what the --from <bundle> form needs.

Layout gate. requiredRootPaths in layout_test.go now lists recipe.yaml for all five deployers. TestBundleLayoutMatchesManifest only fails on removals, treating additions as an un-refreshed manifest, so without this floor a future regression on the other four would have read as benign drift rather than a broken promise.

Out of scope, filed separately. A bundle still does not record which deployer built it, which ADR-021 line 402 and #2528 both assert that it does. Split out as #2758, since it needs a new bundle-root surface and a naming decision rather than a gate removal.

Testing

go test -race ./pkg/bundler/...
golangci-lint run -c .golangci.yaml ./pkg/bundler/...
make lint

Both regression gates were verified to fail without the fix (change reverted, tests run, change restored):

  • TestMake_EveryDeployerEmitsRecipe (new) — fails on argocd, argocd-helm, flux, helmfile with no such file or directory.
  • TestBundleLayoutMatchesManifest — fails on the same four with <deployer> no longer emits "recipe.yaml", which the frozen layout promises.

The new test asserts per deployer that the file exists, parses back as a RecipeResult, appears in the checksum inventory and in the reported result files, and that all five are byte-identical.

Results: go test -race ./pkg/bundler/ passes; golangci-lint reports 0 issues.

Coverage: pkg/bundler: 86.8% → 86.8% (no change).

Four failures in pkg/bundler/attestation (×3) and pkg/bundler/validations (×1) are pre-existing and environmental — they need network egress my local sandbox blocks. Confirmed identical on unmodified main before and after this change.

make lint passes lint-go (0 issues), lint-yaml, license, check-docs-filenames, check-docs-mdx, check-docs-mdx-parse, check-depproxy-kit and check-upgrade-records. Two targets could not execute locally for sandbox reasons unrelated to this diff: check-agents-sync (the tool uses diff <(…) <(…); verified by hand that AGENTS.md matches .claude/CLAUDE.md from line 5) and bom-pinning-check (macOS mktemp denial, and this PR touches no chart, recipe or registry file). CI covers both.

Risk Assessment

  • Low — Isolated change, well-tested, easy to revert
  • Medium — Touches multiple components or has broader impact
  • High — Breaking change, affects critical paths, or complex rollout

Rollout notes: Purely additive to the bundle tree — one new root file on four deployers, none removed or renamed, so no integrator path breaks. Bundle bytes change for those four (a new file plus its checksums.txt entry), so any pinned bundle digest for argocd, argocd-helm, flux or helmfile shifts once. Helm bundles are unchanged.

Checklist

  • Tests pass locally (make test with -race)
  • Linter passes (make lint)
  • I did not skip/disable tests to make CI green
  • I added/updated tests for new functionality
  • I updated docs if user-facing behavior changed
  • Changes follow existing patterns in the codebase
  • Commits are cryptographically signed (git commit -S)
The recipe write was gated on the helm deployer, so argocd, argocd-helm,
flux and helmfile bundles carried no recipe at all. ADR-021 and #2528 both
build on the premise that every bundle embeds a deterministic recipe.yaml,
which held for one deployer out of five.

All five now write it through the same deterministic serializer, so the
bytes are identical across deployers and the file lands in checksums.txt
and therefore in the attestation subject.

Fixes: #2753
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
@lockwobr
lockwobr requested a review from a team as a code owner September 15, 2026 00:14
@lockwobr lockwobr added the theme/deployer Helm, ArgoCD, and deployment bundle generation label Sep 15, 2026
@lockwobr lockwobr self-assigned this Sep 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Recipe evidence check

No leaf overlays affected by this PR.

This gate is warning-only and never blocks merge.

@coderabbitai

coderabbitai Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 7a36065a-a78b-4f8a-8363-06ce9abb5336

📥 Commits

Reviewing files that changed from the base of the PR and between b8c790f and dec0925.

📒 Files selected for processing (10)
  • docs/user/bundling.md
  • docs/user/cli-reference.md
  • pkg/bundler/bundler.go
  • pkg/bundler/bundler_test.go
  • pkg/bundler/doc.go
  • pkg/bundler/layout_test.go
  • pkg/bundler/testdata/layout/manifests/argocd-helm.txt
  • pkg/bundler/testdata/layout/manifests/argocd.txt
  • pkg/bundler/testdata/layout/manifests/flux.txt
  • pkg/bundler/testdata/layout/manifests/helmfile.txt

Included review availability: Your plan provides up to 12 included reviews per hour; 10 remain after this review.


📝 Walkthrough

Walkthrough

The bundler now writes the resolved recipe.yaml at the bundle root for Helm, Argo CD, Argo CD Helm, Flux, and Helmfile. The file is included in reported output files and size calculations. Layout tests and regression tests verify its presence, parseability, deterministic content, and checksum inventory inclusion. User and package documentation now describe the deployer-wide behavior.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Suggested reviewers: mchmarny

Merge Risk: ⚪ Minimal · up to dec09

Every deployer bundle now consistently includes the resolved recipe with checksum and output inventory coverage. No actionable merge risk remains.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #2753 requires a resolved, deterministic recipe.yaml at the bundle root for all deployers, with checksum and attestation coverage. pkg/bundler/bundler.go now calls the shared `writeRecipeFil…
Out of Scope Changes check ✅ Passed The implementation changes, regression tests, layout fixtures, and documentation all support issue #2753. The changes do not add deployer metadata or other unrelated bundle behavior. The documented de…
Title check ✅ Passed The title clearly and concisely identifies the main change: writing recipe.yaml for every deployer instead of only Helm.
Description check ✅ Passed The description directly explains the bundler behavior change, motivation, implementation, testing, documentation updates, and scope.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/2753-bundle-recipe

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor
@lockwobr
lockwobr enabled auto-merge (squash) September 15, 2026 00:20
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report ✅

Metric Value
Coverage 84.5%
Threshold 83%
Status Pass
Coverage Badge
![Coverage](https://img.shields.io/badge/coverage-84.5%25-brightgreen)

Merging this branch will increase overall coverage

Impacted Packages Coverage Δ 🤖
github.com/NVIDIA/aicr/pkg/bundler 86.82% (+0.01%) 👍

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/NVIDIA/aicr/pkg/bundler/bundler.go 86.68% (+0.02%) 1344 (+2) 1165 (+2) 179 👍
github.com/NVIDIA/aicr/pkg/bundler/doc.go 0.00% (ø) 0 0 0

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

@mchmarny mchmarny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approve: no findings against dec0925. Required checks pass at the reviewed SHA.

@lockwobr
lockwobr merged commit cdfb814 into main Sep 15, 2026
78 of 79 checks passed
@lockwobr
lockwobr deleted the fix/2753-bundle-recipe branch September 15, 2026 15:54
lockwobr added a commit that referenced this pull request Sep 15, 2026
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
lockwobr added a commit that referenced this pull request Sep 16, 2026
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
lockwobr added a commit that referenced this pull request Sep 16, 2026
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
lockwobr added a commit that referenced this pull request Sep 17, 2026
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/bundler area/docs size/M theme/deployer Helm, ArgoCD, and deployment bundle generation

2 participants