feat(cli): add evidence digest subcommand for recipe canonical hash#1055
Conversation
Adds `aicr evidence digest -r <recipe-or-overlay>` which prints the
lowercase hex sha256 of the canonical YAML — byte-for-byte the same
value recorded in predicate.recipe.digest by
`aicr validate --emit-attestation`.
Enables CI gates to detect drift between a signed evidence pointer and
the current recipe on the PR branch without pulling the OCI artifact:
signed=$(aicr evidence verify recipes/evidence/<slug>.yaml --format json \
| jq -r .predicate.recipe.digest)
current=$(aicr evidence digest -r recipes/overlays/<file>.yaml)
[[ "$signed" == "$current" ]] || echo "evidence is stale"
Adds a CLI reference section for `aicr evidence digest`, including the drift-gate snippet that pairs it with `aicr evidence verify`.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds a new Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 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🔥 ProblemsGit: Failed to clone repository. Please run the 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 `@docs/user/cli-reference.md`:
- Around line 2075-2094: Add blank lines around the fenced code blocks and the
Flags table to satisfy markdownlint MD031/MD058: ensure there's a blank line
before and after the initial ```shell block under "Synopsis" and similarly a
blank line before the "Flags:" table and one blank line after that table (before
"Exit codes"), and add a blank line before the final ```shell block under
"Examples"; update the fenced blocks to start with "```shell" and have an empty
line immediately after the opening fence and an empty line immediately before
the closing fence to keep spacing consistent.
In `@pkg/cli/evidence_digest.go`:
- Around line 71-100: runEvidenceDigestCmd mixes CLI orchestration with business
logic (hydrating recipe, canonicalizing YAML, computing digest); extract that
logic into a new function (e.g., ComputeRecipeDigest(ctx, path, kubeconfig,
version) placed in a functional package such as attestation or pkg/evidence)
that performs recipe.LoadFromFile, serializer.MarshalYAMLDeterministic and
attestation.SubjectDigest and returns the digest string or an error; then
simplify runEvidenceDigestCmd to only validate flags, call the new
ComputeRecipeDigest with cmd.String("kubeconfig") and path, and print the
returned digest, preserving existing error wrapping behavior.
- Line 82: The recipe loader currently calls serializer.FromFileWithKubeconfig
using context.Background(), which ignores the caller ctx passed into
recipe.LoadFromFile; change recipe.LoadFromFile (in pkg/recipe/loader.go) to
accept and forward the caller ctx into serializer functions, and inside the
serializer call sites replace context.Background() usages with derived contexts
created from the caller ctx using context.WithTimeout(ctx,
defaults.FileReadTimeout) (and defaults.ConfigMapWriteTimeout where applicable)
so caller cancellation/timeouts are honored; update the
serializer.FromFileWithKubeconfig (and related serializer methods) signatures to
accept a context parameter and propagate it through file/ConfigMap I/O calls.
🪄 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: 49782566-c718-41f2-92fd-cb00d0cf71e2
📒 Files selected for processing (4)
docs/user/cli-reference.mdpkg/cli/evidence.gopkg/cli/evidence_digest.gopkg/cli/evidence_digest_test.go
Moves the load → canonicalize → digest pipeline out of `pkg/cli` and into `pkg/evidence/attestation.ComputeRecipeDigest`. `pkg/cli` is supposed to be user-interaction-only — keeping the recipe-builder call inline meant business logic was leaking through the CLI boundary, and any other caller (API server, future workflow tooling) that wanted the same digest would have had to copy the canonicalization sequence verbatim. The helper preserves the existing input contract (`cm://` URIs honor `kubeconfig`; everything else ignores it) so the CLI subcommand is now a thin flag-parsing shim. Also tightens docs/user/cli-reference.md to add a blank line after each `**Bold:**` paragraph label in the new section, matching the spacing the rest of the file already uses.
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 `@pkg/evidence/attestation/recipe_digest.go`:
- Around line 42-45: The marshal error is being unconditionally wrapped with
errors.ErrCodeInternal, which discards any existing structured code; replace the
current wrap in the block that calls serializer.MarshalYAMLDeterministic(rec) so
that you use errors.PropagateOrWrap(err, errors.ErrCodeInternal, "failed to
marshal recipe for digest") instead of errors.Wrap, preserving coded errors from
serializer.MarshalYAMLDeterministic while still providing the contextual message
(update the error return in the function containing recipeYAML and the
serializer call).
🪄 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: 6cdf2ac9-64ab-457d-8a63-14b9f5e183b6
📒 Files selected for processing (3)
docs/user/cli-reference.mdpkg/cli/evidence_digest.gopkg/evidence/attestation/recipe_digest.go
Summary
Adds
aicr evidence digest -r <recipe-or-overlay>— prints the canonical sha256 of a resolved recipe, byte-for-byte identical topredicate.recipe.digestemitted byaicr validate --emit-attestation.Motivation / Context
CI evidence gates need a cheap way to detect drift between a signed evidence pointer (checked into the repo or sitting in an OCI artifact) and the current recipe on the PR branch. Today the only path is
aicr evidence verifyagainst the signed bundle, which requires pulling the OCI artifact and a registry round-trip. A pure-local digest of the resolved recipe lets a workflow short-circuit before any network I/O.Fixes: N/A
Related: docs/design/007-recipe-evidence.md
Type of Change
Component(s) Affected
cmd/aicr,pkg/cli)docs/,examples/)Implementation Notes
recipe.LoadFromFileso overlays/mixins are hydrated on the same path asaicr validate -r— the digest is computed against the fully resolvedRecipeResult, not the raw overlay bytes.serializer.MarshalYAMLDeterministic+attestation.SubjectDigest(the existing canonicalization helpers) so output is bit-identical to the attestation predicate; no parallel canonicalizer.cmd.Root().Writer(testable perpkg/cliconvention). Errors return structuredpkg/errorscodes.--kubeconfigflag is accepted only becauserecipe.LoadFromFilerequires it forcm://URIs; no cluster traffic on file/HTTP paths.Testing
```bash
golangci-lint run -c .golangci.yaml ./pkg/cli/... # 0 issues
go test -race -count=1 ./pkg/cli/... # PASS
```
pkg/clicoverage: 66.7% → 66.7% (no change; new code covered by new tests).Risk Assessment
evidenceCmd().Commands.Rollout notes: N/A — pure addition.
Checklist
go test -race ./pkg/cli/...)golangci-linton./pkg/cli/...)docs/user/cli-reference.md— new### aicr evidence digestsection)git commit -S)