DEV Community

ctrl
ctrl

Posted on • Originally published at github.com

Push-and-pray CI is dumb. I built a step debugger for GitHub Actions.

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
Enter fullscreen mode Exit fullscreen mode

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: → job env: → step env:.
  • $GITHUB_ENV exports — both KEY=value and the KEY<<EOF heredoc form — threaded into later steps.
  • $GITHUB_PATH additions prepended to PATH for later steps.
  • The default step shell (bash --noprofile --norc -eo pipefail), or your shell: override.
  • $GITHUB_WORKSPACE, $CI, $GITHUB_ACTIONS, per-step working-directory.

What it doesn't (yet) — stated, not hidden

  • uses: steps (marketplace actions like actions/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 and matrix are 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
Enter fullscreen mode Exit fullscreen mode

--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
Enter fullscreen mode Exit fullscreen mode

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)