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

support ignoring file or directories #84

Merged
merged 2 commits into from
Aug 9, 2021
Merged
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 .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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ by scanning directory patterns recursively.
It modifies all source files in place and avoids adding a license header
to any file that already has one.

addlicense requires go 1.16 or later.

## install

go get -u github.com/google/addlicense
Expand All @@ -19,10 +21,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)
}
}
}