hive-forge: attach the resolved repo to every verb's error

Wraps the whole verb dispatch in run() with .with_context(|| format!("repo {repo}"))
instead of threading context through ~34 individual verb files. A body-decode
failure surfaces from forgejo-api as a bare ReqwestError with no status code or
URL retained (no client-injection point to capture more), so without this the
error alone can't distinguish a mistyped org/repo from a transient flake — see
the recent hive-forge triage-automation thread this was filed from.

Split run()'s match into a dispatch() fn so the repo can be captured once before
dispatching and the with_context wrap applied once after, uniformly, regardless
of which verb failed.
This commit is contained in:
iris 2026-07-29 21:18:32 +02:00
commit d2dc681fcf

View file

@ -199,43 +199,59 @@ fn run() -> Result<()> {
let cli = Cli::parse();
let client = client::Client::from_env(cli.repo, cli.json, cli.forge)
.context("initialize forge client")?;
match cli.verb {
Verb::View(a) => verbs::view::run(&client, a),
Verb::Issue(a) => verbs::issue_cmd::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_cmd::run(&client, a),
Verb::PrCommits(a) => verbs::pr_commits::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::PrStatus(a) => verbs::pr_status::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::RepoLabels(a) => verbs::repo_labels::run(&client, a),
Verb::RepoSearch(a) => verbs::repo_search::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::PrMerge(a) => verbs::pr_merge::run(&client, a),
Verb::PrReviews(a) => verbs::pr_reviews::run(&client, a),
Verb::PrAssignReviewer(a) => verbs::pr_assign_reviewer::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),
Verb::AttachmentGet(a) => verbs::attachment_get::run(&client, a),
Verb::ArtifactGet(a) => verbs::artifact_get::run(&client, a),
Verb::CiLog(a) => verbs::ci_log::run(&client, a),
Verb::CiRerun(a) => verbs::ci_rerun::run(&client, a),
// Attach the resolved repo to every verb's error uniformly here,
// rather than threading `.with_context` through 30-odd verb files
// individually. Most verb failures bottom out in `forgejo-api`'s
// `ForgejoError`, which for a body-decode failure (an empty/wrong
// body on a write) carries no status code or URL — see
// `client::check_status`'s doc comment for the raw-route case this
// can't reach. Without the repo in the message, a failure on a
// mistyped `owner/name` (nonexistent org/repo) is indistinguishable
// from a transient flake; this turns "EOF while parsing a value at
// line 1 column 0" into "repo typo-org/repo: EOF while parsing a
// value at line 1 column 0", which is diagnosable on sight.
let repo = client.repo().to_owned();
dispatch(&client, cli.verb).with_context(|| format!("repo {repo}"))
}
fn dispatch(client: &client::Client, verb: Verb) -> Result<()> {
match verb {
Verb::View(a) => verbs::view::run(client, a),
Verb::Issue(a) => verbs::issue_cmd::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_cmd::run(client, a),
Verb::PrCommits(a) => verbs::pr_commits::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::PrStatus(a) => verbs::pr_status::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::RepoLabels(a) => verbs::repo_labels::run(client, a),
Verb::RepoSearch(a) => verbs::repo_search::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::PrMerge(a) => verbs::pr_merge::run(client, a),
Verb::PrReviews(a) => verbs::pr_reviews::run(client, a),
Verb::PrAssignReviewer(a) => verbs::pr_assign_reviewer::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),
Verb::AttachmentGet(a) => verbs::attachment_get::run(client, a),
Verb::ArtifactGet(a) => verbs::artifact_get::run(client, a),
Verb::CiLog(a) => verbs::ci_log::run(client, a),
Verb::CiRerun(a) => verbs::ci_rerun::run(client, a),
}
}