Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# First: Install pre-commit https://pre-commit.com/#install
# Then, run `pre-commit install`
repos:
- repo: git://github.com/caitlinelfring/pre-commit-golang
- repo: https://github.com/caitlinelfring/pre-commit-golang
rev: v0.4.0
hooks:
- id: go-fmt
Expand Down
18 changes: 10 additions & 8 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ import (

var (
// flags
exitOneOnFailure bool
cfgFile string
debug bool
stdin bool
outputName string
noIgnore bool
disableDefaultRules bool
exitOneOnFailure bool
cfgFile string
debug bool
stdin bool
outputName string
noIgnore bool
disableDefaultRules bool
disableNestedIgnores bool

// Version is populated by goreleaser during build
// Version...
Expand Down Expand Up @@ -109,7 +110,7 @@ func rootRunE(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
ignorer, err = ignore.NewIgnore(fs, cfg.IgnoreFiles)
ignorer, err = ignore.NewIgnore(fs, cfg.IgnoreFiles, disableNestedIgnores)
if err != nil {
return err
}
Expand Down Expand Up @@ -155,6 +156,7 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&noIgnore, "no-ignore", false, "Ignored files in .gitignore, .ignore, .wokeignore, .git/info/exclude, and inline ignores are processed")
rootCmd.PersistentFlags().StringVarP(&outputName, "output", "o", printer.OutFormatText, fmt.Sprintf("Output type [%s]", printer.OutFormatsString))
rootCmd.PersistentFlags().BoolVar(&disableDefaultRules, "disable-default-rules", false, "Disable the default ruleset")
rootCmd.PersistentFlags().BoolVar(&disableNestedIgnores, "disable-nested-ignores", false, "Disable nested woke ignore traversal")
}

// GetRootCmd returns the rootCmd, which should only be used by the docs generator in cmd/docs/main.go
Expand Down
7 changes: 5 additions & 2 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ import (
// run profiling with
// go test -v -cpuprofile cpu.prof -memprofile mem.prof -bench=. ./cmd
// memory:
// go tool pprof mem.prof
//
// go tool pprof mem.prof
//
// cpu:
// go tool pprof cpu.prof
//
// go tool pprof cpu.prof
func BenchmarkRootRunE(b *testing.B) {
zerolog.SetGlobalLevel(zerolog.NoLevel)
output.Stdout = io.Discard
Expand Down
2 changes: 2 additions & 0 deletions docs/ignore.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ func main() {

`woke` will apply ignore rules from nested ignore files to any child files/folders, similar to a nested `.gitignore` file. Nested ignore files work for any ignore file type listed above.

>Note: To disable nested ignore file functionality, run `woke` with the `--disable-nested-ignores` flag.

```txt
project
│ README.md
Expand Down
45 changes: 40 additions & 5 deletions pkg/ignore/ignore.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ignore

import (
"bufio"
"errors"
"io"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -65,7 +68,7 @@ func GetRootGitDir(workingDir string) (filesystem billy.Filesystem, err error) {

// NewIgnore produces an Ignore object, with compiled lines from defaultIgnoreFiles
// which you can match files against
func NewIgnore(filesystem billy.Filesystem, lines []string) (ignore *Ignore, err error) {
func NewIgnore(filesystem billy.Filesystem, lines []string, disableNestedIgnores bool) (ignore *Ignore, err error) {
start := time.Now()
defer func() {
log.Debug().
Expand All @@ -79,8 +82,16 @@ func NewIgnore(filesystem billy.Filesystem, lines []string) (ignore *Ignore, err
}

var ps []gitignore.Pattern
if ps, err = gitignore.ReadPatterns(filesystem, nil, defaultIgnoreFiles); err != nil {
return

// if opted-out of nested wokeignore traversal, only use top-level ignore files
if disableNestedIgnores {
for _, filename := range defaultIgnoreFiles {
lines = append(lines, readIgnoreFile(filesystem, filename)...)
}
} else {
if ps, err = gitignore.ReadPatterns(filesystem, nil, defaultIgnoreFiles); err != nil {
return
}
}

// get domain for git ignore rules supplied from the lines argument
Expand All @@ -90,11 +101,35 @@ func NewIgnore(filesystem billy.Filesystem, lines []string) (ignore *Ignore, err
ps = append(ps, pattern)
}

ignore = &Ignore{
return &Ignore{
matcher: gitignore.NewMatcher(ps),
}, nil
}

func readIgnoreFile(filesystem billy.Filesystem, file string) []string {
openFile, err := filesystem.Open(file)

if err != nil {
_event := log.Warn()
if errors.Is(err, os.ErrNotExist) {
_event = log.Debug()
}
_event.Err(err).Str("file", file).Msg("skipping ignorefile")
return []string{}
}

defer openFile.Close()
var buffer []byte
buffer, err = io.ReadAll(bufio.NewReader(openFile))

if err != nil {
_event := log.Warn()
_event.Err(err).Str("file", file).Msg("skipping ignorefile")
return []string{}
}

return
log.Debug().Str("file", file).Msg("adding ignorefile")
return strings.Split(strings.TrimSpace(string(buffer)), "\n")
}

// Match returns true if the provided file matches any of the defined ignores
Expand Down
80 changes: 69 additions & 11 deletions pkg/ignore/ignore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,25 @@ func (suite *IgnoreTestSuite) SetupTest() {
err = f.Close()
suite.NoError(err)

err = fs.MkdirAll("nestedIgnoreFolder", os.ModePerm)
suite.NoError(err)

f, err = fs.Create(fs.Join("nestedIgnoreFolder", ".wokeignore"))
suite.NoError(err)
_, err = f.Write([]byte("*.NESTEDIGNORE\n"))
suite.NoError(err)
err = f.Close()
suite.NoError(err)

suite.GFS = fs
}

func BenchmarkIgnore(b *testing.B) {
zerolog.SetGlobalLevel(zerolog.NoLevel)
fs, clean := TempFileSystem()
defer clean()
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
for i := 0; i < 50; i++ {
for j := 0; j < 50; j++ {
err := fs.MkdirAll(fs.Join(fmt.Sprintf("%d", i), fmt.Sprintf("%d", j)), os.ModePerm)
assert.NoError(b, err)
f, err := fs.Create(".wokeignore")
Expand All @@ -102,11 +112,20 @@ func BenchmarkIgnore(b *testing.B) {
}

b.ResetTimer()
for i := 0; i < b.N; i++ {
ignorer, err := NewIgnore(fs, []string{})
assert.NoError(b, err)
ignorer.Match(filepath.Join("not", "foo"), false)
}
b.Run("nested-ignores-enabled", func(b *testing.B) {
for i := 0; i < b.N; i++ {
ignorer, err := NewIgnore(fs, []string{}, false)
assert.NoError(b, err)
ignorer.Match(filepath.Join("not", "foo"), false)
}
})
b.Run("nested-ignores-disabled", func(b *testing.B) {
for i := 0; i < b.N; i++ {
ignorer, err := NewIgnore(fs, []string{}, true)
assert.NoError(b, err)
ignorer.Match(filepath.Join("not", "foo"), false)
}
})
}

func (suite *IgnoreTestSuite) TestGetDomainFromWorkingDir() {
Expand Down Expand Up @@ -135,8 +154,19 @@ func (suite *IgnoreTestSuite) TestGetRootGitDirNotExist() {
suite.NoError(err)
suite.Equal(fs.Root(), rootFs.Root())
}
func (suite *IgnoreTestSuite) TestIgnore_Match() {
i, err := NewIgnore(suite.GFS, []string{"my/files/*"})

func (suite *IgnoreTestSuite) TestIgnoreLines_Match() {
i, err := NewIgnore(suite.GFS, []string{"my/files/*"}, false)
suite.NoError(err)
suite.NotNil(i)

suite.False(i.Match(filepath.Join("not", "foo"), false))
suite.True(i.Match(filepath.Join("my", "files", "file1"), false))
suite.False(i.Match(filepath.Join("my", "files"), false))
}

func (suite *IgnoreTestSuite) TestIgnoreLinesNoTraversal_Match() {
i, err := NewIgnore(suite.GFS, []string{"my/files/*"}, true)
suite.NoError(err)
suite.NotNil(i)

Expand All @@ -147,11 +177,31 @@ func (suite *IgnoreTestSuite) TestIgnore_Match() {

// Test all default ignore files, except for .git/info/exclude, since
// that uses a .git directory that we cannot check in.
func (suite *IgnoreTestSuite) TestIgnoreDefaultIgoreFiles_Match() {
i, err := NewIgnore(suite.GFS, []string{"*.FROMARGUMENT"})
func (suite *IgnoreTestSuite) TestIgnoreDefaultIgnoreFiles_Match() {
i, err := NewIgnore(suite.GFS, []string{"*.FROMARGUMENT"}, false)
suite.NoError(err)
suite.NotNil(i)

// Test top-level ignore files all match
suite.testCommonIgnoreDefaultIgnoreFilesMatch(i)

// Test match from the nested ./nestedIgnoreFolder/.wokeignore when nested ignores is enabled
suite.True(i.Match(filepath.Join("nestedIgnoreFolder", "testdata", "test.NESTEDIGNORE"), false))
}

func (suite *IgnoreTestSuite) TestIgnoreDefaultIgnoreFilesNoTraversal_Match() {
i, err := NewIgnore(suite.GFS, []string{"*.FROMARGUMENT"}, true)
suite.NoError(err)
suite.NotNil(i)

// Test top-level ignore files all match
suite.testCommonIgnoreDefaultIgnoreFilesMatch(i)

// Test no match from the nested ./nestedIgnoreFolder/.wokeignore when nested ignores is disabled
suite.False(i.Match(filepath.Join("nestedIgnoreFolder", "testdata", "test.NESTEDIGNORE"), false))
}

func (suite *IgnoreTestSuite) testCommonIgnoreDefaultIgnoreFilesMatch(i *Ignore) {
suite.False(i.Match(filepath.Join("testdata", "notfoo"), false))
suite.True(i.Match(filepath.Join("testdata", "test.FROMARGUMENT"), false)) // From .gitignore
suite.True(i.Match(filepath.Join("testdata", "test.DS_Store"), false)) // From .gitignore
Expand All @@ -162,6 +212,14 @@ func (suite *IgnoreTestSuite) TestIgnoreDefaultIgoreFiles_Match() {
suite.False(i.Match(filepath.Join("testdata", "test.NOTIGNORED"), false)) // From .notincluded - making sure only default are included
}

func (suite *IgnoreTestSuite) TestReadIgnoreFile() {
ignoreLines := readIgnoreFile(suite.GFS, ".gitignore")
suite.Equal([]string{"*.DS_Store"}, ignoreLines)

noIgnoreLines := readIgnoreFile(suite.GFS, "missing.gitignore")
suite.Equal([]string{}, noIgnoreLines)
}

// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestIgnoreTestSuite(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions pkg/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func testParser() (parser *Parser, err error) {
return
}
fs := osfs.New(cwd)
ignorer, err := ignore.NewIgnore(fs, []string{})
ignorer, err := ignore.NewIgnore(fs, []string{}, false)
if err != nil {
return
}
Expand Down Expand Up @@ -149,7 +149,7 @@ func parsePathTests(t *testing.T) {
cwd, err := os.Getwd()
assert.NoError(t, err)
fs := osfs.New(cwd)
ignorer, err := ignore.NewIgnore(fs, []string{filepath.ToSlash(f.Name())})
ignorer, err := ignore.NewIgnore(fs, []string{filepath.ToSlash(f.Name())}, false)
assert.NoError(t, err)
p.Ignorer = ignorer
pr := new(testPrinter)
Expand Down Expand Up @@ -193,7 +193,7 @@ func parsePathTests(t *testing.T) {
cwd, err := os.Getwd()
assert.NoError(t, err)
fs := osfs.New(cwd)
ignorer, err := ignore.NewIgnore(fs, []string{"*_test.go"})
ignorer, err := ignore.NewIgnore(fs, []string{"*_test.go"}, false)
assert.NoError(t, err)
p.Ignorer = ignorer
pr := new(testPrinter)
Expand Down