internal/control/deps.go
110 lines · 3639 bytes
1package control
2
3import (
4 "errors"
5 "fmt"
6 "io"
7
8 "gitbay.org/gitbay/internal/policy"
9 "gitbay.org/gitbay/internal/protocol"
10 "gitbay.org/gitbay/internal/store"
11)
12
13func init() {
14 // Dependency update checks. Opt-in per repository: the check tells a
15 // public registry what the repository depends on, which is the owner's
16 // disclosure to make, not the instance's.
17 register(Command{Path: []string{"repo", "deps", "enable"},
18 Summary: "check dependencies for updates",
19 Usage: "repo deps enable <owner/name>", Run: runDepsEnable})
20 register(Command{Path: []string{"repo", "deps", "disable"},
21 Summary: "stop checking dependencies",
22 Usage: "repo deps disable <owner/name>", Run: runDepsDisable})
23 register(Command{Path: []string{"repo", "deps", "status"},
24 Summary: "show dependency check state",
25 Usage: "repo deps status <owner/name>", ReadOnly: true, Run: runDepsStatus})
26}
27
28func runDepsEnable(c *Ctx, args []string) int {
29 if len(args) != 1 {
30 return c.fail(protocol.ExitUsage, "usage: repo deps enable <owner/name>")
31 }
32 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
33 if code >= 0 {
34 return code
35 }
36 if err := c.Store.EnableDepCheck(repo.ID); err != nil {
37 return c.fail(protocol.ExitFailure, "%v", err)
38 }
39 return c.emit(map[string]any{"enabled": true}, func(w io.Writer) {
40 fmt.Fprintf(w, "dependency checks enabled on %s\n", repo.Path())
41 })
42}
43
44func runDepsDisable(c *Ctx, args []string) int {
45 if len(args) != 1 {
46 return c.fail(protocol.ExitUsage, "usage: repo deps disable <owner/name>")
47 }
48 repo, code := resolveRepo(c, args[0], policy.CanAdmin)
49 if code >= 0 {
50 return code
51 }
52 if err := c.Store.DisableDepCheck(repo.ID); err != nil {
53 return c.fail(protocol.ExitFailure, "%v", err)
54 }
55 return c.emit(map[string]any{"enabled": false}, func(w io.Writer) {
56 fmt.Fprintf(w, "dependency checks disabled on %s\n", repo.Path())
57 })
58}
59
60func runDepsStatus(c *Ctx, args []string) int {
61 if len(args) != 1 {
62 return c.fail(protocol.ExitUsage, "usage: repo deps status <owner/name>")
63 }
64 repo, code := resolveRepo(c, args[0], policy.CanRead)
65 if code >= 0 {
66 return code
67 }
68 check, err := c.Store.DepCheckFor(repo.ID)
69 if errors.Is(err, store.ErrNotFound) {
70 return c.emit(map[string]any{"enabled": false}, func(w io.Writer) {
71 fmt.Fprintf(w, "dependency checks are off for %s (repo deps enable %s)\n", repo.Path(), repo.Path())
72 })
73 }
74 if err != nil {
75 return c.fail(protocol.ExitFailure, "%v", err)
76 }
77 reports, err := c.Store.ReportedDeps(repo.ID)
78 if err != nil {
79 return c.fail(protocol.ExitFailure, "%v", err)
80 }
81 type behind struct {
82 Ecosystem string `json:"ecosystem"`
83 Name string `json:"name"`
84 Current string `json:"current"`
85 Latest string `json:"latest"`
86 }
87 out := struct {
88 Enabled bool `json:"enabled"`
89 LastCheck string `json:"last_check,omitempty"`
90 LastError string `json:"last_error,omitempty"`
91 IssueNumber int64 `json:"issue_number,omitempty"`
92 Behind []behind `json:"behind"`
93 }{Enabled: true, LastCheck: check.LastCheck, LastError: check.LastError,
94 IssueNumber: check.IssueNumber, Behind: []behind{}}
95 for _, r := range reports {
96 out.Behind = append(out.Behind, behind{r.Ecosystem, r.Name, r.Current, r.Latest})
97 }
98 return c.emit(out, func(w io.Writer) {
99 fmt.Fprintf(w, "checks on, last %s\n", orDash(check.LastCheck))
100 if check.LastError != "" {
101 fmt.Fprintf(w, "last error: %s\n", check.LastError)
102 }
103 if check.IssueNumber != 0 {
104 fmt.Fprintf(w, "tracked in #%d\n", check.IssueNumber)
105 }
106 for _, b := range out.Behind {
107 fmt.Fprintf(w, "%s\t%s\t%s\t-> %s\n", b.Ecosystem, b.Name, b.Current, b.Latest)
108 }
109 })
110}