Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to expand glob patterns #211

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1205c1c
Add support to expand glob patterns
sebastien-rosset Jul 5, 2022
2dbd543
upgrade to doublestart v4.2.0
sebastien-rosset Jul 26, 2022
94efac6
upgrade to doublestart v4.2.0
sebastien-rosset Jul 26, 2022
505a846
sync from main
sebastien-rosset Jul 27, 2022
07b7da0
run go mod tidy
sebastien-rosset Jul 27, 2022
6047192
run go mod tidy
sebastien-rosset Jul 27, 2022
f17d697
run go mod tidy
sebastien-rosset Jul 27, 2022
bb055f0
simplify call to FilepathGlob
sebastien-rosset Jul 27, 2022
f2da348
Merge branch 'get-woke:main' into glob-expansion
sebastien-rosset Jul 28, 2022
9ed2b82
sync from main
sebastien-rosset Nov 6, 2022
0b05fe1
sync from main
sebastien-rosset Nov 6, 2022
5580a09
upgade doublestar
sebastien-rosset Nov 6, 2022
d3228da
upgade doublestar
sebastien-rosset Nov 6, 2022
e6fd20e
fix UT issue
sebastien-rosset Nov 7, 2022
af98f00
fix UT issue for windows
sebastien-rosset Nov 7, 2022
4adaedb
fix usage formatting issues
sebastien-rosset Nov 7, 2022
3202cef
fix UT issue for windows
sebastien-rosset Nov 7, 2022
8c7a203
fix UT issue for windows and stdin
sebastien-rosset Nov 7, 2022
2c0cb4e
this is a test for woke output format
sebastien-rosset Nov 7, 2022
741f6e7
this is a test for woke output format
sebastien-rosset Nov 7, 2022
05fb6ca
rename test files to avoid woke issue in ci
sebastien-rosset Nov 7, 2022
8968dd7
group imports
sebastien-rosset Nov 7, 2022
605f45e
run go mod tidy
sebastien-rosset Nov 7, 2022
db9bfd3
remove woke-args which was used to troubleshoot CI issue
sebastien-rosset Nov 7, 2022
5d44eb3
improve documentation, add unit tests
sebastien-rosset Nov 9, 2022
2c6bd96
add unit tests and code coverage
sebastien-rosset Nov 9, 2022
0533ec6
replace tab with space characters
sebastien-rosset Nov 9, 2022
92f7ec0
remove tabs
sebastien-rosset Nov 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 22 additions & 5 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/get-woke/woke/pkg/parser"
"github.com/get-woke/woke/pkg/printer"

"github.com/bmatcuk/doublestar/v4"
"github.com/mitchellh/go-homedir"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -121,7 +122,11 @@ func rootRunE(cmd *cobra.Command, args []string) error {
return err
}

findings := p.ParsePaths(print, parseArgs(args)...)
files, err := parseArgs(args)
if err != nil {
return err
}
findings := p.ParsePaths(print, files...)

if exitOneOnFailure && findings > 0 {
// We intentionally return an error if exitOneOnFailure is true, but don't want to show usage
Expand Down Expand Up @@ -162,16 +167,28 @@ func GetRootCmd() cobra.Command {
return *rootCmd
}

func parseArgs(args []string) []string {
// parseArgs parses the command-line positional arguments that contain file glob patterns.
// If no argument is provided, return the default path (current directory).
// Perform glob pattern expansion.
func parseArgs(args []string) ([]string, error) {
if len(args) == 0 {
args = parser.DefaultPath
}

var files []string
if stdin {
args = []string{os.Stdin.Name()}
files = []string{os.Stdin.Name()}
} else {
// Perform glob expansion.
for _, arg := range args {
f, err := doublestar.FilepathGlob(arg)
if err != nil {
return nil, err
}
files = append(files, f...)
}
}

return args
return files, nil
}

func setDebugLogLevel() {
Expand Down
171 changes: 163 additions & 8 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ package cmd

import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"testing"

"github.com/get-woke/woke/pkg/output"
"github.com/get-woke/woke/pkg/parser"

"github.com/bmatcuk/doublestar/v4"
"github.com/mitchellh/go-homedir"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
Expand All @@ -20,9 +24,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 Expand Up @@ -70,12 +77,147 @@ func TestParseArgs(t *testing.T) {
t.Cleanup(func() {
stdin = false
})
assert.Equal(t, parser.DefaultPath, parseArgs([]string{}))
assert.Equal(t, []string{"../.."}, parseArgs([]string{"../.."}))
tests := []struct {
stdin bool
args []string
expectedArgs []string
expectedError error
}{
{
stdin: false,
args: []string{},
expectedArgs: parser.DefaultPath,
expectedError: nil,
},
{
stdin: false,
args: []string{"../.."},
expectedArgs: []string{filepath.Join("..", "..")},
expectedError: nil,
},

// Test glob expansion
{
stdin: false,
args: []string{"../testdata/*.yml"},
expectedArgs: []string{
filepath.Join("..", "testdata/bad.yml"),
filepath.Join("..", "testdata/good.yml"),
},
expectedError: nil,
},
{
stdin: false,
args: []string{"../testdata/g??d.yml"}, // matches any single non-separator character
expectedArgs: []string{filepath.Join("..", "testdata/good.yml")},
expectedError: nil,
},
{
stdin: false,
args: []string{"../testdata/[a-z]ood.yml"}, // character range
expectedArgs: []string{filepath.Join("..", "testdata", "good.yml")},
expectedError: nil,
},
{
stdin: false,
args: []string{"../testdata/[^abc]ood.yml"}, // character class with negation.
expectedArgs: []string{filepath.Join("..", "testdata", "good.yml")},
expectedError: nil,
},
{
stdin: false,
args: []string{"../testdata/[!abc]ood.yml"}, // character class with negation.
expectedArgs: []string{filepath.Join("..", "testdata", "good.yml")},
expectedError: nil,
},
{
stdin: false,
args: []string{"../testdata/[^g]ood.yml"}, // character class with negation.
expectedArgs: nil,
expectedError: nil,
},
{
stdin: false,
args: []string{"../testdata/*/*.yml"},
expectedArgs: []string{
filepath.Join("..", "testdata", "subdir1", "bad.yml"),
filepath.Join("..", "testdata", "subdir1", "good.yml"),
},
expectedError: nil,
},
{
stdin: false,
args: []string{"../testdata/**/*.yml"},
expectedArgs: []string{
filepath.Join("..", "testdata", "bad.yml"),
filepath.Join("..", "testdata", "good.yml"),
filepath.Join("..", "testdata", "subdir1", "bad.yml"),
filepath.Join("..", "testdata", "subdir1", "good.yml"),
filepath.Join("..", "testdata", "subdir1", "subdir2", "bad.yml"),
filepath.Join("..", "testdata", "subdir1", "subdir2", "good.yml"),
},
expectedError: nil,
},
{
stdin: false,
args: []string{"../testdata/**/{good,bad}.yml"}, // Alternate pattern
expectedArgs: []string{
filepath.Join("..", "testdata", "bad.yml"),
filepath.Join("..", "testdata", "good.yml"),
filepath.Join("..", "testdata", "subdir1", "bad.yml"),
filepath.Join("..", "testdata", "subdir1", "good.yml"),
filepath.Join("..", "testdata", "subdir1", "subdir2", "bad.yml"),
filepath.Join("..", "testdata", "subdir1", "subdir2", "good.yml"),
},
expectedError: nil,
},
{
stdin: false,
args: []string{"../testdata/**/?ood.yml"},
expectedArgs: []string{
filepath.Join("..", "testdata", "good.yml"),
filepath.Join("..", "testdata", "subdir1", "good.yml"),
filepath.Join("..", "testdata", "subdir1", "subdir2", "good.yml"),
},
expectedError: nil,
},

// Bad glob pattern
{
stdin: false,
args: []string{"r[.go"}, // Invalid character class
expectedArgs: nil,
expectedError: doublestar.ErrBadPattern,
},
{
stdin: false,
args: []string{"{.go"}, // Bad alternate pattern
expectedArgs: nil,
expectedError: doublestar.ErrBadPattern,
},

stdin = true
assert.Equal(t, []string{os.Stdin.Name()}, parseArgs([]string{}))
assert.Equal(t, []string{os.Stdin.Name()}, parseArgs([]string{"../.."}))
{
stdin: true,
args: []string{},
expectedArgs: []string{os.Stdin.Name()},
expectedError: nil,
},
{
stdin: true,
args: []string{"../.."},
expectedArgs: []string{os.Stdin.Name()},
expectedError: nil,
},
}
for _, tt := range tests {
t.Run(strings.Join(tt.args, " "), func(t *testing.T) {
stdin = tt.stdin
files, err := parseArgs(tt.args)
assert.ErrorIs(t, err, tt.expectedError,
fmt.Sprintf("arguments: %v. Expected '%v', Got '%v'", tt.args, err, tt.expectedError))
assert.Equal(t, tt.expectedArgs, files)
})
}
}

func TestRunE(t *testing.T) {
Expand Down Expand Up @@ -109,7 +251,7 @@ func TestRunE(t *testing.T) {
assert.Equal(t, expected, got)
})

t.Run("findings w error", func(t *testing.T) {
t.Run("findings with inclusive language issues", func(t *testing.T) {
exitOneOnFailure = true
// don't ignore testdata folder
noIgnore = true
Expand All @@ -122,6 +264,19 @@ func TestRunE(t *testing.T) {
assert.Regexp(t, regexp.MustCompile(`^files with findings: \d`), err.Error())
})

t.Run("findings with invalid glob pattern", func(t *testing.T) {
exitOneOnFailure = true
// don't ignore testdata folder
noIgnore = true

t.Cleanup(func() {
exitOneOnFailure = false
})
err := rootRunE(new(cobra.Command), []string{"../testdata/**/[.yml"})
assert.Error(t, err)
assert.Regexp(t, regexp.MustCompile(`syntax error in pattern`), err.Error())
})

t.Run("no rules enabled", func(t *testing.T) {
disableDefaultRules = true
t.Cleanup(func() {
Expand Down
22 changes: 21 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,30 @@ No findings found.
### File globs

By default, `woke` will run against all text files in your current directory.
To change this, supply a space-separated list of globs as the first argument.
To change this, supply a space-separated list of file glob patterns.
`woke` supports the following glob pattern:

```console
pattern:
{ term }
term:
* matches any sequence of non-separator characters
? matches any single non-separator character
/**/ matches zero or more directories
[class] matches any single non-path-separator character against a class of characters
{alt1,...} matches a sequence of characters if one of the comma-separated alternatives matches

characters classes:
[abc] matches any single character within the set
[a-z] matches any single character in the range
[^class] matches any single character which does not match the class
[!class] same as ^: negates the class
```

This can be something like `**/*.go`, or a space-separated list of filenames.

If `woke` is invoked from a shell, the invoking shell performs file glob pattern expansion according to the shell glob rules.

```bash
$ woke test.txt
test.txt:2:2-11: `Blacklist` may be insensitive, use `denylist`, `blocklist` instead (warning)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/get-woke/woke
go 1.18

require (
github.com/bmatcuk/doublestar/v4 v4.3.2
github.com/caitlinelfring/go-env-default v1.1.0
github.com/fatih/color v1.13.0
github.com/get-woke/fastwalk v1.0.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bmatcuk/doublestar/v4 v4.3.2 h1:jhwioE79ok+L6P4xulmrNBfCD7yEkrR/BcGSCNDeVag=
github.com/bmatcuk/doublestar/v4 v4.3.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/caitlinelfring/go-env-default v1.1.0 h1:bhDfXmUolvcIGfQCX8qevQX8wxC54NGz0aimoUnhvDM=
github.com/caitlinelfring/go-env-default v1.1.0/go.mod h1:tESXPr8zFPP/cRy3cwxrHBmjJIf2A1x/o4C9CET2rEk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down
3 changes: 0 additions & 3 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ func (p *Parser) ParsePaths(print printer.Printer, paths ...string) int {
return r.Len()
}

if len(paths) == 0 {
paths = DefaultPath
}
var wg sync.WaitGroup

done := make(chan bool)
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions testdata/subdir1/bad.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
this file also has a whitelist finding
2 changes: 2 additions & 0 deletions testdata/subdir1/good.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
this file has no findings.
2 changes: 2 additions & 0 deletions testdata/subdir1/subdir2/bad.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
this file also has a whitelist finding
2 changes: 2 additions & 0 deletions testdata/subdir1/subdir2/good.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
this file has no findings.