hive-forge: add repo-labels verb to list a repo's full label set
`labels <number>` only lists an issue/PR's labels; there was no way to
list the project-wide label set, which triage/labelling needs to discover
valid names. Add `repo-labels [pattern]` hitting GET /repos/{owner}/{repo}/labels
(paginated), with optional name-substring filter. Default prints one label
per line (name + tab-separated description when set); --json emits the full
label objects.
This commit is contained in:
parent
a45b56f658
commit
5a06d1156b
3 changed files with 60 additions and 0 deletions
|
|
@ -91,6 +91,12 @@ enum Verb {
|
||||||
/// Add a collaborator to the active repo (`-r`/`HIVE_FORGE_REPO`)
|
/// Add a collaborator to the active repo (`-r`/`HIVE_FORGE_REPO`)
|
||||||
/// with a permission level. Companion to `repo-create`.
|
/// with a permission level. Companion to `repo-create`.
|
||||||
RepoAddCollaborator(verbs::repo_add_collaborator::Args),
|
RepoAddCollaborator(verbs::repo_add_collaborator::Args),
|
||||||
|
/// List the active repo's full label set (project-wide), optionally
|
||||||
|
/// filtered by a name substring. Unlike `labels <number>` (which lists
|
||||||
|
/// an issue/PR's labels), this shows every label defined on the repo —
|
||||||
|
/// the valid names + descriptions for triage / labelling. `--json`
|
||||||
|
/// emits the full label objects (id, name, color, description).
|
||||||
|
RepoLabels(verbs::repo_labels::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`,
|
||||||
|
|
@ -163,6 +169,7 @@ fn main() -> Result<()> {
|
||||||
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::RepoCreate(a) => verbs::repo_create::run(&client, a),
|
||||||
Verb::RepoAddCollaborator(a) => verbs::repo_add_collaborator::run(&client, a),
|
Verb::RepoAddCollaborator(a) => verbs::repo_add_collaborator::run(&client, a),
|
||||||
|
Verb::RepoLabels(a) => verbs::repo_labels::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),
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ pub mod pr_reviews;
|
||||||
pub mod pr_status;
|
pub mod pr_status;
|
||||||
pub mod repo_add_collaborator;
|
pub mod repo_add_collaborator;
|
||||||
pub mod repo_create;
|
pub mod repo_create;
|
||||||
|
pub mod repo_labels;
|
||||||
pub mod subscription;
|
pub mod subscription;
|
||||||
pub mod timeline;
|
pub mod timeline;
|
||||||
pub mod tree_sha;
|
pub mod tree_sha;
|
||||||
|
|
|
||||||
52
hive-forge/src/verbs/repo_labels.rs
Normal file
52
hive-forge/src/verbs/repo_labels.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
//! `repo-labels [pattern]` — list the active repo's full label set
|
||||||
|
//! (project-wide), not the labels on one issue/PR (that's `labels
|
||||||
|
//! <number>`). Useful for triage / labelling to discover the valid label
|
||||||
|
//! names + what each means before applying them.
|
||||||
|
|
||||||
|
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 {
|
||||||
|
/// Substring pattern to filter label names (case-sensitive).
|
||||||
|
pattern: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(client: &Client, args: Args) -> Result<()> {
|
||||||
|
let repo = client.repo();
|
||||||
|
// Repos can carry more than one page of labels; paginate so the list
|
||||||
|
// is complete rather than capped at the first page.
|
||||||
|
let labels = client.get_json_all(&format!("/repos/{repo}/labels"), 10)?;
|
||||||
|
let filtered: Vec<&Value> = labels
|
||||||
|
.iter()
|
||||||
|
.filter(|l| {
|
||||||
|
let name = l.get("name").and_then(Value::as_str).unwrap_or_default();
|
||||||
|
args.pattern.as_deref().is_none_or(|p| name.contains(p))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if client.json_mode() {
|
||||||
|
// Full label objects (id, name, color, description) as a JSON array.
|
||||||
|
print_json(&json!(filtered))?;
|
||||||
|
} else {
|
||||||
|
// One label per line: `name`, plus its description (tab-separated)
|
||||||
|
// when set, so triage can see what each label means at a glance.
|
||||||
|
for l in filtered {
|
||||||
|
let name = l.get("name").and_then(Value::as_str).unwrap_or_default();
|
||||||
|
let desc = l
|
||||||
|
.get("description")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.unwrap_or_default();
|
||||||
|
if desc.is_empty() {
|
||||||
|
println!("{name}");
|
||||||
|
} else {
|
||||||
|
println!("{name}\t{desc}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue