Build and run
Workflow reference
Runnable reads Actions-compatible YAML from .runnable/workflows, validates the complete execution plan, and preserves that immutable snapshot for every run and rerun.
File location and complete syntax
Use .yml or .yaml files directly under .runnable/workflows. A workflow needs a name, at least one supported trigger, and at least one job.
name: Test and deploy
run-name: "Deploy ${{ github.ref_name }} by ${{ github.actor }}"
on:
push:
branches: [main]
paths: ["src/**", "package-lock.json"]
workflow_dispatch:
inputs:
environment:
type: environment
required: true
permissions:
contents: read
checks: write
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-24.04
strategy:
fail-fast: true
max-parallel: 2
matrix:
node: [20, 24]
outputs:
artifact: ${{ steps.result.outputs.name }}
steps:
- uses: actions/checkout@v4
- id: result
run: echo "name=app-${{ matrix.node }}" >> "$GITHUB_OUTPUT"
- run: npm test
deploy:
needs: test
if: ${{ success() }}
environment:
name: ${{ inputs.environment || 'production' }}
url: https://app.example.com
runs-on: runnable-4vcpu-ubuntu-2404
steps:
- run: ./scripts/deploy.shSix-hour timeout parity
timeout-minutes from 1 through 360. The default is 360 minutes, matching the GitHub-hosted job contract.Triggers
Event filters are evaluated against the immutable provider event. Five-field schedules use UTC and are admitted at most once per workflow, cron expression, and minute.
| Trigger | Configuration | Notes |
|---|---|---|
push | branches, branches-ignore, tags, tags-ignore, paths, paths-ignore | Ordered negative patterns and branch-versus-tag behavior are supported. |
pull_request | branches, paths, types | Runs the synthetic merge ref and exposes base/head refs. Fork safety applies. |
issue_comment | types | Issue and pull-request comments; trusted default-branch workflow and fork safety apply. |
pull_request_review | types | Review lifecycle events, including Claude Code's submitted trigger. |
pull_request_review_comment | types | Inline review-comment events, including Claude Code's created trigger. |
merge_group | branches, types | Supports checks_requested for merge queues. |
release | types | Provider release events. |
issues | types | Provider issue lifecycle events. |
schedule | cron | Five-field POSIX cron in UTC; checked once per minute. |
workflow_dispatch | typed inputs | boolean, choice, environment, number, or string. |
repository_dispatch | types | Matches the supplied event type and exposes client_payload. |
workflow_call | inputs, outputs, secrets | Reusable workflows resolved from the trusted source revision. |
Jobs and steps
Dependencies form a persisted DAG. Runnable schedules a job only after all entries in needs are terminal and its condition evaluates true.
| Key | Supported contract |
|---|---|
runs-on | ubuntu-latest, ubuntu-22.04, ubuntu-24.04, or runnable-{2|4|8}vcpu-ubuntu-{2204|2404}. Existing blacksmith-* labels remain migration aliases up to 8 vCPU. |
needs | One job ID or a list. Results and declared outputs are available through the needs context. |
if | Expressions and success(), failure(), cancelled(), and always() status functions. |
env | Workflow, job, and step maps; narrower YAML scope overrides broader scope. |
defaults.run | bash/sh shell and working-directory defaults. |
continue-on-error | Boolean or expression at job and step scope. |
run / uses | Every step must declare exactly the execution work it performs. Remote action refs include @ref. |
Expression contexts
github, runner, job, strategy, matrix, steps, needs, inputs, secrets, vars, and env where GitHub-compatible context availability permits them. JSON helpers and workspace-scoped hashFiles are supported.Matrices, outputs, and data flow
Static matrices expand when the plan is created. A matrix derived from a dependency output expands atomically after that dependency completes.
- Cartesian products, ordered
include, and deepexclude. fail-fast,max-parallel, and expression-basedcontinue-on-error.- Step outputs written to
$GITHUB_OUTPUT, job outputs, andneeds.job.outputs.name. - Command files for environment, path, state, and summaries, plus annotations, groups, masking, and problem matchers.
Dynamic reusable callers
Permissions and concurrency
Repository tokens are short-lived, scoped to one repository, refreshed per step, and narrowed to the declared workflow or job permissions.
| Control | Behavior |
|---|---|
permissions: read-all | Requests read for every supported repository scope. |
permissions: {} | No repository token permissions. |
| Fine-grained map | Supported read/write scopes are validated against the GitHub App installation grants. |
| Fork pull request | All write scopes are downgraded to read and customer secrets are withheld. |
id-token: write | Enables five-minute RS256 OIDC tokens through the Actions toolkit contract. Issuance is denied for forks. |
concurrency.group | Repository-scoped and case-insensitive at workflow or job scope. |
cancel-in-progress | Boolean or expression; cancels the active member when true. |
queue: single | Newest waiting member replaces the previous pending member. |
queue: max | FIFO admission with up to 100 pending entries; incompatible with cancel-in-progress: true. |
OIDC needs a stable production key
RUNNABLE_OIDC_PRIVATE_KEY, RUNNABLE_OIDC_KEY_ID, and optionally RUNNABLE_OIDC_ISSUER. Discovery and JWKS are published by Runnable; the external verifier must trust that Runnable issuer. Workflows fail closed if issuance is requested before production signing is configured.Reusable workflows
Connected repositories resolve calls recursively at snapshot time. Local references use the trusted commit; remote references use the installation-scoped GitHub client.
# caller
jobs:
checks:
uses: ./.runnable/workflows/reusable.yml
with:
node-version: 24
secrets: inherit
# reusable.yml
on:
workflow_call:
inputs:
node-version:
type: number
required: true
outputs:
result:
value: ${{ jobs.test.outputs.result }}Cycles are rejected. Called jobs are namespaced into the caller DAG, while a virtual terminal job preserves caller needs, outputs, conditions, inputs, secrets, matrices, and concurrency without provisioning an extra machine.
Service containers, Docker, Compose, and Testcontainers
Every Linux job starts with a local Docker daemon, Buildx, and Compose v2. Native Actions services and repository-managed Compose/Testcontainers stacks use that isolated daemon.
jobs:
test:
runs-on: runnable-4vcpu-ubuntu-2404
services:
postgres:
image: postgres:17
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432/tcp
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v4
- run: npm test
env:
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:${{ job.services.postgres.ports['5432'] }}/app- Native
services:supports images, private credentials, environment, ports, volumes, options, health checks, andjob.servicescontext. docker runremains available for a single explicitly managed dependency.docker compose up -d --waitsupports multi-service stacks defined in the repository.- Testcontainers uses the standard local Docker socket; Runnable's own integration suite starts PostgreSQL through Node Testcontainers.
- Native services are logged on startup failure and always cleaned up. Compose and manually started containers remain workflow-managed.
Public registries are reachable
Transfer a workspace across jobs
Runnable's opt-in extension can carry an installed workspace through a dependency chain without re-running npm ci. The default remains GitHub-compatible job isolation.
jobs:
install:
runs-on: ubuntu-latest
env:
RUNNABLE_WORKSPACE: persist
steps:
- uses: actions/checkout@v6
- run: npm ci
test:
needs: install
runs-on: ubuntu-latest
env:
RUNNABLE_WORKSPACE: restore
steps:
- run: npm testSelect a source in fan-in jobs
RUNNABLE_WORKSPACE_FROM to the source job key, or a comma-separated ordered list. Snapshots are integrity-checked, retained for one day, and scrub stored Git credentials before upload.Do not snapshot generated credentials
Actions, cache, and artifacts
A connected repository can resolve action metadata and nested actions that a pasted standalone workflow cannot inspect.
| Capability | State | Boundary |
|---|---|---|
| actions/checkout@v4–v6 | Full | Native GitHub.com checkout including depth, tags, LFS, submodules, sparse checkout, clean, safe-directory, and HTTPS token persistence. |
| actions/cache@v3–v5 | Full | Restore/save variants, immutable keys, exact/prefix matching, branch scopes, seven-day idle expiry, lookup-only, and read-only fork behavior. |
| upload/download-artifact@v4 | Full | Immutable artifacts, glob roots, compression, retention, overwrite, digest outputs, selection/merge behavior, and a 500-artifact-per-job limit. |
| JavaScript, Docker, composite | Full | Resolved Node 20/24 entrypoints, pre/post hooks, state, nested actions, metadata Docker actions, and docker:// actions. |
| Other actions in paste checker | Runtime-dependent | Repository files, action.yml, runtime, nested actions, inputs, and hooks must be resolved. |
Compatibility boundaries
Accepted YAML is not automatically Full. Runnable reports the narrowest state supported by the complete resolved workflow.
| State | Meaning | Migration |
|---|---|---|
| Full | Parity-backed implementation and test evidence. | Eligible. |
| Partial | Executes with a documented semantic difference. | Review required. |
| Runtime-dependent | Needs repository/action/runtime/external-service resolution. | Blocked until resolved. |
| Unsupported | Rejected or cannot preserve semantics. | Blocked. |
- Windows, macOS, ARM, and native job
container:syntax. Service containers, Docker, Compose, and Testcontainers are supported. - Custom orchestration steps:
snapshot,background,wait,wait-all,cancel, andparallel. - Non-bash/sh literal shells, custom GitHub servers for native checkout, and SSH checkout authentication.

