upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/bin/ngit/cli.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-03-04 14:28:38 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-03-04 14:50:01 +0000
commitb3b1a949463d8e18622519866ecee3f1b65cc888 (patch)
tree9c728adb75fb18cb84e8d13efbbbd5e90231ec2f /src/bin/ngit/cli.rs
parent6d9b0cc8fff65447849d0d55db177dcdff315c48 (diff)
restructure CLI around ngit pr/issue subcommand groups
Introduce ngit pr subcommand group (list, view, checkout, apply, send, close, reopen, ready, comment, merge) replacing the former top-level ngit list/checkout/apply commands. ngit send is kept at the top level. Expand ngit issue with view, create, close, reopen, comment subcommands. Status changes (close/reopen/ready) are gated to the PR/issue author or a repository maintainer. ngit pr merge is maintainer-only and publishes a GitStatusApplied event immediately after the git merge.
Diffstat (limited to 'src/bin/ngit/cli.rs')
-rw-r--r--src/bin/ngit/cli.rs220
1 files changed, 184 insertions, 36 deletions
diff --git a/src/bin/ngit/cli.rs b/src/bin/ngit/cli.rs
index c2364e6..452491c 100644
--- a/src/bin/ngit/cli.rs
+++ b/src/bin/ngit/cli.rs
@@ -8,7 +8,7 @@ use crate::sub_commands;
8#[command( 8#[command(
9 author, 9 author,
10 version, 10 version,
11 help_template = "{name} {version}\nnostr plugin for git\n includes a remote helper so native git commands (clone, fetch, push) work with nostr:// URLs\n - clone a nostr repository, or add as a remote, by using the url format nostr://npub123/identifier\n - remote branches beginning with `pr/` are open PRs from contributors; `ngit list` can be used to view all PRs\n - to open a PR, push a branch with the prefix `pr/` or use `ngit send` for advanced options\n set title and description via push options:\n git push -o 'title=My PR' -o 'description=line1\\n\\nline2' -u origin pr/branch\n - publish a repository to nostr with `ngit init`\n\n{usage}\n{all-args}" 11 help_template = "{name} {version}\nnostr plugin for git\n includes a remote helper so native git commands (clone, fetch, push) work with nostr:// URLs\n - clone a nostr repository, or add as a remote, by using the url format nostr://npub123/identifier\n - remote branches beginning with `pr/` are open PRs from contributors; `ngit pr list` can be used to view all PRs\n - to open a PR, push a branch with the prefix `pr/` or use `ngit send` for advanced options\n set title and description via push options:\n git push -o 'title=My PR' -o 'description=line1\\n\\nline2' -u origin pr/branch\n - publish a repository to nostr with `ngit init`\n\n{usage}\n{all-args}"
12)] 12)]
13#[command(propagate_version = true)] 13#[command(propagate_version = true)]
14#[allow(clippy::struct_excessive_bools)] 14#[allow(clippy::struct_excessive_bools)]
@@ -119,6 +119,59 @@ pub enum Commands {
119 long_about = "submit PR with advanced options\n\nfor a simpler flow, push a branch with the `pr/` prefix using native git:\n git push -o 'title=My PR' -o 'description=details here' -u origin pr/my-branch" 119 long_about = "submit PR with advanced options\n\nfor a simpler flow, push a branch with the `pr/` prefix using native git:\n git push -o 'title=My PR' -o 'description=details here' -u origin pr/my-branch"
120 )] 120 )]
121 Send(sub_commands::send::SubCommandArgs), 121 Send(sub_commands::send::SubCommandArgs),
122 /// work with pull requests
123 #[command(
124 long_about = "work with pull requests\n\nPRs are created by pushing a branch with the `pr/` prefix:\n git push -u origin pr/my-branch\nor with advanced options via `ngit send`"
125 )]
126 Pr(PrSubCommandArgs),
127 /// work with issues
128 Issue(IssueSubCommandArgs),
129 /// update repo git servers to reflect nostr state (add, update or delete
130 /// remote refs)
131 Sync(sub_commands::sync::SubCommandArgs),
132 /// create account, login, logout or export keys
133 Account(AccountSubCommandArgs),
134}
135
136#[derive(Subcommand)]
137pub enum AccountCommands {
138 /// login with nsec or nostr connect
139 Login(sub_commands::login::SubCommandArgs),
140 /// remove nostr account details stored in git config
141 Logout,
142 /// export nostr keys to login to other nostr clients
143 ExportKeys,
144 /// create a new nostr account
145 Create(sub_commands::create::SubCommandArgs),
146}
147
148#[derive(clap::Parser)]
149pub struct AccountSubCommandArgs {
150 #[command(subcommand)]
151 pub account_command: AccountCommands,
152}
153
154#[derive(clap::Parser)]
155pub struct RepoSubCommandArgs {
156 #[command(subcommand)]
157 pub repo_command: Option<RepoCommands>,
158 /// Use local cache only, skip network fetch
159 #[arg(long)]
160 pub offline: bool,
161}
162
163// ---------------------------------------------------------------------------
164// PR subcommand group
165// ---------------------------------------------------------------------------
166
167#[derive(clap::Parser)]
168pub struct PrSubCommandArgs {
169 #[command(subcommand)]
170 pub pr_command: PrCommands,
171}
172
173#[derive(Subcommand)]
174pub enum PrCommands {
122 /// list PRs and view details 175 /// list PRs and view details
123 List { 176 List {
124 /// Filter by status (comma-separated: open,draft,closed,applied) 177 /// Filter by status (comma-separated: open,draft,closed,applied)
@@ -134,11 +187,21 @@ pub enum Commands {
134 #[arg(long)] 187 #[arg(long)]
135 offline: bool, 188 offline: bool,
136 }, 189 },
137 /// list issues 190 /// view a PR and its comments
138 Issue(IssueSubCommandArgs), 191 View {
192 /// Proposal event-id (hex) or nevent (bech32)
193 #[arg(value_name = "ID|nevent")]
194 id: String,
195 /// Output as JSON
196 #[arg(long)]
197 json: bool,
198 /// Use local cache only, skip network fetch
199 #[arg(long)]
200 offline: bool,
201 },
139 /// checkout a proposal branch by event-id or nevent 202 /// checkout a proposal branch by event-id or nevent
140 #[command( 203 #[command(
141 long_about = "checkout a proposal branch by event-id or nevent\n\nuse `ngit list` to find proposal IDs" 204 long_about = "checkout a proposal branch by event-id or nevent\n\nuse `ngit pr list` to find proposal IDs"
142 )] 205 )]
143 Checkout { 206 Checkout {
144 /// Proposal event-id (hex) or nevent (bech32) 207 /// Proposal event-id (hex) or nevent (bech32)
@@ -150,7 +213,7 @@ pub enum Commands {
150 }, 213 },
151 /// apply proposal patches to current branch 214 /// apply proposal patches to current branch
152 #[command( 215 #[command(
153 long_about = "apply proposal patches to current branch\n\nuse `ngit list` to find proposal IDs" 216 long_about = "apply proposal patches to current branch\n\nuse `ngit pr list` to find proposal IDs"
154 )] 217 )]
155 Apply { 218 Apply {
156 /// Proposal event-id or nevent 219 /// Proposal event-id or nevent
@@ -163,39 +226,70 @@ pub enum Commands {
163 #[arg(long)] 226 #[arg(long)]
164 offline: bool, 227 offline: bool,
165 }, 228 },
166 /// update repo git servers to reflect nostr state (add, update or delete 229 /// submit PR with advanced options (alias for `ngit send`)
167 /// remote refs) 230 #[command(
168 Sync(sub_commands::sync::SubCommandArgs), 231 long_about = "submit PR with advanced options\n\nfor a simpler flow, push a branch with the `pr/` prefix using native git:\n git push -o 'title=My PR' -o 'description=details here' -u origin pr/my-branch"
169 /// create account, login, logout or export keys 232 )]
170 Account(AccountSubCommandArgs), 233 Send(sub_commands::send::SubCommandArgs),
171} 234 /// close a PR (author or maintainer only)
172 235 Close {
173#[derive(Subcommand)] 236 /// Proposal event-id (hex) or nevent (bech32)
174pub enum AccountCommands { 237 #[arg(value_name = "ID|nevent")]
175 /// login with nsec or nostr connect 238 id: String,
176 Login(sub_commands::login::SubCommandArgs), 239 /// Use local cache only, skip network fetch
177 /// remove nostr account details stored in git config 240 #[arg(long)]
178 Logout, 241 offline: bool,
179 /// export nostr keys to login to other nostr clients 242 },
180 ExportKeys, 243 /// reopen a closed PR (author or maintainer only)
181 /// create a new nostr account 244 Reopen {
182 Create(sub_commands::create::SubCommandArgs), 245 /// Proposal event-id (hex) or nevent (bech32)
183} 246 #[arg(value_name = "ID|nevent")]
184 247 id: String,
185#[derive(clap::Parser)] 248 /// Use local cache only, skip network fetch
186pub struct AccountSubCommandArgs { 249 #[arg(long)]
187 #[command(subcommand)] 250 offline: bool,
188 pub account_command: AccountCommands, 251 },
252 /// mark a draft PR as ready for review (author or maintainer only)
253 Ready {
254 /// Proposal event-id (hex) or nevent (bech32)
255 #[arg(value_name = "ID|nevent")]
256 id: String,
257 /// Use local cache only, skip network fetch
258 #[arg(long)]
259 offline: bool,
260 },
261 /// add a comment to a PR
262 Comment {
263 /// Proposal event-id (hex) or nevent (bech32)
264 #[arg(value_name = "ID|nevent")]
265 id: String,
266 /// Comment body
267 #[arg(long)]
268 body: String,
269 /// Use local cache only, skip network fetch
270 #[arg(long)]
271 offline: bool,
272 },
273 /// merge a PR into the current branch (maintainer only)
274 #[command(
275 long_about = "merge a PR into the current branch (maintainer only)\n\nperforms a git merge of the PR branch; push afterwards to update the nostr state"
276 )]
277 Merge {
278 /// Proposal event-id (hex) or nevent (bech32)
279 #[arg(value_name = "ID|nevent")]
280 id: String,
281 /// Use squash merge
282 #[arg(long)]
283 squash: bool,
284 /// Use local cache only, skip network fetch
285 #[arg(long)]
286 offline: bool,
287 },
189} 288}
190 289
191#[derive(clap::Parser)] 290// ---------------------------------------------------------------------------
192pub struct RepoSubCommandArgs { 291// Issue subcommand group
193 #[command(subcommand)] 292// ---------------------------------------------------------------------------
194 pub repo_command: Option<RepoCommands>,
195 /// Use local cache only, skip network fetch
196 #[arg(long)]
197 pub offline: bool,
198}
199 293
200#[derive(clap::Parser)] 294#[derive(clap::Parser)]
201pub struct IssueSubCommandArgs { 295pub struct IssueSubCommandArgs {
@@ -223,6 +317,60 @@ pub enum IssueCommands {
223 #[arg(long)] 317 #[arg(long)]
224 offline: bool, 318 offline: bool,
225 }, 319 },
320 /// view an issue and its comments
321 View {
322 /// Issue event-id (hex) or nevent (bech32)
323 #[arg(value_name = "ID|nevent")]
324 id: String,
325 /// Output as JSON
326 #[arg(long)]
327 json: bool,
328 /// Use local cache only, skip network fetch
329 #[arg(long)]
330 offline: bool,
331 },
332 /// create a new issue
333 Create {
334 /// Issue title
335 #[arg(long)]
336 title: Option<String>,
337 /// Issue body / description
338 #[arg(long)]
339 body: Option<String>,
340 /// Hashtag labels (repeatable: --label bug --label help-wanted)
341 #[arg(long = "label", value_name = "LABEL")]
342 labels: Vec<String>,
343 },
344 /// close an issue (author or maintainer only)
345 Close {
346 /// Issue event-id (hex) or nevent (bech32)
347 #[arg(value_name = "ID|nevent")]
348 id: String,
349 /// Use local cache only, skip network fetch
350 #[arg(long)]
351 offline: bool,
352 },
353 /// reopen a closed issue (author or maintainer only)
354 Reopen {
355 /// Issue event-id (hex) or nevent (bech32)
356 #[arg(value_name = "ID|nevent")]
357 id: String,
358 /// Use local cache only, skip network fetch
359 #[arg(long)]
360 offline: bool,
361 },
362 /// add a comment to an issue
363 Comment {
364 /// Issue event-id (hex) or nevent (bech32)
365 #[arg(value_name = "ID|nevent")]
366 id: String,
367 /// Comment body
368 #[arg(long)]
369 body: String,
370 /// Use local cache only, skip network fetch
371 #[arg(long)]
372 offline: bool,
373 },
226} 374}
227 375
228#[derive(Subcommand)] 376#[derive(Subcommand)]