cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.59.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

cmd/pint/lint.go

184lines · modecode

1package main
2
3import (
4 "context"
5 "fmt"
6 "log/slog"
7 "os"
8 "regexp"
9
10 "github.com/cloudflare/pint/internal/checks"
11 "github.com/cloudflare/pint/internal/config"
12 "github.com/cloudflare/pint/internal/discovery"
13 "github.com/cloudflare/pint/internal/git"
14 "github.com/cloudflare/pint/internal/reporter"
15
16 "github.com/urfave/cli/v2"
17)
18
19var requireOwnerFlag = "require-owner"
20
21var lintCmd = &cli.Command{
22 Name: "lint",
23 Usage: "Check specified files or directories (can be a glob).",
24 Action: actionLint,
25 Flags: []cli.Flag{
26 &cli.BoolFlag{
27 Name: requireOwnerFlag,
28 Aliases: []string{"r"},
29 Value: false,
30 Usage: "Require all rules to have an owner set via comment.",
31 },
32 &cli.StringFlag{
33 Name: minSeverityFlag,
34 Aliases: []string{"n"},
35 Value: "warning",
36 Usage: "Set minimum severity for reported problems.",
37 },
38 &cli.StringFlag{
39 Name: failOnFlag,
40 Aliases: []string{"w"},
41 Value: "bug",
42 Usage: "Exit with non-zero code if there are problems with given severity (or higher) detected.",
43 },
44 &cli.BoolFlag{
45 Name: teamCityFlag,
46 Aliases: []string{"t"},
47 Value: false,
48 Usage: "Report problems using TeamCity Service Messages.",
49 },
50 },
51}
52
53func actionLint(c *cli.Context) error {
54 meta, err := actionSetup(c)
55 if err != nil {
56 return err
57 }
58
59 paths := c.Args().Slice()
60 if len(paths) == 0 {
61 return fmt.Errorf("at least one file or directory required")
62 }
63
64 slog.Info("Finding all rules to check", slog.Any("paths", paths))
65 finder := discovery.NewGlobFinder(paths, git.NewPathFilter(nil, nil, meta.cfg.Parser.CompileRelaxed()))
66 entries, err := finder.Find()
67 if err != nil {
68 return err
69 }
70
71 ctx := context.WithValue(context.Background(), config.CommandKey, config.LintCommand)
72
73 gen := config.NewPrometheusGenerator(meta.cfg, metricsRegistry)
74 defer gen.Stop()
75
76 if err = gen.GenerateStatic(); err != nil {
77 return err
78 }
79
80 summary, err := checkRules(ctx, meta.workers, meta.isOffline, gen, meta.cfg, entries)
81 if err != nil {
82 return err
83 }
84
85 if c.Bool(requireOwnerFlag) {
86 summary.Report(verifyOwners(entries, meta.cfg.Owners.CompileAllowed())...)
87 }
88
89 minSeverity, err := checks.ParseSeverity(c.String(minSeverityFlag))
90 if err != nil {
91 return fmt.Errorf("invalid --%s value: %w", minSeverityFlag, err)
92 }
93 failOn, err := checks.ParseSeverity(c.String(failOnFlag))
94 if err != nil {
95 return fmt.Errorf("invalid --%s value: %w", failOnFlag, err)
96 }
97
98 var r reporter.Reporter
99 if c.Bool(teamCityFlag) {
100 r = reporter.NewTeamCityReporter(os.Stderr)
101 } else {
102 r = reporter.NewConsoleReporter(os.Stderr, minSeverity)
103 }
104
105 err = r.Submit(summary)
106 if err != nil {
107 return err
108 }
109
110 bySeverity := summary.CountBySeverity()
111 var problems, hiddenProblems, failProblems int
112 for s, c := range bySeverity {
113 if s >= failOn {
114 failProblems++
115 }
116 if s < minSeverity {
117 hiddenProblems++
118 }
119 if s >= checks.Bug {
120 problems += c
121 }
122 }
123 if len(bySeverity) > 0 {
124 slog.Info("Problems found", logSeverityCounters(bySeverity)...)
125 }
126 if hiddenProblems > 0 {
127 slog.Info(fmt.Sprintf("%d problem(s) not visible because of --%s=%s flag", hiddenProblems, minSeverityFlag, c.String(minSeverityFlag)))
128 }
129
130 if failProblems > 0 {
131 return fmt.Errorf("found %d problem(s) with severity %s or higher", failProblems, failOn)
132 }
133 return nil
134}
135
136func verifyOwners(entries []discovery.Entry, allowedOwners []*regexp.Regexp) (reports []reporter.Report) {
137 for _, entry := range entries {
138 if entry.State == discovery.Removed {
139 continue
140 }
141 if entry.PathError != nil {
142 continue
143 }
144 if entry.Owner == "" {
145 reports = append(reports, reporter.Report{
146 Path: discovery.Path{
147 Name: entry.Path.Name,
148 SymlinkTarget: entry.Path.SymlinkTarget,
149 },
150 ModifiedLines: entry.ModifiedLines,
151 Rule: entry.Rule,
152 Problem: checks.Problem{
153 Lines: entry.Rule.Lines,
154 Reporter: discovery.RuleOwnerComment,
155 Text: fmt.Sprintf("`%s` comments are required in all files, please add a `# pint %s $owner` somewhere in this file and/or `# pint %s $owner` on top of each rule.",
156 discovery.RuleOwnerComment, discovery.FileOwnerComment, discovery.RuleOwnerComment),
157 Severity: checks.Bug,
158 },
159 })
160 goto NEXT
161 }
162 for _, re := range allowedOwners {
163 if re.MatchString(entry.Owner) {
164 goto NEXT
165 }
166 }
167 reports = append(reports, reporter.Report{
168 Path: discovery.Path{
169 Name: entry.Path.Name,
170 SymlinkTarget: entry.Path.SymlinkTarget,
171 },
172 ModifiedLines: entry.ModifiedLines,
173 Rule: entry.Rule,
174 Problem: checks.Problem{
175 Lines: entry.Rule.Lines,
176 Reporter: discovery.RuleOwnerComment,
177 Text: fmt.Sprintf("This rule is set as owned by `%s` but `%s` doesn't match any of the allowed owner values.", entry.Owner, entry.Owner),
178 Severity: checks.Bug,
179 },
180 })
181 NEXT:
182 }
183 return reports
184}
185