hive-forge: add -f/--forge <label> selector for external forge accounts

Targets a dashboard-provisioned external forge account (FORGES tab)
instead of the internal forge: resolves `forge-<label>-token` for the
token and `forge-<label>.json`'s base_url for the URL, the same two
files dashboard/extra_forges.rs writes, instead of
HIVE_FORGE_URL/forge-token. Falls back to today's behavior when unset.
Orthogonal to -r/--repo.

An unknown label gives a clear error listing the labels actually found
in the state dir instead of a raw file-not-found. The base_url JSON key
is read via a typed sidecar struct pinned to what extra_forges.rs
writes, so the read side can't silently drift from the write side.
This commit is contained in:
iris 2026-07-14 19:31:08 +02:00
commit 8e58793f79
2 changed files with 110 additions and 11 deletions

View file

@ -5,6 +5,11 @@
//! `HIVE_FORGE_REPO` — default repo, e.g. `hyperhive/hyperhive`
//! `HYPERHIVE_STATE_DIR` — state dir; `forge-token` lives here
//!
//! The global `-f/--forge <label>` flag targets a dashboard-provisioned
//! external forge account instead: it reads `forge-<label>-token` +
//! `forge-<label>.json` (base URL) from the state dir rather than
//! `HIVE_FORGE_URL`/`forge-token`. See `client::Client::from_env`.
//!
//! Single binary with verb subcommands. Replaces the prior bash
//! script (`hive-forge-tools.nix`) so that agents and operators get
//! the same error handling, exit codes, and JSON shapes regardless
@ -36,6 +41,15 @@ struct Cli {
/// positional the bash helper used.
#[arg(short = 'r', long, global = true)]
repo: Option<String>,
/// Target an external forge account provisioned via the dashboard's
/// FORGES tab, by label, instead of the internal forge. Reads
/// `${HYPERHIVE_STATE_DIR}/forge-<label>-token` for the token and
/// `forge-<label>.json` for the base URL (the same two files the
/// dashboard writes) instead of `HIVE_FORGE_URL`/`forge-token`.
/// Orthogonal to `-r/--repo`, which still just picks which repo on
/// whichever forge is selected.
#[arg(short = 'f', long, global = true)]
forge: Option<String>,
/// Emit JSON output instead of the verb's default human-readable
/// shape, for verbs that support both. Verbs whose
/// only output is already JSON (`issue`, `pr`, etc.) ignore this
@ -181,7 +195,8 @@ enum Verb {
fn main() -> Result<()> {
let cli = Cli::parse();
let client = client::Client::from_env(cli.repo, cli.json).context("initialize forge client")?;
let client = client::Client::from_env(cli.repo, cli.json, cli.forge)
.context("initialize forge client")?;
match cli.verb {
Verb::View(a) => verbs::view::run(&client, a),
Verb::Issue(a) => verbs::issue_cmd::run(&client, a),