From b0ad2fd720d0cd335c07f22767844f571e3306ff Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Thu, 5 Mar 2026 12:02:09 +0000 Subject: feat(status): add pr draft, issue resolved, and --reason for issue close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `ngit pr draft ` to convert a PR back to draft (kind-1632). Add `ngit issue resolved [--reason ]` to mark an issue as fixed (kind-1631 GitStatusApplied), distinct from close which signals wontfix/duplicate/invalid. Add `--reason ` to `ngit issue close` — stored in event content. Also fix success/error message wording in pr_status and issue_status to use consistent past-tense action strings. --- src/bin/ngit/sub_commands/issue_status.rs | 30 ++++++++++++++++++++++-------- src/bin/ngit/sub_commands/pr_status.rs | 29 ++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 17 deletions(-) (limited to 'src/bin/ngit/sub_commands') diff --git a/src/bin/ngit/sub_commands/issue_status.rs b/src/bin/ngit/sub_commands/issue_status.rs index 3facee3..840ab8e 100644 --- a/src/bin/ngit/sub_commands/issue_status.rs +++ b/src/bin/ngit/sub_commands/issue_status.rs @@ -30,7 +30,13 @@ fn parse_event_id(id: &str) -> Result { } #[allow(clippy::too_many_lines)] -async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> Result<()> { +async fn launch_status( + id: &str, + offline: bool, + new_kind: Kind, + action: &str, + reason: Option<&str>, +) -> Result<()> { let event_id = parse_event_id(id)?; let git_repo = Repo::discover().context("failed to find a git repository")?; @@ -64,7 +70,7 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> // Only author or maintainer may change status if issue.pubkey != user_pubkey && !repo_ref.maintainers.contains(&user_pubkey) { - bail!("only the issue author or a repository maintainer can {action} an issue"); + bail!("only the issue author or a repository maintainer can change the status of an issue"); } // Fetch existing statuses to check current state @@ -96,6 +102,7 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> let status_str = match new_kind { Kind::GitStatusOpen => "open", Kind::GitStatusClosed => "closed", + Kind::GitStatusApplied => "resolved", _ => "unknown", }; println!("issue is already {status_str}"); @@ -105,6 +112,7 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> let alt_text = match new_kind { Kind::GitStatusOpen => "issue reopened", Kind::GitStatusClosed => "issue closed", + Kind::GitStatusApplied => "issue resolved", _ => "issue status updated", }; @@ -112,8 +120,10 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> repo_ref.maintainers.iter().copied().collect(); public_keys.insert(issue.pubkey); + let content = reason.unwrap_or("").to_string(); + let status_event = sign_event( - EventBuilder::new(new_kind, "").tags( + EventBuilder::new(new_kind, content).tags( [ vec![ Tag::custom( @@ -147,7 +157,7 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> .concat(), ), &signer, - format!("{action} issue"), + format!("issue {action}"), ) .await?; @@ -165,14 +175,18 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> ) .await?; - println!("issue {} {}d", &event_id.to_hex()[..8], action,); + println!("issue {} {action}", &event_id.to_hex()[..8]); Ok(()) } -pub async fn launch_close(id: &str, offline: bool) -> Result<()> { - launch_status(id, offline, Kind::GitStatusClosed, "close").await +pub async fn launch_close(id: &str, offline: bool, reason: Option<&str>) -> Result<()> { + launch_status(id, offline, Kind::GitStatusClosed, "closed", reason).await } pub async fn launch_reopen(id: &str, offline: bool) -> Result<()> { - launch_status(id, offline, Kind::GitStatusOpen, "reopen").await + launch_status(id, offline, Kind::GitStatusOpen, "reopened", None).await +} + +pub async fn launch_resolved(id: &str, offline: bool, reason: Option<&str>) -> Result<()> { + launch_status(id, offline, Kind::GitStatusApplied, "resolved", reason).await } diff --git a/src/bin/ngit/sub_commands/pr_status.rs b/src/bin/ngit/sub_commands/pr_status.rs index e84117d..12aafb7 100644 --- a/src/bin/ngit/sub_commands/pr_status.rs +++ b/src/bin/ngit/sub_commands/pr_status.rs @@ -30,7 +30,13 @@ fn parse_event_id(id: &str) -> Result { } #[allow(clippy::too_many_lines)] -async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> Result<()> { +async fn launch_status( + id: &str, + offline: bool, + new_kind: Kind, + action: &str, + reason: Option<&str>, +) -> Result<()> { let event_id = parse_event_id(id)?; let git_repo = Repo::discover().context("failed to find a git repository")?; @@ -65,7 +71,7 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> // Only author or maintainer may change status if proposal.pubkey != user_pubkey && !repo_ref.maintainers.contains(&user_pubkey) { - bail!("only the PR author or a repository maintainer can {action} a PR"); + bail!("only the PR author or a repository maintainer can change the status of a PR"); } // Fetch existing statuses to check current state @@ -124,8 +130,10 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> repo_ref.maintainers.iter().copied().collect(); public_keys.insert(proposal.pubkey); + let content = reason.unwrap_or("").to_string(); + let status_event = sign_event( - EventBuilder::new(new_kind, "").tags( + EventBuilder::new(new_kind, content).tags( [ vec![ Tag::custom( @@ -159,7 +167,7 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> .concat(), ), &signer, - format!("{action} PR"), + format!("PR {action}"), ) .await?; @@ -178,22 +186,25 @@ async fn launch_status(id: &str, offline: bool, new_kind: Kind, action: &str) -> .await?; println!( - "PR {} {}d: {}", + "PR {} {action}: {}", &event_id.to_hex()[..8], - action, proposal.pubkey.to_bech32().unwrap_or_default() ); Ok(()) } pub async fn launch_close(id: &str, offline: bool) -> Result<()> { - launch_status(id, offline, Kind::GitStatusClosed, "close").await + launch_status(id, offline, Kind::GitStatusClosed, "closed", None).await } pub async fn launch_reopen(id: &str, offline: bool) -> Result<()> { - launch_status(id, offline, Kind::GitStatusOpen, "reopen").await + launch_status(id, offline, Kind::GitStatusOpen, "reopened", None).await } pub async fn launch_ready(id: &str, offline: bool) -> Result<()> { - launch_status(id, offline, Kind::GitStatusOpen, "mark as ready").await + launch_status(id, offline, Kind::GitStatusOpen, "marked as ready", None).await +} + +pub async fn launch_draft(id: &str, offline: bool) -> Result<()> { + launch_status(id, offline, Kind::GitStatusDraft, "converted to draft", None).await } -- cgit v1.2.3