Docs menu

Workflow file reference

.md

Every key `.oak/workflows/*.yml` accepts, with the exact rules of Oak's YAML subset.

Workflows live in .oak/workflows/ and are read from the commit being tested. This page is the complete format.

Oak's workflow files look like GitHub Actions at the trigger / job / step level, but they're a small, strict subset of YAML that Oak parses itself. That keeps behavior predictable: anything outside the subset is a line-numbered error, never a silent misread. A workflow that fails to parse still produces a run โ€” one that fails immediately and shows the error.

Files#

  • Location: directly in .oak/workflows/, ending in .yml or .yaml. Subdirectories are ignored.
  • Name: the file name without its extension. .oak/workflows/ci.yml is the workflow ci โ€” that's the name oak ci and the API use.
  • Size: at most 64 KiB.
  • Independence: each file that matches an event starts its own run. Separate files are the way to run independent checks in parallel.

A complete example#

# .oak/workflows/ci.yml
on: [push, branch.created, merge]

env:
  CARGO_TERM_COLOR: always
  DATABASE_URL: postgres://postgres@localhost:5432/test

jobs:
  test:
    env:
      RUST_BACKTRACE: "1"
    cache:
      key: cargo
      paths:
        - ~/.cargo/registry
        - ~/.cargo/git
        - target
    steps:
      - name: start postgres
        run: bash scripts/ci/start-postgres.sh
      - name: lint
        run: cargo clippy --all-targets -- -D warnings
      - name: test
        run: cargo test --locked

Top-level keys#

Only these three are allowed.

KeyRequiredValue
onyesThe events that start this workflow.
envnoEnvironment variables for every step.
jobsyesOne or more named jobs.

on#

Which events start the workflow. Three spellings are accepted:

on: push
on: [push, branch.created, merge]
on:
  - push
  - branch.created
EventFires when
pushAn existing branch gets new commits (including a revert landing on it).
branch.createdA branch is pushed for the first time.
mergeA branch is squash-merged onto main; the run tests the new main commit.
manualSomeone triggers it with oak ci trigger or the API.

Event names are case-insensitive; anything else is an error. Because a branch's first push is branch.created, not push, use [push, branch.created] to test every push.

env#

Flat KEY: value lines, at the top level and/or inside a job:

env:
  NODE_ENV: test
  RETRIES: "3"
  • Values are strings. A single pair of matching quotes ("โ€ฆ" or 'โ€ฆ') around a value is removed, so quote values that contain # or start with special characters.
  • A job's env overrides the top-level env for the same key.
  • CI secrets override both.
  • Keys starting with OAK_CI_ are reserved and rejected.
  • An env: block can't be empty; leave it out instead.

jobs#

A map of job names to job definitions. Each job accepts only these keys:

KeyRequiredValue
stepsyesThe commands to run, in order. At least one.
envnoVariables for this job's steps (see env).
cachenoPaths to persist between runs (see cache).
imagenoAccepted, but currently ignored โ€” every job runs in the standard runner image.

Jobs run one after another, in one container. Today a workflow's jobs are flattened into a single ordered list of steps that run sequentially in the same container โ€” not in parallel, and not isolated from each other. Their env maps are merged (later jobs win), and only the first job with a cache: block gets a cache. Until that changes, use one job per workflow, and put independent checks in separate workflow files.

steps#

steps:
  - name: unit tests
    run: cargo test --lib
  - run: ./scripts/lint.sh
KeyRequiredValue
runyesOne shell command line.
namenoLabel shown in the UI and oak ci logs. Defaults to step N.

How steps execute:

  • Each step runs in its own shell process, starting in /workspace (the repository root at the commit being tested). A cd or export in one step doesn't carry into the next โ€” chain with && within a step instead.
  • Background processes you start (a database, say) keep running for the rest of the run.
  • A non-zero exit fails the step and the run; later steps are marked skipped.
  • run is one line. Block scalars (run: |) aren't supported. For anything longer, put a script in the repo and call it: run: bash scripts/ci/deploy.sh. &&, ||, pipes and ; work as usual within the line.

cache#

cache:
  key: cargo          # optional; defaults to the job name
  paths:              # required; 1 to 16 entries
    - ~/.cargo/registry
    - node_modules
    - target
  • Paths may be absolute, start with ~/, or be relative to /workspace. They can't contain ", $, backticks, \, or control characters.
  • Restore happens before the first step: the run's own branch's cache if there is one, otherwise main's.
  • Save happens after the last step, only if every step succeeded, and only to the run's own branch. The newest save replaces the old one.
  • Change key to start a fresh cache (for example after a toolchain upgrade).
  • A single cache archive can be up to 12 GiB, and a repo's caches together up to 10 GiB before the least recently used are evicted.
  • Caching is best-effort: a failed restore or save is logged, never fatal.

Environment variables in every step#

VariableValue
OAK_CI_RUN_IDThe run's id, as shown by oak ci runs.
OAK_CI_COMMITThe full hash of the commit being tested.
CINot set by Oak โ€” set it in env: if your tools look for it.

Plus your workflow and job env, and every CI secret. Steps run as root; /root/.cargo/bin and /root/.bun/bin are on PATH.

Syntax rules#

  • Indent with spaces; tabs are rejected.
  • # starts a comment at the beginning of a line or after whitespace, but not inside quotes.
  • Unknown keys are errors at every level โ€” a typo like step: is caught, not ignored.
  • Lists are - item lines or inline [a, b].

Not supported (yet)#

These GitHub Actions features have no equivalent today. The table says what to do instead.

FeatureInstead
uses: / marketplace actionsInstall tools in a step (apt-get, curl, npm, cargo install).
needs:, parallel jobsSeparate workflow files run as separate, parallel runs.
if: conditionsCheck in your script: [ "$X" = y ] || exit 0.
matrix:One workflow file per variant.
timeout-minutes:Runs are capped by your plan's wall-clock limit; use timeout 600 cmd within a step.
services:Start the service in a step โ€” PostgreSQL is pre-installed.
working-directory:cd subdir && cmd within the step.
ArtifactsUpload from a step (rsync, curl, an object store CLI) with credentials in a secret.
Branch filtersNot available yet โ€” every matching event runs, and runs aren't told their branch name. Split work that should only run after merging into a workflow with on: [merge].
Multi-line run: |Put the commands in a script file in the repo.

Something here wrong or missing? Run oak feedback -m "โ€ฆ" or email [email protected].