diff --git a/go.mod b/go.mod index 78042288..da3f6c39 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.21.1 github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 github.com/aws/smithy-go v1.24.0 - github.com/bmatcuk/doublestar/v4 v4.9.1 + github.com/bmatcuk/doublestar/v4 v4.10.0 github.com/containerd/errdefs v1.0.0 github.com/docker/cli v29.1.3+incompatible github.com/pelletier/go-toml/v2 v2.2.4 diff --git a/go.sum b/go.sum index dba5942b..9104f939 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/ github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ= github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= -github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= -github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= +github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= diff --git a/vendor/github.com/bmatcuk/doublestar/v4/.gitignore b/vendor/github.com/bmatcuk/doublestar/v4/.gitignore index af212ecc..f2d8e6b8 100644 --- a/vendor/github.com/bmatcuk/doublestar/v4/.gitignore +++ b/vendor/github.com/bmatcuk/doublestar/v4/.gitignore @@ -29,4 +29,4 @@ _testmain.go *.prof # test directory -test/ +testdata/ diff --git a/vendor/github.com/bmatcuk/doublestar/v4/README.md b/vendor/github.com/bmatcuk/doublestar/v4/README.md index e4d1941e..57d18b0e 100644 --- a/vendor/github.com/bmatcuk/doublestar/v4/README.md +++ b/vendor/github.com/bmatcuk/doublestar/v4/README.md @@ -193,6 +193,27 @@ symlink to a directory. However, from this same example, a pattern such as Note: if combined with the WithFilesOnly option, symlinks to directories _will_ be included in the result since no attempt is made to follow the symlink. +```go +WithNoHidden() +``` + +If passed, doublestar will not match hidden files and directories (those +starting with a dot) when using wildcards. This follows traditional shell glob +behavior where `*` or a `?` at the start will not match dotfiles by default. + +Hidden files can still be matched by explicitly including them in the pattern. +For example, `.*` will match hidden files, and `.config/**` will match files +inside the .config directory. + +The rule is: + - For `**`: do not descend into hidden directories + - For `*` or a pattern starting with `?`: do not match dotfiles or + directories + +On Windows, doublestar will check the file attributes and avoid hidden files +and directories this way, instead of matching the filename. Therefore, any +pattern with a `*` or `?` could potentially match a hidden file/directory. + ### Glob ```go @@ -391,6 +412,38 @@ Class | Meaning `[^class]` | matches any single character which does *not* match the class `[!class]` | same as `^`: negates the class +#### Globs Are Not Regular Expressions + +Occasionally I get bug reports that some regular-expression-style syntax +doesn't work, or feature requests to add some regular-expression-inspired +syntax. Globs are not regular expressions. However, if globs are not +sufficiently expressive for your filtering needs, I recommend a two stage +approach using `GlobWalk`. Something like the following will get you started: + +```go +var matches []string +err := doublestar.GlobWalk(fsys, pattern, func(p string, d fs.DirEntry) error { + if (customFilter(p, d)) { + matches = append(matches, p) + } else if (d.isDir()) { + return doublestar.SkipDir + } + return nil +}) +return matches, err +``` + +In this example, `pattern` should be a glob that does a first pass at fetching +the files you might be interested in; `customFilter` is a function that does a +second pass. This second pass could be anything, including regular expressions. +Try to fashion a `pattern` that reduces the number of files you need to +consider in your second pass `customFilter`. + +One final note: empty alternatives can be used to build some more complicated +globs. For example, `some{thing,}` will match both "something" and "some". +Alternatives can also be nested, like `some{thing{new,},}`, which would match +"somethingnew", "something", and "some". + ## Performance ``` diff --git a/vendor/github.com/bmatcuk/doublestar/v4/glob.go b/vendor/github.com/bmatcuk/doublestar/v4/glob.go index 3471bea7..f3ef0392 100644 --- a/vendor/github.com/bmatcuk/doublestar/v4/glob.go +++ b/vendor/github.com/bmatcuk/doublestar/v4/glob.go @@ -158,7 +158,7 @@ func (g *glob) globAlts(fsys fs.FS, pattern string, openingIdx, closingIdx int, nextIdx += patIdx } - alt := buildAlt(d, pattern, startIdx, openingIdx, patIdx, nextIdx, afterIdx) + alt := buildAlt(escapeMeta(d), pattern, startIdx, openingIdx, patIdx, nextIdx, afterIdx) matches, err = g.doGlob(fsys, alt, matches, firstSegment, beforeMeta) if err != nil { return @@ -223,8 +223,21 @@ func (g *glob) globDir(fsys fs.FS, dir, pattern string, matches []string, canMat } var matched bool + checkForHidden := g.noHidden && couldUnintentionallyMatchHidden(pattern) for _, info := range dirs { name := info.Name() + + // Skip hidden files when noHidden is set + if checkForHidden { + isHidden, err := isHiddenPath(name, info) + if e = g.forwardErrIfFailOnIOErrors(err); e != nil { + return + } + if isHidden { + continue + } + } + matched, e = matchWithSeparator(pattern, name, '/', false, g.caseInsensitive) if e != nil { return @@ -268,6 +281,18 @@ func (g *glob) globDoubleStar(fsys fs.FS, dir string, matches []string, canMatch for _, info := range dirs { name := info.Name() + + // Skip hidden files/directories when noHidden is set + if g.noHidden { + isHidden, err := isHiddenPath(name, info) + if err = g.forwardErrIfFailOnIOErrors(err); err != nil { + return nil, err + } + if isHidden { + continue + } + } + isDir, err := g.isDir(fsys, dir, name, info) if err != nil { return nil, err diff --git a/vendor/github.com/bmatcuk/doublestar/v4/globoptions.go b/vendor/github.com/bmatcuk/doublestar/v4/globoptions.go index 62983560..07c2bb81 100644 --- a/vendor/github.com/bmatcuk/doublestar/v4/globoptions.go +++ b/vendor/github.com/bmatcuk/doublestar/v4/globoptions.go @@ -4,11 +4,12 @@ import "strings" // glob is an internal type to store options during globbing. type glob struct { - caseInsensitive bool + caseInsensitive bool failOnIOErrors bool failOnPatternNotExist bool filesOnly bool noFollow bool + noHidden bool } // GlobOption represents a setting that can be passed to Glob, GlobWalk, and @@ -90,6 +91,30 @@ func WithNoFollow() GlobOption { } } +// WithNoHidden is an option that can be passed to Glob, GlobWalk, or +// FilepathGlob. If passed, doublestar will not match hidden files and +// directories (those starting with a dot) when using wildcards. This follows +// traditional shell glob behavior where `*` or a `?` at the start will not +// match dotfiles by default. +// +// Hidden files can still be matched by explicitly including them in the +// pattern. For example, `.*` will match hidden files, and `.config/**` will +// match files inside the .config directory. +// +// The rule is: +// - For `**`: do not descend into hidden directories +// - For `*` or a pattern starting with `?`: do not match dotfiles or +// directories +// +// On Windows, doublestar will check the file attributes and avoid hidden files +// and directories this way, instead of matching the filename. Therefore, any +// pattern with a `*` or `?` could potentially match a hidden file/directory. +func WithNoHidden() GlobOption { + return func(g *glob) { + g.noHidden = true + } +} + // forwardErrIfFailOnIOErrors is used to wrap the return values of I/O // functions. When failOnIOErrors is enabled, it will return err; otherwise, it // always returns nil. @@ -148,6 +173,13 @@ func (g *glob) GoString() string { b.WriteString("WithNoFollow") hasOpts = true } + if g.noHidden { + if hasOpts { + b.WriteString(", ") + } + b.WriteString("WithNoHidden") + hasOpts = true + } if !hasOpts { b.WriteString("nil") diff --git a/vendor/github.com/bmatcuk/doublestar/v4/globwalk.go b/vendor/github.com/bmatcuk/doublestar/v4/globwalk.go index 16601b76..9516cefd 100644 --- a/vendor/github.com/bmatcuk/doublestar/v4/globwalk.go +++ b/vendor/github.com/bmatcuk/doublestar/v4/globwalk.go @@ -205,7 +205,7 @@ func (g *glob) doGlobAltsWalk(fsys fs.FS, d, pattern string, startIdx, openingId nextIdx += patIdx } - alt := buildAlt(d, pattern, startIdx, openingIdx, patIdx, nextIdx, afterIdx) + alt := buildAlt(escapeMeta(d), pattern, startIdx, openingIdx, patIdx, nextIdx, afterIdx) err = g.doGlobWalk(fsys, alt, firstSegment, beforeMeta, func(p string, d fs.DirEntry) error { // insertion sort, ignoring dups insertIdx := matchesLen @@ -287,8 +287,21 @@ func (g *glob) globDirWalk(fsys fs.FS, dir, pattern string, canMatchFiles, befor } var matched bool + checkForHidden := g.noHidden && couldUnintentionallyMatchHidden(pattern) for _, info := range dirs { name := info.Name() + + // Skip hidden files when noHidden is set + if checkForHidden { + isHidden, err := isHiddenPath(name, info) + if e = g.forwardErrIfFailOnIOErrors(err); e != nil { + return + } + if isHidden { + continue + } + } + matched, e = matchWithSeparator(pattern, name, '/', false, g.caseInsensitive) if e != nil { return @@ -335,6 +348,18 @@ func (g *glob) globDoubleStarWalk(fsys fs.FS, dir string, canMatchFiles bool, fn for _, info := range dirs { name := info.Name() + + // Skip hidden files/directories when noHidden is set + if g.noHidden { + isHidden, err := isHiddenPath(name, info) + if e = g.forwardErrIfFailOnIOErrors(err); e != nil { + return + } + if isHidden { + continue + } + } + isDir, err := g.isDir(fsys, dir, name, info) if err != nil { return err diff --git a/vendor/github.com/bmatcuk/doublestar/v4/utils.go b/vendor/github.com/bmatcuk/doublestar/v4/utils.go index 7831e5c3..498c0441 100644 --- a/vendor/github.com/bmatcuk/doublestar/v4/utils.go +++ b/vendor/github.com/bmatcuk/doublestar/v4/utils.go @@ -14,16 +14,16 @@ import ( // The second string is everything after that slash. For example, given the // pattern: // -// ../../path/to/meta*/** -// ^----------- split here +// ../../path/to/meta*/** +// ^----------- split here // // SplitPattern returns "../../path/to" and "meta*/**". This is useful for // initializing os.DirFS() to call Glob() because Glob() will silently fail if // your pattern includes `/./` or `/../`. For example: // -// base, pattern := SplitPattern("../../path/to/meta*/**") -// fsys := os.DirFS(base) -// matches, err := Glob(fsys, pattern) +// base, pattern := SplitPattern("../../path/to/meta*/**") +// fsys := os.DirFS(base) +// matches, err := Glob(fsys, pattern) // // If SplitPattern cannot find somewhere to split the pattern (for example, // `meta*/**`), it will return "." and the unaltered pattern (`meta*/**` in @@ -35,7 +35,6 @@ import ( // Of course, it is your responsibility to decide if the returned base path is // "safe" in the context of your application. Perhaps you could use Match() to // validate against a list of approved base directories? -// func SplitPattern(p string) (base, pattern string) { base = "." pattern = p @@ -85,7 +84,6 @@ func SplitPattern(p string) (base, pattern string) { // // Note: the returned error doublestar.ErrBadPattern is not equal to // filepath.ErrBadPattern. -// func FilepathGlob(pattern string, opts ...GlobOption) (matches []string, err error) { if pattern == "" { // special case to match filepath.Glob behavior @@ -152,9 +150,16 @@ func indexNextAlt(s string, allowEscaping bool) int { return -1 } -var metaReplacer = strings.NewReplacer("\\*", "*", "\\?", "?", "\\[", "[", "\\]", "]", "\\{", "{", "\\}", "}") +var escapeMetaReplacer = strings.NewReplacer("*", "\\*", "?", "\\?", "[", "\\[", "]", "\\]", "{", "\\{", "}", "\\}") + +// Escapes meta characters (*?[]{}) +func escapeMeta(path string) string { + return escapeMetaReplacer.Replace(path) +} + +var unescapeMetaReplacer = strings.NewReplacer("\\*", "*", "\\?", "?", "\\[", "[", "\\]", "]", "\\{", "{", "\\}", "}") // Unescapes meta characters (*?[]{}) func unescapeMeta(pattern string) string { - return metaReplacer.Replace(pattern) + return unescapeMetaReplacer.Replace(pattern) } diff --git a/vendor/github.com/bmatcuk/doublestar/v4/utils_unix.go b/vendor/github.com/bmatcuk/doublestar/v4/utils_unix.go new file mode 100644 index 00000000..343e55f8 --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/utils_unix.go @@ -0,0 +1,19 @@ +//go:build !windows + +package doublestar + +import ( + "io/fs" +) + +// Returns true if the pattern could "unintentionally" match hidden files/dirs. +// An unintentional pattern would use a meta character that could match +// anything. +func couldUnintentionallyMatchHidden(pattern string) bool { + return len(pattern) > 0 && (pattern[0] == '*' || pattern[0] == '?') +} + +// Returns true if the file is "hidden" +func isHiddenPath(filename string, _info fs.DirEntry) (bool, error) { + return filename[0] == '.', nil +} diff --git a/vendor/github.com/bmatcuk/doublestar/v4/utils_windows.go b/vendor/github.com/bmatcuk/doublestar/v4/utils_windows.go new file mode 100644 index 00000000..5c6a67ca --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/utils_windows.go @@ -0,0 +1,43 @@ +package doublestar + +import ( + "io/fs" + "syscall" +) + +// Returns true if the pattern could "unintentionally" match hidden files/dirs. +// An unintentional pattern would use a meta character that could match +// anything. +func couldUnintentionallyMatchHidden(pattern string) bool { + var c byte + inClass := false + l := len(pattern) + for i := 0; i < l; i++ { + c = pattern[i] + if !inClass && (c == '*' || c == '?') { + return true + } else if c == '[' { + inClass = true + } else if c == '\\' { + // skip next byte + i++ + } else if inClass && c == ']' { + inClass = false + } + } + return false +} + +// Returns true if the file is "hidden" +func isHiddenPath(_filename string, info fs.DirEntry) (bool, error) { + fileinfo, err := info.Info() + if err != nil { + return false, err + } + + if stat, ok := fileinfo.Sys().(*syscall.Win32FileAttributeData); ok { + return stat.FileAttributes&syscall.FILE_ATTRIBUTE_HIDDEN != 0, nil + } + + return false, nil +} diff --git a/vendor/github.com/bmatcuk/doublestar/v4/validate.go b/vendor/github.com/bmatcuk/doublestar/v4/validate.go index c689b9eb..312129e7 100644 --- a/vendor/github.com/bmatcuk/doublestar/v4/validate.go +++ b/vendor/github.com/bmatcuk/doublestar/v4/validate.go @@ -9,7 +9,6 @@ import "path/filepath" // you might want to validate it. // // ValidatePattern assumes your pattern uses '/' as the path separator. -// func ValidatePattern(s string) bool { return doValidatePattern(s, '/') } @@ -18,7 +17,6 @@ func ValidatePattern(s string) bool { // ValidatePattern if you would normally use Match() or Glob(). Use // ValidatePathPattern if you would normally use PathMatch(). Keep in mind, // Glob() requires '/' separators, even if your OS uses something else. -// func ValidatePathPattern(s string) bool { return doValidatePattern(s, filepath.Separator) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 33bde46f..1955ab3b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -141,7 +141,7 @@ github.com/aws/smithy-go/tracing github.com/aws/smithy-go/transport/http github.com/aws/smithy-go/transport/http/internal/io github.com/aws/smithy-go/waiter -# github.com/bmatcuk/doublestar/v4 v4.9.1 +# github.com/bmatcuk/doublestar/v4 v4.10.0 ## explicit; go 1.16 github.com/bmatcuk/doublestar/v4 # github.com/containerd/errdefs v1.0.0