Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Say why workflows have no expression language

390e3404503477342ae434ef92e9ceb2729b7218 · Keenan Nemetz · 7d ago · parent e82bbe7d

Say why workflows have no expression language

The format has no `${{ }}` and no interpolation, but the reference listed that
only as a missing feature. The decision is now recorded where a reader will meet
it: the shell is the expression language, the event file is the context, and
event text stays data instead of becoming part of a command.

The reference gains a Context and expressions section with a manual-input
example, and the workflows guide says the same beside the trigger examples.

Part of #138.

docs/reference/workflow-reference.md +44−1

@@ -687,6 +687,48 @@ Only one job in a workflow may declare `pages`. The target branch is set by the
687 687 instance (`pages_ref`), not by the workflow. The deploy is refused if the run is no
688 688 longer the newest commit on its branch. See [Workflows § Publish a website](../workflows.md#publish-a-website).
689 689
690 ## Context and expressions
691
692 A workflow file is not a template. There is no `${{ }}` and no substitution in a
693 step's `run`: `env` values are literal, a step executes the shell it is given, and
694 everything the run knows arrives as an environment variable, a declared secret, or
695 the event file. A manual input is read from the event file like any other event
696 data:
697
698 ```yaml
699 on:
700 workflow-dispatch:
701 inputs:
702 environment:
703 type: choice
704 options: [staging, production]
705 jobs:
706 deploy:
707 image: alpine:3.22
708 steps:
709 - run: |
710 environment="$(jq -r '.inputs.environment' "$VOLE_EVENT_PATH")"
711 [ "$environment" = production ] || exit 0
712 ./deploy.sh
713 ```
714
715 `jq` is an example, not a dependency: the image is the dependency manifest, so
716 install the JSON reader the step uses.
717
718 This is a decision, not an unfinished feature:
719
720 - The shell is already the expression language. A second one would mean two ways
721 to write the same comparison, with its own parser, type rules, functions, and
722 escaping, all validated on every push.
723 - Event data is untrusted. A comment body, an issue title, or a manual input is
724 text someone else wrote; substituting it into `run` would turn data into code.
725 A file keeps it data, and the shell's quoting rules apply.
726 - A secret is a value, not a name to build. It already arrives as an environment
727 variable; an expression would invite it into a command line or a log line.
728 - The parts of a workflow that usually need expressions — `if`, `needs`, matrix
729 values, outputs — are not in the format. If they arrive, they can be typed
730 fields with their own small, checkable rules instead of a general language.
731
690 732 ## What a workflow cannot express
691 733
692 734 The format deliberately supports push and event triggers with jobs of shell steps.
@@ -696,7 +738,8 @@ It does not support:
696 738 - a build matrix or per-step conditions (`strategy`, `if`, `continue-on-error`)
697 739 - step-scoped secrets, expressions for building a secret name, or a value written
698 740 in the workflow file; secrets are job-scoped and the instance holds the values
699 - expressions or contexts (`${{ }}`); nothing outside the shell is interpolated
741 - expressions or `${{ }}` contexts; the shell is the expression language and the
742 event file is the context. See [Context and expressions](#context-and-expressions).
700 743 - service containers, cache keys, or artifact downloads between jobs
701 744 - job-level timeouts, concurrency groups, or cancel-in-progress
702 745 - workflow-level `env`, working directories, or shells; `env` is per job and every