...
1
16
17 package ignore
18
19 import (
20 "bufio"
21 "bytes"
22 "io"
23 "log"
24 "os"
25 "path/filepath"
26 "strings"
27
28 "github.com/pkg/errors"
29 )
30
31
32 const HelmIgnore = ".helmignore"
33
34
35
36
37
38 type Rules struct {
39 patterns []*pattern
40 }
41
42
43 func Empty() *Rules {
44 return &Rules{patterns: []*pattern{}}
45 }
46
47
48
49
50 func (r *Rules) AddDefaults() {
51 r.parseRule(`templates/.?*`)
52 }
53
54
55 func ParseFile(file string) (*Rules, error) {
56 f, err := os.Open(file)
57 if err != nil {
58 return nil, err
59 }
60 defer f.Close()
61 return Parse(f)
62 }
63
64
65 func Parse(file io.Reader) (*Rules, error) {
66 r := &Rules{patterns: []*pattern{}}
67
68 s := bufio.NewScanner(file)
69 currentLine := 0
70 utf8bom := []byte{0xEF, 0xBB, 0xBF}
71 for s.Scan() {
72 scannedBytes := s.Bytes()
73
74 if currentLine == 0 {
75 scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom)
76 }
77 line := string(scannedBytes)
78 currentLine++
79
80 if err := r.parseRule(line); err != nil {
81 return r, err
82 }
83 }
84 return r, s.Err()
85 }
86
87
88
89
90
91 func (r *Rules) Ignore(path string, fi os.FileInfo) bool {
92
93 if path == "" {
94 return false
95 }
96
97
98
99
100 if path == "." || path == "./" {
101 return false
102 }
103 for _, p := range r.patterns {
104 if p.match == nil {
105 log.Printf("ignore: no matcher supplied for %q", p.raw)
106 return false
107 }
108
109
110
111 if p.negate {
112 if p.mustDir && !fi.IsDir() {
113 return true
114 }
115 if !p.match(path, fi) {
116 return true
117 }
118 continue
119 }
120
121
122
123 if p.mustDir && !fi.IsDir() {
124 continue
125 }
126 if p.match(path, fi) {
127 return true
128 }
129 }
130 return false
131 }
132
133
134 func (r *Rules) parseRule(rule string) error {
135 rule = strings.TrimSpace(rule)
136
137
138 if rule == "" {
139 return nil
140 }
141
142 if strings.HasPrefix(rule, "#") {
143 return nil
144 }
145
146
147 if strings.Contains(rule, "**") {
148 return errors.New("double-star (**) syntax is not supported")
149 }
150
151
152
153 if _, err := filepath.Match(rule, "abc"); err != nil {
154 return err
155 }
156
157 p := &pattern{raw: rule}
158
159
160
161 if strings.HasPrefix(rule, "!") {
162 p.negate = true
163 rule = rule[1:]
164 }
165
166
167
168
169 if strings.HasSuffix(rule, "/") {
170 p.mustDir = true
171 rule = strings.TrimSuffix(rule, "/")
172 }
173
174 if strings.HasPrefix(rule, "/") {
175
176 p.match = func(n string, _ os.FileInfo) bool {
177 rule = strings.TrimPrefix(rule, "/")
178 ok, err := filepath.Match(rule, n)
179 if err != nil {
180 log.Printf("Failed to compile %q: %s", rule, err)
181 return false
182 }
183 return ok
184 }
185 } else if strings.Contains(rule, "/") {
186
187 p.match = func(n string, _ os.FileInfo) bool {
188 ok, err := filepath.Match(rule, n)
189 if err != nil {
190 log.Printf("Failed to compile %q: %s", rule, err)
191 return false
192 }
193 return ok
194 }
195 } else {
196 p.match = func(n string, _ os.FileInfo) bool {
197
198
199 n = filepath.Base(n)
200 ok, err := filepath.Match(rule, n)
201 if err != nil {
202 log.Printf("Failed to compile %q: %s", rule, err)
203 return false
204 }
205 return ok
206 }
207 }
208
209 r.patterns = append(r.patterns, p)
210 return nil
211 }
212
213
214
215
216 type matcher func(name string, fi os.FileInfo) bool
217
218
219 type pattern struct {
220
221 raw string
222
223 match matcher
224
225 negate bool
226
227 mustDir bool
228 }
229
View as plain text