{"slug": "show-hn-localci-run-your-github-actions-jobs-locally-before-you-push", "title": "Show HN: Localci, run your GitHub Actions jobs locally before you push", "summary": "Localci, a new open-source tool from developer peters, runs existing GitHub Actions workflows locally before pushing, cutting a Horizon Rust workspace CI run from a median 27 minutes on GitHub to 2 minutes 16 seconds with `localci run --fast`, according to measurements taken 10-11 October 2026. The tool reads existing `.github/workflows/ci.yml` files with no new config, runs jobs whose `runs-on` matches the local OS, and ships as a single static binary under 800 KB installed in under 2 seconds. Across the last 40 merged Horizon pull requests, large PRs (more than 10 files) averaged 5 review rounds and 5 CI runs at 307 runner-minutes, which the author estimates drops to 1-2 rounds and 60-125 runner-minutes with localci's PR loop.", "body_md": "Waiting for GitHub to pick up your push, queue a runner and rebuild everything\nfrom a cold cache is slow. Sometimes GitHub is just down. **localci runs your\nexisting `.github/workflows/ci.yml` on your own machine, in seconds, with your\nbuild cache still warm.**\n\n- **No new config.** It reads the workflows you already have.\n- **Your OS, your jobs.** It runs the jobs whose`runs-on` matches your machine (Linux, macOS or Windows) and tells you which ones need another OS.\n- **Real steps.** Shell steps, matrices,`needs` , outputs,`GITHUB_ENV` ,`if:` conditions and`services:` (as Docker containers) behave like they do on GitHub. Setup actions such as`actions/checkout` are skipped, because your machine already has the checkout and the tools.\n- **Made for agents.** One command teaches Claude Code, Codex and other agents to run the right subset of your CI before they push.\n- **Tiny.** One static binary, under 800 KB to download, installed in under 2 seconds.\n\nMeasured on [Horizon](https://github.com/peters/horizon), a public Rust\nworkspace with a Linux, macOS and Windows CI matrix, on 10 and 11 October 2026.\n\n**Waiting for GitHub vs. checking locally**\n\n|  | Time | \n|---|---|\n| One GitHub CI run, push to result (median of 18 runs) | **27 minutes** | \n| Same Linux jobs with `localci run` , one after another | 15 to 23 minutes | \n| Same change with `localci run --fast` | **2 minutes 16 seconds** | \n| `localci run --fast` again, nothing changed | **0 seconds** | \n| A failing CI test, found by `localci run repo-checks` before any push | **4 seconds** | \n| One code review round with Copilot on the PR, plus a push | about 5 minutes | \n| One local review round (the skill's Copilot CLI prompt), no push | **1 to 4 minutes** | \n\n**What it saves on a pull request.** Without localci, every push starts a full\nCI run of about 62 runner-minutes, and a fix round waits for it. In the last\n40 merged Horizon PRs:\n\n|  | Review rounds | CI runs | Runner time | \n|---|---|---|---|\n| Small PRs (5 files or fewer), median | 1 | 1 | 63 min | \n| Large PRs (more than 10 files), median | 5 | 5 | 307 min | \n| Large PR with localci and the skill's PR loop (estimate) | 1 to 2 | 1 to 2 | 60 to 125 min | \n\nWith the PR loop, you review and test locally until both are clean, and the\nreview on GitHub becomes a final check instead of the place where you iterate.\nFor a large PR that is about **1½ hours less waiting** and **3 to 4 hours less\nrunner time**. The large-PR row is an estimate; the rows below are measured.\n\n**Real PRs, as the loop improved**\n\n| PR | What was new | Review rounds on GitHub | CI runs | Open to ready | \n|---|---|---|---|---|\n| [#1461](https://github.com/peters/horizon/pull/1461) | localci before each push, fixes pushed without a CI run | 4 (1 with findings) | 2 for 4 pushes | first approval after 9 min | \n| [#1466](https://github.com/peters/horizon/pull/1466) | a general local review before the PR | 5 | 4 started for 5 pushes | 42 min | \n| [#1471](https://github.com/peters/horizon/pull/1471) | a local review that asks for concrete failing inputs, and GitHub's review as the final check | **1** | **1** | **12 min** | \n\nFor comparison, the median small Horizon PR before localci took 24 minutes from opening to merge, with one review round and one CI run of 63 runner-minutes.\n\n**macOS and Linux**\n\n```\ncurl -fsSL https://raw.githubusercontent.com/peters/localci/main/install.sh | sh\n```\n\n**Windows (PowerShell)**\n\n```\nirm https://raw.githubusercontent.com/peters/localci/main/install.ps1 | iex\n```\n\nPrefer not to pipe a script into your shell? Download a binary from\n[Releases](https://github.com/peters/localci/releases): Linux x64 and arm64\n(static), macOS Apple silicon and Intel, and Windows x64. With Rust installed\nyou can also build it yourself in about 10 seconds:\n`cargo install --locked --git https://github.com/peters/localci`.\n\nThen, in any repository with GitHub Actions:\n\n```\nlocalci list     # every job and matrix leg, and which run here\nlocalci run      # run them\nlocalci skill install\n```\n\nThis puts a skill into `~/.claude/skills`, `~/.codex/skills` and\n`~/.agents/skills` (for each agent you have). From then on, just ask:\n\nRun CI locally before you push.\n\nThe agent looks at the plan and your diff, suggests which jobs and steps are worth running, asks you once, runs them, and reads only the log of a step that failed. It knows that a local run is a fast pre-check and not the real GitHub result.\n\n```\nlocalci plan                          # what would run, step by step, without running it\nlocalci run clippy                    # one job, by id\nlocalci run \"linux x64 libs\"          # one matrix leg, by name\nlocalci run --matrix shard=libs       # legs with that matrix value\nlocalci run --skip-setup              # leave out apt-get, brew and installer steps\nlocalci run --skip \"slow tests\"       # leave out steps by name or command\nlocalci run --only lint               # run only matching steps\nlocalci run -k                        # keep going after a failing job\nlocalci run -w release                # another workflow file\nlocalci check                         # can localci handle every workflow here?\n```\n\nAdd `--json -q` for a machine-readable report. Each step's log is kept, and the\npath is printed at the end.\n\n```\nlocalci run --fast\n```\n\n`--fast` is three things at once:\n\n- `--affected` : leave out build and test steps (Cargo, npm, dotnet, Go, Gradle,\nSwift) when none of their inputs changed since your branch left the default\nbranch. A step whose commands localci cannot read always runs.\n- `--cache` : reuse a step that already passed on the same inputs, including its outputs.\n- `--parallel 0` : run independent jobs at the same time (2 to 4, from the CPU count).\n\nOn Horizon, a documentation and CI change went from a 23-minute full Linux run\nto 136 seconds with `--fast`, and to 0 seconds for a second run with no changes. `localci plan --affected` shows why each step runs\nor not. Run once without `--fast` before you push for review.\n\nSome steps should never run on your laptop, like `sudo apt-get install` or a\ndeploy job. Mark them with a normal GitHub condition. localci sets `LOCALCI`,\nand on GitHub it is empty, so GitHub keeps running them:\n\n```\njobs:\n  deploy:\n    if: ${{ !vars.LOCALCI }}          # jobs: use vars\n    ...\n  test:\n    steps:\n      - name: Install system packages\n        if: ${{ !env.LOCALCI }}       # steps: env or vars\n        run: sudo apt-get install -y libssl-dev\n```\n\nJobs with `services:` (PostgreSQL, Redis and so on) get real Docker containers\nwith their health checks and port mappings, removed when the job ends. If you\nalready run those services yourself, add `--no-services`. Set\n`LOCALCI_DOCKER=podman` to use Podman.\n\n[act](https://github.com/nektos/act) is the well-known tool for this, and\n[wrkflw](https://github.com/bahdotsh/wrkflw) is a newer one. Both try to\nreproduce a GitHub runner: by default they run each job in a container, and\nthey run `uses:` actions. That is the right choice when you want to test the\nworkflow itself.\n\nlocalci answers a different question: \"will my change pass CI?\" So it makes the opposite choices:\n\n|  | act / wrkflw (default) | localci | \n|---|---|---|\n| Where steps run | A container that imitates the runner image | Your machine, in your checkout | \n| Build cache | Usually cold: ignored build output stays outside the container | Your warm `target/` ,`node_modules/` and so on | \n| `uses:` actions | Runs them | Skips them; your machine has the tools already | \n| Unchanged steps | Run again | `--fast` leaves them out or reuses the result | \n| Agents | Not a focus | A plan as JSON, a skill, and failing-step logs only | \n\nact can also run directly on the host (`-P ubuntu-latest=-self-hosted`). The\ndifference that remains is what localci leaves out, and that it is built to be\ndriven by an agent.\n\n- **Your CI is mostly marketplace actions.** If the real work happens in`uses:` steps (for example`docker/build-push-action` or a deploy action),\nlocalci skips it. Use act, or GitHub.\n- **You need the exact runner image.** localci uses your tools and versions. A\nstep can pass here and fail on GitHub because a tool differs.\n- **Jobs for another OS.** It runs only the jobs that match your machine. Run\nit on that machine instead.\n- **Reusable workflows and `container:` jobs.** It does not run them.\n- **Results on GitHub.** It does not post checks or statuses. GitHub CI stays\nthe final word.\n\nThe [compatibility table](https://github.com/peters/localci/blob/main/docs/compatibility.md) lists every feature, and the\n[decision records](https://github.com/peters/localci/blob/main/docs/adr/README.md) explain why.\n\nEvery bug becomes a small folder in [`tests/cases/`](https://github.com/peters/localci/blob/main/tests/cases): a workflow\nand the result it should give. No Rust code needed. See\n[the procedure](https://github.com/peters/localci/blob/main/docs/procedures/add-a-regression-case.md), and please open an\nissue or a pull request.\n\nMIT", "url": "https://wpnews.pro/news/show-hn-localci-run-your-github-actions-jobs-locally-before-you-push", "canonical_source": "https://github.com/peters/localci", "published_at": "2026-10-11 05:12:12+00:00", "updated_at": "2026-10-11 06:23:48.965064+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents"], "entities": ["localci", "GitHub Actions", "Horizon", "peters", "Claude Code", "Codex", "GitHub", "Copilot"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-localci-run-your-github-actions-jobs-locally-before-you-push", "markdown": "https://wpnews.pro/news/show-hn-localci-run-your-github-actions-jobs-locally-before-you-push.md", "text": "https://wpnews.pro/news/show-hn-localci-run-your-github-actions-jobs-locally-before-you-push.txt", "jsonld": "https://wpnews.pro/news/show-hn-localci-run-your-github-actions-jobs-locally-before-you-push.jsonld"}}