internal/control/deps.go

d7d409e545d4b0999652559ac53e94ce556b56f6
gitbay/internal/control/deps.go history · blame · raw

117 lines · 3744 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
 28// DepsOut is what `repo deps status` emits: the check's state and what
 29// the last run found behind. Named so the web decodes the same struct
 30// the command writes.
 31type DepsOut struct {
 32	Enabled     bool        `json:"enabled"`
 33	LastCheck   string      `json:"last_check,omitempty"`
 34	LastError   string      `json:"last_error,omitempty"`
 35	IssueNumber int64       `json:"issue_number,omitempty"`
 36	Behind      []DepBehind `json:"behind"`
 37}
 38
 39// DepBehind is one dependency with a newer release than the manifest pins.
 40type DepBehind struct {
 41	Ecosystem string `json:"ecosystem"`
 42	Name      string `json:"name"`
 43	Current   string `json:"current"`
 44	Latest    string `json:"latest"`
 45}
 46
 47func runDepsEnable(c *Ctx, args []string) int {
 48	if len(args) != 1 {
 49		return c.usage()
 50	}
 51	repo, code := resolveRepo(c, args[0], policy.CanAdmin)
 52	if code >= 0 {
 53		return code
 54	}
 55	if err := c.Store.EnableDepCheck(repo.ID); err != nil {
 56		return c.fail(protocol.ExitFailure, "%v", err)
 57	}
 58	return c.emit(map[string]any{"enabled": true}, func(w io.Writer) {
 59		fmt.Fprintf(w, "dependency checks enabled on %s\n", repo.Path())
 60	})
 61}
 62
 63func runDepsDisable(c *Ctx, args []string) int {
 64	if len(args) != 1 {
 65		return c.usage()
 66	}
 67	repo, code := resolveRepo(c, args[0], policy.CanAdmin)
 68	if code >= 0 {
 69		return code
 70	}
 71	if err := c.Store.DisableDepCheck(repo.ID); err != nil {
 72		return c.fail(protocol.ExitFailure, "%v", err)
 73	}
 74	return c.emit(map[string]any{"enabled": false}, func(w io.Writer) {
 75		fmt.Fprintf(w, "dependency checks disabled on %s\n", repo.Path())
 76	})
 77}
 78
 79func runDepsStatus(c *Ctx, args []string) int {
 80	if len(args) != 1 {
 81		return c.usage()
 82	}
 83	repo, code := resolveRepo(c, args[0], policy.CanRead)
 84	if code >= 0 {
 85		return code
 86	}
 87	check, err := c.Store.DepCheckFor(repo.ID)
 88	if errors.Is(err, store.ErrNotFound) {
 89		return c.emit(map[string]any{"enabled": false}, func(w io.Writer) {
 90			fmt.Fprintf(w, "dependency checks are off for %s (repo deps enable %s)\n", repo.Path(), repo.Path())
 91		})
 92	}
 93	if err != nil {
 94		return c.fail(protocol.ExitFailure, "%v", err)
 95	}
 96	reports, err := c.Store.ReportedDeps(repo.ID)
 97	if err != nil {
 98		return c.fail(protocol.ExitFailure, "%v", err)
 99	}
100	out := DepsOut{Enabled: true, LastCheck: check.LastCheck, LastError: check.LastError,
101		IssueNumber: check.IssueNumber, Behind: []DepBehind{}}
102	for _, r := range reports {
103		out.Behind = append(out.Behind, DepBehind{r.Ecosystem, r.Name, r.Current, r.Latest})
104	}
105	return c.emit(out, func(w io.Writer) {
106		fmt.Fprintf(w, "checks on, last %s\n", orDash(check.LastCheck))
107		if check.LastError != "" {
108			fmt.Fprintf(w, "last error: %s\n", check.LastError)
109		}
110		if check.IssueNumber != 0 {
111			fmt.Fprintf(w, "tracked in #%d\n", check.IssueNumber)
112		}
113		for _, b := range out.Behind {
114			fmt.Fprintf(w, "%s\t%s\t%s\t-> %s\n", b.Ecosystem, b.Name, b.Current, b.Latest)
115		}
116	})
117}