actions/runner
The Runner for GitHub Actions :rocket:
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 3d ago
- ✓17 active contributors
- ✓Distributed ownership (top contributor 32% of recent commits)
Show 3 more →Show less
- ✓MIT licensed
- ✓CI configured
- ⚠No test directory detected
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/actions/runner)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/actions/runner on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: actions/runner
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/actions/runner shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 3d ago
- 17 active contributors
- Distributed ownership (top contributor 32% of recent commits)
- MIT licensed
- CI configured
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live actions/runner
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/actions/runner.
What it runs against: a local clone of actions/runner — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in actions/runner | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 33 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of actions/runner. If you don't
# have one yet, run these first:
#
# git clone https://github.com/actions/runner.git
# cd runner
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of actions/runner and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "actions/runner(\\.git)?\\b" \\
&& ok "origin remote is actions/runner" \\
|| miss "origin remote is not actions/runner (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "src/ActionsRunner.sln" \\
&& ok "src/ActionsRunner.sln" \\
|| miss "missing critical file: src/ActionsRunner.sln"
test -f "src/Misc/expressionFunc/hashFiles/src/hashFiles.ts" \\
&& ok "src/Misc/expressionFunc/hashFiles/src/hashFiles.ts" \\
|| miss "missing critical file: src/Misc/expressionFunc/hashFiles/src/hashFiles.ts"
test -f "src/Misc/layoutbin/RunnerService.js" \\
&& ok "src/Misc/layoutbin/RunnerService.js" \\
|| miss "missing critical file: src/Misc/layoutbin/RunnerService.js"
test -f ".github/workflows/release.yml" \\
&& ok ".github/workflows/release.yml" \\
|| miss "missing critical file: .github/workflows/release.yml"
test -f "docs/design/auth.md" \\
&& ok "docs/design/auth.md" \\
|| miss "missing critical file: docs/design/auth.md"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 33 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~3d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/actions/runner"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
GitHub Actions Runner is the self-hosted execution engine that pulls and runs CI/CD jobs from GitHub Actions workflows. It connects to GitHub's API, receives job payloads, executes steps in isolated environments (containers or host), and reports back status/logs. It's the core runtime powering both GitHub-hosted and self-hosted runners, written primarily in C# with TypeScript/JavaScript for expression functions like hashFiles(). Hybrid architecture: Core C# application in root (likely src/) handles runner lifecycle, job polling, container orchestration, and logging. Separate TypeScript/JavaScript modules for expression functions under src/ (hashFiles, etc.) compiled and bundled via @vercel/ncc into layoutbin/. Cross-platform support (Windows batch, shell scripts, Docker configs) via .github/workflows for CI. Docs organized in docs/adrs/ (Architecture Decision Records) and docs/start/ (platform-specific setup).
👥Who it's for
Enterprise and open-source teams who need to run GitHub Actions on their own hardware, in air-gapped networks, or with custom tooling. DevOps engineers and infrastructure teams managing self-hosted runner fleets. GitHub Actions maintainers building the platform itself.
🌱Maturity & risk
Highly mature and production-critical. This is the official GitHub-maintained runner used across millions of workflows. Heavy C# codebase (6.7M LoC) with established CI/CD via .github/workflows/ (build.yml, release.yml, codeql.yml). However, the README explicitly states GitHub is NOT accepting external contributions currently—focus is on bug fixes and security updates only. This is a strategic, maintained project with limited community contribution window.
Low risk for stability (GitHub-backed, widely deployed), but high risk for external contributors: contribution policy explicitly closed except bugs and security issues. Dependencies auto-updated via Dependabot (.github/dependabot.yml). TypeScript/Node components use modern tooling (TypeScript 6.x, ESLint 8.x, @vercel/ncc for bundling). Risks: single owner (GitHub), feature freeze on external PRs, no public roadmap commitment beyond 'strategic areas.'
Active areas of work
Routine maintenance: security updates (codeql.yml), dependency upgrades (dotnet-upgrade.yml, node-upgrade.yml, npm-audit workflows), and bot automation (close-bugs-bot, close-features-bot, stale-bot). No major feature PRs visible—aligned with stated closed-contribution policy. Active ADR documentation (latest: 2494-runner-image-tags.md) suggests internal planning around containerization and versioning.
🚀Get running
Clone: git clone https://github.com/actions/runner.git && cd runner. Install .NET SDK (C# build requirement—check docs/start/). For TypeScript modules: cd src/hashFiles && npm install. Build C#: dotnet build (inferred). Build TS: npm run build. Full pipeline: npm run all (format + lint + build + pack per package.json).
Daily commands: C# build: dotnet build src/Runner.sln (inferred from C# LoC). TypeScript: cd src/<module> && npm run build. Full validation: npm run all in any TS module directory. Docker: see .github/workflows/docker-publish.yml for container build process. Locally, dev environment setup: see .devcontainer/devcontainer.json for VS Code remote container.
🗺️Map of the codebase
src/ActionsRunner.sln— Master C# solution file defining the entire runner application build—contributors must understand this is the entry point for compiling the cross-platform runner executable.src/Misc/expressionFunc/hashFiles/src/hashFiles.ts— Implements the hashFiles() expression function used in workflow conditions; essential for understanding how runners evaluate dynamic file patterns at runtime.src/Misc/layoutbin/RunnerService.js— Windows service wrapper that keeps the runner running as a background service; critical for self-hosted runner lifecycle management..github/workflows/release.yml— Defines the official release pipeline for publishing runner binaries across Linux, macOS, and Windows—every deployment follows this workflow.docs/design/auth.md— Documents runner authentication and registration flow with GitHub; foundational for understanding security and token lifecycle.releaseVersion— Semantic version file that gates all release automation—incrementing this triggers the full CI/CD pipeline.src/Directory.Build.props— Shared MSBuild properties across all C# projects; sets compiler flags, dependency versions, and platform-specific targets for the entire solution.
🛠️How to make changes
Add a new expression function (e.g. newFunc())
- Create TypeScript implementation in src/Misc/expressionFunc/<funcName>/src/<funcName>.ts following hashFiles pattern (
src/Misc/expressionFunc/hashFiles/src/hashFiles.ts) - Create package.json with build, lint, and pack scripts in src/Misc/expressionFunc/<funcName>/package.json (
src/Misc/expressionFunc/hashFiles/package.json) - Configure TypeScript compilation in src/Misc/expressionFunc/<funcName>/tsconfig.json (
src/Misc/expressionFunc/hashFiles/tsconfig.json) - Run 'npm run all' to build and bundle; output goes to src/Misc/layoutbin/<funcName> (
src/Misc/expressionFunc/hashFiles/package.json) - Register new function in runner's expression evaluator C# code (in src/ C# projects) (
src/ActionsRunner.sln)
Update runner version and release new build
- Increment semantic version in releaseVersion file (e.g., '2.314.1' → '2.315.0') (
releaseVersion) - Commit and push to main; this automatically triggers .github/workflows/build.yml (
.github/workflows/build.yml) - Create a git tag matching the version; this triggers .github/workflows/release.yml (
.github/workflows/release.yml) - Review and merge the release workflow artifacts; binaries published to https://github.com/actions/runner/releases (
.github/workflows/release.yml)
Add a new architecture decision or design doc
- Create new ADR file in docs/adrs/NNNN-short-title.md following existing ADR format (
docs/adrs/README.md) - Document decision context, options considered, and chosen solution with rationale (
docs/adrs/0361-wrapper-action.md) - Reference the ADR in relevant source code comments or in docs/design/ files (
docs/design/auth.md) - Ensure ADR is linked in docs/adrs/README.md index for discoverability (
docs/adrs/README.md)
🔧Why these technologies
- C# with .NET (cross-platform) — Native Windows service support via .NET Framework; also runs on Linux/macOS via .NET Core/5+. Tight integration with GitHub's own infrastructure.
- TypeScript for expression functions (hashFiles, etc.) — Sandboxed evaluation of workflow expressions; compiled to JavaScript for runtime isolation from core runner code.
- Node.js for service wrappers (RunnerService.js) — Cross-platform process management and graceful shutdown on Windows; simpler than native C# interop.
- GitHub Actions workflows for CI/CD — Dogfooding: runner is self-testing via its own workflows; enables automated dependency updates and security scanning.
⚖️Trade-offs already made
-
Separate expression functions (hashFiles) as bundled TypeScript modules
- Why: Isolates expression evaluation logic from core runner; enables rapid iteration and security updates without full runner recompile.
- Consequence: Additional build step (ncc bundle) and runtime module loading; slightly increased startup overhead but better modularity.
-
Windows service wrapper in JavaScript (Node.js) rather than native C#
- Why: Faster development and easier cross-platform testing; Node.js already available in runner's .NET runtime.
- Consequence: Requires Node.js at runtime; adds ~50–100MB to Windows installation but simplifies maintenance.
-
Single releaseVersion file gates all release automation
- Why: Prevents accidental partial releases; version change triggers entire build matrix (Linux, macOS, Windows) consistently.
- Consequence: Must coordinate version bumps carefully; no per-platform versioning allowed.
-
Expression evaluation happens at job start, not at workflow parse time
- Why: Allows dynamic conditions based on runtime file state (e.g., cache-busting via hashFiles); enables conditional job dependencies.
- Consequence: Job startup latency increases if conditions reference many files; no static validation possible.
🚫Non-goals (don't propose these)
- This repo does NOT implement GitHub Actions orchestration (server-side job scheduling, queue management) — it only executes jobs sent to it.
- This repo does NOT provide workflow syntax validation or linting — that is GitHub's responsibility upstream.
- This repo does NOT manage container runtimes directly — it shells out to docker/podman binaries already installed on the machine.
- This repo is NOT a real-time distributed tracing system — logging is file-based and local to the runner instance.
- This repo does NOT implement SSH or VPN tunneling — it assumes network connectivity to GitHub.com or GitHub Enterprise Server is already available.
🪤Traps & gotchas
- Contribution policy: README explicitly forbids external PRs except bugs and security issues—fork for local changes. 2. C# runtime requirement: Core runner needs .NET SDK; dev containers provided (.devcontainer/) to avoid setup variance. 3. Bundle-and-pack pattern: TypeScript modules must be bundled with @vercel/ncc and output to layoutbin/ before runner execution—just running tsc won't work. 4. Platform-specific behaviors: Shell scripts, Batch files, and Docker paths vary; test on target OS or use devcontainer. 5. No public test command visible in package.json—tests likely run in CI only (see build.yml for actual test invocations).
🏗️Architecture
💡Concepts to learn
- Self-Hosted Runner Registration & Polling — Understanding the handshake between runner and GitHub API (registration token, polling loop, job queue draining) is essential to debug connectivity issues and job queueing delays in self-hosted setups
- Container Hooks & Lifecycle (ADR-1891) — Runner supports container pre/post setup hooks for init, provision, cleanup—critical for custom network, auth, or image setup in air-gapped environments; see docs/adrs/1891-container-hooks.md
- Expression Functions & hashFiles() Hashing — hashFiles() is a workflow expression function compiled into the runner; understanding how TypeScript modules compile to bundled JavaScript and inject into the expression evaluator helps extend expression language
- Composite Actions & Nested Workflows — Runner executes composite actions (reusable multi-step workflows) and handles conditional nesting; ADR-1144 and ADR-1438 define syntax and evaluation order—needed to debug action composition bugs
- Problem Matchers & Annotation Parsing — Runners parse problem matcher regex patterns from workflow logs to annotate errors/warnings in PR checks; ADR-0276 defines format—required to add new matchers or debug annotation failures
- Wrapper Actions & Step Outcome Tracking — Runner tracks step outcome (success/failure/skipped/neutral) and conclusion separately (ADR-0274); wrapper actions intercept steps—essential for action instrumentation and conditional step logic
- Proxy & Network Configuration (ADR-0263) — Runner supports HTTP/HTTPS proxy and firewall traversal for job polling and artifact upload; critical for corporate/air-gapped deployments; see proxy configuration documentation
🔗Related repos
actions/toolkit— Official GitHub Actions toolkit—runner depends on @actions/glob and other toolkit packages; shared utilities for artifact handling, exec, and core loggingactions/virtual-environments— Companion repo defining images and pre-installed tools for GitHub-hosted runners that execute with this runneractions/setup-dotnet— Example action ecosystem—runner hosts these actions; understanding action structure helps debug action execution and container integrationnodejs/node— Upstream Node.js dependency—runner bundles Node.js; Node.js security updates cascade into runner releasesdotnet/runtime— Upstream .NET runtime—core runner is .NET; breaking changes in .NET affect runner stability and platform support
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for hashFiles expression function
The hashFiles package (src/hashFiles.ts) is a critical expression function used in workflows but lacks visible test coverage. Given its use in security-sensitive contexts (dependency hashing), adding unit tests would improve reliability and prevent regressions. Tests should cover edge cases like symlinks, empty directories, glob pattern variations, and large file handling.
- [ ] Create src/tests/hashFiles.test.ts with Jest configuration
- [ ] Add tests for glob pattern matching against @actions/glob
- [ ] Add tests for hash consistency across different file orderings
- [ ] Add tests for error handling (missing files, permission errors)
- [ ] Add tests for symlink and special file handling
- [ ] Update package.json with 'test' and 'test-coverage' npm scripts
- [ ] Add test coverage reporting to CI in .github/workflows/build.yml
Document architectural decisions for container hooks and job hooks features
The ADR directory contains decisions for container-hooks (0891) and runner-job-hooks (1751), but these lack detailed implementation guides. New contributors need clear documentation on how these hooks are invoked, their lifecycle, error handling, and configuration. This would reduce support burden and improve adoption.
- [ ] Create docs/design/container-hooks.md with: hook invocation flow, configuration examples, error handling strategy, security considerations
- [ ] Create docs/design/job-hooks.md with: lifecycle diagram, available context, integration points with runner lifecycle
- [ ] Add code examples showing how hooks are registered and executed (referencing actual implementation files)
- [ ] Document hook output handling and logging behavior
- [ ] Update docs/README.md to link these new design documents
Add integration tests for runner authentication and registration flows
The docs/design/auth.md and docs/adrs/0397-runner-registration-labels.md suggest complex authentication paths, but there are no visible integration tests validating registration with different auth methods (PAT, OIDC, etc.). This is high-risk for regressions. Adding integration tests in a new CI workflow would catch breakage early.
- [ ] Create .github/workflows/integration-auth-tests.yml to test registration flows
- [ ] Add test fixtures for different token types (PAT, OIDC, GitHub App)
- [ ] Test runner registration with labels against a test GitHub organization
- [ ] Test token refresh and expiration handling
- [ ] Add test for proxy configuration during registration (per ADR 0263)
- [ ] Document test setup requirements in docs/contribute.md
🌿Good first issues
- Documentation gap: docs/checks/ exists but ADRs like 0549-composite-run-steps.md and 1144-composite-actions.md lack step-by-step examples—add worked examples showing composite action syntax and container hook lifecycle in docs/
- Missing TypeScript module docs: src/hashFiles is complete, but README.md doesn't explain how to add new expression functions or extend the expression evaluator—write a contributor guide in docs/ with template and build checklist
- Test coverage visibility: package.json for hashFiles has no 'test' script defined; add Jest/Mocha test setup, write tests for edge cases (nested globs, symlinks, large files), and integrate into build.yml CI
⭐Top contributors
Click to expand
Top contributors
- @dependabot[bot] — 32 commits
- @github-actions[bot] — 15 commits
- @TingluoHuang — 14 commits
- @ericsciple — 12 commits
- @salmanmkc — 9 commits
📝Recent commits
Click to expand
Recent commits
0cdaa36— Move dap setup to setup job step (#4403) (rentziass)5ed0c52— fix: expand commit hash regex to support SHA-256 (64-char) hashes (#4347) (yaananth)16c8a91— Update setup job starting logs (#4383) (GitPaulo)4550db3— Not retry and report action download 403. (#4391) (TingluoHuang)b06c585— feat: propagate actions dependencies (#4372) (nodeselector)c6f978f— Bump @actions/glob from 0.6.1 to 0.7.0 in /src/Misc/expressionFunc/hashFiles (#4367) (dependabot[bot])d1690af— Bump System.ServiceProcess.ServiceController from 10.0.6 to 10.0.7 (#4370) (dependabot[bot])c87d955— Prepping runner release 2.334.0 (#4365) (salmanmkc)7407189— Bump Microsoft.DevTunnels.Connections from 1.3.16 to 1.3.39 (#4339) (dependabot[bot])a84fb36— Bump typescript from 6.0.2 to 6.0.3 in /src/Misc/expressionFunc/hashFiles (#4353) (dependabot[bot])
🔒Security observations
The GitHub Actions Runner codebase demonstrates generally good security practices with linting, formatting, and dependency management workflows. However, several concerns exist: (1) Use of an unstable/pre-release TypeScript version poses significant risk, (2) Dependency version ranges lack strict pinning which could introduce vulnerabilities through transitive dependencies, (3) Pre-commit hooks could be attack vectors if the repository is compromised, and (4) Limited visibility into ESLint configuration rules. The codebase would benefit from stricter version pinning, stable dependency versions, security-focused linting rules, and regular dependency audits. The presence of security workflows (codeql.yml, dependency-check.yml) is positive but their configurations are not visible for review.
- High · Outdated TypeScript Version —
package.json - devDependencies.typescript. The package.json specifies typescript@^6.0.3, which is a pre-release/unstable version. TypeScript 6.x is not officially released as a stable version. Using pre-release versions can introduce breaking changes and security issues. Fix: Update to a stable TypeScript version such as ^5.x or wait for official TypeScript 6.x stable release. Use 'npm install typescript@latest' to get the latest stable version. - Medium · Overly Permissive ESLint Configuration —
.github/workflows (lint step execution) and package.json. The ESLint setup includes eslint-plugin-github and eslint-plugin-prettier, but without visible eslint configuration files (.eslintrc, eslint.config.js), the actual rule enforcement cannot be verified. This could lead to code quality and security issues slipping through. Fix: Ensure an explicit .eslintrc.json or eslint.config.js file exists with strict security-focused rules. Include rules for detecting common vulnerabilities (e.g., no-eval, no-implied-eval). - Medium · Dependency Chain Risk - @actions/glob —
package.json - dependencies.@actions/glob. The dependency @actions/glob@^0.7.0 uses a caret range, allowing any version up to 1.0.0. This could introduce breaking changes or security vulnerabilities in transitive dependencies without explicit control. Fix: Pin to a specific version (e.g., '0.7.0') or use npm audit regularly. Consider using 'npm ci' instead of 'npm install' in CI/CD pipelines to ensure reproducible builds. - Medium · Pre-commit Hook Execution Risk —
.husky/pre-commit and package.json lint-staged configuration. The .husky/pre-commit hook exists and lint-staged is configured to run 'npm run all', which executes build, lint, format, and pack operations. If compromised, this could execute arbitrary code during commits. Fix: Verify the integrity of .husky/pre-commit hooks. Restrict file permissions with 'chmod 400' on hook files. Consider disabling hooks in untrusted environments or auditing their contents regularly. - Low · Missing Security Headers in Package Configuration —
package.json root level. The package.json lacks security-related metadata such as 'publishConfig', 'exports', or 'engines' fields to restrict consumption and execution environment. Fix: Add 'engines' field to specify supported Node.js versions, add 'exports' to control entry points, and consider adding 'publishConfig' if publishing to npm registry. - Low · Insufficient Dependency Pinning in DevDependencies —
package.json - all devDependencies with ^ ranges. Multiple devDependencies use caret (^) ranges, allowing patch and minor version updates automatically. While typically low-risk for dev tools, it can introduce subtle changes in build output or linting behavior. Fix: Consider using more conservative version pinning for critical tools (e.g., @vercel/ncc, typescript) by replacing ^ with exact versions or ~. Regularly run 'npm audit' and 'npm update' in controlled environments.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.