The workspace jobs pulled every image anonymously from Docker Hub and two of them failed on its rate limit in run 39059128678 (#990): the fedora swarm drill on busybox:1.38 on all three nodes, the centos compose drill while building tor-smtp_custom. The regular deploy jobs pull through the GHCR mirror, but the mirror was tied to being a real GitHub runner: Profile.image_mirror_enabled() returned runs_on_github(), and the swarm routine generated mirrors.yml only when cli.meta.runtime reported github, which is act inside make swarm-zombie. The mirror is now its own capability, INFINITO_GHCR_MIRROR (next to INFINITO_GHCR_MIRROR_PREFIX), written as a run fact like INFINITO_CACHE_STACK: true on a real GitHub runner, false everywhere else, and a non-empty exported value wins for the run. Profile.image_mirror_enabled() reads the declaration and keeps runs_on_github() as the fallback, and 02_provision_inventory.sh gates on the same value instead of the runtime name. make swarm-zombie forwards it into act through ACT_ENV, since act ignores the host env. call-test-workspace.yml declares INFINITO_GHCR_MIRROR=true and passes INFINITO_GHCR_MIRROR_PREFIX into the workspace container. Nothing else of the workspace changes: is_ci, runs_on_github, the cache stack and the docker root stay local-like. The step that loads the rescue paths now removes the .env it generated on the runner, because on debian rows load.sh sourced it inside the container and the run inherited INFINITO_RUNNING_ON_GITHUB=true. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3.7 KiB
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.