[r5n-64] hydra cli#16
Merged
Merged
Conversation
create(count) (multi) -> create(name) (single) to make UI creation visuals possible
process tree on stop
runners, and compact formatting
deralaxo
marked this pull request as ready for review
April 2, 2026 01:03
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.
Summary
Implements the Hydra CLI — a tool for managing fleets of GitHub self-hosted runners from a single command. Hydra handles downloading, registering, starting, stopping, and removing runners with disk-optimized storage using hard links and symlinks, reducing disk usage from ~558 MB per runner to near-zero for additional runners.
Changes
Features
initcommand — Interactive and non-interactive setup with profile creation, URL validation,--forceoverwrite guard, and profile name prompt in interactive modecreatecommand — Provisions N runners with per-runner spinner output, "ensure total count" semantics (fills gaps, skips existing), and config tracking. Accepts[profile] [count]positionalsstatuscommand — Shows all runners across all profiles with color-coded status (running/registered/unknown), PID info, natural sort order, and compact single-block outputstart/stopcommands — Start/stop runners with status-aware filtering. Interactive mode shows multiselect with only actionable runners (stopped for start, running for stop)removecommand — Stops, deregisters, and cleans up runners. Accepts[profile] [ids...]positionalsprofile list— Detailed profile view in boxed format showing URL, provider, OS, labels, runner counts, and default indicatorprofile remove— Removes a profile with confirmation, automatically stops and deregisters all associated runnersprofile default— Set the default profile used when no profile is specifiedhydra initprompts for profile name interactively, or accepts it via--profileflag in direct mode.defaultProfileis set automatically on first init or when missingCLI-Core Enhancement
Exiterrors thrown during interactive subcommand execution now return to the main menu instead of terminating the CLI. Applies to all CLIs using@r5n/cli-coreGitHub Runner Provider
bin/directory (84 MB) is hard-linked per runner (0 bytes extra),externals/(345 MB) is symlinked. 10 runners use ~430 MB instead of ~5.5 GBghCLI, runsconfig.sh --unattendedquietlyrun.shwithproc.unref()so CLI exits immediately, tracks PIDs in.pidfiles, kills process tree viapkill -fon stop.runnerJSON filesCLI Design
[profile] [ids...]positionals instead of--profileflag (e.g.,hydra start default runner-1)defaultProfile, omitting IDs targets all runnersresolveProfile()handles empty profiles check, default fallback, and existence validation in one place. Used bycreate,start,stop,remove,profile default, andprofile removeselectProfile()for interactive profile picker (auto-selects when only one),resolveProfile()for validated profile lookup,resolveRunnerIds()for direct mode ID resolutionBug Fixes
proc.unref()prevents CLI from hanging,pkill -f <absDir>kills entire process tree (bash -> run-helper -> Runner.Listener)config.defaultProfileinstead of hardcodedDEFAULT_PROFILEconstantresolve()for absolute paths so symlinks work correctly across directories.runnerwith UTF-8 BOM,stripBom()handles JSON parsingcwdset to runner dir, not via absolute path, because it resolvesBASH_SOURCETesting
test-repo: init -> create runners -> start all -> status (verify running + PIDs) -> stop all -> status (verify registered) -> remove allduconfirms 0 bytes for hard-linkedbin/directories, link count = 2