Add `hivectl gateway {create-user,delete-user,list-users}` subcommands for
managing htpasswd files used by gateway Basic auth. Pure Rust bcrypt
(cost 12, $2y$ prefix nginx accepts). No external htpasswd binary required.
Also fix the NixOS module assertion: `cfg.auth ? htpasswdFile` is always
true in the module system (declared options always exist as keys); switch
to `nullOr path; default = null` + `!= null` check so the assertion
actually fires with a useful error when enable=true but no file is set.
Guard bind-mount and nginx config against null to prevent eval errors.
Update docs/gateway.md to show hivectl commands instead of raw htpasswd.
133 lines
5.7 KiB
Rust
133 lines
5.7 KiB
Rust
//! `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<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,
|
|
}
|
|
|
|
#[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),
|
|
/// List all comments on an issue or PR.
|
|
Comments(verbs::comments::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),
|
|
/// Triage lint queries (unassigned / no-reviewer / stale-branches / assignments).
|
|
Lint(verbs::lint::Args),
|
|
/// List issues / PRs with filters (`--kind`, `--state`, `--assignee`,
|
|
/// `--author`, `--label`, `--limit`). Pretty rows by default; pass
|
|
/// `--json` for raw JSON.
|
|
List(verbs::list::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),
|
|
/// List timeline events on an issue or PR (closes, label adds,
|
|
/// assignments, commit refs, pushes, etc.) — the audit trail
|
|
/// `view` + `comments` don't surface (closes #783).
|
|
Timeline(verbs::timeline::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, 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),
|
|
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::Comments(a) => verbs::comments::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::Lint(a) => verbs::lint::run(&client, a),
|
|
Verb::List(a) => verbs::list::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::Timeline(a) => verbs::timeline::run(&client, a),
|
|
Verb::AttachIssue(a) => verbs::attach::run_issue(&client, a),
|
|
Verb::AttachComment(a) => verbs::attach::run_comment(&client, a),
|
|
}
|
|
}
|