feat(vps-github-arc): ✨ add GitHub Actions Runner Controller via Helm - #3
Conversation
Adds features/github-arc (order 9, opt-in, depends on k3s) installing the gha-runner-scale-set-controller and gha-runner-scale-set Helm charts into the github namespace, authenticated via a GitHub App secret, per the ARC quickstart. Wires it into the existing dispatch.sh/README/lib/summary.sh conventions used by argocd/epinio. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdfpGmChEUw4bDbDVbbRya
897c5bf to
a84371c
Compare
|
The PR's current title, Generated by Claude Code |
…ch.sh Each features/*/package.json now declares vps.inputs/vps.outputs in the same shape as a GitHub composite action's inputs:/outputs:, keyed directly by the env var each feature's run.sh reads. dispatch.sh reads this (new pkg_input_* helpers, brace-depth tracked like pkg_deps) and, on an interactive run, prompts for any input of an enabled step that isn't already set in the environment before running anything. Also fixes a pre-existing bug the hyphenated github-arc feature name exposed: state_get/state_set eval'd a STATE_<name> variable directly, which is invalid shell syntax for a name containing a hyphen.
"vps" was a made-up top-level package.json field a schema validator wouldn't recognize. "config" is npm's own reserved field for package-specific settings, so every features/*/package.json now nests order/default/ports/dependencies/inputs/outputs under that instead. Updates the few places that referenced the key literally: lib/common.sh's jq lookups (.vps.ports -> .config.ports) and doc comments in dispatch.sh, lib/common.sh, features/security/run.sh, and README.md. The dispatch.sh sed/awk parsers themselves needed no change - they match field names generically regardless of the parent object's name.
state_get/state_set (set), ask_missing_inputs (ask), and run_step (run) move out of dispatch.sh into their own sourced POSIX sh file, so the root script stays focused on flag parsing, the menu, and dependency resolution. Sourced once REPO_ROOT is known, after the bootstrap clone/re-exec, so the curl | sh path is unaffected.
Replaces the hand-rolled sed/awk pkg_str/pkg_bool/pkg_num/pkg_deps/ pkg_input_names/pkg_input_block parsers with direct jq queries (feature_* accessors, pkg_input_description/required/default). Adds ensure_jq, called right before the first package.json read, which installs jq via apt if it's missing - needed since this runs before features/system (the step that would otherwise install it) on a totally fresh box. lib/common.sh already used jq for port lookups; dispatch.sh's own parsing is now consistent with that instead of being the odd one out.
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: tomgrv <1809566+tomgrv@users.noreply.github.com>
Co-authored-by: tomgrv <1809566+tomgrv@users.noreply.github.com>
Lost in the merge of develop's workspace restructuring - every other feature's run.sh is 100755.
|
As before, this workflow only reads the PR title from the triggering event's payload, not live, so the re-run I just queued will likely replay the old (still-failing) title rather than picking up this edit - it'll go green on the next real push ( Generated by Claude Code |
|
@copilot enforce workspace scope |
Workspace scope enforcement is already active through |
…ADME.md + test.bats per folder) (#9) * feat(vps-github-arc): add GitHub Actions Runner Controller via Helm (#3) * Add github-arc feature: GitHub Actions Runner Controller via Helm Adds features/github-arc (order 9, opt-in, depends on k3s) installing the gha-runner-scale-set-controller and gha-runner-scale-set Helm charts into the github namespace, authenticated via a GitHub App secret, per the ARC quickstart. Wires it into the existing dispatch.sh/README/lib/summary.sh conventions used by argocd/epinio. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdfpGmChEUw4bDbDVbbRya * Add vps.inputs/outputs to every feature and prompt for them in dispatch.sh Each features/*/package.json now declares vps.inputs/vps.outputs in the same shape as a GitHub composite action's inputs:/outputs:, keyed directly by the env var each feature's run.sh reads. dispatch.sh reads this (new pkg_input_* helpers, brace-depth tracked like pkg_deps) and, on an interactive run, prompts for any input of an enabled step that isn't already set in the environment before running anything. Also fixes a pre-existing bug the hyphenated github-arc feature name exposed: state_get/state_set eval'd a STATE_<name> variable directly, which is invalid shell syntax for a name containing a hyphen. * Rename package.json's "vps" field to "config" for schema compliance "vps" was a made-up top-level package.json field a schema validator wouldn't recognize. "config" is npm's own reserved field for package-specific settings, so every features/*/package.json now nests order/default/ports/dependencies/inputs/outputs under that instead. Updates the few places that referenced the key literally: lib/common.sh's jq lookups (.vps.ports -> .config.ports) and doc comments in dispatch.sh, lib/common.sh, features/security/run.sh, and README.md. The dispatch.sh sed/awk parsers themselves needed no change - they match field names generically regardless of the parent object's name. * Split set/ask/run into lib/dispatch-steps.sh state_get/state_set (set), ask_missing_inputs (ask), and run_step (run) move out of dispatch.sh into their own sourced POSIX sh file, so the root script stays focused on flag parsing, the menu, and dependency resolution. Sourced once REPO_ROOT is known, after the bootstrap clone/re-exec, so the curl | sh path is unaffected. * Read package.json with jq in dispatch.sh, installing it on demand Replaces the hand-rolled sed/awk pkg_str/pkg_bool/pkg_num/pkg_deps/ pkg_input_names/pkg_input_block parsers with direct jq queries (feature_* accessors, pkg_input_description/required/default). Adds ensure_jq, called right before the first package.json read, which installs jq via apt if it's missing - needed since this runs before features/system (the step that would otherwise install it) on a totally fresh box. lib/common.sh already used jq for port lookups; dispatch.sh's own parsing is now consistent with that instead of being the odd one out. * docs(github-arc): align merge comments with workspace layout Co-authored-by: tomgrv <1809566+tomgrv@users.noreply.github.com> * fix(github-arc): restore executable bit on run.sh Lost in the merge of develop's workspace restructuring - every other feature's run.sh is 100755. --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> * Align feature folders with tomgrv/scripts convention: README.md + test.bats per folder Every feature folder now carries its own README.md (usage, up/down, env vars, dependencies) and test.bats (syntax + shape checks), the same one-folder-per-step layout tomgrv/scripts uses for its own scripts. common/'s pure-logic tests moved from tests/test-common.bats into common/test.bats; tests/test-syntax.bats now covers only what spans the whole repo (dispatch.sh/setup.sh, cross-folder invariants) instead of looping over every folder itself. Each package.json gains a "test": "bats test.bats" script, and the root package.json's own "test" script now runs the whole suite recursively. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134CCrDUFyYPUmcbZ2NcKKd * chore: regenerate package-lock.json to drop argocd/epinio/github-arc Left over as extraneous entries after those workspace folders were removed in favor of marketplace/ (#7) - npm install alone didn't prune them, only a fresh lockfile did. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134CCrDUFyYPUmcbZ2NcKKd * refactor: package.json "name" fields become bare folder names, matching tomgrv/scripts Every feature's package.json "name" was "@tomgrv/vps-<folder>", diverging from tomgrv/scripts' own convention where "name" is always exactly the folder name (no scope, no prefix - see e.g. install-feature/package.json). "name" and "dependencies" keys are now bare folder names throughout (rancher's package.json: "name": "rancher", "dependencies": {"k3s": "*", "common": "*"}). dispatch.sh's feature_name()/feature_deps() and common/run.sh's feature_package_json() no longer strip/expect an "@tomgrv/vps-" prefix. feature_deps() now recognizes a real feature dependency by checking the key names an actual <folder>/{package.json,run.sh} (excluding common/summary) instead of by naming-convention prefix, so an ordinary npm dependency wouldn't be mistaken for an auto-enable target if one is ever added. This changes vps-tailscale's dispatch.sh identity from the short "tailscale" to the full "vps-tailscale" (folder name == package name now, with no exception) - flags become --only-vps-tailscale/ --skip-vps-tailscale/--down-vps-tailscale, and the TAILSCALE_AUTHKEY guard's state_get/messages were updated to match. Verified via a real dispatch.sh --help and --only-vps-tailscale run (not just the test suite) that the new flag names resolve correctly. Regenerated package-lock.json for the renamed workspace packages. README.md and every test.bats/README.md updated to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134CCrDUFyYPUmcbZ2NcKKd * refactor: rename every feature folder to vps-<name>, matching its scoped package name Corrects the previous commit's approach: package.json "name" fields stay scoped as "@tomgrv/vps-<name>" (this repo's own convention, not tomgrv/scripts' unscoped one - its packages aren't all prefixed the same way this repo's are), and every folder is renamed to carry the same "vps-" prefix the package name already had, instead of stripping the prefix off the package name to match a bare folder. vps-tailscale/ was already named this way (to avoid zz_use shadowing the real tailscale CLI) - every other folder now follows the same shape uniformly, so that's no longer a special case. Renamed: cockpit -> vps-cockpit, common -> vps-common, dockermanager -> vps-dockermanager, k3s -> vps-k3s, marketplace -> vps-marketplace, rancher -> vps-rancher, security -> vps-security, summary -> vps-summary, system -> vps-system. - Every package.json's "bin" key and "dependencies" keys now match the new folder names / scoped package names. - dispatch.sh's feature_name()/feature_deps() strip only the npm scope ("@tomgrv/") now, not the "vps-" prefix too, so the CLI/state identity (--only-<x>/--skip-<x>/--down-<x>, dependency auto-enable) is the full "vps-<name>" uniformly - e.g. --only-vps-rancher, not --only-rancher. Verified with a real "dispatch.sh --help" and a live --skip-vps-tailscale --only-vps-rancher run (dependency auto-enable message, step skip messages) rather than just the test suite. - common/run.sh's feature_package_json() matches package.json "name" against "@tomgrv/<feature>" (feature already carries "vps-"), and every run.sh's zz_use/". <name>" sourcing of vps-common was updated to match. - Every test.bats/README.md updated for the new folder/bin/dependency names; root package.json's "workspaces" array and package-lock.json regenerated for the renamed workspace packages. - README.md updated throughout (Layout, One folder per feature, flag examples, removal table, interactive menu, env var table) to the new vps-<name> identity everywhere. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134CCrDUFyYPUmcbZ2NcKKd * fix: vps-dockermanager declares its real dependency on vps-cockpit cockpit-packagekit/cockpit-files are Cockpit plugins, and down() tries `systemctl try-restart cockpit.socket` - none of that means anything without Cockpit itself already installed, but vps-dockermanager's package.json only ever declared @tomgrv/vps-common. Verified live (not just via the test suite): `--only-vps-dockermanager` now auto-enables vps-cockpit first, and `--down-vps-cockpit` is refused while vps-dockermanager is still enabled, matching every other dependency edge in this repo. Also regenerated every per-folder README.md's body text to the vps-<name> naming from the previous commit - the file moves/edits there only touched run.sh/test.bats/package.json and the root README, leaving each feature's own README still describing itself with the old bare flag/path names (--only-rancher, common/run.sh, etc.). Root README's "Dependencies between steps" section and "Removing a feature" table updated to match and to correct a pre-existing inaccuracy (it said vps-marketplace depends directly on vps-k3s; it actually depends on vps-rancher, which in turn depends on vps-k3s - dispatch.sh's auto-enable already resolves this transitively, verified live). Regenerated package-lock.json for the new dependency edge. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134CCrDUFyYPUmcbZ2NcKKd * refactor: remove dispatch.sh, fold orchestration into vps-setup (was vps-summary) dispatch.sh's flag parsing, interactive menu, feature discovery, and multi-pass dependency auto-enable now live in vps-setup/run.sh (renamed from vps-summary/, which already printed the final connection summary - that's folded in too, printed at the end of every vps-setup run). Root setup.sh no longer execs anything from this repo - it's now a bulk copy of tomgrv/scripts' own setup.sh (byte-identical), which only bootstraps the core zz_* bundle from tomgrv/scripts and stops. New entry point: bootstrap via setup.sh, then zz_use + run vps-setup as its own separate step - there is no more single "curl dispatch.sh | sudo sh" one-liner that does everything end-to-end. curl -fsSL https://raw.githubusercontent.com/perspikapps/vps/main/setup.sh | sh zz_use perspikapps/vps/vps-setup sudo vps-setup vps-setup isn't itself an installable step (like vps-common, it's excluded from feature discovery) - since a single zz_use fetch only pulls its own folder, it detects whether it's running from a full local checkout or needs to clone/update one itself (VPS_SETUP_REPO_URL/REF/DIR, same as dispatch.sh's old clone logic) before it can enumerate every sibling feature folder to orchestrate them. Also removed the log()/warn()/die() wrapper functions dispatch.sh used to carry (from before vps-common existed) - vps-setup/run.sh and steps.sh now call zz_log directly, matching the "no wrapper functions" convention already enforced repo-wide by tests/test-syntax.bats for every other run.sh. Updated: root package.json (dropped "main", renamed the workspace entry), cloud-init/kairos-vps-setup.yaml (two-step bootstrap+run), every reference to dispatch.sh, tests/test-syntax.bats, and package-lock.json. README.md rewrite (still describes the old dispatch.sh flow) follows in the next commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134CCrDUFyYPUmcbZ2NcKKd * docs: rewrite README for the vps-setup/no-dispatch.sh flow Every dispatch.sh reference replaced with vps-setup throughout: the one-liner, "Running a single step", the interactive menu, "Removing a feature", "Running from a non-standard branch or fork" (which needed a real rewrite - the old sudo/piped-curl env-var gotcha doesn't apply once vps-setup is a real installed command; the new gotcha is sudo's plain env reset, addressed the same way), Layout, "One folder per feature", Rancher Marketplace, Troubleshooting, Tests, and Replicating this pattern in another repo (which now describes copying setup.sh verbatim and fetching an orchestrator as its own explicit zz_use step, not a package.json "main"-field auto-exec). Added a new "Running vps-setup" section up front explaining the two-step bootstrap+run flow and why vps-setup needs its own full checkout to orchestrate its sibling folders. Also fixed two pre-existing inaccuracies while in there: the "Dependencies between steps" example claimed vps-marketplace depends directly on vps-k3s (it depends on vps-rancher, which depends on vps-k3s - the transitive resolution was already real, the docs just didn't say so - verified live), and "One folder per feature" claimed vps-setup's package.json reading used sed/awk (verified: it uses jq, via ensure_jq - the sed/awk description was stale from before that migration too). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134CCrDUFyYPUmcbZ2NcKKd * Address Copilot review: fix feature_deps robustness, git fetch option-injection guard, stale dispatch.sh/vps-summary references feature_deps() now filters vps-common/vps-setup in jq's own select() instead of piping through grep -v (which could exit 1 on an empty result under pipefail), and uses if/then instead of a chained [[ ]] && [[ ]] && printf inside the piped while-read loop, so a single non-matching dependency can no longer abort the whole loop early under set -e. git fetch now passes -- before $REPO_REF to guard against option injection if a ref were to start with -. Several per-folder READMEs (vps-system, vps-tailscale, vps-security, vps-dockermanager, vps-cockpit, vps-rancher, vps-marketplace, vps-common, vps-setup) still referenced the removed dispatch.sh / vps-summary from before the vps-setup rename; updated them to describe the current vps-setup flow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134CCrDUFyYPUmcbZ2NcKKd --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Summary
<name>/{package.json,run.sh}), matchingargocd/epinio/etc.github-arc/(opt-in likeargocd/epinio, depends onk3s): installs thegha-runner-scale-set-controllerandgha-runner-scale-setHelm charts into thegithubnamespace, per the ARC quickstartGITHUB_ARC_APP_ID/GITHUB_ARC_APP_INSTALLATION_ID/GITHUB_ARC_APP_PRIVATE_KEY_FILE(a path to the key, never the key content itself, on the command line)dispatch.sh(auto-discovered),summary/run.sh(status line), andREADME.md(menu, down-table, "Getting the keys you'll need", a full section, and the env var table) so it behaves exactly like every other opt-in steppackage.jsondeclaresvps.inputs/vps.outputs, in the same shape as a GitHub composite action'sinputs:/outputs:, keyed directly by the env var each feature'srun.shreads.dispatch.shreads this and, on an interactive run, prompts for any input of an enabled step that isn't already set in the environment before running anythingdispatch.shreads everypackage.jsonviajq(installed on demand viaensure_jqif missing, since this runs before thesystemstep that would otherwise install it)dispatch.shdrives (state_get/state_set,ask_missing_inputs,run_step) live in their own sourced file (summary/dispatch-steps.sh)github-arcname exposed:state_get/state_seteval'd aSTATE_<name>variable directly, invalid shell syntax for a name containing a hyphenMerge note: this branch predated develop's workspace restructuring (
features/<name>/→<name>/,lib/common.sh→common/run.sh+zz_use,@vps/*→@tomgrv/vps-*). @copilot resolved the resulting conflict (9686a47, 6607f13), correctly re-hominggithub-arc/andsummary/dispatch-steps.shonto the new layout. I reviewed that resolution: syntax checks pass,github-arc/run.shnow bootstrapszz_use/commonlike every other feature, and all the shared helpers it calls (require_root,command_exists,helm_teardown,dispatch_action,zz_log) exist incommon/run.sh. I can't fully exercisedispatch.shend-to-end here sincesetup.shfetchestomgrv/scriptsover the network, which this sandbox blocks - CI is the real signal for that.Layout
Test plan
sh -n dispatch.sh,sh -n summary/dispatch-steps.sh,bash -n github-arc/run.sh(syntax checks)*/package.jsonparses as valid JSONTAILSCALE_AUTHKEYguard passes without re-asking)zz_use/commonconventionsudo sh dispatch.sh --only-github-arcon a real k3s VPS with a configured GitHub App and confirm runners register (network access needed forsetup.sh/OCI Helm charts isn't available in this sandbox)