hive-forge reaction: add --list-allowed to surface the instance's configured shortcodes

This commit is contained in:
damocles 2026-08-18 12:56:54 +02:00 committed by mara
commit fb9cc5635a
4 changed files with 36 additions and 5 deletions

View file

@ -57,6 +57,7 @@ hive-forge issue reaction 42 # list #42's reactions
hive-forge issue reaction 42 add heart # react to #42 with :heart: hive-forge issue reaction 42 add heart # react to #42 with :heart:
hive-forge issue reaction 42 --comment 18042 add +1 # react to a specific comment instead hive-forge issue reaction 42 --comment 18042 add +1 # react to a specific comment instead
hive-forge issue reaction 42 remove heart # remove your own :heart: reaction hive-forge issue reaction 42 remove heart # remove your own :heart: reaction
hive-forge issue reaction 1 --list-allowed # this instance's actual configured shortcodes
hive-forge issue-create --title "..." --body "..." hive-forge issue-create --title "..." --body "..."
hive-forge issue-create --title "..." --body "..." --label area/ops --label type/bug # repeatable hive-forge issue-create --title "..." --body "..." --label area/ops --label type/bug # repeatable
hive-forge issue-edit 42 --title "new title" hive-forge issue-edit 42 --title "new title"
@ -299,7 +300,10 @@ to discover valid label names before triaging or to audit the label set.
resulting reaction list as JSON, same shape as `dependency`. `view`, resulting reaction list as JSON, same shape as `dependency`. `view`,
`issue`, `pr`, and `comment-show` always show a reaction summary; `issue`, `pr`, and `comment-show` always show a reaction summary;
`comments --show-reactions` adds one per comment shown (opt-in — one `comments --show-reactions` adds one per comment shown (opt-in — one
extra request per comment, no server-side inline count). extra request per comment, no server-side inline count). `reaction
<n> --list-allowed` prints the instance's actual configured shortcode
list (`GET /settings/ui`'s `allowed_reactions`) instead of guessing —
instance-global, `<n>`/`--comment` are ignored on this path.
- Do NOT use raw `curl` for forge access -- the CLI handles auth, - Do NOT use raw `curl` for forge access -- the CLI handles auth,
error checking, and output formatting. error checking, and output formatting.
- `issue-create --label <name>` / `pr-create --label <name>` are - `issue-create --label <name>` / `pr-create --label <name>` are

View file

@ -89,7 +89,12 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
verbs::dependency::run(client, a) verbs::dependency::run(client, a)
} }
Cmd::Reaction(a) => { Cmd::Reaction(a) => {
assert_kind(client, a.number, Kind::Issue)?; // `--list-allowed` is instance-global, not issue-scoped — skip
// the kind-assert rather than force a real issue number on a
// call that never uses it.
if !a.list_allowed {
assert_kind(client, a.number, Kind::Issue)?;
}
verbs::reaction::run(client, a) verbs::reaction::run(client, a)
} }
Cmd::Timeline(a) => { Cmd::Timeline(a) => {

View file

@ -113,7 +113,12 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
verbs::dependency::run(client, a) verbs::dependency::run(client, a)
} }
Cmd::Reaction(a) => { Cmd::Reaction(a) => {
assert_kind(client, a.number, Kind::Pr)?; // `--list-allowed` is instance-global, not PR-scoped — skip the
// kind-assert rather than force a real PR number on a call that
// never uses it.
if !a.list_allowed {
assert_kind(client, a.number, Kind::Pr)?;
}
verbs::reaction::run(client, a) verbs::reaction::run(client, a)
} }
Cmd::Timeline(a) => { Cmd::Timeline(a) => {

View file

@ -13,7 +13,12 @@
//! `content` is Forgejo's reaction shortcode vocabulary (`+1`, `-1`, //! `content` is Forgejo's reaction shortcode vocabulary (`+1`, `-1`,
//! `laugh`, `confused`, `heart`, `hooray`, `rocket`, `eyes`, …), not a //! `laugh`, `confused`, `heart`, `hooray`, `rocket`, `eyes`, …), not a
//! raw emoji character — same set the forge web UI's reaction picker //! raw emoji character — same set the forge web UI's reaction picker
//! offers. //! offers. `--list-allowed` prints the *actual* instance-configured
//! list (`GET /settings/ui`'s `allowed_reactions`) instead of leaving a
//! caller to guess it or learn it from a 403 — this is instance-global,
//! not issue/PR-scoped, so `number`/`--comment` are ignored when it's
//! set (the number is still required positionally, same as every other
//! generic verb, but unused on this path).
use anyhow::Result; use anyhow::Result;
use clap::{Args as ClapArgs, Subcommand}; use clap::{Args as ClapArgs, Subcommand};
@ -24,13 +29,19 @@ use crate::verbs::{comment_reactions, issue_reactions, print_json};
#[derive(ClapArgs)] #[derive(ClapArgs)]
pub struct Args { pub struct Args {
/// Issue or PR number. /// Issue or PR number. Ignored (but still required) when
/// `--list-allowed` is set.
pub(crate) number: u64, pub(crate) number: u64,
/// Target a specific comment's reactions instead of the issue/PR /// Target a specific comment's reactions instead of the issue/PR
/// itself — the comment's own id (from `comments`/`comment-show`), /// itself — the comment's own id (from `comments`/`comment-show`),
/// not its position in the thread. /// not its position in the thread.
#[arg(long)] #[arg(long)]
comment: Option<u64>, comment: Option<u64>,
/// Print the instance's actual configured reaction shortcodes
/// (`GET /settings/ui`) instead of listing/adding/removing —
/// instance-global, ignores `number`/`--comment`.
#[arg(long)]
pub(crate) list_allowed: bool,
#[command(subcommand)] #[command(subcommand)]
action: Option<Action>, action: Option<Action>,
} }
@ -46,6 +57,12 @@ enum Action {
} }
pub fn run(client: &Client, args: Args) -> Result<()> { pub fn run(client: &Client, args: Args) -> Result<()> {
if args.list_allowed {
let settings = client.api().get_general_ui_settings().send()?;
return print_json(&serde_json::json!(
settings.allowed_reactions.unwrap_or_default()
));
}
let (owner, name) = client.owner_repo()?; let (owner, name) = client.owner_repo()?;
match args.action.unwrap_or(Action::List) { match args.action.unwrap_or(Action::List) {
Action::List => {} Action::List => {}