|
| 1 | +# Running GitHub Actions Locally with `act` |
| 2 | + |
| 3 | +Push, wait for GitHub, watch it fail, fix, push again — that loop is slow. |
| 4 | +[`act`](https://github.com/nektos/act) runs your **existing** GitHub Actions |
| 5 | +workflows on your own machine inside Docker, using the **same |
| 6 | +`.github/workflows/*.yml` files** — no extra config, no duplicated scripts. |
| 7 | +Get it green locally, *then* push. |
| 8 | + |
| 9 | +> This is the plain, standard tool most developers use. There is nothing to |
| 10 | +> customize: `act` reads the workflow you already have. |
| 11 | +
|
| 12 | +--- |
| 13 | + |
| 14 | +## 1. Prerequisite: Docker |
| 15 | + |
| 16 | +`act` runs each job in a container, so Docker must be installed and running: |
| 17 | + |
| 18 | +```bash |
| 19 | +docker --version # must succeed |
| 20 | +docker ps # daemon must be up |
| 21 | +``` |
| 22 | + |
| 23 | +If Docker isn't installed, see https://docs.docker.com/engine/install/. |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +## 2. Install `act` |
| 28 | + |
| 29 | +Pick whichever fits your setup — they all install the same tool: |
| 30 | + |
| 31 | +```bash |
| 32 | +# a) One-line install script (Linux/macOS) → installs to ./bin or a path you give |
| 33 | +curl -sSL https://raw.githubusercontent.com/nektos/act/master/install.sh | bash -s -- -b ~/.local/bin |
| 34 | + |
| 35 | +# b) As a GitHub CLI extension (if you already have `gh`) |
| 36 | +gh extension install nektos/gh-act # then run it as: gh act ... |
| 37 | + |
| 38 | +# c) Package managers |
| 39 | +brew install act # macOS / Linuxbrew |
| 40 | +# Arch: sudo pacman -S act |
| 41 | +# Windows (choco): choco install act-cli |
| 42 | +``` |
| 43 | + |
| 44 | +Verify: |
| 45 | + |
| 46 | +```bash |
| 47 | +act --version |
| 48 | +``` |
| 49 | + |
| 50 | +--- |
| 51 | + |
| 52 | +## 3. First run: choose an image size |
| 53 | + |
| 54 | +The **first** time you run `act`, it asks which runner image to use. Pick |
| 55 | +**Medium** — it's the standard `catthehacker/ubuntu` image that has git, curl, |
| 56 | +build-essential, etc. (The choice is saved to `~/.actrc`.) |
| 57 | + |
| 58 | +``` |
| 59 | +? Please choose the default image you want to use with act: |
| 60 | + - Large (~17GB, closest to GitHub's runner) |
| 61 | + > Medium (~500MB, has the common tools) ← choose this |
| 62 | + - Micro (~200MB, node only) |
| 63 | +``` |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## 4. Everyday commands |
| 68 | + |
| 69 | +```bash |
| 70 | +act -l # list the jobs act found in .github/workflows |
| 71 | +act # run the default event (push) — runs all matching jobs |
| 72 | +act -j build # run ONE job by id (recommended) |
| 73 | +act pull_request # simulate a pull_request event instead of push |
| 74 | +act -n # dry run: print the plan, execute nothing |
| 75 | +act -v # verbose, when something misbehaves |
| 76 | +``` |
| 77 | + |
| 78 | +### For THIS repo |
| 79 | + |
| 80 | +The CI here builds inside Docker (`docker-build.yml`, job id **`build`**, |
| 81 | +runner `ubuntu-24.04`): |
| 82 | + |
| 83 | +```bash |
| 84 | +act -j build |
| 85 | +``` |
| 86 | + |
| 87 | +`act` automatically shares your host's Docker daemon with the job, so the |
| 88 | +workflow's own `docker build ...` steps work without extra flags. |
| 89 | + |
| 90 | +--- |
| 91 | + |
| 92 | +## 5. The workflow: green locally → push |
| 93 | + |
| 94 | +```bash |
| 95 | +act -j build # 1. run CI locally |
| 96 | +# ...fix anything that fails, repeat until green... |
| 97 | +git add -A && git commit -m "..." |
| 98 | +git push # 2. push only once it passed locally |
| 99 | +``` |
| 100 | + |
| 101 | +That's the whole idea — no scripts, no hooks required. |
| 102 | + |
| 103 | +### Optional: make it automatic before every push |
| 104 | + |
| 105 | +If you want the check to run on its own, add a one-line Git `pre-push` hook. |
| 106 | +This is optional; `act` on its own is already enough. |
| 107 | + |
| 108 | +```bash |
| 109 | +cat > .git/hooks/pre-push <<'EOF' |
| 110 | +#!/usr/bin/env bash |
| 111 | +act -j build || { echo "❌ local CI failed — push aborted"; exit 1; } |
| 112 | +EOF |
| 113 | +chmod +x .git/hooks/pre-push |
| 114 | +``` |
| 115 | + |
| 116 | +Bypass it any time with `git push --no-verify`. |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +## 6. Good to know (limitations) |
| 121 | + |
| 122 | +- **`act` ≈ GitHub, not identical.** It uses `catthehacker` images, not |
| 123 | + GitHub's exact runner image. Close enough to catch the vast majority of |
| 124 | + failures; for maximum parity choose the **Large** image. |
| 125 | +- **Linux jobs only.** `act` cannot run `windows-latest` or `macos-latest` |
| 126 | + jobs. This repo's `windows-build.yml` therefore **cannot** run under `act` — |
| 127 | + only the Linux `docker-build.yml` job does. |
| 128 | +- **First run is slow** — it pulls the runner image (and here, builds the |
| 129 | + project's Docker image). Later runs reuse cached layers and are much faster. |
| 130 | +- **Secrets:** pass them with `act -s NAME=value` or `--secret-file` if a |
| 131 | + workflow needs them (this repo's build doesn't). |
| 132 | + |
| 133 | +--- |
| 134 | + |
| 135 | +## TL;DR |
| 136 | + |
| 137 | +```bash |
| 138 | +docker ps # Docker running? |
| 139 | +act -l # what jobs exist? |
| 140 | +act -j build # run CI locally → green? then git push |
| 141 | +``` |
0 commit comments