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.ymlor.yaml. Subdirectories are ignored. - Name: the file name without its extension.
.oak/workflows/ci.ymlis the workflowciโ that's the nameoak ciand 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.
| Key | Required | Value |
|---|---|---|
on | yes | The events that start this workflow. |
env | no | Environment variables for every step. |
jobs | yes | One 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
| Event | Fires when |
|---|---|
push | An existing branch gets new commits (including a revert landing on it). |
branch.created | A branch is pushed for the first time. |
merge | A branch is squash-merged onto main; the run tests the new main commit. |
manual | Someone 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
envoverrides the top-levelenvfor 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:
| Key | Required | Value |
|---|---|---|
steps | yes | The commands to run, in order. At least one. |
env | no | Variables for this job's steps (see env). |
cache | no | Paths to persist between runs (see cache). |
image | no | Accepted, 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
envmaps are merged (later jobs win), and only the first job with acache: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
| Key | Required | Value |
|---|---|---|
run | yes | One shell command line. |
name | no | Label 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). Acdorexportin 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. runis 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
keyto 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#
| Variable | Value |
|---|---|
OAK_CI_RUN_ID | The run's id, as shown by oak ci runs. |
OAK_CI_COMMIT | The full hash of the commit being tested. |
CI | Not 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
- itemlines or inline[a, b].
Not supported (yet)#
These GitHub Actions features have no equivalent today. The table says what to do instead.
| Feature | Instead |
|---|---|
uses: / marketplace actions | Install tools in a step (apt-get, curl, npm, cargo install). |
needs:, parallel jobs | Separate workflow files run as separate, parallel runs. |
if: conditions | Check 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. |
| Artifacts | Upload from a step (rsync, curl, an object store CLI) with credentials in a secret. |
| Branch filters | Not 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. |