2.1 Your first action
Included from Basic tier
After this you can: scaffold a repository, read an action file line by line, plan it, and run it on your own machine with --local.
Before you start — tick each one:
- Lesson 0.2 — Local mode and server mode (
opschain learn 0.2)
In the GUI: nothing yet — authoring is a working-tree activity; an asset's Actions tab lists this catalog once a template version pins your commit (lesson 3.4).
An action is where you define what "deploy" actually does. In this lesson you scaffold a repository, read the one action file it creates, plan that action and run it — all on the local lane, no server involved. Everything after it in this module builds on these four moves.
1. Scaffold a repository
In a git repository with at least one commit (a local run snapshots your
working copy against HEAD, so an empty repository cannot run yet):
opschain init
created .opschain/config.yaml
created .opschain/actions/hello.yaml
Detected:
git ok git version 2.50.1
container runtime ok Docker version 29.9.2
Next: opschain runs create change hello --env dev --plan
init is idempotent and never touches anything outside .opschain/ —
an existing file reports exists instead of created. (A repository
that already carries an actions.rb — only applicable to existing
MintPress customers and those using the MintPress SDK or the
packs:mintpress feature — also reports that file and the Ruby
toolchain; a fresh repository is YAML-only and the report says so by
omission.) The scaffolded config.yaml names your project and its
environments:
project: myrepo
environments:
- code: dev
properties: .opschain/properties
2. Anatomy of an action file
One file defines one action, and the steps it orchestrates live inside it.
The scaffolded .opschain/actions/hello.yaml:
action: hello
description: Example action scaffolded by opschain init
steps:
- name: say_hello
run: echo "hello from opschain"
The rules are few:
action:matches the filename; names are lowercase[a-z0-9_]+.description:is the full operator detail, printed by the show views and never truncated. An optionalsummary:beside it is the one-line caption — imperative, a single line of at most 120 characters (a multi-line value is refused at load by name) — thatopschain actions listand the GUI's action buttons render; without one, the description's first sentence stands in.- Each step has a
name:and exactly one ofrun:(a shell command),uses:(compose another action — lesson 2.2),change:(invoke another node's action as a child change — lesson 2.2),wait:(pause for a human — lesson 2.3) oremit:(publish a custom event — lesson 5.4). - Top-level steps run sequentially. A
stage:step groups nested steps and sets theirstrategy:(sequentialorparallel).
3. Plan, then run
--plan resolves the step tree without executing anything:
opschain runs create change hello --env dev --plan --local
· hello
└─ · say_hello
The local plan is the compiled step tree, exactly as above. The richer two-tier plan — everything decidable before execution evaluated concretely with its verdict evidence, anything that depends on a step's published outputs rendered symbolically as the expression span — is the server plan's (lesson 4.1). A plan is always advisory: the run re-evaluates every condition at step entry.
Now run it for real. A local run executes right in your terminal — live step tree, streamed logs — and the process exit code is the run's outcome (0 success, 1 failed, 3 cancelled, 4 timed out):
opschain runs create change hello --env dev --local
2026-11-07T00:25:48Z hello pending -> queued
2026-11-07T00:25:48Z hello queued -> running
2026-11-07T00:25:48Z hello/say_hello pending -> queued
2026-11-07T00:25:48Z hello/say_hello queued -> running
[hello/say_hello] hello from opschain
2026-11-07T00:25:48Z hello/say_hello running -> success
2026-11-07T00:25:48Z hello running -> success
run 01a1b365-b05e-7026-9ab3-edd93e92d8b2 success (0.2s)
--env names the target environment from your config.yaml (and
--asset an asset within it). A run executes a snapshot of your working
copy — tracked changes and untracked-but-not-ignored files included, so
you can iterate without committing; git-ignored files are never part of
a run. --ref <git-ref> runs a clean pinned commit instead (lesson 2.6).
4. Browse your catalog
opschain actions list --local
ACTION SOURCE INPUTS APPROVALS STATUS SUMMARY
hello repo no no Example action scaffolded by opschain init
The listing is compiled cheaply from your action files — nothing
executes — and says whether each action declares inputs or approval
requirements. A broken file is listed with invalid: <reason> in STATUS,
never silently dropped. opschain actions show hello --local prints one
action in full: its source file, summary and the complete description.
Your run history is local too — opschain runs list --local,
opschain runs show <run-id> --local and opschain logs <run-id> --local;
lesson 2.6 shows what each prints.
Check yourself
- What does
--planprint that a run does not, and what is the process exit code of a local run that succeeds?
What you learned
opschain initscaffolds.opschain/config.yamland onehelloaction, and never touches anything else.- An action file is one action with named steps; each step is exactly one of
run:,uses:,change:,wait:oremit:. --plan --localprints the compiled step tree;--localruns a snapshot of your working copy in your terminal, exit code as outcome.opschain actions list --locallists the catalog your repository defines, invalid files included.
Next: lesson 2.2 — Namespaces, composition and inputs (opschain learn 2.2), or back to your track (opschain learn).