Bug reports and pull requests are welcome. It is a small tool with a deliberately narrow scope, so the most useful thing you can do before writing code is open an issue and check the change belongs here.
RunPool manages capacity on one Mac. Things that are out of scope by design, not by omission:
- Routing. A workflow's
runs-ondecides where a job lands. RunPool only decides whether runners are up. - Notification delivery. It emits one JSON object to a command of your choosing and stops there.
- Watching workflow results. That should not depend on a laptop being awake.
- Linux and Windows. Both are well served by actions-runner-controller and garm.
AGENTS.md is the full guide: the hard constraints, the layout, and the traps. The short version:
- Stock macOS bash 3.2. No associative arrays, no
mapfile, no${var^^}. It has to run on a machine where nobody has installed a newer bash. - No runtime dependencies beyond
gh. Adding one means everybody installing RunPool installs it too. - Sourced fragments start with
# shellcheck shell=bash, since they have no shebang.
Before opening a pull request:
/bin/bash -n bin/runpool lib/*.sh contrib/*.sh tests/*.sh install.sh
shellcheck --severity=warning bin/runpool lib/*.sh contrib/*.sh tests/*.sh install.shCI runs exactly that. If shellcheck is not installed, Docker gives the same answer and leaves nothing behind:
docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:stable \
--severity=warning bin/runpool lib/*.sh contrib/*.sh tests/*.sh install.shMost changes need runners, which means a GitHub account and a Mac. Two things make that less painful:
contrib/demo-status.shanswersstatuswith invented pools, so anything consuming the JSON can be developed with no runners at all.RUNPOOL_BASEpoints RunPool at a scratch directory, so you can register throwaway pools without touching a real setup. Environment beats config, deliberately, so a single invocation can be isolated. SetRUNPOOL_CACHE_DIRandRUNPOOL_LOG_DIRalongside it: otherwise a test writes caches and logs into a real installation's macOS storage roots.RUNPOOL_POOLS_FILE, orapply --file PATH, as well.RUNPOOL_BASEdoes not move the pools file: that is derived fromXDG_CONFIG_HOME, deliberately, so that isolating the config does not lose it. Isolate the base and forget this one andrunpool applyreads your real pools file and tries to register every pool in it into the scratch base, with real registrations against real GitHub targets.runpool apply --dry-runcalls nothing and changes nothing, so all oflib/apply.shcan be exercised against hand-written pool config files with no GitHub account.
Which makes the whole isolated invocation:
RUNPOOL_BASE=/tmp/rp-test \
RUNPOOL_CACHE_DIR=/tmp/rp-test/cache \
RUNPOOL_LOG_DIR=/tmp/rp-test/logs \
RUNPOOL_CONFIG=/dev/null \
RUNPOOL_POOLS_FILE=/tmp/rp-test/pools \
./bin/runpool apply --dry-run/dev/null is a valid value for both of those: the config assigns nothing and the pools file declares no pools.
Conventional Commits (fix(stats): …), one logical change per pull request, and a description that says what was wrong rather than what was edited. Issues get opened for defects found in passing and closed by the commit that fixes them.