hive-forge: make --json a global flag (closes #421)

This commit is contained in:
damocles 2026-05-26 14:36:00 +02:00 committed by Mara
commit d0a1ee037b
5 changed files with 33 additions and 16 deletions

View file

@ -124,7 +124,7 @@ since #280). Use it instead of ad-hoc curl pipelines:
```bash
hive-forge view 42 # title + body + comments
hive-forge comments 42 # list all comments (human-readable)
hive-forge comments 42 --json # list as JSON array
hive-forge --json comments 42 # same as above, JSON array (global flag, closes #421)
hive-forge comment 42 --body "..." # post comment (inline body)
hive-forge comment 42 --body-file - <<EOF # ...or pipe a HEREDOC
multi-line body

View file

@ -26,14 +26,19 @@ pub struct Client {
/// Default repo used when a verb doesn't carry an explicit
/// `[repo]` override.
pub default_repo: String,
/// Global `--json` flag — verbs that have a human-readable
/// default path branch on `client.json_mode()` to pick the
/// JSON output shape instead. Closes #421.
json_mode: bool,
}
impl Client {
/// Build a client from the standard environment variables.
/// `repo_override` (from the global `-r/--repo` flag) takes
/// priority over `HIVE_FORGE_REPO`; the env var is the fallback
/// default.
pub fn from_env(repo_override: Option<String>) -> Result<Self> {
/// 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<String>, json_mode: bool) -> Result<Self> {
let base = std::env::var("HIVE_FORGE_URL").unwrap_or_else(|_| DEFAULT_URL.to_owned());
let default_repo = repo_override
.or_else(|| std::env::var("HIVE_FORGE_REPO").ok())
@ -55,9 +60,19 @@ impl Client {
http,
base,
default_repo,
json_mode,
})
}
/// True when the operator passed the global `--json` flag.
/// Verbs that have a human-readable default branch on this to
/// emit JSON instead. Verbs whose only output format is JSON
/// (e.g. `issue`, `pr`) can ignore it.
#[must_use]
pub fn json_mode(&self) -> bool {
self.json_mode
}
/// Resolve the API base path (`<base>/api/v1`).
fn api(&self) -> String {
format!("{}/api/v1", self.base)

View file

@ -35,6 +35,12 @@ struct Cli {
/// positional the bash helper used.
#[arg(short = 'r', long, global = true)]
repo: Option<String>,
/// Emit JSON output instead of the verb's default human-readable
/// shape, for verbs that support both (closes #421). Verbs whose
/// only output is already JSON (`issue`, `pr`, etc.) ignore this
/// flag — they always print JSON regardless.
#[arg(long, global = true)]
json: bool,
#[command(subcommand)]
verb: Verb,
}
@ -87,7 +93,8 @@ enum Verb {
fn main() -> Result<()> {
let cli = Cli::parse();
let client = client::Client::from_env(cli.repo).context("initialize forge client")?;
let client =
client::Client::from_env(cli.repo, cli.json).context("initialize forge client")?;
match cli.verb {
Verb::View(a) => verbs::view::run(&client, a),
Verb::Issue(a) => verbs::issue::run(&client, a),

View file

@ -1,5 +1,5 @@
//! `comment-show <id> [--json] [repo]` — print the body (or full
//! JSON) of a single comment by id.
//! `comment-show <id>` — print the body (or full JSON envelope
//! when `--json` is set globally) of a single comment by id.
use anyhow::Result;
use clap::Args as ClapArgs;
@ -12,15 +12,12 @@ use crate::verbs::print_json;
pub struct Args {
/// Comment id.
id: u64,
/// Print full JSON envelope instead of just the body text.
#[arg(long)]
json: bool,
}
pub fn run(client: &Client, args: Args) -> Result<()> {
let repo = client.repo();
let v = client.get_json(&format!("/repos/{repo}/issues/comments/{}", args.id))?;
if args.json {
if client.json_mode() {
let trimmed = json!({
"id": v.get("id"),
"user": v.get("user").and_then(|u| u.get("login")),

View file

@ -1,5 +1,6 @@
//! `comments <number> [--json] [--limit N]` — list all comments on
//! an issue or PR. Closes the curl-fallback gap (#418).
//! `comments <number> [--limit N]` — list all comments on an issue
//! or PR. Closes the curl-fallback gap (#418). Use the global
//! `--json` flag for JSON output (#421).
use anyhow::Result;
use clap::Args as ClapArgs;
@ -12,9 +13,6 @@ use crate::verbs::print_json;
pub struct Args {
/// Issue or PR number.
number: u64,
/// Print as JSON array instead of human-readable markdown.
#[arg(long)]
json: bool,
/// Page size (Forgejo caps at 50 by default).
#[arg(long, default_value_t = 50)]
limit: u64,
@ -26,7 +24,7 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
"/repos/{repo}/issues/{}/comments?limit={}",
args.number, args.limit
))?;
if args.json {
if client.json_mode() {
let trimmed: Vec<Value> = v
.as_array()
.map(|a| {