Commit 77fb535e35
Verified · cmc
Layout: unified · split
internal/control/deps.go +29 −2
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | "errors" | 4 | "errors" |
| 5 | "fmt" | 5 | "fmt" |
| 6 | "io" | 6 | "io" |
| 7 | "strconv" | ||
| 7 | 8 | ||
| 8 | "gitbay.org/gitbay/internal/policy" | 9 | "gitbay.org/gitbay/internal/policy" |
| 9 | "gitbay.org/gitbay/internal/protocol" | 10 | "gitbay.org/gitbay/internal/protocol" |
| @@ -92,9 +93,14 @@ func runDepsStatus(c *Ctx, args []string) int { | |||
| 92 | } | 93 | } |
| 93 | check, err := c.Store.DepCheckFor(repo.ID) | 94 | check, err := c.Store.DepCheckFor(repo.ID) |
| 94 | if errors.Is(err, store.ErrNotFound) { | 95 | if errors.Is(err, store.ErrNotFound) { |
| 95 | return c.emit(map[string]any{"enabled": false}, func(w io.Writer) { | 96 | return c.emitView(map[string]any{"enabled": false}, func(w io.Writer) { |
| 96 | v := c.view(w) | 97 | v := c.view(w) |
| 97 | v.fields("checks", fmt.Sprintf("off (repo deps enable %s)", repo.Path())) | 98 | v.fields("checks", fmt.Sprintf("off (repo deps enable %s)", repo.Path())) |
| 99 | }, func() screen { | ||
| 100 | return screen{ | ||
| 101 | fields: []field{{"Checks", []cell{cText("off")}}}, | ||
| 102 | actions: []action{{"Deps", []string{"repo", "deps", "enable", repo.Path()}}}, | ||
| 103 | } | ||
| 98 | }) | 104 | }) |
| 99 | } | 105 | } |
| 100 | if err != nil { | 106 | if err != nil { |
| @@ -109,7 +115,7 @@ func runDepsStatus(c *Ctx, args []string) int { | |||
| 109 | for _, r := range reports { | 115 | for _, r := range reports { |
| 110 | out.Behind = append(out.Behind, DepBehind{r.Ecosystem, r.Name, r.Current, r.Latest}) | 116 | out.Behind = append(out.Behind, DepBehind{r.Ecosystem, r.Name, r.Current, r.Latest}) |
| 111 | } | 117 | } |
| 112 | return c.emit(out, func(w io.Writer) { | 118 | return c.emitView(out, func(w io.Writer) { |
| 113 | tracked := "" | 119 | tracked := "" |
| 114 | if check.IssueNumber != 0 { | 120 | if check.IssueNumber != 0 { |
| 115 | tracked = fmt.Sprintf("#%d", check.IssueNumber) | 121 | tracked = fmt.Sprintf("#%d", check.IssueNumber) |
| @@ -129,5 +135,26 @@ func runDepsStatus(c *Ctx, args []string) int { | |||
| 129 | } | 135 | } |
| 130 | tb.flush() | 136 | tb.flush() |
| 131 | } | 137 | } |
| 138 | }, func() screen { | ||
| 139 | state := []cell{cGlyph("ok"), cText("on")} | ||
| 140 | if check.LastError != "" { | ||
| 141 | state = []cell{cGlyph("failed"), cText("on")} | ||
| 142 | } | ||
| 143 | s := screen{fields: []field{ | ||
| 144 | {"Checks", state}, | ||
| 145 | {"Last check", []cell{cAge(check.LastCheck)}}, | ||
| 146 | {"Error", []cell{cMark(check.LastError, sgrRed)}}, | ||
| 147 | }} | ||
| 148 | if check.IssueNumber != 0 { | ||
| 149 | n := strconv.FormatInt(check.IssueNumber, 10) | ||
| 150 | s.fields = append(s.fields, field{"Tracked in", []cell{cLink("#"+n, c.siteURL(repo.Path(), "issues", n))}}) | ||
| 151 | } | ||
| 152 | behind := section{title: "Behind", n: len(out.Behind)} | ||
| 153 | for _, b := range out.Behind { | ||
| 154 | behind.rows = append(behind.rows, rowOf(cRef(b.Name), cText(b.Ecosystem), cMeta(b.Current+" → "+b.Latest))) | ||
| 155 | } | ||
| 156 | s.sections = []section{behind} | ||
| 157 | s.actions = []action{{"Deps", []string{"repo", "deps", "disable", repo.Path()}}} | ||
| 158 | return s | ||
| 132 | }) | 159 | }) |
| 133 | } | 160 | } |
internal/control/repo.go +44 −1
| @@ -811,7 +811,7 @@ func runSettingsShow(c *Ctx, args []string) int { | |||
| 811 | if code >= 0 { | 811 | if code >= 0 { |
| 812 | return code | 812 | return code |
| 813 | } | 813 | } |
| 814 | return c.emit(repo.Settings, func(w io.Writer) { | 814 | return c.emitView(repo.Settings, func(w io.Writer) { |
| 815 | v := c.view(w) | 815 | v := c.view(w) |
| 816 | v.title(repo.Path(), "settings", "") | 816 | v.title(repo.Path(), "settings", "") |
| 817 | v.fields( | 817 | v.fields( |
| @@ -824,6 +824,49 @@ func runSettingsShow(c *Ctx, args []string) int { | |||
| 824 | "git daemon", strconv.FormatBool(repo.Settings.GitDaemon), | 824 | "git daemon", strconv.FormatBool(repo.Settings.GitDaemon), |
| 825 | "archived", strconv.FormatBool(repo.Settings.Archived), | 825 | "archived", strconv.FormatBool(repo.Settings.Archived), |
| 826 | ) | 826 | ) |
| 827 | }, func() screen { | ||
| 828 | set := repo.Settings | ||
| 829 | onOff := func(b bool) cell { | ||
| 830 | if b { | ||
| 831 | return cText("on") | ||
| 832 | } | ||
| 833 | return cMeta("off") | ||
| 834 | } | ||
| 835 | approvals := cMeta("off") | ||
| 836 | if set.RequireApprovals > 0 { | ||
| 837 | approvals = cText(strconv.Itoa(set.RequireApprovals)) | ||
| 838 | } | ||
| 839 | list := func(xs []string) cell { | ||
| 840 | if len(xs) == 0 { | ||
| 841 | return cMeta("none") | ||
| 842 | } | ||
| 843 | return cText(strings.Join(xs, ", ")) | ||
| 844 | } | ||
| 845 | mr := "on" | ||
| 846 | if set.RequireMR { | ||
| 847 | mr = "off" | ||
| 848 | } | ||
| 849 | s := screen{fields: []field{ | ||
| 850 | {"Repo", []cell{cLink(repo.Path(), c.siteURL(repo.Path())), cMeta("settings")}}, | ||
| 851 | {"Protected", []cell{list(set.ProtectedBranches)}}, | ||
| 852 | {"Protected tags", []cell{list(set.ProtectedTags)}}, | ||
| 853 | {"Require MR", []cell{onOff(set.RequireMR)}}, | ||
| 854 | {"Require checks", []cell{onOff(set.RequireChecks)}}, | ||
| 855 | {"Contexts", []cell{list(set.RequiredContexts)}}, | ||
| 856 | {"Approvals", []cell{approvals}}, | ||
| 857 | {"Resolved threads", []cell{onOff(set.RequireResolved)}}, | ||
| 858 | {"Code owners", []cell{onOff(set.RequireCodeowners)}}, | ||
| 859 | {"Signed commits", []cell{onOff(set.RequireSignedCommits)}}, | ||
| 860 | {"Git daemon", []cell{onOff(set.GitDaemon)}}, | ||
| 861 | {"Archived", []cell{onOff(set.Archived)}}, | ||
| 862 | }, actions: []action{ | ||
| 863 | {"Settings", []string{"repo", "settings", "protect", repo.Path(), "<branch>"}}, | ||
| 864 | {"Settings", []string{"repo", "settings", "require-mr", repo.Path(), mr}}, | ||
| 865 | }} | ||
| 866 | if set.Website != "" { | ||
| 867 | s.fields = append(s.fields, field{"Website", []cell{cText(set.Website)}}) | ||
| 868 | } | ||
| 869 | return s | ||
| 827 | }) | 870 | }) |
| 828 | } | 871 | } |
| 829 | 872 | ||
internal/control/sig.go +20 −19
| @@ -354,27 +354,28 @@ func runRepoCommit(c *Ctx, args []string) int { | |||
| 354 | fmt.Fprintf(w, "\n%s", d.Diff) | 354 | fmt.Fprintf(w, "\n%s", d.Diff) |
| 355 | return | 355 | return |
| 356 | } | 356 | } |
| 357 | v := c.view(w) | 357 | short, url := d.SHA[:min(10, len(d.SHA))], c.siteURL(repo.OwnerName, repo.Name, "commit", d.SHA[:min(12, len(d.SHA))]) |
| 358 | v.title(d.SHA[:10], d.Subject, d.Signature.State) | 358 | s := screen{body: body, format: "text", fields: []field{ |
| 359 | signer := d.Signature.Signer | 359 | {"Commit", []cell{cLink(short, url), cText(d.Subject)}}, |
| 360 | if signer != "" && d.Signature.Fingerprint != "" { | 360 | {"Author", []cell{cText(fmt.Sprintf("%s <%s>", d.AuthorName, d.AuthorEmail)), cAge(d.Date)}}, |
| 361 | signer += ", key " + d.Signature.Fingerprint | 361 | }} |
| 362 | if d.CommitterEmail != "" && d.CommitterEmail != d.AuthorEmail { | ||
| 363 | s.fields = append(s.fields, field{"Committer", []cell{cText(d.CommitterEmail)}}) | ||
| 362 | } | 364 | } |
| 363 | v.fields( | 365 | s.fields = append(s.fields, field{"Signature", []cell{cGlyph(d.Signature.State), cState(d.Signature.State), cMeta(d.Signature.Signer, d.Signature.Fingerprint)}}) |
| 364 | "author", fmt.Sprintf("%s <%s>, %s", d.AuthorName, d.AuthorEmail, c.when(d.Date)), | 366 | if !c.Term.Links { |
| 365 | "committer", d.CommitterEmail, | 367 | s.fields = append(s.fields, field{"URL", []cell{cText(url)}}) |
| 366 | "signer", signer, | 368 | } |
| 367 | "url", c.siteURL(repo.OwnerName, repo.Name, "commit", d.SHA[:12]), | 369 | checks := section{title: "Checks", n: len(d.Checks)} |
| 368 | ) | 370 | for _, ch := range d.Checks { |
| 369 | v.text(body) | 371 | checks.rows = append(checks.rows, rowOf(cGlyph(ch.State), cFlex(ch.Context))) |
| 370 | if len(d.Checks) > 0 { | 372 | } |
| 371 | v.section("checks") | 373 | s.sections = []section{checks} |
| 372 | tb := c.table(w, "CHECK", "STATE") | 374 | s.actions = []action{ |
| 373 | for _, ch := range d.Checks { | 375 | {"Read", []string{"repo", "log", repo.Path(), "--ref", short}}, |
| 374 | tb.row(cText(ch.Context), cState(ch.State)) | 376 | {"Read", []string{"repo", "tree", repo.Path(), "--ref", short}}, |
| 375 | } | ||
| 376 | tb.flush() | ||
| 377 | } | 377 | } |
| 378 | c.render(w, s) | ||
| 378 | fmt.Fprintf(w, "\n%s", c.Term.diff(d.Diff)) | 379 | fmt.Fprintf(w, "\n%s", c.Term.diff(d.Diff)) |
| 379 | }) | 380 | }) |
| 380 | } | 381 | } |
internal/control/stage3repo_test.go +40
| @@ -244,3 +244,43 @@ func TestRepoBrowseScreens(t *testing.T) { | |||
| 244 | } | 244 | } |
| 245 | checkLegend(t, out) | 245 | checkLegend(t, out) |
| 246 | } | 246 | } |
| 247 | |||
| 248 | func TestRepoShowsPlainPinned(t *testing.T) { | ||
| 249 | st, repo, u, root := browseFixture(t) | ||
| 250 | p := repo.Path() | ||
| 251 | pinPlain(t, "repo-settings-show", dispatchIn(t, st, u, root, "", "repo", "settings", "show", p)) | ||
| 252 | pinPlain(t, "repo-deps-status-off", dispatchIn(t, st, u, root, "", "repo", "deps", "status", p)) | ||
| 253 | pinPlain(t, "repo-commit", dispatchIn(t, st, u, root, "", "repo", "commit", p, "aee4475ad9")) | ||
| 254 | dispatchIn(t, st, u, root, "", "repo", "deps", "enable", p) | ||
| 255 | pinPlain(t, "repo-deps-status", dispatchIn(t, st, u, root, "", "repo", "deps", "status", p)) | ||
| 256 | } | ||
| 257 | |||
| 258 | func TestRepoShowScreens(t *testing.T) { | ||
| 259 | st, repo, u, root := browseFixture(t) | ||
| 260 | p := repo.Path() | ||
| 261 | for _, tc := range []struct { | ||
| 262 | argv []string | ||
| 263 | want []string | ||
| 264 | }{ | ||
| 265 | {[]string{"repo", "settings", "show", p}, []string{"Repo:", "alice/app", "Require MR:", "off", "Approvals:"}}, | ||
| 266 | {[]string{"repo", "deps", "status", p}, []string{"Checks: off\n", "gitbay repo deps enable alice/app"}}, | ||
| 267 | {[]string{"repo", "commit", p, "aee4475ad9"}, []string{"Commit:", "aee4475ad9 docs: guide", "Author:", "t <t@example.test>", "gitbay repo log alice/app --ref aee4475ad9", "+# guide"}}, | ||
| 268 | } { | ||
| 269 | out := atTerminalIn(t, st, u, root, tc.argv...) | ||
| 270 | for _, w := range tc.want { | ||
| 271 | if !strings.Contains(out, w) { | ||
| 272 | t.Errorf("%v: missing %q in:\n%s", tc.argv, w, out) | ||
| 273 | } | ||
| 274 | } | ||
| 275 | checkLegend(t, out) | ||
| 276 | } | ||
| 277 | dispatchIn(t, st, u, root, "", "repo", "deps", "enable", p) | ||
| 278 | out := atTerminalIn(t, st, u, root, "repo", "deps", "status", p) | ||
| 279 | if !strings.Contains(out, "Checks:") || !strings.Contains(out, "gitbay repo deps disable alice/app") { | ||
| 280 | t.Errorf("deps on:\n%s", out) | ||
| 281 | } | ||
| 282 | checkLegend(t, out) | ||
| 283 | if i, j := strings.Index(out, "gitbay repo deps"), strings.Index(out, "Checks:"); i < j { | ||
| 284 | t.Errorf("legend before the header:\n%s", out) | ||
| 285 | } | ||
| 286 | } | ||
internal/control/testdata/plain/repo-commit.txt added +16
| @@ -0,0 +1,16 @@ | |||
| 1 | commit aee4475ad9033773a17e148aafa3105212ac9c10 | ||
| 2 | Author: t <t@example.test> | ||
| 3 | Date: <time> | ||
| 4 | |||
| 5 | docs: guide | ||
| 6 | |||
| 7 | docs/guide.md | 1 + | ||
| 8 | 1 file changed, 1 insertion(+) | ||
| 9 | |||
| 10 | diff --git a/docs/guide.md b/docs/guide.md | ||
| 11 | new file mode 100644 | ||
| 12 | index 0000000..cd51204 | ||
| 13 | --- /dev/null | ||
| 14 | +++ b/docs/guide.md | ||
| 15 | @@ -0,0 +1 @@ | ||
| 16 | +# guide | ||
internal/control/testdata/plain/repo-deps-status-off.txt added +1
| @@ -0,0 +1 @@ | |||
| 1 | checks off (repo deps enable alice/app) | ||
internal/control/testdata/plain/repo-deps-status.txt added +1
| @@ -0,0 +1 @@ | |||
| 1 | checks on | ||
internal/control/testdata/plain/repo-settings-show.txt added +7
| @@ -0,0 +1,7 @@ | |||
| 1 | alice/app settings | ||
| 2 | |||
| 3 | require mr false | ||
| 4 | require checks false | ||
| 5 | require signed commits false | ||
| 6 | git daemon false | ||
| 7 | archived false | ||