Runnable

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.

.runnable/workflows/deploy.ymlYAML
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.sh

Six-hour timeout parity

A job or individual step may set 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.

TriggerConfigurationNotes
pushbranches, branches-ignore, tags, tags-ignore, paths, paths-ignoreOrdered negative patterns and branch-versus-tag behavior are supported.
pull_requestbranches, paths, typesRuns the synthetic merge ref and exposes base/head refs. Fork safety applies.
issue_commenttypesIssue and pull-request comments; trusted default-branch workflow and fork safety apply.
pull_request_reviewtypesReview lifecycle events, including Claude Code's submitted trigger.
pull_request_review_commenttypesInline review-comment events, including Claude Code's created trigger.
merge_groupbranches, typesSupports checks_requested for merge queues.
releasetypesProvider release events.
issuestypesProvider issue lifecycle events.
schedulecronFive-field POSIX cron in UTC; checked once per minute.
workflow_dispatchtyped inputsboolean, choice, environment, number, or string.
repository_dispatchtypesMatches the supplied event type and exposes client_payload.
workflow_callinputs, outputs, secretsReusable 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.

KeySupported contract
runs-onubuntu-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.
needsOne job ID or a list. Results and declared outputs are available through the needs context.
ifExpressions and success(), failure(), cancelled(), and always() status functions.
envWorkflow, job, and step maps; narrower YAML scope overrides broader scope.
defaults.runbash/sh shell and working-directory defaults.
continue-on-errorBoolean or expression at job and step scope.
run / usesEvery step must declare exactly the execution work it performs. Remote action refs include @ref.

Expression contexts

Runnable evaluates 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 deep exclude.
  • fail-fast, max-parallel, and expression-based continue-on-error.
  • Step outputs written to $GITHUB_OUTPUT, job outputs, and needs.job.outputs.name.
  • Command files for environment, path, state, and summaries, plus annotations, groups, masking, and problem matchers.

Dynamic reusable callers

Dynamic matrices are supported for ordinary jobs, but a reusable-workflow caller job cannot itself use a dynamic matrix.

Permissions and concurrency

Repository tokens are short-lived, scoped to one repository, refreshed per step, and narrowed to the declared workflow or job permissions.

ControlBehavior
permissions: read-allRequests read for every supported repository scope.
permissions: {}No repository token permissions.
Fine-grained mapSupported read/write scopes are validated against the GitHub App installation grants.
Fork pull requestAll write scopes are downgraded to read and customer secrets are withheld.
id-token: writeEnables five-minute RS256 OIDC tokens through the Actions toolkit contract. Issuance is denied for forks.
concurrency.groupRepository-scoped and case-insensitive at workflow or job scope.
cancel-in-progressBoolean or expression; cancels the active member when true.
queue: singleNewest waiting member replaces the previous pending member.
queue: maxFIFO admission with up to 100 pending entries; incompatible with cancel-in-progress: true.

OIDC needs a stable production key

Configure 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 and called workflowYAML
# 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.

.runnable/workflows/integration.ymlYAML
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, and job.services context.
  • docker run remains available for a single explicitly managed dependency.
  • docker compose up -d --wait supports 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

Container image pulls can use any publicly routable registry. Private, loopback, link-local, and cloud-metadata networks stay blocked. Private-registry credentials belong in Runnable secrets and should be passed only to the login step that needs them.

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.

workspace handoff extensionYAML
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 test

Select a source in fan-in jobs

When multiple dependencies persisted workspaces, set 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

The snapshot contains workspace files as written by the job. Runnable removes stored Git HTTPS credentials, but it does not inspect or rewrite arbitrary files. Keep generated tokens, private keys, and decrypted configuration outside the workspace or delete them before persistence.

Actions, cache, and artifacts

A connected repository can resolve action metadata and nested actions that a pasted standalone workflow cannot inspect.

CapabilityStateBoundary
actions/checkout@v4–v6FullNative GitHub.com checkout including depth, tags, LFS, submodules, sparse checkout, clean, safe-directory, and HTTPS token persistence.
actions/cache@v3–v5FullRestore/save variants, immutable keys, exact/prefix matching, branch scopes, seven-day idle expiry, lookup-only, and read-only fork behavior.
upload/download-artifact@v4FullImmutable artifacts, glob roots, compression, retention, overwrite, digest outputs, selection/merge behavior, and a 500-artifact-per-job limit.
JavaScript, Docker, compositeFullResolved Node 20/24 entrypoints, pre/post hooks, state, nested actions, metadata Docker actions, and docker:// actions.
Other actions in paste checkerRuntime-dependentRepository 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.

StateMeaningMigration
FullParity-backed implementation and test evidence.Eligible.
PartialExecutes with a documented semantic difference.Review required.
Runtime-dependentNeeds repository/action/runtime/external-service resolution.Blocked until resolved.
UnsupportedRejected 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, and parallel.
  • Non-bash/sh literal shells, custom GitHub servers for native checkout, and SSH checkout authentication.
NextGitHub setupInstall the app and discover workflows from selected repositories.