Commit fa2d1b2316

fa2d1b23169162c5040bff6f908f6c702a13431b

parent: f0903b7b21

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-12 05:58 UTC

control: one source for every command's usage

A bad invocation prints the registered usage through c.usage(); the
hand-written copies in 189 call sites are gone, so help and the error
cannot drift apart.

Ref #183
internal/control/admin.go +8 −8
@@ -131,7 +131,7 @@ func runAdminUserShow(c *Ctx, args []string) int {
131131 return code
132132 }
133133 if len(args) != 1 {
134 return c.fail(protocol.ExitUsage, "usage: admin user show <username>")
134 return c.usage()
135135 }
136136 name := args[0]
137137 u, err := c.Store.UserByUsername(name)
@@ -291,7 +291,7 @@ func setAdmin(c *Ctx, args []string, admin bool) int {
291291 verb = "promote"
292292 }
293293 if len(args) != 1 {
294 return c.fail(protocol.ExitUsage, "usage: admin user %s <username>", verb)
294 return c.usage()
295295 }
296296 u, err := c.Store.UserByUsername(args[0])
297297 if errors.Is(err, store.ErrNotFound) {
@@ -389,7 +389,7 @@ func adminArchive(c *Ctx, args []string, archived bool) int {
389389 verb = "unarchive"
390390 }
391391 if len(args) != 1 {
392 return c.fail(protocol.ExitUsage, "usage: admin repo %s <owner/name>", verb)
392 return c.usage()
393393 }
394394 repo, code := adminRepo(c, args[0])
395395 if code >= 0 {
@@ -404,7 +404,7 @@ func adminArchive(c *Ctx, args []string, archived bool) int {
404404
405405func runAdminRepoVisibility(c *Ctx, args []string) int {
406406 if len(args) != 2 || (args[1] != "public" && args[1] != "private") {
407 return c.fail(protocol.ExitUsage, "usage: admin repo visibility <owner/name> public|private")
407 return c.usage()
408408 }
409409 repo, code := adminRepo(c, args[0])
410410 if code >= 0 {
@@ -426,11 +426,11 @@ func runAdminRepoDelete(c *Ctx, args []string) int {
426426 } else if path == "" {
427427 path = a
428428 } else {
429 return c.fail(protocol.ExitUsage, "usage: admin repo delete <owner/name> --yes")
429 return c.usage()
430430 }
431431 }
432432 if path == "" {
433 return c.fail(protocol.ExitUsage, "usage: admin repo delete <owner/name> --yes")
433 return c.usage()
434434 }
435435 repo, code := adminRepo(c, path)
436436 if code >= 0 {
@@ -451,7 +451,7 @@ func runAdminRunnersForget(c *Ctx, args []string) int {
451451 return code
452452 }
453453 if len(args) != 1 {
454 return c.fail(protocol.ExitUsage, "usage: admin runners forget <fingerprint>")
454 return c.usage()
455455 }
456456 if err := c.Store.ForgetRunner(args[0]); err != nil {
457457 if errors.Is(err, store.ErrNotFound) {
@@ -470,7 +470,7 @@ func runAdminRunners(c *Ctx, args []string) int {
470470 return code
471471 }
472472 if len(args) != 0 {
473 return c.fail(protocol.ExitUsage, "usage: admin runners")
473 return c.usage()
474474 }
475475 runners, err := c.Store.ListRunners()
476476 if err != nil {
internal/control/adminhost.go +7 −8
@@ -55,8 +55,7 @@ func runAdminUserCreate(c *Ctx, args []string) int {
5555 if code := requireInstanceAdmin(c); code >= 0 {
5656 return code
5757 }
58 const usage = "usage: admin user create <username> [--admin] [--email <address> [--verified]] [--key -] < key.pub"
59 f, err := parseFlags(args, flagSpec{Values: []string{"--email", "--key"}, Bools: []string{"--admin", "--verified"}, MaxPos: 1, Usage: usage})
58 f, err := parseFlags(args, flagSpec{Values: []string{"--email", "--key"}, Bools: []string{"--admin", "--verified"}, MaxPos: 1, Usage: c.Cmd.Usage})
6059 if err != nil {
6160 return c.fail(protocol.ExitUsage, "%v", err)
6261 }
@@ -66,10 +65,10 @@ func runAdminUserCreate(c *Ctx, args []string) int {
6665 return c.fail(protocol.ExitUsage, "--key only supports - (the public key on stdin)")
6766 }
6867 if username == "" || username[0] == '-' {
69 return c.fail(protocol.ExitUsage, usage)
68 return c.usage()
7069 }
7170 if username == "" || (verified && email == "") {
72 return c.fail(protocol.ExitUsage, usage)
71 return c.usage()
7372 }
7473 if err := policy.ValidateOwnerName(username); err != nil {
7574 return c.failInput(err)
@@ -128,7 +127,7 @@ func adminUserArg(c *Ctx, args []string, usage string) (store.User, int) {
128127 return store.User{}, code
129128 }
130129 if len(args) != 1 {
131 return store.User{}, c.fail(protocol.ExitUsage, "usage: %s", usage)
130 return store.User{}, c.usage()
132131 }
133132 u, err := c.Store.UserByUsername(args[0])
134133 if errors.Is(err, store.ErrNotFound) {
@@ -201,7 +200,7 @@ func runAdminEmailVerify(c *Ctx, args []string) int {
201200 return code
202201 }
203202 if len(args) != 2 {
204 return c.fail(protocol.ExitUsage, "usage: admin email verify <username> <address>")
203 return c.usage()
205204 }
206205 u, err := c.Store.UserByUsername(args[0])
207206 if errors.Is(err, store.ErrNotFound) {
@@ -228,7 +227,7 @@ func runAdminInvite(c *Ctx, args []string) int {
228227 email = args[1]
229228 }
230229 if email == "" {
231 return c.fail(protocol.ExitUsage, "usage: admin invite --email <address>")
230 return c.usage()
232231 }
233232 if used, err := c.Store.EmailInUse(email); err != nil {
234233 return c.fail(protocol.ExitFailure, "%v", err)
@@ -271,7 +270,7 @@ func runAdminStats(c *Ctx, args []string) int {
271270 return code
272271 }
273272 if len(args) != 0 {
274 return c.fail(protocol.ExitUsage, "usage: admin stats")
273 return c.usage()
275274 }
276275 counts, err := c.Store.InstanceCounts()
277276 if err != nil {
internal/control/audit.go +1 −3
@@ -18,14 +18,12 @@ func init() {
1818 ReadOnly: true, SSHOnly: true, Run: runAudit})
1919}
2020
21const auditUsage = "usage: audit [--actor <user>|-] [--action <prefix>] [--since <duration|date>] [--limit <n>]"
22
2321func runAudit(c *Ctx, args []string) int {
2422 if !c.User.IsAdmin {
2523 return c.fail(protocol.ExitDenied, "the audit log is for instance admins")
2624 }
2725 f := store.AuditFilter{Limit: 100}
28 fl, err := parseFlags(args, flagSpec{Values: []string{"--limit", "--actor", "--action", "--since"}, MaxPos: 0, Usage: auditUsage})
26 fl, err := parseFlags(args, flagSpec{Values: []string{"--limit", "--actor", "--action", "--since"}, MaxPos: 0, Usage: c.Cmd.Usage})
2927 if err != nil {
3028 return c.fail(protocol.ExitUsage, "%v", err)
3129 }
internal/control/build.go +8 −8
@@ -104,7 +104,7 @@ func buildRef(c *Ctx, args []string) (store.Repo, store.Build, int) {
104104
105105func runBuildList(c *Ctx, args []string) int {
106106 if len(args) != 1 {
107 return c.fail(protocol.ExitUsage, "usage: build list <owner/name>")
107 return c.usage()
108108 }
109109 repo, code := resolveRepo(c, args[0], policy.CanRead)
110110 if code >= 0 {
@@ -182,7 +182,7 @@ func repoJobs(c *Ctx, repo store.Repo) ([]ci.Job, string, int) {
182182// that can read the repository's git could offer the choice.
183183func runBuildJobs(c *Ctx, args []string) int {
184184 if len(args) != 1 {
185 return c.fail(protocol.ExitUsage, "usage: build jobs <owner/name>")
185 return c.usage()
186186 }
187187 repo, code := resolveRepo(c, args[0], policy.CanRead)
188188 if code >= 0 {
@@ -212,7 +212,7 @@ func runBuildJobs(c *Ctx, args []string) int {
212212
213213func runBuildTrigger(c *Ctx, args []string) int {
214214 if len(args) != 2 {
215 return c.fail(protocol.ExitUsage, "usage: build trigger <owner/name> <job>")
215 return c.usage()
216216 }
217217 repo, code := resolveRepo(c, args[0], policy.CanWrite)
218218 if code >= 0 {
@@ -246,7 +246,7 @@ var secretName = regexp.MustCompile(`^[A-Z_][A-Z0-9_]{0,63}$`)
246246
247247func runSecretSet(c *Ctx, args []string) int {
248248 if len(args) != 2 {
249 return c.fail(protocol.ExitUsage, "usage: repo secret set <owner/name> <NAME> (value on stdin)")
249 return c.usage()
250250 }
251251 if !secretName.MatchString(args[1]) {
252252 return c.fail(protocol.ExitUsage, "secret names are env-var shaped: uppercase letters, digits, _")
@@ -273,7 +273,7 @@ func runSecretSet(c *Ctx, args []string) int {
273273
274274func runSecretRemove(c *Ctx, args []string) int {
275275 if len(args) != 2 {
276 return c.fail(protocol.ExitUsage, "usage: repo secret remove <owner/name> <NAME>")
276 return c.usage()
277277 }
278278 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
279279 if code >= 0 {
@@ -292,7 +292,7 @@ func runSecretRemove(c *Ctx, args []string) int {
292292
293293func runSecretList(c *Ctx, args []string) int {
294294 if len(args) != 1 {
295 return c.fail(protocol.ExitUsage, "usage: repo secret list <owner/name>")
295 return c.usage()
296296 }
297297 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
298298 if code >= 0 {
@@ -464,7 +464,7 @@ func runRunnerLog(c *Ctx, args []string) int {
464464 return code
465465 }
466466 if len(args) != 1 {
467 return c.fail(protocol.ExitUsage, "usage: runner log <build-id> (chunk on stdin)")
467 return c.usage()
468468 }
469469 id, err := strconv.ParseInt(args[0], 10, 64)
470470 if err != nil {
@@ -542,7 +542,7 @@ func runRunnerDone(c *Ctx, args []string) int {
542542 return code
543543 }
544544 if len(args) != 2 || (args[1] != "success" && args[1] != "failure") {
545 return c.fail(protocol.ExitUsage, "usage: runner done <build-id> success|failure")
545 return c.usage()
546546 }
547547 id, err := strconv.ParseInt(args[0], 10, 64)
548548 if err != nil {
internal/control/commitfile.go +2 −3
@@ -35,15 +35,14 @@ const maxCommitFileBytes = 1 << 20
3535// A repository that requires verified signatures therefore refuses the
3636// command rather than writing a commit its own policy would reject.
3737func runCommitFile(c *Ctx, args []string) int {
38 const usage = "repo commit-file <owner/name> <path> --ref <branch> [--message <m>] [--file -]"
39 f, err := parseFlags(args, flagSpec{Values: []string{"--ref", "--message", "--file"}, MaxPos: -1, Usage: usage})
38 f, err := parseFlags(args, flagSpec{Values: []string{"--ref", "--message", "--file"}, MaxPos: -1, Usage: c.Cmd.Usage})
4039 if err != nil {
4140 return c.fail(protocol.ExitUsage, "%v", err)
4241 }
4342 rest := f.Pos
4443 ref, message, file := f.Value("--ref"), f.Value("--message"), f.Value("--file")
4544 if len(rest) != 2 || ref == "" {
46 return c.fail(protocol.ExitUsage, "usage: %s", usage)
45 return c.usage()
4746 }
4847 repo, code := resolveRepo(c, rest[0], policy.CanWrite)
4948 if code >= 0 {
internal/control/control.go +10
@@ -36,6 +36,15 @@ type Ctx struct {
3636 // Source identifies the credential behind this session for the audit
3737 // log: an SSH key fingerprint, or "api" for token requests.
3838 Source string
39 // Cmd is the command being run, set by Dispatch, so a usage error can
40 // print the registered usage rather than a copy of it.
41 Cmd Command
42}
43
44// usage reports a bad invocation with the command's registered usage,
45// the one source of it.
46func (c *Ctx) usage() int {
47 return c.fail(protocol.ExitUsage, "usage: %s", c.Cmd.Usage)
3948}
4049
4150type Command struct {
@@ -82,6 +91,7 @@ func Dispatch(c *Ctx, argv []string) int {
8291 return c.fail(protocol.ExitUsage, "no command given; try: ssh <host> help")
8392 }
8493 cmd, rest, ok := Lookup(argv)
94 c.Cmd = cmd
8595 if !ok {
8696 return c.fail(protocol.ExitUsage, "unknown command %q", argv[0])
8797 }
internal/control/dashboard.go +2 −2
@@ -84,7 +84,7 @@ type DashboardOut struct {
8484
8585func runDashboard(c *Ctx, args []string) int {
8686 if len(args) != 0 {
87 return c.fail(protocol.ExitUsage, "usage: dashboard")
87 return c.usage()
8888 }
8989 d := DashboardOut{
9090 Reviews: []DashboardItem{}, Assigned: []DashboardItem{}, MRs: []DashboardItem{},
@@ -260,7 +260,7 @@ func runFeed(c *Ctx, args []string) int {
260260 return code
261261 }
262262 if len(rest) != 0 {
263 return c.fail(protocol.ExitUsage, "usage: feed [--limit <n>] [--cursor <c>]")
263 return c.usage()
264264 }
265265 if p.limit == 0 {
266266 p.limit = feedDefaultLimit
internal/control/deploykey.go +4 −4
@@ -34,13 +34,13 @@ func runDeployKeyAdd(c *Ctx, args []string) int {
3434 mode = "rw"
3535 default:
3636 if path != "" {
37 return c.fail(protocol.ExitUsage, "usage: repo deploy-key add <owner/name> [--rw] < key.pub")
37 return c.usage()
3838 }
3939 path = a
4040 }
4141 }
4242 if path == "" {
43 return c.fail(protocol.ExitUsage, "usage: repo deploy-key add <owner/name> [--rw] < key.pub")
43 return c.usage()
4444 }
4545 repo, code := resolveRepo(c, path, policy.CanAdmin)
4646 if code >= 0 {
@@ -73,7 +73,7 @@ func runDeployKeyAdd(c *Ctx, args []string) int {
7373
7474func runDeployKeyList(c *Ctx, args []string) int {
7575 if len(args) != 1 {
76 return c.fail(protocol.ExitUsage, "usage: repo deploy-key list <owner/name>")
76 return c.usage()
7777 }
7878 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
7979 if code >= 0 {
@@ -106,7 +106,7 @@ func runDeployKeyList(c *Ctx, args []string) int {
106106
107107func runDeployKeyRemove(c *Ctx, args []string) int {
108108 if len(args) != 2 {
109 return c.fail(protocol.ExitUsage, "usage: repo deploy-key remove <owner/name> <fingerprint>")
109 return c.usage()
110110 }
111111 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
112112 if code >= 0 {
internal/control/deps.go +3 −3
@@ -46,7 +46,7 @@ type DepBehind struct {
4646
4747func runDepsEnable(c *Ctx, args []string) int {
4848 if len(args) != 1 {
49 return c.fail(protocol.ExitUsage, "usage: repo deps enable <owner/name>")
49 return c.usage()
5050 }
5151 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
5252 if code >= 0 {
@@ -62,7 +62,7 @@ func runDepsEnable(c *Ctx, args []string) int {
6262
6363func runDepsDisable(c *Ctx, args []string) int {
6464 if len(args) != 1 {
65 return c.fail(protocol.ExitUsage, "usage: repo deps disable <owner/name>")
65 return c.usage()
6666 }
6767 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
6868 if code >= 0 {
@@ -78,7 +78,7 @@ func runDepsDisable(c *Ctx, args []string) int {
7878
7979func runDepsStatus(c *Ctx, args []string) int {
8080 if len(args) != 1 {
81 return c.fail(protocol.ExitUsage, "usage: repo deps status <owner/name>")
81 return c.usage()
8282 }
8383 repo, code := resolveRepo(c, args[0], policy.CanRead)
8484 if code >= 0 {
internal/control/diffcomment.go +2 −2
@@ -143,7 +143,7 @@ func runMRThreads(c *Ctx, args []string) int {
143143 return code
144144 }
145145 if len(args) != 2 {
146 return c.fail(protocol.ExitUsage, "usage: mr threads <owner/name> <n>")
146 return c.usage()
147147 }
148148 comments, err := c.Store.ListDiffComments(mr.ID, c.User.ID)
149149 if err != nil {
@@ -202,7 +202,7 @@ func runMRThreads(c *Ctx, args []string) int {
202202
203203func setThreadResolved(c *Ctx, args []string, resolved bool) int {
204204 if len(args) != 3 {
205 return c.fail(protocol.ExitUsage, "usage: mr resolve|unresolve <owner/name> <n> <thread-id>")
205 return c.usage()
206206 }
207207 repo, mr, code := mrRef(c, args[:2], policy.CanRead)
208208 if code >= 0 {
internal/control/explore.go +3 −4
@@ -37,7 +37,7 @@ func runExplore(c *Ctx, args []string) int {
3737 return code
3838 }
3939 if len(rest) != 0 {
40 return c.fail(protocol.ExitUsage, "usage: explore [--limit <n>] [--cursor <c>]")
40 return c.usage()
4141 }
4242 repos, err := c.Store.ListPublicRepos()
4343 if err != nil {
@@ -79,14 +79,13 @@ func runExplore(c *Ctx, args []string) int {
7979// release asset get writes an asset. The web's /archive route is the
8080// same bytes with a Content-Disposition on them.
8181func runRepoDownload(c *Ctx, args []string) int {
82 const usage = "repo download <owner/name> [--ref <r>] > repo.tar.gz"
83 f, err := parseFlags(args, flagSpec{Values: []string{"--ref"}, MaxPos: -1, Usage: usage})
82 f, err := parseFlags(args, flagSpec{Values: []string{"--ref"}, MaxPos: -1, Usage: c.Cmd.Usage})
8483 if err != nil {
8584 return c.fail(protocol.ExitUsage, "%v", err)
8685 }
8786 rest, ref := f.Pos, f.Value("--ref")
8887 if len(rest) != 1 {
89 return c.fail(protocol.ExitUsage, "usage: %s", usage)
88 return c.usage()
9089 }
9190 repo, code := resolveRepo(c, rest[0], policy.CanRead)
9291 if code >= 0 {
internal/control/ghimport.go +1 −1
@@ -149,7 +149,7 @@ func runImportIssues(c *Ctx, args []string) int {
149149 }
150150 path, from, apiBase, tokenStdin := f.pos(0), f.Value("--from"), f.Value("--api-base"), f.Has("--token-stdin")
151151 if path == "" || from == "" {
152 return c.fail(protocol.ExitUsage, "usage: repo import-issues <owner/name> --from <owner/repo> [--token-stdin] [--api-base <url>]")
152 return c.usage()
153153 }
154154 if apiBase == "" {
155155 apiBase = "https://api.github.com"
internal/control/identity.go +4 −4
@@ -51,7 +51,7 @@ func init() {
5151
5252func runWhoami(c *Ctx, args []string) int {
5353 if len(args) != 0 {
54 return c.fail(protocol.ExitUsage, "usage: whoami [--json]")
54 return c.usage()
5555 }
5656 type out struct {
5757 Username string `json:"username"`
@@ -66,7 +66,7 @@ func runWhoami(c *Ctx, args []string) int {
6666
6767func runKeysList(c *Ctx, args []string) int {
6868 if len(args) != 0 {
69 return c.fail(protocol.ExitUsage, "usage: keys list [--json]")
69 return c.usage()
7070 }
7171 keys, err := c.Store.ListSSHKeys(c.User.ID)
7272 if err != nil {
@@ -161,7 +161,7 @@ func runKeysAdd(c *Ctx, args []string) int {
161161
162162func runKeysLabel(c *Ctx, args []string) int {
163163 if len(args) < 1 || len(args) > 2 {
164 return c.fail(protocol.ExitUsage, "usage: keys label <fingerprint> [<text>]")
164 return c.usage()
165165 }
166166 label := ""
167167 if len(args) == 2 {
@@ -189,7 +189,7 @@ func runKeysLabel(c *Ctx, args []string) int {
189189
190190func runKeysRemove(c *Ctx, args []string) int {
191191 if len(args) != 1 {
192 return c.fail(protocol.ExitUsage, "usage: keys remove <fingerprint>")
192 return c.usage()
193193 }
194194 if err := c.Store.RemoveSSHKey(c.User.ID, args[0]); err != nil {
195195 if errors.Is(err, store.ErrNotFound) {
internal/control/import.go +2 −2
@@ -40,11 +40,11 @@ func runRepoImport(c *Ctx, args []string) int {
4040 }
4141 path, from, private, tokenStdin := f.pos(0), f.Value("--from"), f.Has("--private"), f.Has("--token-stdin")
4242 if path == "" || from == "" {
43 return c.fail(protocol.ExitUsage, "usage: repo import <owner/name> --from <url> [--private] [--token-stdin]")
43 return c.usage()
4444 }
4545 owner, name, ok := strings.Cut(path, "/")
4646 if !ok {
47 return c.fail(protocol.ExitUsage, "usage: repo import <owner/name> --from <url>")
47 return c.usage()
4848 }
4949 if err := policy.ValidateName(name); err != nil {
5050 return c.failInput(err)
internal/control/issue.go +8 −9
@@ -123,7 +123,7 @@ func runIssueCreate(c *Ctx, args []string) int {
123123 }
124124 path, title, body, file, format := f.pos(0), f.Value("--title"), f.Value("--body"), f.Value("--file"), f.Value("--format")
125125 if path == "" || title == "" {
126 return c.fail(protocol.ExitUsage, "usage: issue create <owner/name> --title <t> [--body <b> | --file -] [--format md|org]")
126 return c.usage()
127127 }
128128 fmtName, err := markupFormat(format)
129129 if err != nil {
@@ -168,9 +168,8 @@ func runIssueList(c *Ctx, args []string) int {
168168 if code >= 0 {
169169 return code
170170 }
171 const usage = "usage: issue list <owner/name> [--state open|closed|all] [--label <l>] [--assignee <user>] [--author <user>] [--milestone <title>|none] [--search <text>] [--limit <n>] [--cursor <c>]"
172171 f := store.IssueFilter{State: "open"}
173 fl, err := parseFlags(args, flagSpec{Values: []string{"--state", "--label", "--assignee", "--author", "--milestone", "--search"}, MaxPos: 1, Usage: usage})
172 fl, err := parseFlags(args, flagSpec{Values: []string{"--state", "--label", "--assignee", "--author", "--milestone", "--search"}, MaxPos: 1, Usage: c.Cmd.Usage})
174173 if err != nil {
175174 return c.fail(protocol.ExitUsage, "%v", err)
176175 }
@@ -186,7 +185,7 @@ func runIssueList(c *Ctx, args []string) int {
186185 }
187186 }
188187 if path == "" || (f.State != "open" && f.State != "closed" && f.State != "all") {
189 return c.fail(protocol.ExitUsage, usage)
188 return c.usage()
190189 }
191190 repo, code := resolveRepo(c, path, policy.CanRead)
192191 if code >= 0 {
@@ -217,7 +216,7 @@ func runIssueShow(c *Ctx, args []string) int {
217216 return code
218217 }
219218 if len(args) != 2 {
220 return c.fail(protocol.ExitUsage, "usage: issue show <owner/name> <n>")
219 return c.usage()
221220 }
222221 comments, err := c.Store.ListIssueComments(issue.ID)
223222 if err != nil {
@@ -265,7 +264,7 @@ func setIssueState(c *Ctx, args []string, state string) int {
265264 return code
266265 }
267266 if len(args) != 2 {
268 return c.fail(protocol.ExitUsage, "usage: issue %s <owner/name> <n>", state)
267 return c.usage()
269268 }
270269 if code := authorOrWrite(c, repo, issue.Author, map[string]string{"open": "reopen", "closed": "close"}[state]+" this issue"); code >= 0 {
271270 return code
@@ -312,7 +311,7 @@ func editText(c *Ctx, args []string, kind string) (rest []string, title, body, f
312311 return nil, nil, nil, nil, c.failInput(err)
313312 }
314313 if !haveTitle && !haveBody && fmtName == "" {
315 return nil, nil, nil, nil, c.fail(protocol.ExitUsage, "usage: %s edit <owner/name> <n> [--title <t>] [--body <b> | --file -] [--format md|org]", kind)
314 return nil, nil, nil, nil, c.usage()
316315 }
317316 if haveTitle {
318317 if strings.TrimSpace(titleV) == "" {
@@ -378,7 +377,7 @@ func runIssueLabel(c *Ctx, args []string) int {
378377 return c.failInput(err)
379378 }
380379 if len(adds)+len(removes) == 0 {
381 return c.fail(protocol.ExitUsage, "usage: issue label <owner/name> <n> [--add <l>]... [--remove <l>]...")
380 return c.usage()
382381 }
383382 repo, issue, code := issueRef(c, rest, policy.CanWrite)
384383 if code >= 0 {
@@ -417,7 +416,7 @@ func runIssueAssign(c *Ctx, args []string) int {
417416 return c.failInput(err)
418417 }
419418 if len(adds)+len(removes) == 0 {
420 return c.fail(protocol.ExitUsage, "usage: issue assign <owner/name> <n> [--add <user>]... [--remove <user>]...")
419 return c.usage()
421420 }
422421 repo, issue, code := issueRef(c, rest, policy.CanWrite)
423422 if code >= 0 {
internal/control/label.go +4 −5
@@ -31,7 +31,7 @@ var labelColorPat = regexp.MustCompile(`^#?[0-9a-fA-F]{6}$`)
3131
3232func runLabelList(c *Ctx, args []string) int {
3333 if len(args) != 1 {
34 return c.fail(protocol.ExitUsage, "usage: label list <owner/name>")
34 return c.usage()
3535 }
3636 repo, code := resolveRepo(c, args[0], policy.CanRead)
3737 if code >= 0 {
@@ -53,15 +53,14 @@ func runLabelList(c *Ctx, args []string) int {
5353}
5454
5555func runLabelSet(c *Ctx, args []string) int {
56 const usage = "usage: label set <owner/name> <label> [--color #rrggbb|'']"
57 f, err := parseFlags(args, flagSpec{Values: []string{"--color"}, MaxPos: -1, Usage: usage})
56 f, err := parseFlags(args, flagSpec{Values: []string{"--color"}, MaxPos: -1, Usage: c.Cmd.Usage})
5857 if err != nil {
5958 return c.fail(protocol.ExitUsage, "%v", err)
6059 }
6160 rest := f.Pos
6261 color, colorSet := strings.ToLower(f.Value("--color")), f.Has("--color")
6362 if len(rest) != 2 {
64 return c.fail(protocol.ExitUsage, usage)
63 return c.usage()
6564 }
6665 if colorSet && color != "" {
6766 if !labelColorPat.MatchString(color) {
@@ -103,7 +102,7 @@ func runLabelSet(c *Ctx, args []string) int {
103102
104103func runLabelRemove(c *Ctx, args []string) int {
105104 if len(args) != 2 {
106 return c.fail(protocol.ExitUsage, "usage: label remove <owner/name> <label>")
105 return c.usage()
107106 }
108107 repo, code := resolveRepo(c, args[0], policy.CanWrite)
109108 if code >= 0 {
internal/control/migrate.go +1 −1
@@ -81,7 +81,7 @@ type bundle struct {
8181
8282func runAccountExport(c *Ctx, args []string) int {
8383 if len(args) != 0 {
84 return c.fail(protocol.ExitUsage, "usage: account export > bundle.json")
84 return c.usage()
8585 }
8686 b := bundle{Bundle: bundleVersion, Username: c.User.Username}
8787 b.Profile, _ = c.Store.OwnerProfile("user", c.User.ID)
internal/control/milestone.go +6 −6
@@ -48,7 +48,7 @@ func runMilestoneCreate(c *Ctx, args []string) int {
4848 }
4949 path, title, description, due := f.pos(0), f.pos(1), f.Value("--description"), f.Value("--due")
5050 if path == "" || title == "" {
51 return c.fail(protocol.ExitUsage, "usage: milestone create <owner/name> <title> [--description <d>] [--due YYYY-MM-DD]")
51 return c.usage()
5252 }
5353 if due != "" && !duePat.MatchString(due) {
5454 return c.fail(protocol.ExitUsage, "--due must be YYYY-MM-DD")
@@ -81,7 +81,7 @@ func runMilestoneList(c *Ctx, args []string) int {
8181 state = f.Value("--state")
8282 }
8383 if path == "" || (state != "open" && state != "closed" && state != "all") {
84 return c.fail(protocol.ExitUsage, "usage: milestone list <owner/name> [--state open|closed|all]")
84 return c.usage()
8585 }
8686 repo, code := resolveRepo(c, path, policy.CanRead)
8787 if code >= 0 {
@@ -137,7 +137,7 @@ func setMilestoneState(c *Ctx, args []string, state string) int {
137137 verb = "reopen"
138138 }
139139 if len(args) != 2 {
140 return c.fail(protocol.ExitUsage, "usage: milestone %s <owner/name> <title>", verb)
140 return c.usage()
141141 }
142142 repo, code := resolveRepo(c, args[0], policy.CanWrite)
143143 if code >= 0 {
@@ -177,7 +177,7 @@ func runIssueMilestone(c *Ctx, args []string) int {
177177 return code
178178 }
179179 if len(args) != 3 {
180 return c.fail(protocol.ExitUsage, "usage: issue milestone <owner/name> <n> <title|none>")
180 return c.usage()
181181 }
182182 return setItemMilestone(c, repo, "issue", issue.Number, args[2], func(id int64) error {
183183 return c.Store.SetIssueMilestone(issue.ID, id)
@@ -193,7 +193,7 @@ func runMRMilestone(c *Ctx, args []string) int {
193193 return code
194194 }
195195 if len(args) != 3 {
196 return c.fail(protocol.ExitUsage, "usage: mr milestone <owner/name> <n> <title|none>")
196 return c.usage()
197197 }
198198 return setItemMilestone(c, repo, "mr", mr.Number, args[2], func(id int64) error {
199199 return c.Store.SetMRMilestone(mr.ID, id)
@@ -232,7 +232,7 @@ func setItemMilestone(c *Ctx, repo store.Repo, noun string, number int64, title
232232// runIssueTemplates lists .gitbay/issue-template*.md at the default branch.
233233func runIssueTemplates(c *Ctx, args []string) int {
234234 if len(args) != 1 {
235 return c.fail(protocol.ExitUsage, "usage: issue templates <owner/name>")
235 return c.usage()
236236 }
237237 repo, code := resolveRepo(c, args[0], policy.CanRead)
238238 if code >= 0 {
internal/control/mirrorcmd.go +4 −4
@@ -39,7 +39,7 @@ func runMirrorAdd(c *Ctx, args []string) int {
3939 path, urlArg := f.pos(0), f.pos(1)
4040 direction, username, tokenStdin := f.Value("--direction"), f.Value("--username"), f.Has("--token-stdin")
4141 if path == "" || urlArg == "" || (direction != "push" && direction != "pull") {
42 return c.fail(protocol.ExitUsage, "usage: repo mirror add <owner/name> <https-url> --direction push|pull [--username <u>] [--token-stdin]")
42 return c.usage()
4343 }
4444 // The worker's git process dials this URL from the server: same SSRF
4545 // surface as a webhook target, same rules.
@@ -76,7 +76,7 @@ func runMirrorAdd(c *Ctx, args []string) int {
7676
7777func runMirrorList(c *Ctx, args []string) int {
7878 if len(args) != 1 {
79 return c.fail(protocol.ExitUsage, "usage: repo mirror list <owner/name>")
79 return c.usage()
8080 }
8181 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
8282 if code >= 0 {
@@ -123,7 +123,7 @@ func orDash(s string) string {
123123
124124func runMirrorRemove(c *Ctx, args []string) int {
125125 if len(args) != 2 {
126 return c.fail(protocol.ExitUsage, "usage: repo mirror remove <owner/name> <id>")
126 return c.usage()
127127 }
128128 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
129129 if code >= 0 {
@@ -146,7 +146,7 @@ func runMirrorRemove(c *Ctx, args []string) int {
146146
147147func runMirrorSync(c *Ctx, args []string) int {
148148 if len(args) != 1 {
149 return c.fail(protocol.ExitUsage, "usage: repo mirror sync <owner/name>")
149 return c.usage()
150150 }
151151 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
152152 if code >= 0 {
internal/control/mr.go +19 −26
@@ -104,7 +104,7 @@ func runRepoFork(c *Ctx, args []string) int {
104104 }
105105 path, name := f.pos(0), f.Value("--name")
106106 if path == "" {
107 return c.fail(protocol.ExitUsage, "usage: repo fork <owner/name> [--name <n>]")
107 return c.usage()
108108 }
109109 src, code := resolveRepo(c, path, policy.CanRead)
110110 if code >= 0 {
@@ -150,7 +150,7 @@ func runRepoFork(c *Ctx, args []string) int {
150150
151151func runRequireApprovals(c *Ctx, args []string) int {
152152 if len(args) != 2 {
153 return c.fail(protocol.ExitUsage, "usage: repo settings require-approvals <owner/name> <n>")
153 return c.usage()
154154 }
155155 n, err := strconv.Atoi(args[1])
156156 if err != nil || n < 0 || n > 20 {
@@ -171,7 +171,7 @@ func runRequireApprovals(c *Ctx, args []string) int {
171171
172172func runRequireResolved(c *Ctx, args []string) int {
173173 if len(args) != 2 || (args[1] != "on" && args[1] != "off") {
174 return c.fail(protocol.ExitUsage, "usage: repo settings require-resolved <owner/name> on|off")
174 return c.usage()
175175 }
176176 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
177177 if code >= 0 {
@@ -188,7 +188,7 @@ func runRequireResolved(c *Ctx, args []string) int {
188188
189189func runRequireCodeowners(c *Ctx, args []string) int {
190190 if len(args) != 2 || (args[1] != "on" && args[1] != "off") {
191 return c.fail(protocol.ExitUsage, "usage: repo settings require-codeowners <owner/name> on|off")
191 return c.usage()
192192 }
193193 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
194194 if code >= 0 {
@@ -205,7 +205,7 @@ func runRequireCodeowners(c *Ctx, args []string) int {
205205
206206func runRequireChecks(c *Ctx, args []string) int {
207207 if len(args) != 2 || (args[1] != "on" && args[1] != "off") {
208 return c.fail(protocol.ExitUsage, "usage: repo settings require-checks <owner/name> on|off")
208 return c.usage()
209209 }
210210 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
211211 if code >= 0 {
@@ -222,7 +222,7 @@ func runRequireChecks(c *Ctx, args []string) int {
222222
223223func runRequireMR(c *Ctx, args []string) int {
224224 if len(args) != 2 || (args[1] != "on" && args[1] != "off") {
225 return c.fail(protocol.ExitUsage, "usage: repo settings require-mr <owner/name> on|off")
225 return c.usage()
226226 }
227227 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
228228 if code >= 0 {
@@ -239,7 +239,7 @@ func runRequireMR(c *Ctx, args []string) int {
239239
240240func runRequireSigned(c *Ctx, args []string) int {
241241 if len(args) != 2 || (args[1] != "on" && args[1] != "off") {
242 return c.fail(protocol.ExitUsage, "usage: repo settings require-signed <owner/name> on|off")
242 return c.usage()
243243 }
244244 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
245245 if code >= 0 {
@@ -282,7 +282,7 @@ func runMRCreate(c *Ctx, args []string) int {
282282 path, source, target := f.pos(0), f.Value("--source"), f.Value("--target")
283283 title, body, file, format := f.Value("--title"), f.Value("--body"), f.Value("--file"), f.Value("--format")
284284 if path == "" || source == "" || title == "" {
285 return c.fail(protocol.ExitUsage, "usage: mr create <target owner/name> --source [owner/name:]<branch> --target <branch> --title <t> [--draft]")
285 return c.usage()
286286 }
287287 fmtName, err := markupFormat(format)
288288 if err != nil {
@@ -441,9 +441,8 @@ func runMRList(c *Ctx, args []string) int {
441441 if code >= 0 {
442442 return code
443443 }
444 const usage = "usage: mr list <owner/name> [--state open|merged|closed|source_gone|all] [--author <user>] [--milestone <title>|none] [--search <text>] [--limit <n>] [--cursor <c>]"
445444 f := store.MRFilter{State: "open"}
446 fl, err := parseFlags(args, flagSpec{Values: []string{"--state", "--author", "--milestone", "--search"}, MaxPos: 1, Usage: usage})
445 fl, err := parseFlags(args, flagSpec{Values: []string{"--state", "--author", "--milestone", "--search"}, MaxPos: 1, Usage: c.Cmd.Usage})
447446 if err != nil {
448447 return c.fail(protocol.ExitUsage, "%v", err)
449448 }
@@ -460,7 +459,7 @@ func runMRList(c *Ctx, args []string) int {
460459 }
461460 valid := map[string]bool{"open": true, "merged": true, "closed": true, "source_gone": true, "all": true}
462461 if path == "" || !valid[f.State] {
463 return c.fail(protocol.ExitUsage, usage)
462 return c.usage()
464463 }
465464 repo, code := resolveRepo(c, path, policy.CanRead)
466465 if code >= 0 {
@@ -510,7 +509,7 @@ func runMRShow(c *Ctx, args []string) int {
510509 return code
511510 }
512511 if len(args) != 2 {
513 return c.fail(protocol.ExitUsage, "usage: mr show <owner/name> <n>")
512 return c.usage()
514513 }
515514 comments, err := c.Store.ListMRComments(mr.ID)
516515 if err != nil {
@@ -701,7 +700,7 @@ func runMREdit(c *Ctx, args []string) int {
701700// same repository.
702701func runMRRetarget(c *Ctx, args []string) int {
703702 if len(args) != 3 {
704 return c.fail(protocol.ExitUsage, "usage: mr retarget <owner/name> <n> <branch>")
703 return c.usage()
705704 }
706705 repo, mr, code := mrRef(c, args[:2], policy.CanRead)
707706 if code >= 0 {
@@ -779,12 +778,11 @@ func runMRReview(c *Ctx, args []string) int {
779778 rest = append(rest, a)
780779 }
781780 }
782 const usage = "mr review <owner/name> <n> --approve|--request-changes|--comment|--discard"
783781 if discard && verdict != "" {
784782 return c.fail(protocol.ExitUsage, "--discard throws the batch away; it takes no verdict")
785783 }
786784 if verdict == "" && !discard {
787 return c.fail(protocol.ExitUsage, "usage: %s", usage)
785 return c.usage()
788786 }
789787 repo, mr, code := mrRef(c, rest, policy.CanRead)
790788 if code >= 0 {
@@ -848,7 +846,7 @@ func runMRReviewRequest(c *Ctx, args []string) int {
848846 return c.failInput(err)
849847 }
850848 if len(adds)+len(removes) == 0 {
851 return c.fail(protocol.ExitUsage, "usage: mr review request <owner/name> <n> [--add <user>]... [--remove <user>]...")
849 return c.usage()
852850 }
853851 repo, mr, code := mrRef(c, rest, policy.CanWrite)
854852 if code >= 0 {
@@ -1404,10 +1402,6 @@ func runMRDraft(c *Ctx, args []string) int { return setMRDraft(c, args, true) }
14041402func runMRReady(c *Ctx, args []string) int { return setMRDraft(c, args, false) }
14051403
14061404func setMRDraft(c *Ctx, args []string, draft bool) int {
1407 verb := "ready"
1408 if draft {
1409 verb = "draft"
1410 }
14111405 repo, mr, code := mrRef(c, args, policy.CanRead)
14121406 if code >= 0 {
14131407 return code
@@ -1416,7 +1410,7 @@ func setMRDraft(c *Ctx, args []string, draft bool) int {
14161410 return code
14171411 }
14181412 if len(args) != 2 {
1419 return c.fail(protocol.ExitUsage, "usage: mr %s <owner/name> <n>", verb)
1413 return c.usage()
14201414 }
14211415 if code := authorOrWrite(c, repo, mr.Author, "change this merge request"); code >= 0 {
14221416 return code
@@ -1471,7 +1465,7 @@ func runMRClose(c *Ctx, args []string) int {
14711465 return code
14721466 }
14731467 if len(args) != 2 {
1474 return c.fail(protocol.ExitUsage, "usage: mr close <owner/name> <n>")
1468 return c.usage()
14751469 }
14761470 if code := authorOrWrite(c, repo, mr.Author, "close this merge request"); code >= 0 {
14771471 return code
@@ -1532,7 +1526,7 @@ func runMRRevisions(c *Ctx, args []string) int {
15321526 return code
15331527 }
15341528 if len(args) != 2 {
1535 return c.fail(protocol.ExitUsage, "usage: mr revisions <owner/name> <n>")
1529 return c.usage()
15361530 }
15371531 revs, err := mrRevisions(c, mr)
15381532 if err != nil {
@@ -1554,8 +1548,7 @@ func runMRRevisions(c *Ctx, args []string) int {
15541548}
15551549
15561550func runMRRangeDiff(c *Ctx, args []string) int {
1557 const usage = "mr range-diff <owner/name> <n> [--from <sha>] [--to <sha>]"
1558 f, err := parseFlags(args, flagSpec{Values: []string{"--from", "--to"}, MaxPos: 2, Usage: usage})
1551 f, err := parseFlags(args, flagSpec{Values: []string{"--from", "--to"}, MaxPos: 2, Usage: c.Cmd.Usage})
15591552 if err != nil {
15601553 return c.fail(protocol.ExitUsage, "%v", err)
15611554 }
@@ -1564,7 +1557,7 @@ func runMRRangeDiff(c *Ctx, args []string) int {
15641557 return code
15651558 }
15661559 if len(f.Pos) != 2 {
1567 return c.fail(protocol.ExitUsage, "usage: %s", usage)
1560 return c.usage()
15681561 }
15691562 revs, err := mrRevisions(c, mr)
15701563 if err != nil {
internal/control/notifications.go +7 −9
@@ -168,7 +168,7 @@ func emitNotificationSettings(c *Ctx) int {
168168
169169func runNotificationsSettingsShow(c *Ctx, args []string) int {
170170 if len(args) != 0 {
171 return c.fail(protocol.ExitUsage, "usage: notifications settings show")
171 return c.usage()
172172 }
173173 return emitNotificationSettings(c)
174174}
@@ -177,7 +177,7 @@ func runNotificationsSettingsShow(c *Ctx, args []string) int {
177177// inbox is filed either way, the mail half consults it (#194).
178178func runNotificationsSettingsMail(c *Ctx, args []string) int {
179179 if len(args) != 1 || (args[0] != "on" && args[0] != "off") {
180 return c.fail(protocol.ExitUsage, "usage: notifications settings mail on|off")
180 return c.usage()
181181 }
182182 if err := c.Store.SetMailEnabled(c.User.ID, args[0] == "on"); err != nil {
183183 return c.fail(protocol.ExitFailure, "%v", err)
@@ -190,7 +190,7 @@ func runNotificationsSettingsMail(c *Ctx, args []string) int {
190190// delivered, so a grant or a revoke needs no watch row of its own (#194).
191191func runNotificationsSettingsWatch(c *Ctx, args []string) int {
192192 if len(args) != 1 || (args[0] != "on" && args[0] != "off") {
193 return c.fail(protocol.ExitUsage, "usage: notifications settings watch on|off")
193 return c.usage()
194194 }
195195 if err := c.Store.SetWatchEnabled(c.User.ID, args[0] == "on"); err != nil {
196196 return c.fail(protocol.ExitFailure, "%v", err)
@@ -202,12 +202,11 @@ func runNotificationsSettingsWatch(c *Ctx, args []string) int {
202202const noticesDefaultLimit = 50
203203
204204func runNotificationsList(c *Ctx, args []string) int {
205 const usage = "notifications list [--all] [--limit <n>] [--cursor <c>]"
206205 rest, p, code := parsePageFlags(c, args, "notifications", true)
207206 if code >= 0 {
208207 return code
209208 }
210 fl, err := parseFlags(rest, flagSpec{Bools: []string{"--all"}, Usage: usage})
209 fl, err := parseFlags(rest, flagSpec{Bools: []string{"--all"}, Usage: c.Cmd.Usage})
211210 if err != nil {
212211 return c.fail(protocol.ExitUsage, "%v", err)
213212 }
@@ -249,15 +248,14 @@ func runNotificationsList(c *Ctx, args []string) int {
249248}
250249
251250func runNotificationsRead(c *Ctx, args []string) int {
252 const usage = "notifications read <id>... | --all"
253 fl, err := parseFlags(args, flagSpec{Bools: []string{"--all"}, MaxPos: -1, Usage: usage})
251 fl, err := parseFlags(args, flagSpec{Bools: []string{"--all"}, MaxPos: -1, Usage: c.Cmd.Usage})
254252 if err != nil {
255253 return c.fail(protocol.ExitUsage, "%v", err)
256254 }
257255 // --all and a list of ids are two ways of saying which rows: taking
258256 // both would leave which one won unstated.
259257 if fl.Has("--all") == (len(fl.Pos) > 0) {
260 return c.fail(protocol.ExitUsage, "usage: %s", usage)
258 return c.usage()
261259 }
262260 var ids []int64
263261 for _, a := range fl.Pos {
@@ -285,7 +283,7 @@ func runRepoUnwatch(c *Ctx, args []string) int { return setWatch(c, args, "unwat
285283// same way.
286284func setWatch(c *Ctx, args []string, verb, state string) int {
287285 if len(args) != 1 {
288 return c.fail(protocol.ExitUsage, "usage: repo %s <owner/name>", verb)
286 return c.usage()
289287 }
290288 repo, code := resolveRepo(c, args[0], policy.CanRead)
291289 if code >= 0 {
internal/control/org.go +8 −8
@@ -60,7 +60,7 @@ func orgAdmin(c *Ctx, name string) (store.Org, int) {
6060
6161func runOrgCreate(c *Ctx, args []string) int {
6262 if len(args) != 1 {
63 return c.fail(protocol.ExitUsage, "usage: org create <name>")
63 return c.usage()
6464 }
6565 if err := policy.ValidateOwnerName(args[0]); err != nil {
6666 return c.failInput(err)
@@ -95,7 +95,7 @@ func runOrgList(c *Ctx, args []string) int {
9595
9696func runOrgShow(c *Ctx, args []string) int {
9797 if len(args) != 1 {
98 return c.fail(protocol.ExitUsage, "usage: org show <name>")
98 return c.usage()
9999 }
100100 org, err := c.Store.OrgByName(args[0])
101101 if errors.Is(err, store.ErrNotFound) {
@@ -130,7 +130,7 @@ func runOrgShow(c *Ctx, args []string) int {
130130
131131func runOrgRename(c *Ctx, args []string) int {
132132 if len(args) != 2 {
133 return c.fail(protocol.ExitUsage, "usage: org rename <old> <new>")
133 return c.usage()
134134 }
135135 org, code := orgAdmin(c, args[0])
136136 if code >= 0 {
@@ -170,11 +170,11 @@ func runOrgDelete(c *Ctx, args []string) int {
170170 } else if name == "" {
171171 name = a
172172 } else {
173 return c.fail(protocol.ExitUsage, "usage: org delete <name> --yes")
173 return c.usage()
174174 }
175175 }
176176 if name == "" {
177 return c.fail(protocol.ExitUsage, "usage: org delete <name> --yes")
177 return c.usage()
178178 }
179179 org, code := orgAdmin(c, name)
180180 if code >= 0 {
@@ -201,7 +201,7 @@ func runOrgMembersAdd(c *Ctx, args []string) int {
201201 role = f.Value("--role")
202202 }
203203 if len(rest) != 2 || (role != "member" && role != "admin") {
204 return c.fail(protocol.ExitUsage, "usage: org members add <org> <user> [--role member|admin]")
204 return c.usage()
205205 }
206206 org, code := orgAdmin(c, rest[0])
207207 if code >= 0 {
@@ -224,7 +224,7 @@ func runOrgMembersAdd(c *Ctx, args []string) int {
224224
225225func runOrgMembersRemove(c *Ctx, args []string) int {
226226 if len(args) != 2 {
227 return c.fail(protocol.ExitUsage, "usage: org members remove <org> <user>")
227 return c.usage()
228228 }
229229 org, code := orgAdmin(c, args[0])
230230 if code >= 0 {
@@ -250,7 +250,7 @@ func runOrgMembersRemove(c *Ctx, args []string) int {
250250
251251func runOrgMembersList(c *Ctx, args []string) int {
252252 if len(args) != 1 {
253 return c.fail(protocol.ExitUsage, "usage: org members list <org>")
253 return c.usage()
254254 }
255255 return runOrgShow(c, args)
256256}
internal/control/orglabel.go +8 −10
@@ -62,15 +62,14 @@ func orgReader(c *Ctx, name string) (store.Org, []int64, int) {
6262}
6363
6464func runOrgLabelSet(c *Ctx, args []string) int {
65 const usage = "usage: org label set <org> <label> [--color rrggbb|'']"
66 f, err := parseFlags(args, flagSpec{Values: []string{"--color"}, MaxPos: 2, Usage: usage})
65 f, err := parseFlags(args, flagSpec{Values: []string{"--color"}, MaxPos: 2, Usage: c.Cmd.Usage})
6766 if err != nil {
6867 return c.fail(protocol.ExitUsage, "%v", err)
6968 }
7069 orgName, name := f.pos(0), f.pos(1)
7170 color, colorSet := strings.ToLower(f.Value("--color")), f.Has("--color")
7271 if orgName == "" || name == "" {
73 return c.fail(protocol.ExitUsage, usage)
72 return c.usage()
7473 }
7574 if name == "" || len(name) > 50 {
7675 return c.fail(protocol.ExitUsage, "a label is 1 to 50 characters")
@@ -118,7 +117,7 @@ func runOrgLabelSet(c *Ctx, args []string) int {
118117
119118func runOrgLabelList(c *Ctx, args []string) int {
120119 if len(args) != 1 {
121 return c.fail(protocol.ExitUsage, "usage: org label list <org>")
120 return c.usage()
122121 }
123122 org, readable, code := orgReader(c, args[0])
124123 if code >= 0 {
@@ -137,7 +136,7 @@ func runOrgLabelList(c *Ctx, args []string) int {
137136
138137func runOrgLabelRemove(c *Ctx, args []string) int {
139138 if len(args) != 2 {
140 return c.fail(protocol.ExitUsage, "usage: org label remove <org> <label>")
139 return c.usage()
141140 }
142141 org, code := orgAdmin(c, args[0])
143142 if code >= 0 {
@@ -155,14 +154,13 @@ func runOrgLabelRemove(c *Ctx, args []string) int {
155154}
156155
157156func runOrgMilestoneCreate(c *Ctx, args []string) int {
158 const usage = "usage: org milestone create <org> <title> [--description <d>] [--due YYYY-MM-DD]"
159 f, err := parseFlags(args, flagSpec{Values: []string{"--description", "--due"}, MaxPos: 2, Usage: usage})
157 f, err := parseFlags(args, flagSpec{Values: []string{"--description", "--due"}, MaxPos: 2, Usage: c.Cmd.Usage})
160158 if err != nil {
161159 return c.fail(protocol.ExitUsage, "%v", err)
162160 }
163161 orgName, title, description, due := f.pos(0), f.pos(1), f.Value("--description"), f.Value("--due")
164162 if orgName == "" || title == "" {
165 return c.fail(protocol.ExitUsage, usage)
163 return c.usage()
166164 }
167165 if due != "" && !duePat.MatchString(due) {
168166 return c.fail(protocol.ExitUsage, "--due must be YYYY-MM-DD")
@@ -197,7 +195,7 @@ func runOrgMilestoneList(c *Ctx, args []string) int {
197195 state = f.Value("--state")
198196 }
199197 if orgName == "" || (state != "open" && state != "closed" && state != "all") {
200 return c.fail(protocol.ExitUsage, "usage: org milestone list <org> [--state open|closed|all]")
198 return c.usage()
201199 }
202200 org, readable, code := orgReader(c, orgName)
203201 if code >= 0 {
@@ -219,7 +217,7 @@ func setOrgMilestoneState(c *Ctx, args []string, state string) int {
219217 verb = "reopen"
220218 }
221219 if len(args) != 2 {
222 return c.fail(protocol.ExitUsage, "usage: org milestone %s <org> <title>", verb)
220 return c.usage()
223221 }
224222 org, code := orgAdmin(c, args[0])
225223 if code >= 0 {
internal/control/pagescmd.go +4 −4
@@ -70,7 +70,7 @@ func challengeRecord(domain, token string) (name, value string) {
7070
7171func runDomainAdd(c *Ctx, args []string) int {
7272 if len(args) != 2 {
73 return c.fail(protocol.ExitUsage, "usage: repo domain add <owner/name> <domain>")
73 return c.usage()
7474 }
7575 domain := strings.ToLower(args[1])
7676 if err := validatePageDomain(c, domain); err != nil {
@@ -121,7 +121,7 @@ func lookupTXT(name string) ([]string, error) {
121121
122122func runDomainVerify(c *Ctx, args []string) int {
123123 if len(args) != 2 {
124 return c.fail(protocol.ExitUsage, "usage: repo domain verify <owner/name> <domain>")
124 return c.usage()
125125 }
126126 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
127127 if code >= 0 {
@@ -167,7 +167,7 @@ func runDomainVerify(c *Ctx, args []string) int {
167167
168168func runDomainRemove(c *Ctx, args []string) int {
169169 if len(args) != 2 {
170 return c.fail(protocol.ExitUsage, "usage: repo domain remove <owner/name> <domain>")
170 return c.usage()
171171 }
172172 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
173173 if code >= 0 {
@@ -187,7 +187,7 @@ func runDomainRemove(c *Ctx, args []string) int {
187187
188188func runDomainList(c *Ctx, args []string) int {
189189 if len(args) != 1 {
190 return c.fail(protocol.ExitUsage, "usage: repo domain list <owner/name>")
190 return c.usage()
191191 }
192192 repo, code := resolveRepo(c, args[0], policy.CanRead)
193193 if code >= 0 {
internal/control/profile.go +3 −5
@@ -257,7 +257,7 @@ func runProfileShow(c *Ctx, args []string) int {
257257 if len(args) == 1 {
258258 name = args[0]
259259 } else if len(args) > 1 {
260 return c.fail(protocol.ExitUsage, "usage: profile show [name]")
260 return c.usage()
261261 }
262262 kind, id := "", int64(0)
263263 if u, err := c.Store.UserByUsername(name); err == nil {
@@ -360,8 +360,7 @@ func runProfileSet(c *Ctx, args []string) int {
360360 return c.failInput(err)
361361 }
362362 if len(rest) != 0 {
363 return c.fail(protocol.ExitUsage,
364 "usage: profile set [--description <d>] [--website <url>] [--about <text>|--file -] [--about-format md|org] [--link <label|url>]...")
363 return c.usage()
365364 }
366365 if e.empty() {
367366 return c.fail(protocol.ExitUsage, "nothing to set: pass --description, --website, --about and/or --link")
@@ -388,8 +387,7 @@ func runOrgProfile(c *Ctx, args []string) int {
388387 return c.failInput(err)
389388 }
390389 if len(rest) != 1 {
391 return c.fail(protocol.ExitUsage,
392 "usage: org profile <org> [--description <d>] [--website <url>] [--about <text>|--file -] [--about-format md|org] [--link <label|url>]...")
390 return c.usage()
393391 }
394392 name := rest[0]
395393 if e.empty() {
internal/control/quota.go +2 −2
@@ -93,7 +93,7 @@ func runAdminUserLimits(c *Ctx, args []string) int {
9393 return code
9494 }
9595 if len(args) < 1 {
96 return c.fail(protocol.ExitUsage, "usage: admin user limits <username> [--repos <n>|default] [--bytes <n>|default]")
96 return c.usage()
9797 }
9898 u, err := c.Store.UserByUsername(args[0])
9999 if err != nil {
@@ -116,7 +116,7 @@ func runAdminUserLimits(c *Ctx, args []string) int {
116116 case "--bytes":
117117 target = &l.Bytes
118118 default:
119 return c.fail(protocol.ExitUsage, "usage: admin user limits <username> [--repos <n>|default] [--bytes <n>|default]")
119 return c.usage()
120120 }
121121 if v == "default" {
122122 *target = nil
internal/control/read.go +8 −11
@@ -48,7 +48,7 @@ func init() {
4848
4949func runRepoRefs(c *Ctx, args []string) int {
5050 if len(args) != 1 {
51 return c.fail(protocol.ExitUsage, "usage: repo refs <owner/name>")
51 return c.usage()
5252 }
5353 repo, code := resolveRepo(c, args[0], policy.CanRead)
5454 if code >= 0 {
@@ -93,8 +93,7 @@ func runRepoRefs(c *Ctx, args []string) int {
9393const BlameSpan = 1000
9494
9595func runRepoBlame(c *Ctx, args []string) int {
96 const usage = "repo blame <owner/name> <path> [--ref <ref>] [--from <n>] [--to <n>]"
97 f, err := parseFlags(args, flagSpec{Values: []string{"--ref", "--from", "--to"}, MaxPos: -1, Usage: usage})
96 f, err := parseFlags(args, flagSpec{Values: []string{"--ref", "--from", "--to"}, MaxPos: -1, Usage: c.Cmd.Usage})
9897 if err != nil {
9998 return c.fail(protocol.ExitUsage, "%v", err)
10099 }
@@ -111,7 +110,7 @@ func runRepoBlame(c *Ctx, args []string) int {
111110 *dst = n
112111 }
113112 if len(rest) != 2 {
114 return c.fail(protocol.ExitUsage, "usage: %s", usage)
113 return c.usage()
115114 }
116115 repo, code := resolveRepo(c, rest[0], policy.CanRead)
117116 if code >= 0 {
@@ -204,7 +203,7 @@ func runRepoBlame(c *Ctx, args []string) int {
204203// off argv. Positionals are returned in order so each command can name them
205204// in its own usage message.
206205func readArgs(c *Ctx, args []string, usage string, maxPos int) (pos []string, ref string, code int) {
207 f, err := parseFlags(args, flagSpec{Values: []string{"--ref"}, MaxPos: maxPos, Usage: usage})
206 f, err := parseFlags(args, flagSpec{Values: []string{"--ref"}, MaxPos: maxPos, Usage: c.Cmd.Usage})
208207 if err != nil {
209208 return nil, "", c.fail(protocol.ExitUsage, "%v", err)
210209 }
@@ -239,13 +238,12 @@ type entryOut struct {
239238}
240239
241240func runRepoTree(c *Ctx, args []string) int {
242 const usage = "repo tree <owner/name> [<path>] [--ref <ref>]"
243 pos, ref, code := readArgs(c, args, usage, 2)
241 pos, ref, code := readArgs(c, args, c.Cmd.Usage, 2)
244242 if code >= 0 {
245243 return code
246244 }
247245 if len(pos) == 0 {
248 return c.fail(protocol.ExitUsage, "usage: %s", usage)
246 return c.usage()
249247 }
250248 repo, code := resolveRepo(c, pos[0], policy.CanRead)
251249 if code >= 0 {
@@ -303,13 +301,12 @@ func sizeCol(e entryOut) string {
303301}
304302
305303func runRepoCat(c *Ctx, args []string) int {
306 const usage = "repo cat <owner/name> <path> [--ref <ref>]"
307 pos, ref, code := readArgs(c, args, usage, 2)
304 pos, ref, code := readArgs(c, args, c.Cmd.Usage, 2)
308305 if code >= 0 {
309306 return code
310307 }
311308 if len(pos) != 2 {
312 return c.fail(protocol.ExitUsage, "usage: %s", usage)
309 return c.usage()
313310 }
314311 repo, code := resolveRepo(c, pos[0], policy.CanRead)
315312 if code >= 0 {
internal/control/register.go +5 −5
@@ -45,7 +45,7 @@ func init() {
4545
4646func runEmailList(c *Ctx, args []string) int {
4747 if len(args) != 0 {
48 return c.fail(protocol.ExitUsage, "usage: email list [--json]")
48 return c.usage()
4949 }
5050 emails, err := c.Store.ListEmails(c.User.ID)
5151 if err != nil {
@@ -89,7 +89,7 @@ func emailErr(c *Ctx, verb string, err error) int {
8989
9090func runEmailRemove(c *Ctx, args []string) int {
9191 if len(args) != 1 {
92 return c.fail(protocol.ExitUsage, "usage: email remove <address>")
92 return c.usage()
9393 }
9494 if err := c.Store.RemoveEmail(c.User.ID, args[0]); err != nil {
9595 return emailErr(c, "removing address", err)
@@ -101,7 +101,7 @@ func runEmailRemove(c *Ctx, args []string) int {
101101
102102func runEmailPrimary(c *Ctx, args []string) int {
103103 if len(args) != 1 {
104 return c.fail(protocol.ExitUsage, "usage: email primary <address>")
104 return c.usage()
105105 }
106106 if err := c.Store.SetPrimaryEmail(c.User.ID, args[0]); err != nil {
107107 return emailErr(c, "setting primary", err)
@@ -136,7 +136,7 @@ const maxEmailAddsPerHour = 5
136136
137137func runEmailAdd(c *Ctx, args []string) int {
138138 if len(args) != 1 || !strings.Contains(args[0], "@") {
139 return c.fail(protocol.ExitUsage, "usage: email add <address>")
139 return c.usage()
140140 }
141141 if c.Cfg.Mail.SMTPHost == "" {
142142 return c.fail(protocol.ExitFailure, "this instance has no SMTP configured; ask an admin to verify the address (gitbayd admin email verify)")
@@ -161,7 +161,7 @@ func runEmailAdd(c *Ctx, args []string) int {
161161
162162func runEmailVerify(c *Ctx, args []string) int {
163163 if len(args) != 1 {
164 return c.fail(protocol.ExitUsage, "usage: email verify <code>")
164 return c.usage()
165165 }
166166 hash := store.HashToken(args[0])
167167 address, err := c.Store.ConsumeEmailToken(c.User.ID, hash)
internal/control/release.go +8 −10
@@ -76,15 +76,14 @@ func releaseRef(c *Ctx, args []string, perm func(store.User, store.Repo, string)
7676}
7777
7878func runReleaseCreate(c *Ctx, args []string) int {
79 const usage = "usage: release create <owner/name> <tag> [--title <t>] [--notes <n> | --file -] [--format md|org]"
80 f, err := parseFlags(args, flagSpec{Values: []string{"--title", "--notes", "--file", "--format"}, MaxPos: 2, Usage: usage})
79 f, err := parseFlags(args, flagSpec{Values: []string{"--title", "--notes", "--file", "--format"}, MaxPos: 2, Usage: c.Cmd.Usage})
8180 if err != nil {
8281 return c.fail(protocol.ExitUsage, "%v", err)
8382 }
8483 path, tag := f.pos(0), f.pos(1)
8584 title, notes, file, format := f.Value("--title"), f.Value("--notes"), f.Value("--file"), f.Value("--format")
8685 if path == "" || tag == "" {
87 return c.fail(protocol.ExitUsage, usage)
86 return c.usage()
8887 }
8988 fmtName, err := markupFormat(format)
9089 if err != nil {
@@ -149,8 +148,7 @@ func releaseToOut(r store.Release, withNotes bool) releaseOut {
149148}
150149
151150func runReleaseEdit(c *Ctx, args []string) int {
152 const usage = "usage: release edit <owner/name> <tag> [--title <t>] [--notes <n> | --file -] [--format md|org]"
153 f, err := parseFlags(args, flagSpec{Values: []string{"--title", "--notes", "--file", "--format"}, MaxPos: 2, Usage: usage})
151 f, err := parseFlags(args, flagSpec{Values: []string{"--title", "--notes", "--file", "--format"}, MaxPos: 2, Usage: c.Cmd.Usage})
154152 if err != nil {
155153 return c.fail(protocol.ExitUsage, "%v", err)
156154 }
@@ -162,7 +160,7 @@ func runReleaseEdit(c *Ctx, args []string) int {
162160 return c.failInput(err)
163161 }
164162 if path == "" || tag == "" || (!setTitle && !setNotes && fmtName == "") {
165 return c.fail(protocol.ExitUsage, usage)
163 return c.usage()
166164 }
167165 repo, code := resolveRepo(c, path, policy.CanWrite)
168166 if code >= 0 {
@@ -200,7 +198,7 @@ func runReleaseEdit(c *Ctx, args []string) int {
200198
201199func runReleaseList(c *Ctx, args []string) int {
202200 if len(args) != 1 {
203 return c.fail(protocol.ExitUsage, "usage: release list <owner/name>")
201 return c.usage()
204202 }
205203 repo, code := resolveRepo(c, args[0], policy.CanRead)
206204 if code >= 0 {
@@ -267,7 +265,7 @@ func runReleaseDelete(c *Ctx, args []string) int {
267265
268266func runAssetAdd(c *Ctx, args []string) int {
269267 if len(args) != 3 {
270 return c.fail(protocol.ExitUsage, "usage: release asset add <owner/name> <tag> <filename> < file")
268 return c.usage()
271269 }
272270 repo, rel, code := releaseRef(c, args[:2], policy.CanWrite)
273271 if code >= 0 {
@@ -319,7 +317,7 @@ func runAssetAdd(c *Ctx, args []string) int {
319317
320318func runAssetGet(c *Ctx, args []string) int {
321319 if len(args) != 3 {
322 return c.fail(protocol.ExitUsage, "usage: release asset get <owner/name> <tag> <filename> > file")
320 return c.usage()
323321 }
324322 repo, rel, code := releaseRef(c, args[:2], policy.CanRead)
325323 if code >= 0 {
@@ -342,7 +340,7 @@ func runAssetGet(c *Ctx, args []string) int {
342340
343341func runAssetRemove(c *Ctx, args []string) int {
344342 if len(args) != 3 {
345 return c.fail(protocol.ExitUsage, "usage: release asset remove <owner/name> <tag> <filename>")
343 return c.usage()
346344 }
347345 repo, rel, code := releaseRef(c, args[:2], policy.CanWrite)
348346 if code >= 0 {
internal/control/repo.go +27 −35
@@ -181,7 +181,7 @@ func runRepoCreate(c *Ctx, args []string) int {
181181 }
182182 owner, name, ok := strings.Cut(path, "/")
183183 if !ok {
184 return c.fail(protocol.ExitUsage, "usage: repo create <owner/name> [--private]")
184 return c.usage()
185185 }
186186 if err := policyValidateRepoName(name); err != nil {
187187 return c.failInput(err)
@@ -247,7 +247,7 @@ func runRepoList(c *Ctx, args []string) int {
247247 return code
248248 }
249249 if len(args) != 0 {
250 return c.fail(protocol.ExitUsage, "usage: repo list [--limit <n>] [--cursor <c>]")
250 return c.usage()
251251 }
252252 repos, err := c.Store.ListReposForUser(c.User.ID, p.queryLimit(), p.key)
253253 if err != nil {
@@ -278,7 +278,7 @@ func runRepoList(c *Ctx, args []string) int {
278278
279279func runRepoShow(c *Ctx, args []string) int {
280280 if len(args) != 1 {
281 return c.fail(protocol.ExitUsage, "usage: repo show <owner/name>")
281 return c.usage()
282282 }
283283 repo, code := resolveRepo(c, args[0], policy.CanRead)
284284 if code >= 0 {
@@ -393,7 +393,7 @@ func runRepoShow(c *Ctx, args []string) int {
393393
394394func runRepoTransfer(c *Ctx, args []string) int {
395395 if len(args) != 2 {
396 return c.fail(protocol.ExitUsage, "usage: repo transfer <owner/name> <new-owner>")
396 return c.usage()
397397 }
398398 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
399399 if code >= 0 {
@@ -452,7 +452,7 @@ func runRepoTransfer(c *Ctx, args []string) int {
452452
453453func runRepoRename(c *Ctx, args []string) int {
454454 if len(args) != 2 {
455 return c.fail(protocol.ExitUsage, "usage: repo rename <owner/name> <new-name>")
455 return c.usage()
456456 }
457457 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
458458 if code >= 0 {
@@ -496,11 +496,11 @@ func runRepoDelete(c *Ctx, args []string) int {
496496 } else if path == "" {
497497 path = a
498498 } else {
499 return c.fail(protocol.ExitUsage, "usage: repo delete <owner/name> --yes")
499 return c.usage()
500500 }
501501 }
502502 if path == "" {
503 return c.fail(protocol.ExitUsage, "usage: repo delete <owner/name> --yes")
503 return c.usage()
504504 }
505505 repo, code := resolveRepo(c, path, policy.CanAdmin)
506506 if code >= 0 {
@@ -540,7 +540,7 @@ func deleteRepo(c *Ctx, repo store.Repo) int {
540540
541541func runAccessGrant(c *Ctx, args []string) int {
542542 if len(args) != 3 || !slices.Contains([]string{"read", "write", "admin"}, args[2]) {
543 return c.fail(protocol.ExitUsage, "usage: repo access grant <owner/name> <user> read|write|admin")
543 return c.usage()
544544 }
545545 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
546546 if code >= 0 {
@@ -559,7 +559,7 @@ func runAccessGrant(c *Ctx, args []string) int {
559559
560560func runAccessRevoke(c *Ctx, args []string) int {
561561 if len(args) != 2 {
562 return c.fail(protocol.ExitUsage, "usage: repo access revoke <owner/name> <user>")
562 return c.usage()
563563 }
564564 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
565565 if code >= 0 {
@@ -581,7 +581,7 @@ func runAccessRevoke(c *Ctx, args []string) int {
581581
582582func runAccessList(c *Ctx, args []string) int {
583583 if len(args) != 1 {
584 return c.fail(protocol.ExitUsage, "usage: repo access list <owner/name>")
584 return c.usage()
585585 }
586586 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
587587 if code >= 0 {
@@ -609,7 +609,7 @@ func runAccessList(c *Ctx, args []string) int {
609609
610610func runSettingsShow(c *Ctx, args []string) int {
611611 if len(args) != 1 {
612 return c.fail(protocol.ExitUsage, "usage: repo settings show <owner/name>")
612 return c.usage()
613613 }
614614 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
615615 if code >= 0 {
@@ -623,7 +623,7 @@ func runSettingsShow(c *Ctx, args []string) int {
623623
624624func runSetDescription(c *Ctx, args []string) int {
625625 if len(args) != 2 {
626 return c.fail(protocol.ExitUsage, "usage: repo settings description <owner/name> <text>")
626 return c.usage()
627627 }
628628 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
629629 if code >= 0 {
@@ -640,7 +640,7 @@ func runSetDescription(c *Ctx, args []string) int {
640640
641641func runSetDefaultBranch(c *Ctx, args []string) int {
642642 if len(args) != 2 {
643 return c.fail(protocol.ExitUsage, "usage: repo settings default-branch <owner/name> <branch>")
643 return c.usage()
644644 }
645645 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
646646 if code >= 0 {
@@ -664,7 +664,7 @@ func runSetDefaultBranch(c *Ctx, args []string) int {
664664
665665func runSetWebsite(c *Ctx, args []string) int {
666666 if len(args) != 2 {
667 return c.fail(protocol.ExitUsage, "usage: repo settings website <owner/name> <url>")
667 return c.usage()
668668 }
669669 site := strings.TrimSpace(args[1])
670670 if err := validateWebsite(site); err != nil {
@@ -691,7 +691,7 @@ func runSetWebsite(c *Ctx, args []string) int {
691691
692692func runSetVisibility(c *Ctx, args []string) int {
693693 if len(args) != 2 || (args[1] != "public" && args[1] != "private") {
694 return c.fail(protocol.ExitUsage, "usage: repo settings visibility <owner/name> public|private")
694 return c.usage()
695695 }
696696 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
697697 if code >= 0 {
@@ -724,7 +724,7 @@ func setRepoVisibility(c *Ctx, repo store.Repo, visibility string) int {
724724
725725func runGitDaemon(c *Ctx, args []string) int {
726726 if len(args) != 2 || (args[1] != "on" && args[1] != "off") {
727 return c.fail(protocol.ExitUsage, "usage: repo settings git-daemon <owner/name> on|off")
727 return c.usage()
728728 }
729729 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
730730 if code >= 0 {
@@ -748,12 +748,8 @@ func runArchive(c *Ctx, args []string) int { return setArchived(c, args, true)
748748func runUnarchive(c *Ctx, args []string) int { return setArchived(c, args, false) }
749749
750750func setArchived(c *Ctx, args []string, archived bool) int {
751 verb := "archive"
752 if !archived {
753 verb = "unarchive"
754 }
755751 if len(args) != 1 {
756 return c.fail(protocol.ExitUsage, "usage: repo %s <owner/name>", verb)
752 return c.usage()
757753 }
758754 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
759755 if code >= 0 {
@@ -782,7 +778,7 @@ func archiveRepo(c *Ctx, repo store.Repo, archived bool) int {
782778
783779func runTopicsList(c *Ctx, args []string) int {
784780 if len(args) != 1 {
785 return c.fail(protocol.ExitUsage, "usage: repo topics <owner/name>")
781 return c.usage()
786782 }
787783 repo, code := resolveRepo(c, args[0], policy.CanRead)
788784 if code >= 0 {
@@ -803,12 +799,8 @@ func runTopicsAdd(c *Ctx, args []string) int { return editTopics(c, args, tru
803799func runTopicsRemove(c *Ctx, args []string) int { return editTopics(c, args, false) }
804800
805801func editTopics(c *Ctx, args []string, add bool) int {
806 verb := "add"
807 if !add {
808 verb = "remove"
809 }
810802 if len(args) < 2 {
811 return c.fail(protocol.ExitUsage, "usage: repo topics %s <owner/name> <topic>...", verb)
803 return c.usage()
812804 }
813805 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
814806 if code >= 0 {
@@ -862,7 +854,7 @@ func editTopics(c *Ctx, args []string, add bool) int {
862854// and topics of every repository the caller can see.
863855func runRepoSearch(c *Ctx, args []string) int {
864856 if len(args) != 1 {
865 return c.fail(protocol.ExitUsage, "usage: repo search <query>")
857 return c.usage()
866858 }
867859 if err := validQuery(args[0]); err != nil {
868860 return c.failInput(err)
@@ -929,7 +921,7 @@ func runRepoGrep(c *Ctx, args []string) int {
929921 }
930922 path, query, ref := f.pos(0), f.pos(1), f.Value("--ref")
931923 if path == "" || query == "" {
932 return c.fail(protocol.ExitUsage, "usage: repo grep <owner/name> <query> [--ref <ref>]")
924 return c.usage()
933925 }
934926 if err := validQuery(query); err != nil {
935927 return c.failInput(err)
@@ -974,7 +966,7 @@ func setPinned(c *Ctx, args []string, pin bool) int {
974966 verb = "unpin"
975967 }
976968 if len(args) != 1 {
977 return c.fail(protocol.ExitUsage, "usage: repo %s <owner/name>", verb)
969 return c.usage()
978970 }
979971 repo, code := resolveRepo(c, args[0], policy.CanRead)
980972 if code >= 0 {
@@ -1008,7 +1000,7 @@ func setBookmarked(c *Ctx, args []string, on bool) int {
10081000 verb = "unbookmark"
10091001 }
10101002 if len(args) != 1 {
1011 return c.fail(protocol.ExitUsage, "usage: repo %s <owner/name>", verb)
1003 return c.usage()
10121004 }
10131005 repo, code := resolveRepo(c, args[0], policy.CanRead)
10141006 if code >= 0 {
@@ -1040,7 +1032,7 @@ type BookmarkOut struct {
10401032
10411033func runRepoBookmarks(c *Ctx, args []string) int {
10421034 if len(args) != 0 {
1043 return c.fail(protocol.ExitUsage, "usage: repo bookmarks")
1035 return c.usage()
10441036 }
10451037 repos, err := c.Store.ListBookmarks(c.User.ID)
10461038 if err != nil {
@@ -1077,7 +1069,7 @@ func runUnprotectTag(c *Ctx, args []string) int { return setProtectTag(c, args,
10771069
10781070func setProtectTag(c *Ctx, args []string, protect bool) int {
10791071 if len(args) != 2 {
1080 return c.fail(protocol.ExitUsage, "usage: repo settings protect-tag|unprotect-tag <owner/name> <glob>")
1072 return c.usage()
10811073 }
10821074 glob := args[1]
10831075 if _, err := path.Match(glob, "x"); err != nil || glob == "" {
@@ -1114,7 +1106,7 @@ func runUnprotect(c *Ctx, args []string) int { return setProtect(c, args, false)
11141106
11151107func setProtect(c *Ctx, args []string, protect bool) int {
11161108 if len(args) != 2 {
1117 return c.fail(protocol.ExitUsage, "usage: repo settings protect|unprotect <owner/name> <branch>")
1109 return c.usage()
11181110 }
11191111 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
11201112 if code >= 0 {
@@ -1149,7 +1141,7 @@ func setProtect(c *Ctx, args []string, protect bool) int {
11491141func runRepoDiff(c *Ctx, args []string) int {
11501142 f, err := parseFlags(args, flagSpec{MaxPos: 3, Usage: "repo diff <owner/name> <base> <head>"})
11511143 if err != nil || len(f.Pos) != 3 {
1152 return c.fail(protocol.ExitUsage, "usage: repo diff <owner/name> <base> <head>")
1144 return c.usage()
11531145 }
11541146 repo, code := resolveRepo(c, f.pos(0), policy.CanRead)
11551147 if code >= 0 {
internal/control/runnerrepo.go +3 −3
@@ -33,7 +33,7 @@ func init() {
3333func runRepoRunnerAdd(c *Ctx, args []string) int {
3434 f, err := parseFlags(args, flagSpec{MaxPos: 1, Usage: "repo runner add <owner/name> < key.pub"})
3535 if err != nil || len(f.Pos) != 1 {
36 return c.fail(protocol.ExitUsage, "usage: repo runner add <owner/name> < key.pub")
36 return c.usage()
3737 }
3838 repo, code := resolveRepo(c, f.Pos[0], policy.CanAdmin)
3939 if code >= 0 {
@@ -95,7 +95,7 @@ func runRepoRunnerAdd(c *Ctx, args []string) int {
9595
9696func runRepoRunnerList(c *Ctx, args []string) int {
9797 if len(args) != 1 {
98 return c.fail(protocol.ExitUsage, "usage: repo runner list <owner/name>")
98 return c.usage()
9999 }
100100 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
101101 if code >= 0 {
@@ -125,7 +125,7 @@ func runRepoRunnerList(c *Ctx, args []string) int {
125125
126126func runRepoRunnerRemove(c *Ctx, args []string) int {
127127 if len(args) != 2 {
128 return c.fail(protocol.ExitUsage, "usage: repo runner remove <owner/name> <fingerprint>")
128 return c.usage()
129129 }
130130 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
131131 if code >= 0 {
internal/control/search.go +2 −3
@@ -77,13 +77,12 @@ func Search(st *store.Store, root string, userID int64, q string, kinds []string
7777}
7878
7979func runSearch(c *Ctx, args []string) int {
80 const usage = "search <query> [--kind repo|issue|mr]"
81 f, err := parseFlags(args, flagSpec{Multi: []string{"--kind"}, MaxPos: 1, Usage: usage})
80 f, err := parseFlags(args, flagSpec{Multi: []string{"--kind"}, MaxPos: 1, Usage: c.Cmd.Usage})
8281 if err != nil {
8382 return c.fail(protocol.ExitUsage, "%v", err)
8483 }
8584 if len(f.Pos) != 1 {
86 return c.fail(protocol.ExitUsage, "usage: %s", usage)
85 return c.usage()
8786 }
8887 if err := validQuery(f.Pos[0]); err != nil {
8988 return c.failInput(err)
internal/control/sig.go +4 −5
@@ -37,7 +37,7 @@ func init() {
3737
3838func runPGPAdd(c *Ctx, args []string) int {
3939 if len(args) != 0 {
40 return c.fail(protocol.ExitUsage, "usage: pgp add < key.asc")
40 return c.usage()
4141 }
4242 raw, err := io.ReadAll(io.LimitReader(c.Stdin, 1<<20))
4343 if err != nil {
@@ -88,7 +88,7 @@ func runPGPList(c *Ctx, args []string) int {
8888
8989func runPGPRemove(c *Ctx, args []string) int {
9090 if len(args) != 1 {
91 return c.fail(protocol.ExitUsage, "usage: pgp remove <fingerprint>")
91 return c.usage()
9292 }
9393 if err := c.Store.RemovePGPKey(c.User.ID, args[0]); err != nil {
9494 if errors.Is(err, store.ErrNotFound) {
@@ -140,7 +140,7 @@ func runRepoLog(c *Ctx, args []string) int {
140140 limit = n
141141 }
142142 if path == "" {
143 return c.fail(protocol.ExitUsage, "usage: repo log <owner/name> [--ref <r>] [--limit n] [--path <file>]")
143 return c.usage()
144144 }
145145 repo, code := resolveRepo(c, path, policy.CanRead)
146146 if code >= 0 {
@@ -221,9 +221,8 @@ func runRepoLog(c *Ctx, args []string) int {
221221// statuses, and its patch. The web's commit page read these straight from
222222// git, which is why no other surface could open a commit.
223223func runRepoCommit(c *Ctx, args []string) int {
224 const usage = "repo commit <owner/name> <sha>"
225224 if len(args) != 2 {
226 return c.fail(protocol.ExitUsage, "usage: %s", usage)
225 return c.usage()
227226 }
228227 repo, code := resolveRepo(c, args[0], policy.CanRead)
229228 if code >= 0 {
internal/control/snippet.go +10 −12
@@ -132,14 +132,13 @@ func newSnippetID() string {
132132}
133133
134134func runSnippetCreate(c *Ctx, args []string) int {
135 const usage = "usage: snippet create <filename> [--description <d>] [--visibility public|unlisted|private] < file"
136 f, err := parseFlags(args, flagSpec{Values: []string{"--description", "--visibility"}, MaxPos: 1, Usage: usage})
135 f, err := parseFlags(args, flagSpec{Values: []string{"--description", "--visibility"}, MaxPos: 1, Usage: c.Cmd.Usage})
137136 if err != nil {
138137 return c.fail(protocol.ExitUsage, "%v", err)
139138 }
140139 name := f.pos(0)
141140 if name == "" {
142 return c.fail(protocol.ExitUsage, usage)
141 return c.usage()
143142 }
144143 if code := checkSnippetFileName(c, name); code >= 0 {
145144 return code
@@ -186,7 +185,7 @@ func runSnippetCreate(c *Ctx, args []string) int {
186185
187186func runSnippetShow(c *Ctx, args []string) int {
188187 if len(args) != 1 {
189 return c.fail(protocol.ExitUsage, "usage: snippet show <id>")
188 return c.usage()
190189 }
191190 sn, code := snippetRef(c, args[0], false)
192191 if code >= 0 {
@@ -215,7 +214,7 @@ func runSnippetList(c *Ctx, args []string) int {
215214 return code
216215 }
217216 if len(rest) > 1 {
218 return c.fail(protocol.ExitUsage, "usage: snippet list [<owner>] [--limit n] [--cursor c]")
217 return c.usage()
219218 }
220219 owner := c.User
221220 if len(rest) == 1 {
@@ -253,13 +252,12 @@ func runSnippetList(c *Ctx, args []string) int {
253252}
254253
255254func runSnippetEdit(c *Ctx, args []string) int {
256 const usage = "usage: snippet edit <id> [--description <d>] [--visibility public|unlisted|private]"
257 f, err := parseFlags(args, flagSpec{Values: []string{"--description", "--visibility"}, MaxPos: 1, Usage: usage})
255 f, err := parseFlags(args, flagSpec{Values: []string{"--description", "--visibility"}, MaxPos: 1, Usage: c.Cmd.Usage})
258256 if err != nil {
259257 return c.fail(protocol.ExitUsage, "%v", err)
260258 }
261259 if f.pos(0) == "" || (!f.Has("--description") && !f.Has("--visibility")) {
262 return c.fail(protocol.ExitUsage, usage)
260 return c.usage()
263261 }
264262 sn, code := snippetRef(c, f.pos(0), true)
265263 if code >= 0 {
@@ -289,7 +287,7 @@ func runSnippetEdit(c *Ctx, args []string) int {
289287
290288func runSnippetDelete(c *Ctx, args []string) int {
291289 if len(args) != 1 {
292 return c.fail(protocol.ExitUsage, "usage: snippet delete <id>")
290 return c.usage()
293291 }
294292 sn, code := snippetRef(c, args[0], true)
295293 if code >= 0 {
@@ -305,7 +303,7 @@ func runSnippetDelete(c *Ctx, args []string) int {
305303
306304func runSnippetFileSet(c *Ctx, args []string) int {
307305 if len(args) != 2 {
308 return c.fail(protocol.ExitUsage, "usage: snippet file set <id> <filename> < file")
306 return c.usage()
309307 }
310308 sn, code := snippetRef(c, args[0], true)
311309 if code >= 0 {
@@ -336,7 +334,7 @@ func runSnippetFileSet(c *Ctx, args []string) int {
336334
337335func runSnippetFileGet(c *Ctx, args []string) int {
338336 if len(args) != 2 {
339 return c.fail(protocol.ExitUsage, "usage: snippet file get <id> <filename> > file")
337 return c.usage()
340338 }
341339 sn, code := snippetRef(c, args[0], false)
342340 if code >= 0 {
@@ -360,7 +358,7 @@ func runSnippetFileGet(c *Ctx, args []string) int {
360358
361359func runSnippetFileRemove(c *Ctx, args []string) int {
362360 if len(args) != 2 {
363 return c.fail(protocol.ExitUsage, "usage: snippet file remove <id> <filename>")
361 return c.usage()
364362 }
365363 sn, code := snippetRef(c, args[0], true)
366364 if code >= 0 {
internal/control/status.go +2 −2
@@ -55,7 +55,7 @@ func runStatusSet(c *Ctx, args []string) int {
5555 }
5656 }
5757 if path == "" || sha == "" || context == "" || !validStatusState[state] {
58 return c.fail(protocol.ExitUsage, "usage: status set <owner/name> <sha> --context <c> --state pending|success|failure|error")
58 return c.usage()
5959 }
6060 if url != "" && !strings.HasPrefix(url, "https://") && !strings.HasPrefix(url, "http://") {
6161 return c.fail(protocol.ExitUsage, "--url must be http(s)")
@@ -86,7 +86,7 @@ func runStatusSet(c *Ctx, args []string) int {
8686
8787func runStatusList(c *Ctx, args []string) int {
8888 if len(args) != 2 {
89 return c.fail(protocol.ExitUsage, "usage: status list <owner/name> <sha>")
89 return c.usage()
9090 }
9191 repo, code := resolveRepo(c, args[0], policy.CanRead)
9292 if code >= 0 {
internal/control/teams.go +8 −8
@@ -89,7 +89,7 @@ func teamRef(c *Ctx, org store.Org, name string) (store.Team, int) {
8989
9090func runTeamCreate(c *Ctx, args []string) int {
9191 if len(args) != 2 {
92 return c.fail(protocol.ExitUsage, "usage: org team create <org> <team>")
92 return c.usage()
9393 }
9494 org, code := orgAdminRef(c, args[0])
9595 if code >= 0 {
@@ -108,7 +108,7 @@ func runTeamCreate(c *Ctx, args []string) int {
108108
109109func runTeamDelete(c *Ctx, args []string) int {
110110 if len(args) != 2 {
111 return c.fail(protocol.ExitUsage, "usage: org team delete <org> <team>")
111 return c.usage()
112112 }
113113 org, code := orgAdminRef(c, args[0])
114114 if code >= 0 {
@@ -128,7 +128,7 @@ func runTeamDelete(c *Ctx, args []string) int {
128128
129129func runTeamList(c *Ctx, args []string) int {
130130 if len(args) != 1 {
131 return c.fail(protocol.ExitUsage, "usage: org team list <org>")
131 return c.usage()
132132 }
133133 org, code := orgMemberRef(c, args[0])
134134 if code >= 0 {
@@ -151,7 +151,7 @@ func runTeamList(c *Ctx, args []string) int {
151151
152152func runTeamShow(c *Ctx, args []string) int {
153153 if len(args) != 2 {
154 return c.fail(protocol.ExitUsage, "usage: org team show <org> <team>")
154 return c.usage()
155155 }
156156 org, code := orgMemberRef(c, args[0])
157157 if code >= 0 {
@@ -191,7 +191,7 @@ func editTeamMembers(c *Ctx, args []string, add bool) int {
191191 verb = "remove"
192192 }
193193 if len(args) < 3 {
194 return c.fail(protocol.ExitUsage, "usage: org team %s <org> <team> <user>...", verb)
194 return c.usage()
195195 }
196196 org, code := orgAdminRef(c, args[0])
197197 if code >= 0 {
@@ -233,7 +233,7 @@ func editTeamMembers(c *Ctx, args []string, add bool) int {
233233
234234func runTeamGrant(c *Ctx, args []string) int {
235235 if len(args) != 4 || !slices.Contains([]string{"read", "write", "admin"}, args[3]) {
236 return c.fail(protocol.ExitUsage, "usage: org team grant <org> <team> <owner/name> read|write|admin")
236 return c.usage()
237237 }
238238 org, code := orgAdminRef(c, args[0])
239239 if code >= 0 {
@@ -257,7 +257,7 @@ func runTeamGrant(c *Ctx, args []string) int {
257257
258258func runTeamRevoke(c *Ctx, args []string) int {
259259 if len(args) != 3 {
260 return c.fail(protocol.ExitUsage, "usage: org team revoke <org> <team> <owner/name>")
260 return c.usage()
261261 }
262262 org, code := orgAdminRef(c, args[0])
263263 if code >= 0 {
@@ -284,7 +284,7 @@ func runTeamRevoke(c *Ctx, args []string) int {
284284
285285func runOrgMembersRole(c *Ctx, args []string) int {
286286 if len(args) != 2 || !slices.Contains([]string{"write", "read", "none"}, args[1]) {
287 return c.fail(protocol.ExitUsage, "usage: org settings members-role <org> write|read|none")
287 return c.usage()
288288 }
289289 org, code := orgAdminRef(c, args[0])
290290 if code >= 0 {
internal/control/token.go +2 −2
@@ -48,7 +48,7 @@ func runTokenCreate(c *Ctx, args []string) int {
4848 scope = f.Value("--scope")
4949 }
5050 if name == "" || (scope != "full" && scope != "read") {
51 return c.fail(protocol.ExitUsage, "usage: token create --name <n> [--scope full|read] [--ttl 30d]")
51 return c.usage()
5252 }
5353 var expires *time.Time
5454 if ttl != "" {
@@ -108,7 +108,7 @@ func runTokenList(c *Ctx, args []string) int {
108108
109109func runTokenRevoke(c *Ctx, args []string) int {
110110 if len(args) != 1 {
111 return c.fail(protocol.ExitUsage, "usage: token revoke <name>")
111 return c.usage()
112112 }
113113 if err := c.Store.RevokeAPIToken(c.User.ID, args[0]); err != nil {
114114 if errors.Is(err, store.ErrNotFound) {
internal/control/web.go +3 −3
@@ -26,7 +26,7 @@ func init() {
2626
2727func runWebSessionsList(c *Ctx, args []string) int {
2828 if len(args) != 0 {
29 return c.fail(protocol.ExitUsage, "usage: web sessions list")
29 return c.usage()
3030 }
3131 sessions, err := c.Store.ListWebSessions(c.User.ID)
3232 if err != nil {
@@ -41,7 +41,7 @@ func runWebSessionsList(c *Ctx, args []string) int {
4141
4242func runWebSessionsRevoke(c *Ctx, args []string) int {
4343 if len(args) != 1 {
44 return c.fail(protocol.ExitUsage, "usage: web sessions revoke <id>|--all")
44 return c.usage()
4545 }
4646 if args[0] == "--all" {
4747 n, err := c.Store.RevokeAllWebSessions(c.User.ID)
@@ -65,7 +65,7 @@ func runWebSessionsRevoke(c *Ctx, args []string) int {
6565
6666func runWebLogin(c *Ctx, args []string) int {
6767 if len(args) != 0 {
68 return c.fail(protocol.ExitUsage, "usage: web login [--json]")
68 return c.usage()
6969 }
7070 if c.Cfg.Web.Mode != "accounts" {
7171 return c.fail(protocol.ExitDenied,
internal/control/webhook.go +5 −5
@@ -40,7 +40,7 @@ func runWebhookAdd(c *Ctx, args []string) int {
4040 events = f.Value("--events")
4141 }
4242 if path == "" || url == "" {
43 return c.fail(protocol.ExitUsage, "usage: webhook add <owner/name> <url> [--secret <s>] [--events <k1,k2>|*]")
43 return c.usage()
4444 }
4545 repo, code := resolveRepo(c, path, policy.CanAdmin)
4646 if code >= 0 {
@@ -68,7 +68,7 @@ func runWebhookAdd(c *Ctx, args []string) int {
6868
6969func runWebhookList(c *Ctx, args []string) int {
7070 if len(args) != 1 {
71 return c.fail(protocol.ExitUsage, "usage: webhook list <owner/name>")
71 return c.usage()
7272 }
7373 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
7474 if code >= 0 {
@@ -98,7 +98,7 @@ func runWebhookList(c *Ctx, args []string) int {
9898
9999func runWebhookRemove(c *Ctx, args []string) int {
100100 if len(args) != 2 {
101 return c.fail(protocol.ExitUsage, "usage: webhook remove <owner/name> <id>")
101 return c.usage()
102102 }
103103 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
104104 if code >= 0 {
@@ -133,7 +133,7 @@ func runWebhookDeliveries(c *Ctx, args []string) int {
133133 limit = n
134134 }
135135 if path == "" {
136 return c.fail(protocol.ExitUsage, "usage: webhook deliveries <owner/name> [--limit n]")
136 return c.usage()
137137 }
138138 repo, code := resolveRepo(c, path, policy.CanAdmin)
139139 if code >= 0 {
@@ -169,7 +169,7 @@ func runWebhookDeliveries(c *Ctx, args []string) int {
169169
170170func runWebhookRedeliver(c *Ctx, args []string) int {
171171 if len(args) != 2 {
172 return c.fail(protocol.ExitUsage, "usage: webhook redeliver <owner/name> <delivery-id>")
172 return c.usage()
173173 }
174174 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
175175 if code >= 0 {
internal/control/wiki.go +2 −2
@@ -79,7 +79,7 @@ func wikiPages(dir, branch string) []string {
7979
8080func runWikiList(c *Ctx, args []string) int {
8181 if len(args) != 1 {
82 return c.fail(protocol.ExitUsage, "usage: wiki list <owner/name>")
82 return c.usage()
8383 }
8484 repo, dir, branch, code := wikiDir(c, args[0])
8585 if code >= 0 {
@@ -120,7 +120,7 @@ func wikiHome(pages []string) string {
120120
121121func runWikiShow(c *Ctx, args []string) int {
122122 if len(args) < 1 || len(args) > 2 {
123 return c.fail(protocol.ExitUsage, "usage: wiki show <owner/name> [<page>]")
123 return c.usage()
124124 }
125125 repo, dir, branch, code := wikiDir(c, args[0])
126126 if code >= 0 {