//! `hive-forge` — typed CLI wrapper around the in-cluster Forgejo's //! REST API. Reads credentials from the environment: //! //! `HIVE_FORGE_URL` — base URL, e.g. `http://localhost:3000` //! `HIVE_FORGE_REPO` — default repo, e.g. `hyperhive/hyperhive` //! `HYPERHIVE_STATE_DIR` — state dir; `forge-token` lives here //! //! 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 //! of how the bash mood was that day (closes #280). #![warn(missing_docs)] // Clap-derived `Args` structs are intentionally consumed by their // per-verb `run` handler so we can move owned String fields out // without cloning. The pedantic lint flags every one of them. #![allow(clippy::needless_pass_by_value)] mod body; mod client; mod verbs; use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; #[derive(Parser)] #[command( name = "hive-forge", about = "Forgejo CLI wrapper for hyperhive (closes #280)", disable_help_subcommand = true )] struct Cli { /// Repo override (default from `HIVE_FORGE_REPO`). /// Applies to any verb; replaces the per-verb `[repo]` trailing /// positional the bash helper used. #[arg(short = 'r', long, global = true)] repo: Option, #[command(subcommand)] verb: Verb, } #[derive(Subcommand)] enum Verb { /// Dump title + body + all comments for an issue or PR. View(verbs::view::Args), /// Print key fields of an issue as JSON. Issue(verbs::issue::Args), /// Create an issue. Prints the issue URL on success. IssueCreate(verbs::issue_create::Args), /// Edit an issue's title, body, state, or milestone. IssueEdit(verbs::issue_edit::Args), /// Print key fields of a PR as JSON. Pr(verbs::pr::Args), /// Create a pull request. Prints the PR URL on success. PrCreate(verbs::pr_create::Args), /// Post a comment on an issue or PR. Comment(verbs::comment::Args), /// Print the body (or full JSON) of a single comment by id. CommentShow(verbs::comment_show::Args), /// Edit an existing comment by id. CommentEdit(verbs::comment_edit::Args), /// Assign or unassign a user on an issue or PR. Assign(verbs::assign::Args), /// Close an issue or PR. Close(verbs::close::Args), /// List, add, or remove labels on an issue or PR. Labels(verbs::labels::Args), /// Manage milestones (list / create / close). Milestone(verbs::milestone::Args), /// List reviews on a PR. PrReviews(verbs::pr_reviews::Args), /// List branches, optionally filtered. Branches(verbs::branches::Args), /// Print the tree SHA at a branch or commit. TreeSha(verbs::tree_sha::Args), /// Print the unified diff for a PR. Diff(verbs::diff::Args), /// Get or set this user's watch subscription on a repo. Subscription(verbs::subscription::Args), /// Upload a file as an attachment to an issue. AttachIssue(verbs::attach::IssueArgs), /// Upload a file as an attachment to a comment. AttachComment(verbs::attach::CommentArgs), } fn main() -> Result<()> { let cli = Cli::parse(); let client = client::Client::from_env(cli.repo).context("initialize forge client")?; match cli.verb { Verb::View(a) => verbs::view::run(&client, a), Verb::Issue(a) => verbs::issue::run(&client, a), Verb::IssueCreate(a) => verbs::issue_create::run(&client, a), Verb::IssueEdit(a) => verbs::issue_edit::run(&client, a), Verb::Pr(a) => verbs::pr::run(&client, a), Verb::PrCreate(a) => verbs::pr_create::run(&client, a), Verb::Comment(a) => verbs::comment::run(&client, a), Verb::CommentShow(a) => verbs::comment_show::run(&client, a), Verb::CommentEdit(a) => verbs::comment_edit::run(&client, a), Verb::Assign(a) => verbs::assign::run(&client, a), Verb::Close(a) => verbs::close::run(&client, a), Verb::Labels(a) => verbs::labels::run(&client, a), Verb::Milestone(a) => verbs::milestone::run(&client, a), Verb::PrReviews(a) => verbs::pr_reviews::run(&client, a), Verb::Branches(a) => verbs::branches::run(&client, a), Verb::TreeSha(a) => verbs::tree_sha::run(&client, a), Verb::Diff(a) => verbs::diff::run(&client, a), Verb::Subscription(a) => verbs::subscription::run(&client, a), Verb::AttachIssue(a) => verbs::attach::run_issue(&client, a), Verb::AttachComment(a) => verbs::attach::run_comment(&client, a), } }