From 67e25da8c07c4808b8aa41a37b04f112e18abffd Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sun, 3 Aug 2025 00:17:20 +0000
Subject: [PATCH] feat: New flag to `patch apply and merge` to mention patches
Signed-off-by: Awiteb
---
CHANGELOG.md | 7 ++++
docs/patch/apply.md | 1 +
docs/patch/merge.md | 1 +
src/cli/commands/patch/apply.rs | 7 +++-
src/cli/commands/patch/close.rs | 1 +
src/cli/commands/patch/draft.rs | 1 +
src/cli/commands/patch/merge.rs | 13 ++++---
src/cli/commands/patch/reopen.rs | 1 +
src/cli/common_commands.rs | 61 +++++++++++++++++++++++++-------
src/cli/traits.rs | 13 ++++++-
src/nostr_utils/mod.rs | 2 +-
11 files changed, 88 insertions(+), 20 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1437b8d..047eed8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,10 +11,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support signing using NIP-46 bunker - by Awiteb
- Keyring the secret key `n34 config keyring --enable` - by Awiteb
+- New flag to `patch apply and merge` to mention patches - by Awiteb
### Dependencies
- Add `keyring`, `nostr-connect`, `nostr-keyring` and `url` to the dependencies - by Awiteb
+- Remove `url` from `n34` dependencies - by Awiteb
+
+### Documentation
+
+- N34 book - by Awiteb
+- Fix status command docs - by Awiteb
## [0.3.0] - 2025-07-05
diff --git a/docs/patch/apply.md b/docs/patch/apply.md
index 4004f9c..28f6415 100644
--- a/docs/patch/apply.md
+++ b/docs/patch/apply.md
@@ -14,6 +14,7 @@ Arguments:
Options:
--repo Repository address in `naddr` format (`naddr1...`), NIP-05 format (`4rs.nl/n34` or `_@4rs.nl/n34`), or a set name like `kernel`
+ --patches Patches that have been applied. Use this when only some patches have been applied, not all
```
Creates a kind `1631` event (Applied/Merged status) for the specified patch. The
diff --git a/docs/patch/merge.md b/docs/patch/merge.md
index 0a41934..9f87c64 100644
--- a/docs/patch/merge.md
+++ b/docs/patch/merge.md
@@ -14,6 +14,7 @@ Arguments:
Options:
--repo Repository address in `naddr` format (`naddr1...`), NIP-05 format (`4rs.nl/n34` or `_@4rs.nl/n34`), or a set name like `kernel`
+ --patches Patches that have been merged. Use this when only some patches have been merged, not all
```
Creates a kind `1631` event (Applied/Merged status) for the specified patch. The
diff --git a/src/cli/commands/patch/apply.rs b/src/cli/commands/patch/apply.rs
index 8af6b67..adebbc0 100644
--- a/src/cli/commands/patch/apply.rs
+++ b/src/cli/commands/patch/apply.rs
@@ -21,7 +21,7 @@ use super::PatchStatus;
use crate::{
cli::{
CliOptions,
- traits::CommandRunner,
+ traits::{CommandRunner, VecNostrEventExt},
types::{NaddrOrSet, NostrEvent},
},
error::{N34Error, N34Result},
@@ -38,6 +38,10 @@ pub struct ApplyArgs {
/// The open patch id to apply it. Must be orignal root patch or
/// revision root
patch_id: NostrEvent,
+ /// Patches that have been applied. Use this when only some patches have
+ /// been applied, not all.
+ #[arg(long = "patches", value_name = "PATCH-EVENT-ID")]
+ applied_patches: Vec,
/// The applied commits
#[arg(num_args = 1..)]
applied_commits: Vec,
@@ -51,6 +55,7 @@ impl CommandRunner for ApplyArgs {
self.naddrs,
PatchStatus::MergedApplied,
Some(either::Either::Right(self.applied_commits)),
+ self.applied_patches.into_event_ids(),
|patch_status| {
if patch_status.is_merged_or_applied() {
return Err(N34Error::InvalidStatus(
diff --git a/src/cli/commands/patch/close.rs b/src/cli/commands/patch/close.rs
index 31588ed..313cea5 100644
--- a/src/cli/commands/patch/close.rs
+++ b/src/cli/commands/patch/close.rs
@@ -46,6 +46,7 @@ impl CommandRunner for CloseArgs {
self.naddrs,
PatchStatus::Closed,
None,
+ Vec::new(),
|patch_status| {
if patch_status.is_closed() {
return Err(N34Error::InvalidStatus(
diff --git a/src/cli/commands/patch/draft.rs b/src/cli/commands/patch/draft.rs
index 0cedf44..e048bae 100644
--- a/src/cli/commands/patch/draft.rs
+++ b/src/cli/commands/patch/draft.rs
@@ -46,6 +46,7 @@ impl CommandRunner for DraftArgs {
self.naddrs,
PatchStatus::Draft,
None,
+ Vec::new(),
|patch_status| {
if patch_status.is_drafted() {
return Err(N34Error::InvalidStatus(
diff --git a/src/cli/commands/patch/merge.rs b/src/cli/commands/patch/merge.rs
index 8cb0f0b..8b5c846 100644
--- a/src/cli/commands/patch/merge.rs
+++ b/src/cli/commands/patch/merge.rs
@@ -21,7 +21,7 @@ use super::PatchStatus;
use crate::{
cli::{
CliOptions,
- traits::CommandRunner,
+ traits::{CommandRunner, VecNostrEventExt},
types::{NaddrOrSet, NostrEvent},
},
error::{N34Error, N34Result},
@@ -34,12 +34,16 @@ pub struct MergeArgs {
///
/// If omitted, looks for a `nostr-address` file.
#[arg(value_name = "NADDR-NIP05-OR-SET", long = "repo")]
- naddrs: Option>,
+ naddrs: Option>,
/// The open patch id to merge it. Must be orignal root patch or
/// revision root
- patch_id: NostrEvent,
+ patch_id: NostrEvent,
+ /// Patches that have been merged. Use this when only some patches have been
+ /// merged, not all.
+ #[arg(long = "patches", value_name = "PATCH-EVENT-ID")]
+ merged_patches: Vec,
/// The merge commit id
- merge_commit: Sha1Hash,
+ merge_commit: Sha1Hash,
}
impl CommandRunner for MergeArgs {
@@ -50,6 +54,7 @@ impl CommandRunner for MergeArgs {
self.naddrs,
PatchStatus::MergedApplied,
Some(either::Either::Left(self.merge_commit)),
+ self.merged_patches.into_event_ids(),
|patch_status| {
if patch_status.is_merged_or_applied() {
return Err(N34Error::InvalidStatus(
diff --git a/src/cli/commands/patch/reopen.rs b/src/cli/commands/patch/reopen.rs
index 72b27b8..1fa6337 100644
--- a/src/cli/commands/patch/reopen.rs
+++ b/src/cli/commands/patch/reopen.rs
@@ -46,6 +46,7 @@ impl CommandRunner for ReopenArgs {
self.naddrs,
PatchStatus::Open,
None,
+ Vec::new(),
|patch_status| {
if patch_status.is_open() {
return Err(N34Error::InvalidStatus(
diff --git a/src/cli/common_commands.rs b/src/cli/common_commands.rs
index d2c16ee..f65ce04 100644
--- a/src/cli/common_commands.rs
+++ b/src/cli/common_commands.rs
@@ -23,6 +23,7 @@ use nostr::{
filter::Filter,
hashes::sha1::Hash as Sha1Hash,
nips::{nip10::Marker, nip19::ToBech32},
+ types::RelayUrl,
};
use super::{
@@ -130,6 +131,7 @@ pub async fn patch_status_command(
naddrs: Option>,
new_status: PatchStatus,
merge_or_applied_commits: Option>>,
+ merge_or_applied_patches: Vec,
check_fn: impl FnOnce(&PatchStatus) -> N34Result<()>,
) -> N34Result<()> {
let user_pkey = options.pubkey().await?;
@@ -209,22 +211,20 @@ pub async fn patch_status_command(
.tags(commits.into_iter().map(Tag::reference));
};
- let root = if let Some(root_revision) = root_revision {
+ if let Some(root_revision) = root_revision {
status_builder = status_builder.tag(utils::event_reply_tag(
&root_revision,
relay_hint.as_ref(),
Marker::Reply,
));
- root_revision
- } else {
- root_patch
- };
- let patches = client.fetch_patch_series(root, patch_event.pubkey).await?;
- status_builder = status_builder.tags(
- patches
- .into_iter()
- .map(|p| utils::event_reply_tag(&p.id, relay_hint.as_ref(), Marker::Mention)),
- );
+ }
+
+ if !merge_or_applied_patches.is_empty() {
+ status_builder = status_builder.tags(
+ build_patches_quote(client.clone(), relay_hint.clone(), merge_or_applied_patches)
+ .await,
+ );
+ }
}
let status_event = status_builder.dedup_tags().build(user_pkey);
@@ -279,7 +279,6 @@ pub async fn list_patches_and_issues(
.add_relays(&client.read_relays_from_users(&authorized_pubkeys).await)
.await;
-
let kind = if list_patches {
Kind::GitPatch
} else {
@@ -352,7 +351,6 @@ pub async fn list_patches_and_issues(
Ok(())
}
-
/// Returns a tuple of (root_id, patch_id) if this is a valid root or revision
/// patch.
fn get_patch_root_revision(patch_event: &Event) -> N34Result<(EventId, Option)> {
@@ -401,3 +399,40 @@ fn format_patch_and_issue(event: &Event, status: Either,
+ patches: Vec,
+) -> Vec {
+ let client = Arc::new(client);
+ let relay_hint = Arc::new(relay_hint);
+
+ future::join_all(patches.into_iter().map(|eid| {
+ let task_relay = Arc::clone(&relay_hint);
+ let task_client = Arc::clone(&client);
+
+ async move {
+ Tag::custom(
+ TagKind::q(),
+ [
+ eid.to_hex(),
+ task_relay
+ .as_ref()
+ .as_ref()
+ .map(|r| r.to_string())
+ .unwrap_or_default(),
+ task_client
+ .event_author(eid)
+ .await
+ .ok()
+ .flatten()
+ .map(|p| p.to_hex())
+ .unwrap_or_default(),
+ ],
+ )
+ }
+ }))
+ .await
+}
diff --git a/src/cli/traits.rs b/src/cli/traits.rs
index 917338a..52cc767 100644
--- a/src/cli/traits.rs
+++ b/src/cli/traits.rs
@@ -14,8 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+use nostr::event::EventId;
+
use super::CliOptions;
-use crate::error::N34Result;
+use crate::{cli::types::NostrEvent, error::N34Result};
/// A trait defining the interface for command runners in the CLI.
pub trait CommandRunner {
@@ -29,3 +31,12 @@ pub trait CommandRunner {
/// Executes the command and returns a Result indicating success or failure.
fn run(self, options: CliOptions) -> impl Future