Skip to content

Commit

Permalink
fix: resolve ../ on pattern (#21)
Browse files Browse the repository at this point in the history
* fix: resolve ../ on pattern

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: log

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: do not break api

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: lint

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: docs

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: tonixpath

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* chore: debug

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* chore: debug

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* chore: debug

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* chore: debug

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* chore: debug

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: paths on windows

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: tests

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: tonixpath quotemeta

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: toslash

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: tests

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: tests

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: toslash

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>
  • Loading branch information
caarlos0 committed Feb 28, 2021
1 parent d5e84dd commit 975308f
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 33 deletions.
52 changes: 31 additions & 21 deletions glob.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io"
"io/fs"
"os"
"path"
"path/filepath"
"strings"

Expand All @@ -27,6 +26,8 @@ type globOptions struct {
matchDirectoriesDirectly bool

prefix string

pattern string
}

// OptFunc is a function that allow to customize Glob.
Expand All @@ -43,22 +44,20 @@ func WithFs(f fs.FS) OptFunc {
// volume (on windows) if the given pattern is an absolute path.
//
// Result will also be prepended with the root path or volume.
func MaybeRootFS(pattern string) OptFunc {
if !filepath.IsAbs(pattern) {
return func(opts *globOptions) {}
func MaybeRootFS(opts *globOptions) {
if !filepath.IsAbs(opts.pattern) {
return
}
return func(opts *globOptions) {
prefix := ""
if strings.HasPrefix(pattern, separatorString) {
prefix = separatorString
}
if vol := filepath.VolumeName(pattern); vol != "" {
prefix = vol + "/"
}
if prefix != "" {
opts.prefix = prefix
opts.fs = os.DirFS(prefix)
}
prefix := ""
if strings.HasPrefix(opts.pattern, separatorString) {
prefix = separatorString
}
if vol := filepath.VolumeName(opts.pattern); vol != "" {
prefix = vol + "/"
}
if prefix != "" {
opts.prefix = prefix
opts.fs = os.DirFS(prefix)
}
}

Expand Down Expand Up @@ -95,14 +94,24 @@ func QuoteMeta(pattern string) string {
// toNixPath converts the path to the nix style path
// Windows style path separators are escape characters so cause issues with the compiled glob.
func toNixPath(s string) string {
return path.Clean(filepath.ToSlash(s))
return filepath.ToSlash(filepath.Clean(s))
}

// Glob returns all files that match the given pattern in the current directory.
// If the given pattern indicates an absolute path, it will glob from `/`.
// If the given pattern starts with `../`, it will resolve to its absolute path and glob from `/`.
func Glob(pattern string, opts ...OptFunc) ([]string, error) { // nolint:funlen,cyclop
var matches []string
options := compileOptions(opts)

if strings.HasPrefix(pattern, "../") {
p, err := filepath.Abs(pattern)
if err != nil {
return matches, fmt.Errorf("failed to resolve pattern: %s: %w", pattern, err)
}
pattern = filepath.ToSlash(p)
}

options := compileOptions(opts, pattern)

pattern = strings.TrimSuffix(strings.TrimPrefix(pattern, options.prefix), separatorString)
matcher, err := glob.Compile(pattern, separatorRune)
Expand Down Expand Up @@ -178,10 +187,11 @@ func Glob(pattern string, opts ...OptFunc) ([]string, error) { // nolint:funlen,
return cleanFilepaths(matches, options.prefix), nil
}

func compileOptions(optFuncs []OptFunc) *globOptions {
func compileOptions(optFuncs []OptFunc, pattern string) *globOptions {
opts := &globOptions{
fs: os.DirFS("."),
prefix: "./",
fs: os.DirFS("."),
prefix: "./",
pattern: pattern,
}

for _, apply := range optFuncs {
Expand Down
57 changes: 45 additions & 12 deletions glob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestGlob(t *testing.T) { // nolint:funlen
"glob_test.go",
"prefix_test.go",
}, matches)
require.Equal(t, "&{fs:. matchDirectoriesDirectly:false prefix:./}", w.String())
require.Equal(t, "&{fs:. matchDirectoriesDirectly:false prefix:./ pattern:*_test.go}", w.String())
})

t.Run("real with rootfs", func(t *testing.T) {
Expand All @@ -35,20 +35,49 @@ func TestGlob(t *testing.T) { // nolint:funlen
require.NoError(t, err)

prefix := "/"
if runtime.GOOS == "windows" {
if isWindows() {
prefix = filepath.VolumeName(wd) + "/"
}

pattern := toNixPath(filepath.Join(wd, "*_test.go"))

var w bytes.Buffer
matches, err := Glob(pattern, MaybeRootFS(pattern), WriteOptions(&w))
matches, err := Glob(pattern, MaybeRootFS, WriteOptions(&w))
require.NoError(t, err)
require.Equal(t, []string{
toNixPath(filepath.Join(wd, "glob_test.go")),
toNixPath(filepath.Join(wd, "prefix_test.go")),
}, matches)
require.Equal(t, fmt.Sprintf("&{fs:%s matchDirectoriesDirectly:false prefix:%s}", prefix, prefix), w.String())
require.Equal(t, fmt.Sprintf("&{fs:%s matchDirectoriesDirectly:false prefix:%s pattern:%s}", prefix, prefix, pattern), w.String())
})

t.Run("real with rootfs on relative path to parent", func(t *testing.T) {
t.Parallel()

wd, err := os.Getwd()
require.NoError(t, err)

dir := filepath.Base(wd)

prefix := "/"
if isWindows() {
prefix = filepath.VolumeName(wd) + "/"
}

pattern := "../" + dir + "/*_test.go"
abs, err := filepath.Abs(pattern)
require.NoError(t, err)

abs = toNixPath(abs)

var w bytes.Buffer
matches, err := Glob(pattern, MaybeRootFS, WriteOptions(&w))
require.NoError(t, err)
require.Equal(t, []string{
toNixPath(filepath.Join(wd, "glob_test.go")),
toNixPath(filepath.Join(wd, "prefix_test.go")),
}, matches)
require.Equal(t, fmt.Sprintf("&{fs:%s matchDirectoriesDirectly:false prefix:%s pattern:%s}", prefix, prefix, abs), w.String())
})

t.Run("real with rootfs on relative path", func(t *testing.T) {
Expand All @@ -57,13 +86,13 @@ func TestGlob(t *testing.T) { // nolint:funlen
pattern := "./*_test.go"

var w bytes.Buffer
matches, err := Glob(pattern, MaybeRootFS(pattern), WriteOptions(&w))
matches, err := Glob(pattern, MaybeRootFS, WriteOptions(&w))
require.NoError(t, err)
require.Equal(t, []string{
"glob_test.go",
"prefix_test.go",
}, matches)
require.Equal(t, "&{fs:. matchDirectoriesDirectly:false prefix:./}", w.String())
require.Equal(t, "&{fs:. matchDirectoriesDirectly:false prefix:./ pattern:./*_test.go}", w.String())
})

t.Run("real with rootfs on relative path match dir", func(t *testing.T) {
Expand All @@ -72,12 +101,12 @@ func TestGlob(t *testing.T) { // nolint:funlen
pattern := ".github"

var w bytes.Buffer
matches, err := Glob(pattern, MaybeRootFS(pattern), MatchDirectoryAsFile, WriteOptions(&w))
matches, err := Glob(pattern, MaybeRootFS, MatchDirectoryAsFile, WriteOptions(&w))
require.NoError(t, err)
require.Equal(t, []string{
".github",
}, matches)
require.Equal(t, "&{fs:. matchDirectoriesDirectly:true prefix:./}", w.String())
require.Equal(t, "&{fs:. matchDirectoriesDirectly:true prefix:./ pattern:.github}", w.String())
})

t.Run("real with rootfs on relative path match dir", func(t *testing.T) {
Expand All @@ -86,12 +115,12 @@ func TestGlob(t *testing.T) { // nolint:funlen
pattern := ".github/workflows/"

var w bytes.Buffer
matches, err := Glob(pattern, MaybeRootFS(pattern), MatchDirectoryAsFile, WriteOptions(&w))
matches, err := Glob(pattern, MaybeRootFS, MatchDirectoryAsFile, WriteOptions(&w))
require.NoError(t, err)
require.Equal(t, []string{
".github/workflows",
}, matches)
require.Equal(t, "&{fs:. matchDirectoriesDirectly:true prefix:./}", w.String())
require.Equal(t, "&{fs:. matchDirectoriesDirectly:true prefix:./ pattern:.github/workflows/}", w.String())
})

t.Run("simple", func(t *testing.T) {
Expand All @@ -113,7 +142,7 @@ func TestGlob(t *testing.T) { // nolint:funlen
"a/d/file1.txt",
"a/nope/file1.txt",
}, matches)
require.Equal(t, fmt.Sprintf("&{fs:%+v matchDirectoriesDirectly:false prefix:./}", fsys), w.String())
require.Equal(t, fmt.Sprintf("&{fs:%+v matchDirectoriesDirectly:false prefix:./ pattern:./a/*/*}", fsys), w.String())
})

t.Run("single file", func(t *testing.T) {
Expand Down Expand Up @@ -270,7 +299,7 @@ func TestGlob(t *testing.T) { // nolint:funlen

t.Run("escaped asterisk", func(t *testing.T) {
t.Parallel()
if runtime.GOOS == "windows" {
if isWindows() {
t.Skip("can't create paths with * on Windows")
}
matches, err := Glob("a/\\*/b", WithFs(testFs(t, []string{
Expand Down Expand Up @@ -408,3 +437,7 @@ func testFs(tb testing.TB, files, dirs []string) fs.FS {

return tmpfs
}

func isWindows() bool {
return runtime.GOOS == "windows"
}

0 comments on commit 975308f

Please sign in to comment.