Repository navigation
feat(arc): repository-scoped runner scale sets - #142
Merged
Merged
Conversation
An entry with repository registers its scale sets in name/repository, where name may be a user or an organisation, so a repository of a personal account can use the fleet. Each profile now records its scope, repository and githubConfigUrl. Names derived for a repository scope join the login and the repository with a double hyphen, which no GitHub login contains, so they never collide with the organisation's own or another repository's. The duplicate, release and cross-cluster checks key on the scope instead of the login, and arc_scope gives the tasks the same key.
Helm refuses a release name over 53 characters, so a name between that and the DNS label limit passed validation and failed only at helm upgrade. Release names are now checked against Helm's own limit.
The adapter read one key per entry name, so two github_runner_arc_orgs entries of one login naming different keys (an organisation and one of its repositories) got the same key. Keys are now read, cached and filled by their 1Password reference.
A profile registers with its scope's githubConfigUrl, so a repository
entry's scale sets register in that repository. Installation ids, pull
tokens, an entry's profiles and an existing App Secret's app_id check
are looked up by scope rather than by login, so an organisation entry
and its repository entries never stand in for one another.
A repository scope resolves its installation through
GET /repos/{owner}/{repo}/installation, which answers only for an
installation that can reach the repository.
github_runner_arc_app_setup_repository makes the manifest flow create an App for a repository entry: it asks for the repository administration write and metadata read permissions ARC documents for that scope, waits for an installation that reaches the repository, and writes the App Secret into that entry's namespaces under its derived name. github_runner_arc_app_setup_owner_type user posts the manifest to a personal account's App settings, and fails without a repository, since a personal account has no organisation runners. An empty github_runner_arc_app_setup_permissions now means the scope's documented permissions, and arc_default_app_secret gives the setup and the profile expansion one derivation of the App Secret's name.
RUNNER_REPOSITORY sets the entry's repository, so a single machine can bring up runners for one repository of a user or an organisation.
Covers the repository key and its githubConfigUrl, the names it derives and why they never collide, the App permissions for each scope with their sources, and that every repository needs an entry of its own.
Mearman
marked this pull request as ready for review
October 25, 2026 08:10
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
This security review couldn’t start because the paying account has exhausted its Codex allowance and has insufficient available credits. The payer can check their balance and add credits in the Codex usage dashboard, then try again. If you do not manage the paying account, contact this repository's admins. |
|
🎉 This PR is included in version 2.9.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A
github_runner_arc_orgsentry can now setrepository, which registers its scale sets in that one repository:githubConfigUrlbecomeshttps://github.com/<name>/<repository>, andnamemay be a user or an organisation. That is the only way a repository of a personal account can use the fleet, since a personal account has no organisation runners. Withoutrepositoryan entry behaves exactly as before.An entry is now identified by its scope (
name, orname/repository, from the newarc_scopefilter). Everything that keyed on the login keys on the scope instead: the duplicate, release-name and cross-cluster checks inarc_profilesandarc_cluster_profiles, resolved installation ids, App-minted pull tokens, the selection of an entry's profiles ininstall_org.ymlandmain.yml, and theapp_idcheck against an existing App Secret (which previously took the first entry with a matching name). So an organisation's entry and any number of its repositories' entries can sit side by side.Derived names for a repository scope join the login and the repository with
--(lower-cased, with.and_turned into-):example-user/example-repogives releaseexample-user--example-repo-runners, namespacearc-runners-example-user--example-repo, App Secretexample-user--example-repo-github-appand defaultruns-onlabelexample-user--example-repo-runners. GitHub logins cannot contain--, so these never meet an organisation entry's names, and anything that still collides (two repository names differing only in.and_) is refused by the existing namespace and release checks.A repository scope resolves its installation through
GET /repos/{owner}/{repo}/installationrather than matchingaccount.logininGET /app/installations, so an App installed on the account but not on that repository fails at install time. The organisation path is unchanged;accountthere is a user or an organisation, so the login match would also find a user's installation, but not prove it reaches the repository.The manifest flow gains
github_runner_arc_app_setup_repositoryandgithub_runner_arc_app_setup_owner_type(organizationoruser). A repository App asks for repository Administration write and Metadata read; auserApp posts tohttps://github.com/settings/apps/newinstead of the organisation's App settings and refuses to run without a repository.github_runner_arc_app_setup_permissionsnow defaults to empty, meaning the scope's documented permissions (unchanged for an organisation).bootstrap.shtakesRUNNER_REPOSITORY.Two fixes this needed:
maxReleaseNameLenin Helm'spkg/chart/v2/util/validate_name.go) rather than the 63-character DNS label limit, since a repository name makes a too-long release far more likely and Helm would only refuse it athelm upgrade.name, so two entries of one login naming different keys got the same one. It now keys them byprivate_key_op_reference. An existing cache keyed by name is simply re-read for the missing references.Unchanged and documented as scope-independent: the runner reaper (it reads runs from
/repos/{owner}/{repo}/actions/runs/...for the repository ARC recorded on the runner), the autoscaler and its pooled budget and the heartbeat (both work onnamespace/releasetargets), and the pull Secret renewal (it uses whatever App Secret the profile's namespace holds). Withgithub_runner_arc_manage_secrets: falsea repository entry reads its App's id from the existing App Secret under itsapp_secret_nameor derived name, like any other.Sources:
values.yaml(archived):githubConfigUrltakes a repository URL, and scale set names are unique within their runner group.https://github.com/settings/apps/newfor a personal account,https://github.com/organizations/ORGANIZATION/settings/apps/newfor an organisation.GET /repos/{owner}/{repo}/installationwith an App JWT, and theaccountof a listed installation.Not verified here, and said so in the role README where it matters: ARC's own instructions describe an organisation-owned App, and I have not run a repository scale set against a personal account's own App; nor whether an installation token of an App installed on a user reads that user's private GHCR packages (
github_runner_arc_image_pull_secret_source: app), so the README points a personal account atstaticornoneuntil a run proves it.Each new test was checked by breaking the code it covers (scope keying in every check, the separator, the URL, validation, the Helm limit, the 1Password keying, the App Secret
app_idlookup, the renderedgithubConfigUrl, and the manifest form's action, permissions, secret targets, user-without-repository check and phase 2 command) and watching it fail.