Infrastructure and tooling that supports the Cratis development loop.
.
├── Source/
│ └── GitHub/ Runner image, related workflows, and GitHub helper scripts
└── Documentation/
└── GitHub/ How to build, run, and integrate the runner and Copilot
The "agent" is our custom GitHub Actions self-hosted runner image - a Docker image with .NET, Node, pnpm, Python, Go, and the GitHub CLI pre-installed. You build it once, then register it against either a single repository or an entire GitHub organization.
cd Source/GitHub
./build.shOverride IMAGE_NAME, IMAGE_TAG, or PLATFORM via environment variables.
Requires Docker 24+ with Buildx. See below if you're on Apple Silicon.
The Dockerfile detects its architecture at build time (dpkg --print-architecture) and pulls the matching arm64 binaries for the GitHub
Actions runner and Go automatically - no changes needed to build or run an
arm64 image.
On an M-series Mac, build natively for arm64 instead of the linux/amd64
default:
PLATFORM=linux/arm64 ./build.shBuilding/running the default linux/amd64 image still works under Docker
Desktop's emulation, but it's noticeably slower to build and to execute jobs
- prefer the native arm64 build above.
run-local.sh and docker-compose.yml don't pin a platform themselves; they
just run whatever IMAGE_NAME:IMAGE_TAG you built and loaded locally, so
once you've built with PLATFORM=linux/arm64, ./run-local.sh and
docker compose up work unmodified.
If you run both amd64 and arm64 agents against the same repo/org, consider
adding an arch label (e.g. RUNNER_LABELS=self-hosted,linux,arm64,cratis) so
workflows can target one or the other with runs-on.
The multi-arch publish workflow
(Source/GitHub/workflows/publish-runner-image.yml, which builds both
linux/amd64 and linux/arm64) also runs fine from an Apple Silicon host -
Docker Desktop ships the QEMU/binfmt setup needed to cross-build the amd64
leg under emulation.
The agent needs a token to register itself as a runner - either a personal access token (PAT), which it exchanges for a short-lived registration token, or a registration token you generate yourself.
Classic PAT (simplest; works for both repo- and org-level registration)
- GitHub -> your avatar -> Settings -> Developer settings -> Personal access tokens -> Tokens (classic) -> Generate new token (classic).
- Select scopes:
repo+workflow- for repo-level registration (step 3 below).admin:org- for org-level registration (step 4 below).
- Set an expiration, generate, and copy the token immediately - GitHub only
shows it once. Use it as
GITHUB_PAT(see step 3 below for where it goes).
Fine-grained PAT (narrower scope; repo-level registration)
- GitHub -> your avatar -> Settings -> Developer settings -> Personal access tokens -> Fine-grained tokens -> Generate new token.
- Set Resource owner to the org, and Repository access to the specific repo (or a chosen set of repos).
- Under Repository permissions, grant Actions: Read and write and Administration: Read and write.
- Generate and copy the token. Use it as
GITHUB_PAT(see step 3 below for where it goes).
Fine-grained tokens can also be scoped to organization permissions (Self-
hosted runners: Read and write) for org-level registration, but only if
your org allows fine-grained PAT access to org resources - many orgs
restrict this. If yours does, or if you'd rather skip PATs entirely, generate
a registration token directly instead and pass it as RUNNER_TOKEN:
# Repo-level (needs a token/gh session with access to the repo):
gh api -X POST repos/<owner>/<repo>/actions/runners/registration-token --jq .token
# Org-level (needs a token/gh session with admin:org, e.g. `gh auth refresh -s admin:org`):
gh api -X POST orgs/<org>/actions/runners/registration-token --jq .tokenRegistration tokens expire after about an hour, so generate one right before running the agent.
Don't export GITHUB_PAT/RUNNER_TOKEN and pass them straight to
run-local.sh - anything exported in your shell and then handed to a script
as -e on a docker run line ends up in that command's argv, which any
local process can read via ps. Instead, put them in ~/.cratis-gh-runner.env
(chmod 600 it) and run-local.sh/docker-compose.yml pick it up via
--env-file/env_file:, so the secret only ever touches a file descriptor.
Register the agent against a single repo with a PAT (repo + workflow
scopes) - create ~/.cratis-gh-runner.env:
GITHUB_URL=https://github.com/<owner>/<repo>
GITHUB_PAT=ghp_xxxxxxxxxxxxxxxxThen just run:
./run-local.shOr exchange a repo-scoped registration token yourself and skip the PAT - add
RUNNER_TOKEN to the same file instead of GITHUB_PAT:
gh api -X POST repos/<owner>/<repo>/actions/runners/registration-token --jq .tokenEach container registers as an ephemeral runner (one job, then it
deregisters). Run several at once with docker compose up --scale runner=N.
Register against the whole org so any repo in it can target the agent. Get an org-level registration token either from Org settings -> Actions -> Runners -> New self-hosted runner in the GitHub UI, or via:
RUNNER_TOKEN=$(gh api -X POST \
orgs/<org>/actions/runners/registration-token --jq .token)Then put these in ~/.cratis-gh-runner.env:
GITHUB_URL=https://github.com/<org>
RUNNER_TOKEN=<org-level-registration-token>
RUNNER_LABELS=self-hosted,linux,cratisAnd run:
./run-local.shIn Org settings -> Actions -> Runner groups, create a group, restrict it to the repos allowed to use these agents, and add the new runner to it - this is what scopes an org-registered agent down to specific repos.
RUNNER_MODE=shell ./run-local.sh- interactive shell for debugging the image, no registration.RUNNER_MODE=copilot ./run-local.sh- idles so you candocker execinto it the way the Copilot coding agent does.
Full details, including Kubernetes/Actions Runner Controller for scale, in
Documentation/GitHub/local-setup.md
and
Documentation/GitHub/github-configuration.md.
Documentation/GitHub/README.md- overviewDocumentation/GitHub/local-setup.md- run locallyDocumentation/GitHub/github-configuration.md- wire up in GitHubDocumentation/GitHub/copilot-agent.md- run Copilot on assigned issuesDocumentation/GitHub/notifications.md- get notified about the right repos