Skip to content

feat: custom runner labels, and a rename command - #72

Merged
aicayzer merged 1 commit into
mainfrom
feat/labels-and-rename
Sep 5, 2026
Merged

aicayzer merged 1 commit into
mainfrom
feat/labels-and-rename

Conversation

@aicayzer

@aicayzer aicayzer commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Closes #66 and #67.

Labels

register --labels and a --labels field in the pools file, appended to the base set. Previously the only way to give a pool an extra label was to edit POOL_LABELS in its config by hand, after which register could not reproduce it, apply could not see it, pools did not show it, and anything that recreated the pool dropped it. Nothing failed when that happened: the runners came up healthy and the workflow's runs-on simply stopped matching.

One field, not two. POOL_LABELS stays the full authoritative list handed to config.sh, and the extras are derived back out of it at every point of use. A second field caching that derivation would go stale the first time somebody edited the config by hand, and the following apply would silently revert it, which is the same defect in a new place.

Append, never replace. GitHub assigns self-hosted, the OS and the architecture regardless, so a replacing flag would promise something GitHub overrides. The pool name is in the base set too, because it is the routing contract. Naming an implicit label is refused rather than dropped, which also keeps apply idempotent: a token accepted and normalised away would leave declared and derived permanently unequal and re-register the pool for ever.

Validation is the pool-name character class. Narrower than GitHub allows, deliberately: POOL_LABELS is written into a config every command sources, so $ or a backtick there is command execution on every invocation and every 60-second tick; apply separates records with |; the pools file is word-split; and rename matches plists with find -name. One rule closes all four.

Applying a label change re-registers the pool, because labels live on GitHub's registration. The plan says so, names what stops matching, and the summary counts the pools affected. Absent --labels means none, so a pool whose config was hand-edited needs the label declared in the file.

Rename

runpool rename <old> <new> [--drain]. Moves the config, runner tree, cache tree, launch agents and state, then re-registers every runner.

mv, not copy. migrate-storage copies because it crosses storage roots; a rename keeps the same parent by construction, so mv is atomic and a second on-disk copy of runner credentials buys nothing. Reusing the idempotent move helper also makes an interrupted rename resumable, which is covered by a test.

It must deregister explicitly. config.sh --replace replaces a registration of the same name, and the name is what changes, so GitHub would keep the old one: permanently offline, still carrying the old pool name as a label so a surviving runs-on matches a dead runner, and unreachable afterwards because config.sh overwrites the .runner holding its agentId.

Two locks, old then new, released in reverse. Runner directories are iterated as they exist rather than 1..POOL_COUNT, so a count lowered by hand does not strand a registration; those surplus runners are deregistered and deliberately not re-registered.

_rp_migrate_update_pool_conf is not reused: its END clause adds POOL_CACHE_DIR when absent, and that absence is exactly how a legacy pool is recognised. There is a test for it.

Also here

_rp_reregister now takes the reconfiguration lock. It stood the pool down and then spent a long time in config.sh with nothing stopping autoscale or up restarting it. Latent while it was only hand-run; apply now calls it.

runpool pools prints a pool's extra labels, which is how "why does my runs-on not match" gets answered without reading a config.

Verification

Two new offline tests, 80 cases between them. tests/pool-labels.sh covers the derivation (including recovering a hand-added label, and the first-occurrence-only rule for a pool named after its own label), the validator, and the repo's first apply coverage via --dry-run. tests/pool-rename.sh stubs gh and each runner's config.sh so both log their arguments, which is what proves the GitHub contract offline: the DELETE calls, the new runner names, and the new label set.

bash -n, shellcheck --severity=warning and all ten tests pass.

Labels could only be set by hand-editing a pool config. register could not
produce one, the pools file could not express one, apply could not see
one, and anything that recreated the pool dropped it silently, which is
a workflow queuing for ever against a pool reporting perfect health.

--labels now appends to the base set on register and in the pools file.
POOL_LABELS stays the full authoritative list and the extras are derived
back out of it every time, so a config edited by hand is respected
rather than reverted. runpool pools shows them.

rename moves a pool's directories, config, launch agents and state, then
re-registers every runner with GitHub. It deletes the old registrations
rather than relying on --replace, which only covers a name collision and
so would strand them: offline, unreachable, and still advertising the
old pool name as a label.

reregister now takes the reconfiguration lock, which it never did.

Closes #66
Closes #67
@aicayzer
aicayzer merged commit da47c1d into main Sep 5, 2026
2 checks passed
@aicayzer
aicayzer deleted the feat/labels-and-rename branch September 5, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

custom runner labels can only be set by hand-editing a pool conf

1 participant