cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.29.3

Branches

Tags

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

Clone

HTTPS

Download ZIP

cmd/pint/ci.go

252lines · modecode

1package main
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "regexp"
8 "strconv"
9 "strings"
10 "time"
11
12 "github.com/cloudflare/pint/internal/checks"
13 "github.com/cloudflare/pint/internal/config"
14 "github.com/cloudflare/pint/internal/discovery"
15 "github.com/cloudflare/pint/internal/git"
16 "github.com/cloudflare/pint/internal/reporter"
17
18 "github.com/rs/zerolog/log"
19 "github.com/urfave/cli/v2"
20)
21
22var baseBranchFlag = "base-branch"
23
24var ciCmd = &cli.Command{
25 Name: "ci",
26 Usage: "Lint CI changes",
27 Action: actionCI,
28 Flags: []cli.Flag{
29 &cli.BoolFlag{
30 Name: requireOwnerFlag,
31 Aliases: []string{"r"},
32 Value: false,
33 Usage: "Require all rules to have an owner set via comment",
34 },
35 &cli.StringFlag{
36 Name: baseBranchFlag,
37 Aliases: []string{"b"},
38 Value: "",
39 Usage: "Set base branch to use for PR checks (main, master, ...)",
40 },
41 },
42}
43
44func actionCI(c *cli.Context) error {
45 meta, err := actionSetup(c)
46 if err != nil {
47 return err
48 }
49
50 includeRe := []*regexp.Regexp{}
51 for _, pattern := range meta.cfg.CI.Include {
52 includeRe = append(includeRe, regexp.MustCompile("^"+pattern+"$"))
53 }
54
55 meta.cfg.CI = detectCI(meta.cfg.CI)
56 baseBranch := strings.Split(meta.cfg.CI.BaseBranch, "/")[len(strings.Split(meta.cfg.CI.BaseBranch, "/"))-1]
57 if c.String(baseBranchFlag) != "" {
58 baseBranch = c.String(baseBranchFlag)
59 }
60 currentBranch, err := git.CurrentBranch(git.RunGit)
61 if err != nil {
62 return fmt.Errorf("failed to get the name of current branch")
63 }
64 log.Debug().Str("current", currentBranch).Str("base", baseBranch).Msg("Got branch information")
65 if currentBranch == baseBranch {
66 log.Info().Str("branch", currentBranch).Msg("Running from base branch, skipping checks")
67 return nil
68 }
69
70 finder := discovery.NewGitBranchFinder(git.RunGit, includeRe, meta.cfg.CI.BaseBranch, meta.cfg.CI.MaxCommits, meta.cfg.Parser.CompileRelaxed())
71 entries, err := finder.Find()
72 if err != nil {
73 return err
74 }
75
76 for _, prom := range meta.cfg.PrometheusServers {
77 prom.StartWorkers()
78 }
79 defer meta.cleanup()
80
81 ctx := context.WithValue(context.Background(), config.CommandKey, config.CICommand)
82 summary := checkRules(ctx, meta.workers, meta.cfg, entries)
83
84 if c.Bool(requireOwnerFlag) {
85 summary.Report(verifyOwners(entries)...)
86 }
87
88 reps := []reporter.Reporter{
89 reporter.NewConsoleReporter(os.Stderr, checks.Information),
90 }
91
92 if meta.cfg.Repository != nil && meta.cfg.Repository.BitBucket != nil {
93 token, ok := os.LookupEnv("BITBUCKET_AUTH_TOKEN")
94 if !ok {
95 return fmt.Errorf("BITBUCKET_AUTH_TOKEN env variable is required when reporting to BitBucket")
96 }
97
98 timeout, _ := time.ParseDuration(meta.cfg.Repository.BitBucket.Timeout)
99 br := reporter.NewBitBucketReporter(
100 version,
101 meta.cfg.Repository.BitBucket.URI,
102 timeout,
103 token,
104 meta.cfg.Repository.BitBucket.Project,
105 meta.cfg.Repository.BitBucket.Repository,
106 git.RunGit,
107 )
108 reps = append(reps, br)
109 }
110
111 meta.cfg.Repository = detectRepository(meta.cfg.Repository)
112 if meta.cfg.Repository != nil && meta.cfg.Repository.GitHub != nil {
113 token, ok := os.LookupEnv("GITHUB_AUTH_TOKEN")
114 if !ok {
115 return fmt.Errorf("GITHUB_AUTH_TOKEN env variable is required when reporting to GitHub")
116 }
117
118 prVal, ok := os.LookupEnv("GITHUB_PULL_REQUEST_NUMBER")
119 if !ok {
120 return fmt.Errorf("GITHUB_PULL_REQUEST_NUMBER env variable is required when reporting to GitHub")
121 }
122
123 prNum, err := strconv.Atoi(prVal)
124 if err != nil {
125 return fmt.Errorf("got not a valid number via GITHUB_PULL_REQUEST_NUMBER: %w", err)
126 }
127
128 timeout, _ := time.ParseDuration(meta.cfg.Repository.GitHub.Timeout)
129 gr := reporter.NewGithubReporter(
130 meta.cfg.Repository.GitHub.BaseURI,
131 meta.cfg.Repository.GitHub.UploadURI,
132 timeout,
133 token,
134 meta.cfg.Repository.GitHub.Owner,
135 meta.cfg.Repository.GitHub.Repo,
136 prNum,
137 git.RunGit,
138 )
139 reps = append(reps, gr)
140 }
141
142 foundBugOrHigher := false
143 bySeverity := map[string]interface{}{} // interface{} is needed for log.Fields()
144 for s, c := range summary.CountBySeverity() {
145 if s >= checks.Bug {
146 foundBugOrHigher = true
147 }
148 bySeverity[s.String()] = c
149 }
150 if len(bySeverity) > 0 {
151 log.Info().Fields(bySeverity).Msg("Problems found")
152 }
153
154 if err := submitReports(reps, summary); err != nil {
155 return fmt.Errorf("submitting reports: %w", err)
156 }
157
158 if foundBugOrHigher {
159 return fmt.Errorf("problems found")
160 }
161
162 return nil
163}
164
165func detectCI(cfg *config.CI) *config.CI {
166 var isNil, isDirty bool
167
168 if cfg == nil {
169 isNil = true
170 cfg = &config.CI{}
171 }
172
173 if bb := os.Getenv("GITHUB_BASE_REF"); bb != "" {
174 isDirty = true
175 cfg.BaseBranch = bb
176 }
177
178 if isNil && !isDirty {
179 return nil
180 }
181 return cfg
182}
183
184func detectRepository(cfg *config.Repository) *config.Repository {
185 var isNil, isDirty bool
186
187 if cfg == nil {
188 isNil = true
189 cfg = &config.Repository{}
190 }
191
192 if os.Getenv("GITHUB_ACTION") != "" {
193 isDirty = true
194 cfg.GitHub = detectGithubActions(cfg.GitHub)
195 }
196
197 if isNil && !isDirty {
198 return nil
199 }
200 return cfg
201}
202
203func detectGithubActions(gh *config.GitHub) *config.GitHub {
204 if os.Getenv("GITHUB_PULL_REQUEST_NUMBER") == "" &&
205 os.Getenv("GITHUB_EVENT_NAME") == "pull_request" &&
206 os.Getenv("GITHUB_REF") != "" {
207 parts := strings.Split(os.Getenv("GITHUB_REF"), "/")
208 if len(parts) >= 4 {
209 log.Info().Str("pr", parts[2]).Msg("Setting GITHUB_PULL_REQUEST_NUMBER from GITHUB_REF env variable")
210 os.Setenv("GITHUB_PULL_REQUEST_NUMBER", parts[2])
211 }
212 }
213
214 var isDirty, isNil bool
215
216 if gh == nil {
217 isNil = true
218 gh = &config.GitHub{Timeout: time.Minute.String()}
219 }
220
221 if repo := os.Getenv("GITHUB_REPOSITORY"); repo != "" {
222 parts := strings.SplitN(repo, "/", 2)
223 if len(parts) == 2 {
224 if gh.Owner == "" {
225 log.Info().Str("owner", parts[0]).Msg("Setting repository owner from GITHUB_REPOSITORY env variable")
226 gh.Owner = parts[0]
227 isDirty = true
228 }
229 if gh.Repo == "" {
230 log.Info().Str("repo", parts[1]).Msg("Setting repository name from GITHUB_REPOSITORY env variable")
231 gh.Repo = parts[1]
232 isDirty = true
233 }
234 }
235 }
236
237 if api := os.Getenv("GITHUB_API_URL"); api != "" {
238 if gh.BaseURI == "" {
239 log.Info().Str("baseuri", api).Msg("Setting repository base URI from GITHUB_API_URL env variable")
240 gh.BaseURI = api
241 }
242 if gh.UploadURI == "" {
243 log.Info().Str("uploaduri", api).Msg("Setting repository upload URI from GITHUB_API_URL env variable")
244 gh.UploadURI = api
245 }
246 }
247
248 if isNil && !isDirty {
249 return nil
250 }
251 return gh
252}
253