runthrough

The full
rehearsal
before you push

A run-through is the last rehearsal before opening night. runthrough runs your GitHub Actions workflows on your own machine, in Docker, Apple container or straight on the host. It is an act-style runner shipped as one Rust binary.

Install with Homebrew
$ brew tap simota/runthrough https://github.com/simota/runthrough$ brew trust --formula simota/runthrough/runthrough$ brew install runthrough

Prebuilt for macOS and Linux, or cargo install --path . from a checkout. Needs git. Container jobs need Docker or Apple container. Host jobs use the shells you already have.

~/repos/runthrough · push
$ runthrough
[build/build] 🚀 start job on catthehacker/ubuntu:act-latest
[build/build] ⭐ Run actions/checkout@v4
[build/build]   ✅ Run actions/checkout@v4 (0.1s)
[build/build] ⭐ Run dtolnay/rust-toolchain@stable
[build/build]   | info: default toolchain set to 'stable'
[build/build]   ✅ Run dtolnay/rust-toolchain@stable (21.4s)
[build/build] ⭐ Run cargo build
[build/build]   |    Finished `dev` profile in 38.02s
[build/build]   ✅ Run cargo build (38.6s)
[build/build] ⭐ Run cargo test
[build/build]   | test result: ok. 40 passed; 0 failed
[build/build]   ✅ Run cargo test (52.3s)
[build/build] 🏁 job success in 114.2s
✅ build / build: success
$ 
Cue sheet

Call it from the wings

Flags keep act's names, so --matrix, --reuse, -j and your .actrc carry over.

Q1runthrough

Run every workflow triggered by push.

Q2runthrough pull_request -j test

One job, plus the jobs it needs, for another event.

Q3runthrough -l

List jobs by stage. -n shows the plan without running it.

Q4runthrough --host

Run on the host instead of in containers.

Q5runthrough --watch

Run again whenever a file changes.

Q6runthrough --graph

Draw the job graph.

Q7runthrough --validate --strict

Check the workflows, including unknown keys, then exit.

Q8runthrough --dashboard

Browse recorded runs at 127.0.0.1:7766.

Q9runthrough --install-hook

Rehearse before every push: a pre-push hook runs the push workflows on the commit being pushed.

Repertoire

Knows the whole script

Workflows are read from .github/workflows and from .runthrough/workflows, for workflows you only run locally.

Workflows & jobs

  • needs stages run their jobs in parallel; matrices with include, exclude, fail-fast, max-parallel
  • Reusable workflows, local or owner/repo/path@ref, with typed inputs, secrets: inherit and outputs
  • on.<event> filters for branches, tags, paths and types, checked against the event payload
  • concurrency, environment, timeout-minutes, continue-on-error

Steps & actions

  • Composite, node and Docker actions with pre/post, plus docker:// steps
  • bash, sh, python, pwsh, node or a custom {0} shell
  • background, wait, wait-all, cancel and parallel steps
  • actions/checkout copies your working tree; other repositories are cloned

Expressions

  • Every operator, .* filters and the github, env, vars, secrets, matrix, needs, steps contexts
  • hashFiles, fromJSON, format, case, success(), failure() and the rest
  • GITHUB_OUTPUT, GITHUB_ENV, GITHUB_STEP_SUMMARY, workflow commands and problem matchers
  • Secrets and ::add-mask:: values print as ***

Props backstage

  • A built-in cache server for actions/cache and BuildKit's type=gha cache
  • An artifact server for upload-artifact / download-artifact v3 and v4
  • Service containers on a per-job network, reachable by name
  • setup-* tools download once into a shared tool cache
Venues

Three stages to play on

runs-on labels map to an image or to the host. ubuntu-* defaults to catthehacker/ubuntu:act-latest.

Default

Docker

runthrough

Local or remote. With DOCKER_HOST=ssh://… job files live in volumes kept in sync with docker cp.

macOS 26+, Apple silicon

Apple container

runthrough --engine container

Runs on Apple's container after container system start. Services are written into the job's /etc/hosts.

No engine needed

Host

runthrough --host

Runs on your machine with your own tools. On macOS, self-hosted and macos-* labels run here by default.

Override any label with -P ubuntu-latest=node:20-bookworm-slim. Jobs get the engine socket only when asked: --daemon-socket-job deploy mounts /var/run/docker.sock into that job alone.

House view

Watch every run from the back row

Every run is recorded with secrets masked, and runthrough --dashboard serves a local page laid out like GitHub's Actions tab. It shows the runs list, the job graph by stage with matrix legs and annotations, and each step's log, updated while a run is in progress.

A failed job has Copy for LLM, which copies the job's context and the last 200 lines of each failed step as Markdown.

Resources lists the ports, containers, networks and volumes runthrough created, and flags the ones a finished run left behind.

ci · push · main @ 3af2a9fExample run

Stage 0

lint0:41
build1:54

Stage 1

test (18)0:37
test (20)0:35
test (22)0:12

Stage 2

deployskipped
Copy for LLM test (22) · step "npm test" exited 1
Understudies

What stays different from GitHub

A local rehearsal can't do everything the real stage does. These are the known gaps, and a run says so when a job uses one of them.

Cache service v2The ACTIONS_CACHE_SERVICE_V2 API is not served. The v1 API is.
Permissionspermissions is validated but has no local effect.
EnvironmentsThe name and url are reported. Environment secrets and protection rules are not applied.
ConcurrencyGroups span a single runthrough invocation. A cancelled job stops before its next step.
Curtain call

Exit codes you can script

runthrough --install-hook makes it a pre-push hook; use it in your own scripts too. Secrets come from -s or .secrets, variables from --var or .vars.

0

Every job succeeded or was skipped.

1

A job failed.

2

runthrough itself failed: a bad workflow or a missing engine.