Workflow Loop¶
Use this page for iterating on GitHub Actions workflows locally through Act. For role-level and spec-level iteration, see Compose Loop and Playwright Spec Loop. For the swarm deploy loop, see Swarm Loop.
When to use¶
Use this loop while developing, optimizing, or debugging a GitHub Actions workflow locally through Act.
For role-level or spec-level iteration use the Compose Loop or Playwright Spec Loop; for the swarm deploy use the Swarm Loop.
The loop¶
When you are developing, optimizing, or debugging GitHub Actions workflows, you SHOULD explicitly propose
make act-workflowas the default iterative local debug loop.You MUST NOT assume that Act should be used automatically for workflow work. If the user agrees with the proposal, you SHOULD use
make act-workflowfor the iteration loop.After the user agrees to use Act, you SHOULD rerun
make act-workflowafter each focused workflow change and inspect the new output before making further edits.If the workflow uses a distro matrix, you MUST iterate on one distro at a time instead of rerunning the whole matrix during the default debug loop.
Debian SHOULD be the preferred distro for that focused workflow iteration unless the failure is clearly distro-specific or the user asked for a different distro.
When you constrain an Act matrix run through
ACT_MATRIX, you MUST use Act’skey:valuesyntax instead ofkey=value. Otherwise Act may ignore the filter and rerun the whole matrix.For
.github/workflows/call-test-workspace.yml, the preferred focused Debian example ismake act-workflow ACT_WORKFLOW=.github/workflows/call-test-workspace.yml ACT_JOB=test-workspace ACT_MATRIX="dev_runtime_image:$(python3 -c 'from utils.distros import dev_runtime_image; print(dev_runtime_image("debian"))')".You SHOULD avoid jumping straight to repeated remote CI reruns when
make act-workflowcan validate the workflow locally and the user agreed to use it.
Recovery & gotchas¶
act fails at “Set up job” on recent Docker¶
make act-* aborts with failed to copy content to container: mkdirat var/run... because the stock runner image’s /var/run symlink trips Docker 28/29’s stricter docker cp.
Fix: run make act-runner-image once, then prefix any act target with ACT_PLATFORM_IMAGE=local/act-runner-fixed:latest (e.g. ACT_PLATFORM_IMAGE=local/act-runner-fixed:latest make swarm-zombie app=<app>).
A green local workspace run does not clear CI¶
Two properties of the workspace suite differ between make act-workflow and a GitHub runner, so a class of defects is structurally invisible locally.
Nested Docker state does not survive a teardown locally, but does in CI. INFINITO_DOCKER_VOLUME is the named volume docker (default.env) and a host path (/mnt/docker) on GitHub. compose down -v removes a named volume and cannot remove a bind mount, so every workspace track starts on a clean inner daemon locally while CI carries the previous track’s containers into the next one. Defects that need a container, network, or bind source to outlive a teardown cannot reproduce under act.
The runtime identity differs. act sets ACT=true, so detect_gha_act() in runtime.py resolves INFINITO_RUNNING_ON_GITHUB=false where a real runner resolves true. Anything keyed off that fact behaves differently, and act cannot prove a fix to it either way. The image mirror is the exception: CI declares INFINITO_GHCR_MIRROR=true for the workspace suite, while a local run keeps the generated false and pulls upstream.
Run the local loop to iterate quickly; treat CI as the only authority for both classes.