Skip to content

Commit

Permalink
support ignoring file or directories
Browse files Browse the repository at this point in the history
use the doublestar library to support pattern matching of files or
directories to ignore. This replaces (and deprecates) the previous
-skip flag which only supported file extensions.
  • Loading branch information
willnorris committed Aug 9, 2021
1 parent b431030 commit 6e54dc1
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
test:
strategy:
matrix:
go-version: [1.x, 1.15.x]
go-version: [1.x, 1.16.x]
platform: [ubuntu-latest]
include:
# only update test coverage stats with the most recent go version on linux
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ to any file that already has one.
-l license type: apache, bsd, mit, mpl (defaults to "apache")
-y year (defaults to current year)
-check check only mode: verify presence of license headers and exit with non-zero code if missing
-ignore file patterns to ignore, for example: -ignore **/*.go -ignore vendor/**

The pattern argument can be provided multiple times, and may also refer
to single files.

The `-ignore` flag can use any pattern [supported by
doublestar](https://github.com/bmatcuk/doublestar#patterns).

## Running in a Docker Container

- Clone the repository using `git clone https://github.com/google/addlicense.git`
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module github.com/google/addlicense

go 1.13

require golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
require (
github.com/bmatcuk/doublestar/v4 v4.0.2
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
github.com/bmatcuk/doublestar/v4 v4.0.2 h1:X0krlUVAVmtr2cRoTqR8aDMrDqnB36ht8wpWTiQ3jsA=
github.com/bmatcuk/doublestar/v4 v4.0.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
45 changes: 35 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"text/template"
"time"

doublestar "github.com/bmatcuk/doublestar/v4"
"golang.org/x/sync/errgroup"
)

Expand All @@ -48,7 +49,8 @@ Flags:
`

var (
skipExtensionFlags skipExtensionFlag
skipExtensionFlags stringSlice
ignorePatterns stringSlice
spdx spdxFlag

holder = flag.String("c", "Google LLC", "copyright holder")
Expand All @@ -64,17 +66,19 @@ func init() {
fmt.Fprintln(os.Stderr, helpText)
flag.PrintDefaults()
}
flag.Var(&skipExtensionFlags, "skip", "To skip files to check/add the header file, for example: -skip rb -skip go")
flag.Var(&skipExtensionFlags, "skip", "[deprecated: see -ignore] file extensions to skip, for example: -skip rb -skip go")
flag.Var(&ignorePatterns, "ignore", "file patterns to ignore, for example: -ignore **/*.go -ignore vendor/**")
flag.Var(&spdx, "s", "Include SPDX identifier in license header. Set -s=only to only include SPDX identifier.")
}

type skipExtensionFlag []string
// stringSlice stores the results of a repeated command line flag as a string slice.
type stringSlice []string

func (i *skipExtensionFlag) String() string {
func (i *stringSlice) String() string {
return fmt.Sprint(*i)
}

func (i *skipExtensionFlag) Set(value string) error {
func (i *stringSlice) Set(value string) error {
*i = append(*i, value)
return nil
}
Expand Down Expand Up @@ -109,6 +113,17 @@ func main() {
os.Exit(1)
}

// convert -skip flags to -ignore equivalents
for _, s := range skipExtensionFlags {
ignorePatterns = append(ignorePatterns, fmt.Sprintf("**/*.%s", s))
}
// verify that all ignorePatterns are valid
for _, p := range ignorePatterns {
if !doublestar.ValidatePattern(p) {
log.Fatalf("-ignore pattern %q is not valid", p)
}
}

// map legacy license values
if t, ok := legacyLicenseTypes[*license]; ok {
*license = t
Expand Down Expand Up @@ -200,17 +215,27 @@ func walk(ch chan<- *file, start string) error {
if fi.IsDir() {
return nil
}
for _, skip := range skipExtensionFlags {
if strings.TrimPrefix(filepath.Ext(fi.Name()), ".") == skip || fi.Name() == skip {
log.Printf("%s: skipping this file", fi.Name())
return nil
}
if fileMatches(path, ignorePatterns) {
log.Printf("skipping: %s", path)
return nil
}
ch <- &file{path, fi.Mode()}
return nil
})
}

// fileMatches determines if path matches one of the provided file patterns.
// Patterns are assumed to be valid.
func fileMatches(path string, patterns []string) bool {
for _, p := range patterns {
// ignore error, since we assume patterns are valid
if match, _ := doublestar.Match(p, path); match {
return true
}
}
return false
}

// addLicense add a license to the file if missing.
//
// It returns true if the file was updated.
Expand Down
60 changes: 60 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,63 @@ func TestHasLicense(t *testing.T) {
}
}
}

func TestFileMatches(t *testing.T) {
tests := []struct {
pattern string
path string
wantMatch bool
}{
// basic single directory patterns
{"", "file.c", false},
{"*.c", "file.h", false},
{"*.c", "file.c", true},

// subdirectory patterns
{"*.c", "vendor/file.c", false},
{"**/*.c", "vendor/file.c", true},
{"vendor/**", "vendor/file.c", true},
{"vendor/**/*.c", "vendor/file.c", true},
{"vendor/**/*.c", "vendor/a/b/file.c", true},

// single character "?" match
{"*.?", "file.c", true},
{"*.?", "file.go", false},
{"*.??", "file.c", false},
{"*.??", "file.go", true},

// character classes - sets and ranges
{"*.[ch]", "file.c", true},
{"*.[ch]", "file.h", true},
{"*.[ch]", "file.ch", false},
{"*.[a-z]", "file.c", true},
{"*.[a-z]", "file.h", true},
{"*.[a-z]", "file.go", false},
{"*.[a-z]", "file.R", false},

// character classes - negations
{"*.[^ch]", "file.c", false},
{"*.[^ch]", "file.h", false},
{"*.[^ch]", "file.R", true},
{"*.[!ch]", "file.c", false},
{"*.[!ch]", "file.h", false},
{"*.[!ch]", "file.R", true},

// comma-separated alternative matches
{"*.{c,go}", "file.c", true},
{"*.{c,go}", "file.go", true},
{"*.{c,go}", "file.h", false},

// negating alternative matches
{"*.[^{c,go}]", "file.c", false},
{"*.[^{c,go}]", "file.go", false},
{"*.[^{c,go}]", "file.h", true},
}

for _, tt := range tests {
patterns := []string{tt.pattern}
if got := fileMatches(tt.path, patterns); got != tt.wantMatch {
t.Errorf("fileMatches(%q, %q) returned %v, want %v", tt.path, patterns, got, tt.wantMatch)
}
}
}

0 comments on commit 6e54dc1

Please sign in to comment.