Skip to main content
Version: v5.7.0

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 optional summary: 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) — that opschain actions list and the GUI's action buttons render; without one, the description's first sentence stands in.
  • Each step has a name: and exactly one of run: (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) or emit: (publish a custom event — lesson 5.4).
  • Top-level steps run sequentially. A stage: step groups nested steps and sets their strategy: (sequential or parallel).

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 --plan print that a run does not, and what is the process exit code of a local run that succeeds?

What you learned​

  • opschain init scaffolds .opschain/config.yaml and one hello action, and never touches anything else.
  • An action file is one action with named steps; each step is exactly one of run:, uses:, change:, wait: or emit:.
  • --plan --local prints the compiled step tree; --local runs a snapshot of your working copy in your terminal, exit code as outcome.
  • opschain actions list --local lists 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).