Test expressions fast with the eval playground
orchstep eval evaluates any template or JavaScript expression against your real workflow context — environments, vars-files, --var and all — without running a single step. It replaces the throwaway echo-step debug loop.
Here is the debug loop everyone knows: you are not sure what
{{ vars.db_host }} resolves to under --env production, so you add a throwaway
echo step, run the task, read the line, delete the step. Repeat for the next
expression. It works, but it is slow and it executes real steps you may not want
to run.
orchstep eval collapses that loop to one command. It answers a single
question instantly: "what value would this expression have, with these exact
flags?" It builds the same merged variable context orchstep run would —
workflow defaults, environment groups, --vars-file, --var — then evaluates
your expression against it. No steps run; nothing is touched.
It is a calculator, even with no workflow
With no orchstep.yml around, eval is a bare expression calculator — handy
for checking a Sprig function or a bit of JS before you wire it in:
$ orchstep eval '1 + 2 * 3'
7
$ orchstep eval --lang template '{{ "" | default "fallback" }}'
fallback
$ orchstep eval --lang template '{{ regexFind "v([0-9]+)" "release-v42" }}'
42Fully wrapped in {{ }} evaluates as a Go template; anything else as
JavaScript — the identical rule if: and assert use at run time.
Where it shines: against a real environment
Point it at a workflow with environments and you can interrogate exactly what
each --env produces. Given a service whose production environment overrides
the database host and replica count:
$ orchstep eval --env production 'vars.db_host'
prod-db.internal:5432
$ orchstep eval --env production --var replicas=99 '{{ vars.replicas }}'
99
$ orchstep eval --env production 'vars.require_approval === "true" && Number(vars.replicas) > 4'
trueThat third one is the real win — you can test the exact JavaScript condition a
retry.when or assert will run, against the exact merged context, before
committing it. Mixed text works too, with --lang template:
$ orchstep eval --env staging --lang template 'deploying {{ vars.app_name }} to {{ vars.env_name }} ({{ vars.replicas }} replicas)'
deploying checkout-service to staging (4 replicas)Objects print as JSON; scalars print plainly:
$ orchstep eval --env staging '({host: vars.db_host, level: vars.log_level})'
{
"host": "staging-db.internal:5432",
"level": "debug"
}And errors surface exactly as they would in a workflow — and exit non-zero, so
eval is scriptable as a check:
$ orchstep eval 'vars.nope.deep'
failed to evaluate JavaScript expression: TypeError: Cannot read property 'deep' of undefined--explain: not just what, but why
eval answers "what is the value." Add --explain and it answers "why is it
that value — which precedence layer won." With no key it prints every effective
variable and the layer that supplied it, like git config --show-origin:
$ orchstep eval --explain --env production --var replicas=20
VARIABLE SOURCE VALUE
region group eu-west-1 (over defaults)
replicas runtime 20 (over defaults, environment)The (over ...) column shows which lower-precedence layers each value shadowed.
When a variable is mysteriously "wrong," this tells you instantly whether a
group file, an environment, or a stray --var won — no more bisecting your
config by hand.
The REPL
Run eval with no expression in a terminal and you get an interactive
playground — dump the full merged variable map, switch languages, iterate:
$ orchstep eval -f orchstep.yml --env staging
orchstep eval - interactive expression playground
eval(auto)> vars.replicas
[javascript] 4 (truthy=true)
eval(auto)> {{ vars.db_host }}
[template] staging-db.internal:5432 (truthy=true)
eval(auto)> :vars # dump the full merged variable map as JSON
eval(auto)> :qWhat you gained
| The old loop | With orchstep eval |
|---|---|
add echo step → run → read → delete | one command, no edit |
| runs real steps to see a value | nothing executes |
| guess which layer set a variable | --explain shows the winner |
| "will this condition pass?" | test the exact JS/template expression |
Why the results can't lie
eval is not a re-implementation. It calls the engine's own evaluation paths —
the same condition evaluator and template pipeline a run uses. Whatever it
prints is what your if:, assert, until:, or {{ }} will do at run time,
byte for byte. The honest boundary: eval evaluates expressions against
context; it does not execute steps, so it will not tell you whether a shell
command succeeds or what a step outputs at run time. For "what does this
expression resolve to" and "why," though, it is the fastest tool in the box.
Where to go next
- Expression Playground — every
evalflag and the REPL commands - Templates & Expressions — the Go-template + Sprig side
- JavaScript Expressions — the JS-first syntax guide
Next time you reach for a throwaway echo step — don't. orchstep eval '<expression>' answers in milliseconds, against your real context.
curl -fsSL https://orchstep.dev/install.sh | sh