Commit 3fc5fe7f24
3fc5fe7f247636086983ad0bbc48a16cd7f0010a
parent: 99ec0bbcaf
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-24 03:39 UTC
control: examples redirect like a shell
Ref #254
internal/control/commitfile.go
+1 −1
| @@ -23,7 +23,7 @@ func init() { |
| 23 | 23 | {"--file", "-", "read the new content from stdin", ""}, |
| 24 | 24 | }, |
| 25 | 25 | Examples: []string{ |
| 26 | | `repo commit-file krz/gitbay CHANGELOG.org --ref main --message "note the release" --file - '< CHANGELOG.org'`, |
| 26 | `repo commit-file krz/gitbay CHANGELOG.org --ref main --message "note the release" --file - < CHANGELOG.org`, |
| 27 | 27 | }, |
| 28 | 28 | ReadsStdin: true, |
| 29 | 29 | Run: runCommitFile, |
internal/control/deploykey.go
+1 −1
| @@ -19,7 +19,7 @@ func init() { |
| 19 | 19 | Flags: []Flag{ |
| 20 | 20 | {"--rw", "", "the key may push, not just fetch", ""}, |
| 21 | 21 | }, |
| 22 | | Examples: []string{"repo deploy-key add krz/gitbay '< key.pub'"}, |
| 22 | Examples: []string{"repo deploy-key add krz/gitbay < key.pub"}, |
| 23 | 23 | ReadsStdin: true, Run: runDeployKeyAdd}) |
| 24 | 24 | register(Command{Path: []string{"repo", "deploy-key", "list"}, |
| 25 | 25 | Summary: "list deploy keys", |
internal/control/diffcomment.go
+1 −1
| @@ -29,7 +29,7 @@ func init() { |
| 29 | 29 | }, |
| 30 | 30 | Examples: []string{ |
| 31 | 31 | `mr diff-comment krz/gitbay 431 --path internal/control/build.go --line 42 --message "why is this a switch"`, |
| 32 | | "mr diff-comment krz/gitbay 431 --reply 12 --file - '< notes.md'", |
| 32 | "mr diff-comment krz/gitbay 431 --reply 12 --file - < notes.md", |
| 33 | 33 | }, |
| 34 | 34 | ReadsStdin: true, Run: runDiffComment}) |
| 35 | 35 | register(Command{Path: []string{"mr", "threads"}, |
internal/control/explore.go
+1 −1
| @@ -31,7 +31,7 @@ func init() { |
| 31 | 31 | Flags: []Flag{ |
| 32 | 32 | {"--ref", "<r>", "branch, tag or commit to archive", ""}, |
| 33 | 33 | }, |
| 34 | | Examples: []string{"repo download krz/gitbay --ref main '> gitbay.tar.gz'"}, |
| 34 | Examples: []string{"repo download krz/gitbay --ref main > gitbay.tar.gz"}, |
| 35 | 35 | ReadOnly: true, |
| 36 | 36 | Run: runRepoDownload, |
| 37 | 37 | }) |
internal/control/help_test.go
+11 −1
| @@ -11,6 +11,12 @@ import ( |
| 11 | 11 | |
| 12 | 12 | var usageFlag = regexp.MustCompile(`--[a-z][a-z0-9-]*`) |
| 13 | 13 | |
| 14 | // trailingRedirect strips a shell redirect an example ends with, the way a |
| 15 | // person's shell would before gitbay ever sees argv: protocol.Tokenize |
| 16 | // rejects a bare < or > as a shell metacharacter, so the redirected part |
| 17 | // is not something the command's own tokenizer parses. |
| 18 | var trailingRedirect = regexp.MustCompile(`^(.*?)\s+[<>]\s*\S+$`) |
| 19 | |
| 14 | 20 | // helpCovered restricts TestHelpIsComplete to the path prefixes whose help |
| 15 | 21 | // text this commit filled in. Task 4.3 fills the rest and removes this set. |
| 16 | 22 | // "org label" and "org milestone" name the two prefixes under "org" this |
| @@ -66,7 +72,11 @@ func TestHelpIsComplete(t *testing.T) { |
| 66 | 72 | t.Errorf("%s: no example", path) |
| 67 | 73 | } |
| 68 | 74 | for _, ex := range cmd.Examples { |
| 69 | | argv, err := protocol.Tokenize(ex) |
| 75 | toParse := ex |
| 76 | if m := trailingRedirect.FindStringSubmatch(ex); m != nil { |
| 77 | toParse = m[1] |
| 78 | } |
| 79 | argv, err := protocol.Tokenize(toParse) |
| 70 | 80 | if err != nil { |
| 71 | 81 | t.Errorf("%s: example %q: %v", path, ex, err) |
| 72 | 82 | continue |
internal/control/issue.go
+3 −3
| @@ -26,7 +26,7 @@ func init() { |
| 26 | 26 | }, |
| 27 | 27 | Examples: []string{ |
| 28 | 28 | `issue create krz/gitbay --title "crash on empty repo" --body "steps to reproduce..."`, |
| 29 | | "issue create krz/gitbay --title notes --file - '< notes.md'", |
| 29 | "issue create krz/gitbay --title notes --file - < notes.md", |
| 30 | 30 | }, |
| 31 | 31 | ReadsStdin: true, Run: runIssueCreate}) |
| 32 | 32 | register(Command{Path: []string{"issue", "list"}, |
| @@ -63,7 +63,7 @@ func init() { |
| 63 | 63 | }, |
| 64 | 64 | Examples: []string{ |
| 65 | 65 | `issue edit krz/gitbay 42 --title "crash on empty repo, take two"`, |
| 66 | | "issue edit krz/gitbay 42 --file - '< notes.md'", |
| 66 | "issue edit krz/gitbay 42 --file - < notes.md", |
| 67 | 67 | }, |
| 68 | 68 | ReadsStdin: true, Run: runIssueEdit}) |
| 69 | 69 | register(Command{Path: []string{"issue", "comment"}, |
| @@ -76,7 +76,7 @@ func init() { |
| 76 | 76 | }, |
| 77 | 77 | Examples: []string{ |
| 78 | 78 | `issue comment krz/gitbay 42 --message "can't reproduce on main"`, |
| 79 | | "issue comment krz/gitbay 42 --file - '< notes.md'", |
| 79 | "issue comment krz/gitbay 42 --file - < notes.md", |
| 80 | 80 | }, |
| 81 | 81 | ReadsStdin: true, Run: runIssueComment}) |
| 82 | 82 | register(Command{Path: []string{"issue", "close"}, |
internal/control/mr.go
+1 −1
| @@ -71,7 +71,7 @@ func init() { |
| 71 | 71 | }, |
| 72 | 72 | Examples: []string{ |
| 73 | 73 | `mr create krz/gitbay --source cli-output-help --target main --title "control: flag help"`, |
| 74 | | "mr create krz/gitbay --source cli-output-help --target main --title notes --file - '< notes.md'", |
| 74 | "mr create krz/gitbay --source cli-output-help --target main --title notes --file - < notes.md", |
| 75 | 75 | }, |
| 76 | 76 | ReadsStdin: true, Run: runMRCreate}) |
| 77 | 77 | register(Command{Path: []string{"mr", "range-diff"}, |
internal/control/release.go
+3 −3
| @@ -30,7 +30,7 @@ func init() { |
| 30 | 30 | }, |
| 31 | 31 | Examples: []string{ |
| 32 | 32 | `release create krz/gitbay v1.31.0 --title "v1.31.0" --notes "flag help"`, |
| 33 | | "release create krz/gitbay v1.31.0 --file - '< notes.md'", |
| 33 | "release create krz/gitbay v1.31.0 --file - < notes.md", |
| 34 | 34 | }, |
| 35 | 35 | ReadsStdin: true, Run: runReleaseCreate}) |
| 36 | 36 | register(Command{Path: []string{"release", "edit"}, |
| @@ -69,12 +69,12 @@ func init() { |
| 69 | 69 | register(Command{Path: []string{"release", "asset", "add"}, |
| 70 | 70 | Summary: "upload an asset from stdin", |
| 71 | 71 | Usage: "release asset add <owner/name> <tag> <filename> < file", |
| 72 | | Examples: []string{"release asset add krz/gitbay v1.30.0 gitbay-darwin-arm64 '< gitbay-darwin-arm64'"}, |
| 72 | Examples: []string{"release asset add krz/gitbay v1.30.0 gitbay-darwin-arm64 < gitbay-darwin-arm64"}, |
| 73 | 73 | ReadsStdin: true, Run: runAssetAdd}) |
| 74 | 74 | register(Command{Path: []string{"release", "asset", "get"}, |
| 75 | 75 | Summary: "write an asset to stdout", |
| 76 | 76 | Usage: "release asset get <owner/name> <tag> <filename> > file", |
| 77 | | Examples: []string{"release asset get krz/gitbay v1.30.0 gitbay-darwin-arm64 '> gitbay-darwin-arm64'"}, |
| 77 | Examples: []string{"release asset get krz/gitbay v1.30.0 gitbay-darwin-arm64 > gitbay-darwin-arm64"}, |
| 78 | 78 | ReadOnly: true, Run: runAssetGet}) |
| 79 | 79 | register(Command{Path: []string{"release", "asset", "remove"}, |
| 80 | 80 | Summary: "remove an asset", |
internal/control/runnerrepo.go
+1 −1
| @@ -21,7 +21,7 @@ func init() { |
| 21 | 21 | register(Command{Path: []string{"repo", "runner", "add"}, |
| 22 | 22 | Summary: "attach a runner's public key to a repository", |
| 23 | 23 | Usage: "repo runner add <owner/name> < key.pub", |
| 24 | | Examples: []string{"repo runner add krz/gitbay '< key.pub'"}, |
| 24 | Examples: []string{"repo runner add krz/gitbay < key.pub"}, |
| 25 | 25 | ReadsStdin: true, Run: runRepoRunnerAdd}) |
| 26 | 26 | register(Command{Path: []string{"repo", "runner", "list"}, |
| 27 | 27 | Summary: "list the runners attached to a repository", |
internal/control/snippet.go
+3 −3
| @@ -25,7 +25,7 @@ func init() { |
| 25 | 25 | {"--description", "<d>", "one line about the snippet", ""}, |
| 26 | 26 | {"--visibility", "public|unlisted|private", "who can find it", "unlisted"}, |
| 27 | 27 | }, |
| 28 | | Examples: []string{"snippet create notes.md --visibility private '< notes.md'"}, |
| 28 | Examples: []string{"snippet create notes.md --visibility private < notes.md"}, |
| 29 | 29 | ReadsStdin: true, Run: runSnippetCreate}) |
| 30 | 30 | register(Command{Path: []string{"snippet", "show"}, |
| 31 | 31 | Summary: "show a snippet's metadata and files", |
| @@ -58,12 +58,12 @@ func init() { |
| 58 | 58 | register(Command{Path: []string{"snippet", "file", "set"}, |
| 59 | 59 | Summary: "add a file to a snippet, or replace one, from stdin", |
| 60 | 60 | Usage: "snippet file set <id> <filename> < file", |
| 61 | | Examples: []string{"snippet file set a1b2c3 notes.md '< notes.md'"}, |
| 61 | Examples: []string{"snippet file set a1b2c3 notes.md < notes.md"}, |
| 62 | 62 | ReadsStdin: true, Run: runSnippetFileSet}) |
| 63 | 63 | register(Command{Path: []string{"snippet", "file", "get"}, |
| 64 | 64 | Summary: "write a snippet file to stdout", |
| 65 | 65 | Usage: "snippet file get <id> <filename> > file", |
| 66 | | Examples: []string{"snippet file get a1b2c3 notes.md '> notes.md'"}, |
| 66 | Examples: []string{"snippet file get a1b2c3 notes.md > notes.md"}, |
| 67 | 67 | ReadOnly: true, Run: runSnippetFileGet}) |
| 68 | 68 | register(Command{Path: []string{"snippet", "file", "remove"}, |
| 69 | 69 | Summary: "remove a file from a snippet", |