Compare commits
5 changed files with 0 additions and 174 deletions
|
|
@ -206,29 +206,6 @@ impl Client {
|
||||||
decode_json(resp, &format!("PUT {url}"))
|
decode_json(resp, &format!("PUT {url}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT a JSON body to `<api>/<path>` for an endpoint that returns
|
|
||||||
/// `204 No Content` (empty body), so there is nothing to decode.
|
|
||||||
/// Used by `repo-add-collaborator` (Forgejo's add-collaborator PUT
|
|
||||||
/// answers 204 on success).
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an error if the request fails to send (transport/network
|
|
||||||
/// error) or the server responds with a non-2xx status (the response
|
|
||||||
/// body is included in the error).
|
|
||||||
pub fn put_no_content<B: Serialize>(&self, path: &str, body: &B) -> Result<()> {
|
|
||||||
let url = format!("{}{}", self.api(), path);
|
|
||||||
let resp = self
|
|
||||||
.http
|
|
||||||
.put(&url)
|
|
||||||
.header(CONTENT_TYPE, "application/json")
|
|
||||||
.json(body)
|
|
||||||
.send()
|
|
||||||
.context("PUT")?;
|
|
||||||
check_status(resp, &format!("PUT {url}"))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DELETE `<api>/<path>`. Optional JSON body for endpoints that
|
/// DELETE `<api>/<path>`. Optional JSON body for endpoints that
|
||||||
/// need it (Forgejo's subscription unwatch uses bodyless DELETE).
|
/// need it (Forgejo's subscription unwatch uses bodyless DELETE).
|
||||||
pub fn delete(&self, path: &str, body: Option<&Value>) -> Result<()> {
|
pub fn delete(&self, path: &str, body: Option<&Value>) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -80,13 +80,6 @@ enum Verb {
|
||||||
/// Clone a forge repo (default `-r`/`HIVE_FORGE_REPO`) with
|
/// Clone a forge repo (default `-r`/`HIVE_FORGE_REPO`) with
|
||||||
/// credentials auto-injected. Pairs with `pr-create --agit`.
|
/// credentials auto-injected. Pairs with `pr-create --agit`.
|
||||||
Clone(verbs::clone::Args),
|
Clone(verbs::clone::Args),
|
||||||
/// Create a forge repo under the current user (or `--org`). Prints
|
|
||||||
/// the repo URL. The instance disables push-to-create, so this is
|
|
||||||
/// the supported path to a new repo. Pairs with `repo-add-collaborator`.
|
|
||||||
RepoCreate(verbs::repo_create::Args),
|
|
||||||
/// Add a collaborator to the active repo (`-r`/`HIVE_FORGE_REPO`)
|
|
||||||
/// with a permission level. Companion to `repo-create`.
|
|
||||||
RepoAddCollaborator(verbs::repo_add_collaborator::Args),
|
|
||||||
/// Triage lint queries (unassigned / no-reviewer / stale-branches / assignments).
|
/// Triage lint queries (unassigned / no-reviewer / stale-branches / assignments).
|
||||||
Lint(verbs::lint::Args),
|
Lint(verbs::lint::Args),
|
||||||
/// List issues / PRs with filters (`--kind`, `--state`, `--assignee`,
|
/// List issues / PRs with filters (`--kind`, `--state`, `--assignee`,
|
||||||
|
|
@ -137,8 +130,6 @@ fn main() -> Result<()> {
|
||||||
Verb::Labels(a) => verbs::labels::run(&client, a),
|
Verb::Labels(a) => verbs::labels::run(&client, a),
|
||||||
Verb::PrStatus(a) => verbs::pr_status::run(&client, a),
|
Verb::PrStatus(a) => verbs::pr_status::run(&client, a),
|
||||||
Verb::Clone(a) => verbs::clone::run(&client, a),
|
Verb::Clone(a) => verbs::clone::run(&client, a),
|
||||||
Verb::RepoCreate(a) => verbs::repo_create::run(&client, a),
|
|
||||||
Verb::RepoAddCollaborator(a) => verbs::repo_add_collaborator::run(&client, a),
|
|
||||||
Verb::Lint(a) => verbs::lint::run(&client, a),
|
Verb::Lint(a) => verbs::lint::run(&client, a),
|
||||||
Verb::List(a) => verbs::list::run(&client, a),
|
Verb::List(a) => verbs::list::run(&client, a),
|
||||||
Verb::Milestone(a) => verbs::milestone::run(&client, a),
|
Verb::Milestone(a) => verbs::milestone::run(&client, a),
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,6 @@ pub mod pr;
|
||||||
pub mod pr_create;
|
pub mod pr_create;
|
||||||
pub mod pr_reviews;
|
pub mod pr_reviews;
|
||||||
pub mod pr_status;
|
pub mod pr_status;
|
||||||
pub mod repo_add_collaborator;
|
|
||||||
pub mod repo_create;
|
|
||||||
pub mod subscription;
|
pub mod subscription;
|
||||||
pub mod timeline;
|
pub mod timeline;
|
||||||
pub mod tree_sha;
|
pub mod tree_sha;
|
||||||
|
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
//! `repo-add-collaborator <user> [--permission read|write|admin] [-r <repo>]`
|
|
||||||
//! — add `<user>` as a collaborator on the active repo (from `-r` /
|
|
||||||
//! `HIVE_FORGE_REPO`). Prints a one-line confirmation.
|
|
||||||
//!
|
|
||||||
//! Wraps `PUT /api/v1/repos/{owner}/{repo}/collaborators/{collaborator}`.
|
|
||||||
//! A fresh agent-namespace repo (see `repo-create`) usually needs peers
|
|
||||||
//! added before they can collaborate; this is the companion verb.
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use clap::Args as ClapArgs;
|
|
||||||
use clap::ValueEnum;
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::client::Client;
|
|
||||||
|
|
||||||
/// Collaborator permission level accepted by Forgejo.
|
|
||||||
#[derive(Clone, Copy, ValueEnum)]
|
|
||||||
pub enum Permission {
|
|
||||||
/// Pull (read-only) access.
|
|
||||||
Read,
|
|
||||||
/// Push (read/write) access.
|
|
||||||
Write,
|
|
||||||
/// Full administrative access to the repo.
|
|
||||||
Admin,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Permission {
|
|
||||||
/// The wire string Forgejo's API expects.
|
|
||||||
fn as_api(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Permission::Read => "read",
|
|
||||||
Permission::Write => "write",
|
|
||||||
Permission::Admin => "admin",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ClapArgs)]
|
|
||||||
pub struct Args {
|
|
||||||
/// Collaborator's forge login to add.
|
|
||||||
user: String,
|
|
||||||
/// Permission level to grant (default: write — a freshly added
|
|
||||||
/// collaborator usually needs to push).
|
|
||||||
#[arg(long, value_enum, default_value_t = Permission::Write)]
|
|
||||||
permission: Permission,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Propagates any transport error from the Forgejo REST call (network
|
|
||||||
/// unreachable, 4xx/5xx response such as an unknown user or insufficient
|
|
||||||
/// permission on the repo, token missing/invalid) and any I/O error from
|
|
||||||
/// writing the confirmation to stdout.
|
|
||||||
pub fn run(client: &Client, args: Args) -> Result<()> {
|
|
||||||
let repo = client.repo();
|
|
||||||
let perm = args.permission.as_api();
|
|
||||||
client.put_no_content(
|
|
||||||
&format!("/repos/{repo}/collaborators/{}", args.user),
|
|
||||||
&json!({ "permission": perm }),
|
|
||||||
)?;
|
|
||||||
println!("added {} to {repo} ({perm})", args.user);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
//! `repo-create <name> [--description <d>] [--private] [--default-branch <b>]
|
|
||||||
//! [--org <org>] [--auto-init]` — create a forge repository under the current
|
|
||||||
//! user (or under `--org`). Prints the new repo's web URL (or the full repo
|
|
||||||
//! object under `--json`).
|
|
||||||
//!
|
|
||||||
//! Wraps `POST /api/v1/user/repos` (or `/orgs/{org}/repos`) so agents don't
|
|
||||||
//! reach for raw API calls when the operator greenlights a new repo — the
|
|
||||||
//! instance has push-to-create disabled, so `git push` to a non-existent
|
|
||||||
//! repo 403s. Pairs with `clone` (then push your content) and
|
|
||||||
//! `repo-add-collaborator` (add peers to a fresh agent-namespace repo).
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use clap::Args as ClapArgs;
|
|
||||||
use serde_json::{Value, json};
|
|
||||||
|
|
||||||
use crate::client::Client;
|
|
||||||
use crate::verbs::print_json;
|
|
||||||
|
|
||||||
#[derive(ClapArgs)]
|
|
||||||
pub struct Args {
|
|
||||||
/// Repository name (required). Created under the authenticated user
|
|
||||||
/// unless `--org` is given.
|
|
||||||
name: String,
|
|
||||||
/// Repository description.
|
|
||||||
#[arg(long)]
|
|
||||||
description: Option<String>,
|
|
||||||
/// Create the repo as private (default: public).
|
|
||||||
#[arg(long)]
|
|
||||||
private: bool,
|
|
||||||
/// Default branch name (e.g. `main`). Forgejo applies it to the
|
|
||||||
/// initial commit, so it only takes effect alongside `--auto-init`.
|
|
||||||
#[arg(long = "default-branch")]
|
|
||||||
default_branch: Option<String>,
|
|
||||||
/// Create under this organisation (`POST /orgs/<org>/repos`) instead
|
|
||||||
/// of the authenticated user's namespace.
|
|
||||||
#[arg(long)]
|
|
||||||
org: Option<String>,
|
|
||||||
/// Seed an initial commit (README) so the repo is non-empty and can
|
|
||||||
/// be cloned immediately. Omit to create a bare repo you push into.
|
|
||||||
#[arg(long = "auto-init")]
|
|
||||||
auto_init: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Propagates any transport error from the Forgejo REST call (network
|
|
||||||
/// unreachable, 4xx/5xx response such as a name clash or insufficient
|
|
||||||
/// permission on the target namespace, token missing/invalid) and any
|
|
||||||
/// I/O error from writing the result to stdout.
|
|
||||||
pub fn run(client: &Client, args: Args) -> Result<()> {
|
|
||||||
let mut payload = json!({
|
|
||||||
"name": args.name,
|
|
||||||
"private": args.private,
|
|
||||||
"auto_init": args.auto_init,
|
|
||||||
});
|
|
||||||
if let Some(d) = args.description {
|
|
||||||
payload["description"] = json!(d);
|
|
||||||
}
|
|
||||||
if let Some(b) = args.default_branch {
|
|
||||||
payload["default_branch"] = json!(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = match args.org.as_deref() {
|
|
||||||
Some(org) => format!("/orgs/{org}/repos"),
|
|
||||||
None => "/user/repos".to_owned(),
|
|
||||||
};
|
|
||||||
let resp = client.post_json(&path, &payload)?;
|
|
||||||
|
|
||||||
if client.json_mode() {
|
|
||||||
return print_json(&resp);
|
|
||||||
}
|
|
||||||
// Default human path: print the web URL, like issue-create / pr-create.
|
|
||||||
if let Some(url) = resp.get("html_url").and_then(Value::as_str) {
|
|
||||||
println!("{url}");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue