Skip to content

feat(cli): auto-hydrate RecipeMetadata overlays in validate and bundle#595

Merged
mchmarny merged 2 commits intoNVIDIA:mainfrom
njhensley:feat/auto-hydrate-overlay-in-validate-bundle
Apr 16, 2026
Merged

feat(cli): auto-hydrate RecipeMetadata overlays in validate and bundle#595
mchmarny merged 2 commits intoNVIDIA:mainfrom
njhensley:feat/auto-hydrate-overlay-in-validate-bundle

Conversation

@njhensley
Copy link
Copy Markdown
Contributor

@njhensley njhensley commented Apr 16, 2026

Summary

Auto-hydrate RecipeMetadata overlay files when passed to aicr validate or aicr bundle, and reject non-RecipeResult kinds with a clear error instead of silently producing empty results.

Motivation / Context

When aicr validate --recipe or aicr bundle --recipe receives a RecipeMetadata overlay file instead of a hydrated RecipeResult, Go's YAML unmarshaler silently succeeds — the kind field parses correctly, but validation and componentRefs end up nil because they live under spec: in overlays vs at the top level in RecipeResult. For validate, this caused all validators to be silently skipped (selected=0). For bundle, it hit a confusing "recipe must contain at least one component reference" error.

Fixes: #526
Related: N/A

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/api, 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

  • New shared function recipe.LoadFromFile (pkg/recipe/loader.go) encapsulates recipe loading with kind detection and auto-hydration. Both validate.go and bundle.go now call this instead of raw serializer.FromFileWithKubeconfig[recipe.RecipeResult].
  • Auto-hydration flow: When kind: RecipeMetadata is detected, the file is re-parsed as RecipeMetadata, spec.criteria is extracted, and builder.BuildFromCriteria produces a hydrated RecipeResult in-memory. No file is written to disk.
  • Kind rejection: RecipeMixin and other non-RecipeResult kinds get a clear error with remediation guidance. Empty kind is allowed for backward compatibility with older files.
  • Overlay without criteria (only base.yaml in practice — 1 of 58 overlays): returns a specific error explaining that only leaf overlays with spec.criteria can be auto-hydrated.
  • Error propagation: BuildFromCriteria errors are returned as-is (not re-wrapped) to preserve the inner error code per project conventions.
  • Data provider: initDataProvider(cmd) already runs before recipe loading in both commands, so the builder has access to embedded + external overlays.

Testing

make qualify   # Pre-existing trust test fails due to sandbox network (sigstore.dev blocked)
go test -race ./pkg/cli/... ./pkg/recipe/... ./pkg/bundler/...   # All pass

New tests:

  • pkg/recipe/loader_test.go — 7 cases: nonexistent file, RecipeResult passthrough, RecipeMetadata auto-hydration (verifies Kind == RecipeResult and len(ComponentRefs) > 0), no-criteria error, RecipeMixin rejection, unknown kind rejection, empty kind backward-compat
  • pkg/cli/validate_test.go — 6 cases: same branches exercised through the CLI harness, with positive assertions that hydrated recipes proceed to the snapshot stage (errContain: "kubernetes client")

Coverage: LoadFromFile at 88.9%; pkg/recipe at 90.7% (no regression)

Risk Assessment

  • Low — Isolated change, well-tested, easy to revert

Rollout notes: Fully backward compatible. Existing RecipeResult files load identically. The only behavioral change is that RecipeMetadata overlays now auto-hydrate instead of silently failing. No migration needed.

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) — GPG signing info

Summary by CodeRabbit

  • New Features

    • Recipe metadata overlays are now automatically hydrated when loaded, simplifying recipe configuration and composition workflows.
  • Bug Fixes

    • Improved recipe validation with stricter kind checking and overlay criteria validation, providing clearer error messages on validation failures.
  • Tests

    • Added comprehensive test coverage for recipe loading, validating proper handling of recipe kinds, criteria requirements, and overlay hydration.

njhensley and others added 2 commits April 16, 2026 10:48
Users often pass overlay files directly to validate/bundle; detect the
RecipeMetadata kind and hydrate it through the recipe builder so a
separate "aicr recipe" step is not required. Shared loader in
pkg/recipe.LoadFromFile is used by both commands and rejects
non-RecipeResult kinds (other than empty, for backward compatibility)
with a clear error pointing at the fix.
@njhensley njhensley requested a review from a team as a code owner April 16, 2026 18:03
@coderabbitai

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@mchmarny mchmarny enabled auto-merge (squash) April 16, 2026 20:12
@mchmarny mchmarny merged commit 1b25135 into NVIDIA:main Apr 16, 2026
23 checks passed
@njhensley njhensley deleted the feat/auto-hydrate-overlay-in-validate-bundle branch April 16, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate command silently skips all validators when given a RecipeMetadata overlay

2 participants