Commit 8eb3623130
Verified · cmc
cmd/gitbay/main.go +1 −1
| @@ -561,7 +561,7 @@ func releaseCmd() *cobra.Command { | ||
| 561 | 561 | passOpts{server: []string{"release", "create"}, needsRepo: true, stdinOK: true, editor: "release"}), |
| 562 | 562 | pass("edit", "update title and notes: <tag> [--title <t>] [--notes|--file -]", |
| 563 | 563 | passOpts{server: []string{"release", "edit"}, needsRepo: true, stdinOK: true}), |
| 564 | pass("list", "list releases", passOpts{server: []string{"release", "list"}, needsRepo: true}), | |
| 564 | pass("list", "releases: <owner/name> [--limit <n>] [--cursor <c>]", passOpts{server: []string{"release", "list"}, needsRepo: true}), | |
| 565 | 565 | pass("show", "show a release with assets: <tag>", passOpts{server: []string{"release", "show"}, needsRepo: true}), |
| 566 | 566 | pass("delete", "delete a release and its assets: <tag> --yes", passOpts{server: []string{"release", "delete"}, needsRepo: true}), |
| 567 | 567 | group("asset", "binary assets on a release", |
internal/control/release.go +15 −6
| @@ -28,7 +28,7 @@ func init() { | ||
| 28 | 28 | ReadsStdin: true, Run: runReleaseEdit}) |
| 29 | 29 | register(Command{Path: []string{"release", "list"}, |
| 30 | 30 | Summary: "list releases", |
| 31 | Usage: "release list <owner/name>", ReadOnly: true, Run: runReleaseList}) | |
| 31 | Usage: "release list <owner/name> [--limit <n>] [--cursor <c>]", ReadOnly: true, Run: runReleaseList}) | |
| 32 | 32 | register(Command{Path: []string{"release", "show"}, |
| 33 | 33 | Summary: "show a release with assets", |
| 34 | 34 | Usage: "release show <owner/name> <tag>", ReadOnly: true, Run: runReleaseShow}) |
| @@ -197,24 +197,33 @@ func runReleaseEdit(c *Ctx, args []string) int { | ||
| 197 | 197 | } |
| 198 | 198 | |
| 199 | 199 | func runReleaseList(c *Ctx, args []string) int { |
| 200 | if len(args) != 1 { | |
| 200 | rest, p, code := parsePageFlags(c, args, "release", true) | |
| 201 | if code >= 0 { | |
| 202 | return code | |
| 203 | } | |
| 204 | if len(rest) != 1 { | |
| 201 | 205 | return c.usage() |
| 202 | 206 | } |
| 203 | repo, code := resolveRepo(c, args[0], policy.CanRead) | |
| 207 | repo, code := resolveRepo(c, rest[0], policy.CanRead) | |
| 204 | 208 | if code >= 0 { |
| 205 | 209 | return code |
| 206 | 210 | } |
| 207 | rels, err := c.Store.ListReleases(repo.ID) | |
| 211 | rels, err := c.Store.ListReleasesPage(repo.ID, p.queryLimit(), p.keyInt()) | |
| 208 | 212 | if err != nil { |
| 209 | 213 | return c.fail(protocol.ExitFailure, "%v", err) |
| 210 | 214 | } |
| 215 | rels, next := trimPage(p, rels, "release", func(r store.Release) string { return strconv.FormatInt(r.ID, 10) }) | |
| 211 | 216 | var ds []releaseOut |
| 212 | 217 | for _, r := range rels { |
| 213 | 218 | ds = append(ds, releaseToOut(r, false)) |
| 214 | 219 | } |
| 215 | return c.emit(ds, func(w io.Writer) { | |
| 220 | return c.emitPage(p, ds, next, func(w io.Writer) { | |
| 216 | 221 | for _, d := range ds { |
| 217 | fmt.Fprintf(w, "%s\t%s\t%d asset(s)\n", d.Tag, d.Title, len(d.Assets)) | |
| 222 | title := d.Title | |
| 223 | if title == d.Tag { | |
| 224 | title = "" | |
| 225 | } | |
| 226 | fmt.Fprintf(w, "%s\t%s\t%d asset(s)\n", d.Tag, title, len(d.Assets)) | |
| 218 | 227 | } |
| 219 | 228 | }) |
| 220 | 229 | } |
internal/control/release_test.go added +40
| @@ -0,0 +1,40 @@ | ||
| 1 | package control | |
| 2 | ||
| 3 | import ( | |
| 4 | "bytes" | |
| 5 | "strings" | |
| 6 | "testing" | |
| 7 | ||
| 8 | "gitbay.org/gitbay/internal/protocol" | |
| 9 | "gitbay.org/gitbay/internal/store" | |
| 10 | ) | |
| 11 | ||
| 12 | func TestReleaseListPagesAndHidesTagTitle(t *testing.T) { | |
| 13 | st, repo, uid := newQueueTestRepo(t) | |
| 14 | for _, tag := range []string{"v1", "v2", "v3"} { | |
| 15 | title := tag | |
| 16 | if tag == "v2" { | |
| 17 | title = "Second" | |
| 18 | } | |
| 19 | if _, err := st.CreateRelease(repo.ID, tag, title, "", uid, "md"); err != nil { | |
| 20 | t.Fatal(err) | |
| 21 | } | |
| 22 | } | |
| 23 | c, errOut := pruneCtx(st, t.TempDir(), store.User{ID: uid}) | |
| 24 | if code := Dispatch(c, []string{"release", "list", repo.Path(), "--limit", "2"}); code != protocol.ExitOK { | |
| 25 | t.Fatalf("exit %d: %s", code, errOut) | |
| 26 | } | |
| 27 | lines := strings.Split(strings.TrimSpace(c.Stdout.(*bytes.Buffer).String()), "\n") | |
| 28 | if len(lines) != 3 || lines[0] != "v3\t\t0 asset(s)" || lines[1] != "v2\tSecond\t0 asset(s)" || !strings.HasPrefix(lines[2], "next\t") { | |
| 29 | t.Fatalf("page 1:\n%s", strings.Join(lines, "\n")) | |
| 30 | } | |
| 31 | cursor := strings.TrimPrefix(lines[2], "next\t") | |
| 32 | ||
| 33 | c, errOut = pruneCtx(st, t.TempDir(), store.User{ID: uid}) | |
| 34 | if code := Dispatch(c, []string{"release", "list", repo.Path(), "--cursor", cursor}); code != protocol.ExitOK { | |
| 35 | t.Fatalf("exit %d: %s", code, errOut) | |
| 36 | } | |
| 37 | if got := c.Stdout.(*bytes.Buffer).String(); got != "v1\t\t0 asset(s)\n" { | |
| 38 | t.Fatalf("page 2: %q", got) | |
| 39 | } | |
| 40 | } | |
internal/store/releases.go +18 −1
| @@ -90,7 +90,24 @@ func (s *Store) ReleaseByTag(repoID int64, tag string) (Release, error) { | ||
| 90 | 90 | |
| 91 | 91 | // ListReleases returns releases newest-first, assets included. |
| 92 | 92 | func (s *Store) ListReleases(repoID int64) ([]Release, error) { |
| 93 | rows, err := s.DB.Query(releaseSelect+" WHERE r.repo_id = ? ORDER BY r.created_at DESC", repoID) | |
| 93 | return s.ListReleasesPage(repoID, 0, 0) | |
| 94 | } | |
| 95 | ||
| 96 | // ListReleasesPage lists newest first. limit 0 is every row; afterID is | |
| 97 | // the last release of the previous page, 0 for the first. | |
| 98 | func (s *Store) ListReleasesPage(repoID int64, limit int, afterID int64) ([]Release, error) { | |
| 99 | q := releaseSelect + " WHERE r.repo_id = ?" | |
| 100 | args := []any{repoID} | |
| 101 | if afterID > 0 { | |
| 102 | q += " AND (r.created_at, r.id) < (SELECT created_at, id FROM releases WHERE id = ?)" | |
| 103 | args = append(args, afterID) | |
| 104 | } | |
| 105 | q += " ORDER BY r.created_at DESC, r.id DESC" | |
| 106 | if limit > 0 { | |
| 107 | q += " LIMIT ?" | |
| 108 | args = append(args, limit) | |
| 109 | } | |
| 110 | rows, err := s.DB.Query(q, args...) | |
| 94 | 111 | if err != nil { |
| 95 | 112 | return nil, err |
| 96 | 113 | } |