cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.65.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/glob.go

174lines · modecode

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