Repository navigation
feat(recipe): run a recipe from a file, parsed strictly - #213
Merged
kuny0707 merged 1 commit intoAug 6, 2026
Merged
Conversation
`recipe run` and `recipe show` could only reach the five recipes embedded in the binary, so the workflows this is meant to replace could not be expressed at all. Both now take `--file <path>`, and a new `recipe validate --file` parses and checks one without running a step. Exactly one source, never both: a command handed a name AND a --file refuses rather than silently preferring one, since the caller would then be running something other than what they named. The result records where the recipe came from (`source`: "builtin:<name>" or the path) — without it a run from disk is indistinguishable in its output from a run of the built-in with the same name. The file loader is strict where the embedded loader is lenient, because the two have different failure modes. An embedded recipe is reviewed at build time and its tests run in CI. A file handed to --file is typed by someone who wants it to run now, against real nodes: - **Unknown fields are errors.** `on_failre: continue` parses clean under lenient YAML and silently means "abort" — the recipe does the opposite of what it says, at the moment a step has already failed. - **Multi-document files are refused.** yaml.Unmarshal returns document one and says nothing, so a file holding two recipes runs half of what it contains. - **Structure is validated up front, reporting every problem at once.** Missing ids, missing commands, an on_failure outside abort/continue/rollback, duplicate step ids, duplicate params. A recipe is a sequence of real operations: the expensive place to discover a typo is step four, after steps one to three have already changed something. Reporting one problem per run means one run per typo. A test asserts every embedded recipe passes the new validation, so a rule that would reject something we ship fails here rather than in a user's hands. `recipe validate` gets a real schema rather than an entry in TestSchemaCoverage's accepted-gaps list, whose comment says the goal is to drive that list to zero. SchemaVersion 1.12.5 -> 1.13.0: two additive changes (the `source` property, the new file), baseline re-snapshotted. Deliberately not here: no `--expect-sha256` provenance pin. A recipe's steps are trond subcommands, each individually gated and audited, so --file does not widen what trond can do — it changes who typed the sequence. A checksum pin protects against a file changing between review and run, which is a real concern but a separate one, and it should land with `kind: host` where the capability ceiling actually moves.
This was referenced Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
recipe runandrecipe showcould only reach the five recipes embedded in the binary, so the workflows this is meant to replace could not be expressed at all. Both now take--file <path>, and a newrecipe validate --fileparses and checks one without running a step.Exactly one source, never both: a command handed a name and a
--filerefuses rather than silently preferring one, since the caller would then be running something other than what they named. The result records where the recipe came from (source:"builtin:<name>"or the path) — without it a run from disk is indistinguishable in its output from a run of the built-in with the same name.Why the file loader is strict where the embedded one is not
The two have different failure modes. An embedded recipe is reviewed at build time and its tests run in CI. A file handed to
--fileis typed by someone who wants it to run now, against real nodes.on_failre: continueparses clean under lenient YAML and silently means "abort" — the recipe does the opposite of what it says, at the moment a step has already failed.yaml.Unmarshalreturns document one and says nothing, so a file holding two recipes runs half of what it contains.on_failureoutside abort/continue/rollback, duplicate step ids, duplicate params. A recipe is a sequence of real operations: the expensive place to discover a typo is step four, after steps one to three have already changed something. Reporting one problem per run means one run per typo.A test asserts every embedded recipe passes the new validation, so a rule that would reject something we ship fails here rather than in a user's hands.
Schema
recipe validategets a real schema rather than an entry inTestSchemaCoverage's accepted-gaps list, whose own comment says the goal is to drive that list to zero.SchemaVersion1.12.5 → 1.13.0 — two additive changes (thesourceproperty on recipe-run, the new recipe-validate file), baseline re-snapshotted. Both copies underschemas/output/andinternal/schema/files/are byte-identical, as the drift test requires.Testing
make test -race— 25 packages, 0 failuresmake lint— no findingsinternal/recipe/load_test.gocovering strict parsing, multi-document rejection, empty input, the all-problems-at-once validator, and the embedded-recipe compatibility checkvalidate,run --filewith the gate forwarded,sourcein the JSON result, the both-sources refusal, and the error text for a misspelled fieldDeliberately not here
No
--expect-sha256provenance pin. A recipe's steps are trond subcommands, each individually gated (#211) and audited, so--filedoes not widen what trond can do — it changes who typed the sequence. A checksum pin protects against a file changing between review and run, which is a real concern but a separate one, and it belongs withkind: host, where the capability ceiling actually moves.