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

internal/discovery/glob.go

173lines · modecode

1package discovery
2
3import (
4 "fmt"
5 "io/fs"
6 "log/slog"
7 "os"
8 "path/filepath"
9
10 "github.com/cloudflare/pint/internal/git"
11)
12
13func NewGlobFinder(patterns []string, filter git.PathFilter) GlobFinder {
14 return GlobFinder{
15 patterns: patterns,
16 filter: filter,
17 }
18}
19
20type GlobFinder struct {
21 patterns []string
22 filter git.PathFilter
23}
24
25func (f GlobFinder) Find() (entries []Entry, err error) {
26 paths := filePaths{}
27 for _, p := range f.patterns {
28 matches, err := filepath.Glob(p)
29 if err != nil {
30 return nil, fmt.Errorf("failed to expand file path pattern %s: %w", p, err)
31 }
32
33 for _, path := range matches {
34 if path == ".git" && isDir(path) {
35 slog.Debug(
36 "Excluding git directory from glob results",
37 slog.String("path", path),
38 slog.String("glob", p),
39 )
40 continue
41 }
42
43 subpaths, err := findFiles(path)
44 if err != nil {
45 return nil, err
46 }
47 for _, subpath := range subpaths {
48 if !paths.hasPath(subpath.path) {
49 paths = append(paths, subpath)
50 }
51 }
52 }
53 }
54
55 if len(paths) == 0 {
56 return nil, fmt.Errorf("no matching files")
57 }
58
59 for _, fp := range paths {
60 if !f.filter.IsPathAllowed(fp.path) {
61 continue
62 }
63
64 fd, err := os.Open(fp.path)
65 if err != nil {
66 return nil, err
67 }
68 el, err := readRules(fp.target, fp.path, fd, !f.filter.IsRelaxed(fp.target))
69 if err != nil {
70 fd.Close()
71 return nil, fmt.Errorf("invalid file syntax: %w", err)
72 }
73 fd.Close()
74 for _, e := range el {
75 e.State = Noop
76 if len(e.ModifiedLines) == 0 {
77 e.ModifiedLines = e.Rule.Lines.Expand()
78 }
79 entries = append(entries, e)
80 }
81 }
82
83 slog.Debug("Glob finder completed", slog.Int("count", len(entries)))
84 return entries, nil
85}
86
87func isDir(path string) bool {
88 info, err := os.Stat(path)
89 if err != nil {
90 return false
91 }
92 return info.IsDir()
93}
94
95type filePath struct {
96 path string
97 target string
98}
99
100type filePaths []filePath
101
102func (fps filePaths) hasPath(p string) bool {
103 for _, fp := range fps {
104 if fp.path == p {
105 return true
106 }
107 }
108 return false
109}
110
111func findFiles(path string) (paths filePaths, err error) {
112 target, err := filepath.EvalSymlinks(path)
113 if err != nil {
114 return nil, fmt.Errorf("%s is a symlink but target file cannot be evaluated: %w", path, err)
115 }
116
117 s, err := os.Stat(path)
118 if err != nil {
119 return nil, err
120 }
121
122 // nolint: exhaustive
123 switch {
124 case s.IsDir():
125 subpaths, err := walkDir(path)
126 if err != nil {
127 return nil, err
128 }
129 paths = append(paths, subpaths...)
130 default:
131 paths = append(paths, filePath{path: path, target: target})
132 }
133
134 return paths, nil
135}
136
137func walkDir(dirname string) (paths filePaths, err error) {
138 err = filepath.WalkDir(dirname,
139 func(path string, d fs.DirEntry, err error) error {
140 if err != nil {
141 return err
142 }
143
144 // nolint: exhaustive
145 switch d.Type() {
146 case fs.ModeDir:
147 return nil
148 default:
149 dest, err := filepath.EvalSymlinks(path)
150 if err != nil {
151 return fmt.Errorf("%s is a symlink but target file cannot be evaluated: %w", path, err)
152 }
153
154 s, err := os.Stat(dest)
155 if err != nil {
156 return err
157 }
158 if s.IsDir() {
159 subpaths, err := findFiles(dest)
160 if err != nil {
161 return err
162 }
163 paths = append(paths, subpaths...)
164 } else {
165 paths = append(paths, filePath{path: path, target: dest})
166 }
167 }
168
169 return nil
170 })
171
172 return paths, err
173}
174