Documentation
¶
Overview ¶
Package recipe loads, validates, and resolves recipes: saved `session` scripts with a small header.
A recipe is deliberately not a language. Every step's `run` is exactly the argv array a `session` stdin line carries, so anything valid in `session` is valid here and vice versa. This package never touches Chrome and never runs anything: it reads a file, validates it, substitutes declared inputs into argv elements, and hands back a Plan of argv lines for the caller to feed through the existing `session` execution path.
The split is the point. Everything a recipe can get wrong statically — malformed YAML, an undeclared `{{placeholder}}`, a missing required input, an unknown --set key, an out-of-range --from-step — is an error from this package, raised before the CLI connects to a browser.
Index ¶
Constants ¶
const ( OnErrorAbort = "abort" OnErrorContinue = "continue" )
Error abort/continue values for a step's on_error.
const MaxSteps = 200
MaxSteps caps a recipe's length. A bound removes a whole category of design questions (and of runaway shared files) for the price of one constant.
Variables ¶
var ErrNotFound = errors.New("recipe not found")
ErrNotFound reports a name that matched no file in any search directory.
Functions ¶
func Find ¶
Find locates a recipe file by name, returning the first match in the search path along with the source that provided it.
Types ¶
type Dir ¶
Dir is one entry in the recipe search path.
func SearchPath ¶
SearchPath returns the ordered recipe search path; the first match wins.
Project-local beats user-global on purpose: a repo that carries .chrome-cdp/recipes/ hands its automations to everyone who clones it, and a teammate's checkout must win over whatever they happen to have in their own config dir. Empty arguments are skipped, so a caller that cannot determine a working directory simply gets a shorter path.
type Entry ¶
Entry is one row of a recipe listing: a loaded recipe, or the error that stopped it loading. One malformed file must not hide every other recipe.
type Input ¶
type Input struct {
Required bool `json:"required"`
Default string `json:"default,omitempty"`
Description string `json:"description,omitempty"`
}
Input declares one parameter. That is the whole schema — no types, no validation expressions; a recipe that needs them should be a program calling `session`.
type Opts ¶
type Opts struct {
Set map[string]string // values from --set
Target string // overrides the recipe's own `target:` when set
FromStep int // 1-based start step; 0 means "from the beginning"
Split Splitter // optional; see Splitter
}
Opts are the run-time knobs Resolve applies.
type Plan ¶
type Plan struct {
Recipe *Recipe
Inputs map[string]string
Steps []PlanStep
FromStep int // 1-based; 1 unless --from-step moved it
Target string
}
Plan is a fully resolved recipe: nothing left to decide, nothing left to look up.
func Resolve ¶
Resolve turns a validated Recipe into a Plan: it checks the supplied inputs against the declared ones, substitutes them into argv elements, injects the effective --target, and applies --from-step.
Substitution is a single pass, so a value that itself contains `{{...}}` is carried through literally rather than re-expanded — an input can never smuggle in another input's value.
type PlanStep ¶
type PlanStep struct {
// Index is the 1-based position in the recipe file, preserved across
// --from-step so a summary points at the step the author would recognise.
Index int `json:"step"`
Label string `json:"label,omitempty"`
Argv []string `json:"run"`
OnError string `json:"on_error"`
}
PlanStep is one resolved step: argv with every placeholder substituted, ready to hand to the `session` execution path unchanged.
type Recipe ¶
type Recipe struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Inputs map[string]Input `json:"inputs,omitempty"`
Target string `json:"target,omitempty"`
Steps []Step `json:"steps"`
// Path is where the recipe was read from; Source names the search-path
// entry that provided it ("project", "user", or "--dir").
Path string `json:"path"`
Source string `json:"source"`
}
Recipe is a loaded, schema-valid recipe. Inputs are unresolved at this stage: Run still carries `{{placeholder}}` text, and every placeholder in it is guaranteed to name a declared input.
func Load ¶
Load reads and validates one recipe file. source labels which search-path entry provided it and is carried through to the envelope.
func (*Recipe) InputNames ¶
InputNames returns the declared input names in sorted order, so listing and showing a recipe is deterministic despite the underlying map.
type Splitter ¶
Splitter classifies one step's argv AS WRITTEN — before any substitution — into the elements the command tree may parse (the command path and the step's own flags, with their values) and the elements that must arrive as data. Indices are into the argv it was given; an index in neither slice is dropped, which is how a step's own `--` is absorbed and re-emitted in the one place it belongs. ok is false when the argv names no command it can resolve, in which case Resolve leaves the argv alone and lets the command tree produce its ordinary usage error.
It reads the argv as written because that is the only text the recipe's AUTHOR wrote. What is a flag has to be decided there and never by an input value: `--set sel=--target=@2` substituted into a step's positional is data, not a second target.
The recipe package cannot implement this — flag arity lives in the command tree — so the CLI supplies it. Resolve without one keeps the argv in written order, which is what the pure tests here use.