{"id":50351081,"url":"https://github.com/srid/localci","last_synced_at":"2026-05-29T21:01:19.039Z","repository":{"id":346431240,"uuid":"1189942276","full_name":"srid/localci","owner":"srid","description":"Local CI with multi-system support","archived":false,"fork":false,"pushed_at":"2026-04-26T00:42:03.000Z","size":222,"stargazers_count":3,"open_issues_count":10,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-26T02:24:14.613Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/srid.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-23T20:21:38.000Z","updated_at":"2026-04-02T10:14:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/srid/localci","commit_stats":null,"previous_names":["srid/giton"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/srid/localci","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srid%2Flocalci","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srid%2Flocalci/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srid%2Flocalci/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srid%2Flocalci/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/srid","download_url":"https://codeload.github.com/srid/localci/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srid%2Flocalci/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33670211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-29T21:01:18.240Z","updated_at":"2026-05-29T21:01:19.032Z","avatar_url":"https://github.com/srid.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# localci\n\nLocal CI from your terminal. Run any command and the result shows up as a green check on the GitHub PR — no hosted runner needed.\n\n```bash\nnix run github:srid/localci -- -- make build\n```\n\nlocalci extracts the repo at HEAD into a temp directory (via `git archive`), runs the command there, and posts a GitHub commit status. The clean extraction means your uncommitted changes can't leak into the build. The working tree must be clean, or localci refuses to run.\n\nWith `-s`, localci can target remote Nix systems over SSH — run builds on `aarch64-darwin` from your Linux box.\n\n\u003cimg width=\"1220\" height=\"1010\" alt=\"image\" src=\"https://github.com/user-attachments/assets/2c687668-5f08-425c-922a-7595b2bef5b0\" /\u003e\n\n\n## Cross-system builds\n\nPass `-s` to run on a different Nix platform. localci pipes the archive over SSH and executes remotely:\n\n```bash\nnix run github:srid/localci -- -s aarch64-darwin -n build -- nix build\n```\n\nOn first use, localci prompts for the SSH hostname for that system. The mapping is saved to `$XDG_CONFIG_HOME/localci/hosts.json` and reused on subsequent runs.\n\nThe GitHub status context includes the system: `localci/build/aarch64-darwin`.\n\n\u003e [!NOTE]\n\u003e This approach runs the entire build on the remote machine via SSH, rather than using Nix remote builders. Remote builders frequently hang during builds, making them unreliable for CI. localci sidesteps this by treating the remote as a plain execution target.\n\n## Multi-step\n\nFor projects with multiple CI steps, define them in a JSON config:\n\n```json\n{\n  \"steps\": {\n    \"build\": {\n      \"systems\": [\"x86_64-linux\", \"aarch64-darwin\"],\n      \"command\": \"nix build\"\n    },\n    \"test\": {\n      \"systems\": [\"x86_64-linux\", \"aarch64-darwin\"],\n      \"command\": \"nix run .#test\",\n      \"depends_on\": [\"build\"]\n    }\n  }\n}\n```\n\n```bash\nnix run github:srid/localci -- -f localci.json\n```\n\nThis expands into a step×system matrix: `build` and `test` each run on both systems, in parallel. Dependencies resolve per-system — `test` on x86_64-linux waits for `build` on x86_64-linux, not on aarch64-darwin. Each cell in the matrix gets its own GitHub commit status.\n\nUnder the hood, localci generates a [process-compose](https://github.com/F1bonacc1/process-compose) config and delegates orchestration to it. Each step is a self-invocation of localci with `--sha` pinning. Pass `--tui` to get the process-compose terminal UI.\n\n## GitHub Actions\n\n\u003e [!NOTE]\n\u003e Running localci in GitHub Actions defeats the purpose of *local* CI — you're back to waiting for hosted runners. Consider using the [MCP integration](#agent-integration-mcp) with a coding agent instead. That said, nothing prevents you from using both.\n\nlocalci works in hosted CI too. Use `--sha` to pin to the PR commit (the clean-tree check doesn't apply in CI since there's no working tree to protect):\n\n```yaml\njobs:\n  ci:\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest]\n    runs-on: ${{ matrix.os }}\n    env:\n      GH_TOKEN: ${{ github.token }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: nixbuild/nix-quick-install-action@v34\n      - run: nix run github:srid/localci -- --sha ${{ github.sha }} -f localci.json\n```\n\nEach step posts its own commit status (`localci/build`, `localci/test`), so the PR shows fine-grained check results even though it's a single CI job.\n\n## Agent integration (MCP)\n\nlocalci can expose CI steps as [MCP](https://modelcontextprotocol.io/) tools via process-compose's built-in MCP server. Coding agents (Claude Code, etc.) connect over stdio and invoke steps individually.\n\n### Setup\n\nAdd two files to your project root:\n\n**`localci.json`** — define your CI steps:\n```json\n{\n  \"steps\": {\n    \"build\": { \"command\": \"nix build\" },\n    \"test\": { \"command\": \"nix run .#test\", \"depends_on\": [\"build\"] }\n  }\n}\n```\n\n**`.mcp.json`** — register the MCP server (auto-loaded by Claude Code):\n```json\n{\n  \"mcpServers\": {\n    \"localci\": {\n      \"type\": \"stdio\",\n      \"command\": \"nix\",\n      \"args\": [\"run\", \"github:srid/localci\", \"--\", \"--mcp\", \"-f\", \"localci.json\"]\n    }\n  }\n}\n```\n\nThen in your project's `CLAUDE.md`, tell the agent how to use it:\n\n```markdown\n# Dev workflow\n\nUse the localci MCP tools (mcp__localci__\u003cstep\u003e) — never run build or test commands directly.\n\n1. Make changes, commit\n2. Run localci MCP tools to verify\n3. If failures: fix, amend commit, re-run MCP tools\n4. Once green: push, then run localci MCP tools again to post GitHub statuses\n```\n\nEach step from `localci.json` appears as an MCP tool (named `mcp__localci__\u003cstep\u003e`). Dependencies are respected — invoking a step auto-starts its dependencies first. Steps can be re-invoked after fixing code. Each tool invocation returns the full step output directly.\n\n\u003e [!IMPORTANT]\n\u003e The MCP server reads `localci.json` once at startup. If you change the steps, restart your MCP client (e.g. Claude Code) to pick up the new config. Code changes are always tested fresh — each invocation resolves HEAD at runtime.\n\n### Branch protection\n\nRequire localci checks to pass before merging PRs. This reads `localci.json`, expands the step×system matrix, and sets the correct status contexts as required checks on the default branch:\n\n```bash\nlocalci protect -f localci.json\n```\n\n## Reference\n\n```\nlocalci [run] [options] -- \u003ccommand...\u003e    Single-step mode\nlocalci [run] -f \u003cconfig.json\u003e             Multi-step mode\nlocalci protect -f \u003cconfig.json\u003e           Set branch protection\n\nOptions:\n  -s, --system \u003csystem\u003e   Nix system to run on (remote if different from current host)\n  -n, --name \u003cname\u003e       GitHub status check name (default: command basename)\n  -f, --file \u003cpath\u003e       Multi-step JSON config\n  --sha \u003csha\u003e             Pin to a commit SHA (skips clean-tree check)\n  --tui                   Show process-compose TUI (multi-step only)\n  --mcp                   Expose steps as MCP tools (multi-step only)\n  --no-signoff            Skip GitHub status posting (test before pushing)\n```\n\nRequires `git`, [`gh`](https://cli.github.com/) (authenticated), and `nix`. Must be run inside a git repository with a GitHub remote.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrid%2Flocalci","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsrid%2Flocalci","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrid%2Flocalci/lists"}