cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.15.3

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/git_branch.go

143lines · modecode

1package discovery
2
3import (
4 "fmt"
5 "regexp"
6 "strings"
7
8 "github.com/cloudflare/pint/internal/git"
9
10 "github.com/rs/zerolog/log"
11)
12
13func NewGitBranchFinder(gitCmd git.CommandRunner, include []*regexp.Regexp, baseBranch string, maxCommits int) GitBranchFinder {
14 return GitBranchFinder{
15 gitCmd: gitCmd,
16 include: include,
17 baseBranch: baseBranch,
18 maxCommits: maxCommits,
19 }
20}
21
22type GitBranchFinder struct {
23 gitCmd git.CommandRunner
24 include []*regexp.Regexp
25 baseBranch string
26 maxCommits int
27}
28
29func (f GitBranchFinder) Find() (entries []Entry, err error) {
30 cr, err := git.CommitRange(f.gitCmd, f.baseBranch)
31 if err != nil {
32 return nil, fmt.Errorf("failed to get the list of commits to scan: %w", err)
33 }
34
35 log.Debug().Str("from", cr.From).Str("to", cr.To).Msg("Got commit range from git")
36
37 if f.maxCommits > 0 && len(cr.Commits) > f.maxCommits {
38 return nil, fmt.Errorf("number of commits to check (%d) is higher than maxCommits (%d), exiting", len(cr.Commits), f.maxCommits)
39 }
40
41 out, err := f.gitCmd("log", "--reverse", "--no-merges", "--pretty=format:%H", "--name-status", cr.String())
42 if err != nil {
43 return nil, fmt.Errorf("failed to get the list of modified files from git: %w", err)
44 }
45
46 pathCommits := map[string]map[string]struct{}{}
47 var commit string
48
49 for _, line := range strings.Split(string(out), "\n") {
50 parts := strings.Split(removeRedundantSpaces(line), " ")
51 if len(parts) == 1 && parts[0] != "" {
52 commit = parts[0]
53 } else if len(parts) >= 2 {
54 op := parts[0]
55 srcPath := parts[1]
56 dstPath := parts[len(parts)-1]
57 log.Debug().
58 Str("path", dstPath).
59 Str("commit", commit).
60 Bool("allowed", f.isPathAllowed(dstPath)).
61 Msg("Git file change")
62 if !f.isPathAllowed(dstPath) {
63 continue
64 }
65 if _, ok := pathCommits[dstPath]; !ok {
66 pathCommits[dstPath] = map[string]struct{}{}
67 }
68 // check if we're dealing with a rename and if so we need to
69 // rename results in pathCommits
70 if strings.HasPrefix(op, "R") {
71 if commits, ok := pathCommits[srcPath]; ok {
72 for c := range commits {
73 pathCommits[dstPath][c] = struct{}{}
74 }
75 delete(pathCommits, srcPath)
76 }
77 }
78 // check if file is being removed, if so drop it from the results
79 if strings.HasPrefix(op, "D") {
80 delete(pathCommits, srcPath)
81 continue
82 }
83 pathCommits[dstPath][commit] = struct{}{}
84 }
85 }
86
87 for path, commits := range pathCommits {
88 lbs, err := git.Blame(path, f.gitCmd)
89 if err != nil {
90 return nil, fmt.Errorf("failed to run git blame for %s: %w", path, err)
91 }
92
93 alloweLines := []int{}
94 for _, lb := range lbs {
95 // skip commits that are not part of our diff
96 if _, ok := commits[lb.Commit]; !ok {
97 continue
98 }
99 alloweLines = append(alloweLines, lb.Line)
100 }
101
102 els, err := readFile(path)
103 if err != nil {
104 return nil, err
105 }
106 for _, e := range els {
107 e.ModifiedLines = alloweLines
108 if isOverlap(alloweLines, e.Rule.Lines()) {
109 entries = append(entries, e)
110 }
111 }
112 }
113
114 return entries, nil
115}
116
117func (f GitBranchFinder) isPathAllowed(path string) bool {
118 if len(f.include) == 0 {
119 return true
120 }
121
122 for _, pattern := range f.include {
123 if pattern.MatchString(path) {
124 return true
125 }
126 }
127 return false
128}
129
130func removeRedundantSpaces(line string) string {
131 return strings.Join(strings.Fields(line), " ")
132}
133
134func isOverlap(a, b []int) bool {
135 for _, i := range a {
136 for _, j := range b {
137 if i == j {
138 return true
139 }
140 }
141 }
142 return false
143}
144