Skip to content

Writing Pipelines

A pipeline is a YAML file with a name, an optional description, optional vars, and a list of steps.

Minimal example

name: deploy
description: "Build and deploy the app"
steps:
- id: build
run: "go build -o app ."
- id: deploy
run: "scp app server:/opt/app"

Step anatomy

Every step requires two fields:

FieldDescription
idUnique identifier — used in logs, state files, env vars, and depends_on references
runCommand(s) to execute (see run modes below)

Optional fields:

FieldDescription
depends_onStep(s) that must complete before this one starts
retryNumber of retries on failure (default 0)
sensitiveWhen true, output is excluded from the state file
cacheCache successful results to skip re-execution

See the YAML Schema for the complete field reference.

Three run modes

Single command

A scalar string runs one command. Its stdout is captured and exposed as PIPE_<STEP_ID>.

- id: get-version
run: "git describe --tags --always"

Downstream steps can use $PIPE_GET_VERSION.

Parallel strings

A list of strings runs all commands concurrently. Output is not captured — these are fire-and-forget tasks like linting or health checks.

- id: checks
run:
- "go vet ./..."
- "golangci-lint run"
- "go test -race ./..."

Named sub-runs

A list of mappings runs named commands concurrently, each with individual output capture. Each sub-run’s output is available as PIPE_<STEP_ID>_<SUBRUN_ID>.

- id: build
run:
- id: linux
run: "GOOS=linux go build -o dist/app-linux ."
- id: darwin
run: "GOOS=darwin go build -o dist/app-darwin ."

Sub-runs also support the sensitive flag individually.

Naming rules

  • Step and sub-run IDs must be unique within the pipeline.
  • IDs are used to build environment variable names: hyphens become underscores, everything is uppercased, and prefixed with PIPE_.
  • Pipeline names cannot conflict with reserved command words: init, list, validate, cache, login, logout, pull, push, mv, alias, inspect, switch.

Passing output between steps

Each single-command step’s stdout (trimmed) is injected into the environment for all subsequent steps:

steps:
- id: get-version
run: "cat VERSION"
- id: build
run: "docker build -t app:$PIPE_GET_VERSION ."

For named sub-runs, each sub-run produces its own variable: PIPE_<STEP>_<SUBRUN>.

steps:
- id: fetch
run:
- id: api-version
run: "curl -s https://api.example.com/version"
- id: db-version
run: "psql -t -c 'SELECT version()'"
- id: report
run: "echo api=$PIPE_FETCH_API_VERSION db=$PIPE_FETCH_DB_VERSION"