diff --git a/hive-forge/src/client.rs b/hive-forge/src/client.rs index 289d1037..2ec8b41d 100644 --- a/hive-forge/src/client.rs +++ b/hive-forge/src/client.rs @@ -11,7 +11,7 @@ use anyhow::{Context, Result, bail}; use forgejo_api::{Auth, ForgejoError}; use reqwest::blocking::{Client as HttpClient, Response}; use reqwest::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE, HeaderMap, HeaderValue}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use serde_json::Value; /// Default Forgejo URL when `HIVE_FORGE_URL` is unset. @@ -54,12 +54,18 @@ impl Client { /// priority over `HIVE_FORGE_REPO`; the env var is the fallback /// default. `json_mode` comes from the global `--json` flag — /// per-verb output formatters key off it via `Client::json_mode`. - pub fn from_env(repo_override: Option, json_mode: bool) -> Result { - let base = std::env::var("HIVE_FORGE_URL").unwrap_or_else(|_| DEFAULT_URL.to_owned()); + /// `forge_label` (from the global `-f/--forge` flag) targets a + /// dashboard-provisioned external forge account instead of the + /// internal forge — see [`resolve_credentials`]. + pub fn from_env( + repo_override: Option, + json_mode: bool, + forge_label: Option, + ) -> Result { + let (base, token) = resolve_credentials(forge_label.as_deref())?; let default_repo = repo_override .or_else(|| std::env::var("HIVE_FORGE_REPO").ok()) .unwrap_or_else(|| DEFAULT_REPO.to_owned()); - let token = read_token().context("read forge-token")?; let url = url::Url::parse(&base).with_context(|| format!("parse HIVE_FORGE_URL {base}"))?; let api = forgejo_api::sync::Forgejo::new(Auth::Token(&token), url) @@ -225,15 +231,93 @@ pub fn index(n: u64) -> Result { i64::try_from(n).with_context(|| format!("number {n} out of range")) } +/// Resolve the `(base_url, token)` pair the client authenticates with. +/// `None` (the default) resolves the internal forge exactly as before: +/// `HIVE_FORGE_URL` (default [`DEFAULT_URL`]) + `read_token()`. +/// `Some(label)` (from `-f/--forge