You know the loop.
You edit a .yml. You commit. You push. You wait for the runner to spin up. You watch it churn through five green steps and then fail on step six — some path that isn't set, some env var that didn't thread through, a script that behaves differently than you remembered. You tweak one line. Commit. Push. Wait again.
Every iteration of that loop costs you a commit you'll squash later and three-to-five minutes of staring at a log that scrolls on a server you can't touch. The feedback is slow and it's blind: when step six fails, you can't get into the machine at the moment it failed and look around. You just get the corpse in the log.
I got tired of it and built walkflow — a tool that runs your workflow's steps on your own machine, one at a time, and pauses between them so you can inspect, fix, and continue. No commit. No push. No waiting.
What it feels like
$ walkflow
walkflow — .github/workflows/ci.yml · job 'build'
▶ job build — 6 step(s)
┌─ step 1/6: checkout
│ uses: actions/checkout@v4 — not executable in host mode, skipping.
┌─ step 2/6: install deps
│ $ npm ci
│ [enter] run · [s]hell · [k] skip · [q] quit > # ⏎
│ running in /home/me/app
└─ ok
┌─ step 3/6: run tests
│ $ npm test
│ [enter] run · [s]hell · [k] skip · [q] quit > # ⏎
FAIL src/auth.test.ts ✗ token refresh
│ step failed. [r]etry · [s]hell · [c]ontinue · [q]uit > # s
│ entering shell (/bin/zsh); `exit` to return to walkflow
$ echo $NODE_ENV # inspect the exact env step 3 saw
test
$ vim src/auth.ts # fix it right here
$ exit
│ back in walkflow
│ step failed. [r]etry · [s]hell · [c]ontinue · [q]uit > # r
└─ ok
The important part is the s. When a step fails — or before any step runs — you can drop into a shell in the workspace, with every environment variable the previous steps exported. Not a fresh shell. The exact state that step would have seen: the PATH additions from earlier steps, the values written to $GITHUB_ENV, the working directory. You poke around, figure out what's wrong, fix the file right there, exit, and hit r to retry — and r even offers to open the failing command in $EDITOR first.
The inner loop that used to be "push and pray, wait four minutes" is now seconds long.
"Isn't that what act does?"
Sort of, and this is the first question everyone asks, so let me be precise.
act runs your whole workflow locally in Docker. It's great for replaying — top to bottom, start to finish, then it stops. What it doesn't do is pause between steps, drop you into the live intermediate state, or let you edit a failed step and re-run it in place. That's been an open feature request on act for years.
walkflow is built around exactly that missing piece — the interactive inner loop, not the full replay:
act |
walkflow |
|
|---|---|---|
| Run workflow locally | ✅ | ✅ |
| Pause between steps | ❌ | ✅ |
| Drop into a shell with live step state | ❌ | ✅ |
| Edit a failed step and retry in place | ❌ | ✅ |
Faithful $GITHUB_ENV / $GITHUB_PATH threading |
✅ | ✅ |
They're not really competitors. act answers "does the whole thing pass in a container?" walkflow answers "why is step six broken, and can I fix it without pushing eight times?"
What it faithfully reproduces
I care about this being honest, because a debugger that lies to you is worse than no debugger. walkflow reproduces:
- Environment layering: workflow
env:→ jobenv:→ stepenv:. -
$GITHUB_ENVexports — bothKEY=valueand theKEY<<EOFheredoc form — threaded into later steps. -
$GITHUB_PATHadditions prepended toPATHfor later steps. - The default step shell (
bash --noprofile --norc -eo pipefail), or yourshell:override. -
$GITHUB_WORKSPACE,$CI,$GITHUB_ACTIONS, per-stepworking-directory.
What it doesn't (yet) — stated, not hidden
-
uses:steps (marketplace actions likeactions/checkout,actions/setup-node) are skipped with a note. Most are no-ops against your local checkout anyway. Running them with full Docker environment parity is the headline feature on the roadmap. -
${{ expressions }}aren't evaluated — steps run with the literal env.if:conditions andmatrixare shown, not enforced.
When walkflow hits one of these, it tells you. No silent surprises.
Handy flags
walkflow # finds the single workflow under .github/workflows/
walkflow ci.yml --job build # pick a file and job
walkflow --list # print jobs and steps, then exit
walkflow --from 4 # auto-run steps 1–3, go interactive from step 4
walkflow --from "run tests" # ...or select that step by name
walkflow -y # run it all, no pausing — a fast local sanity check
--from is the one I reach for most: it fast-forwards through the steps you already trust (accumulating their env correctly) and hands you the controls exactly at the step you're actually debugging.
Install
It's a single Rust binary, no runtime, no daemon:
cargo install --git https://github.com/mrvlyouknowwho/walkflow
MIT licensed. Repo, issues, and roadmap are here: github.com/mrvlyouknowwho/walkflow.
If you live in GitHub Actions and you've ever pushed a commit whose entire message was fix ci (then fix ci again, then pls), I'd genuinely like to know whether this shortens your loop. Issues and reactions welcome.
Top comments (0)