Skip to content

#30: support --skip-files and --skip-dirs options: they skip files an… #74

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

Merged
merged 1 commit into from
Jun 7, 2018
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
4 changes: 4 additions & 0 deletions .golangci.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ run:
tests: true
build-tags:
- mytag
skip-dirs:
- external_libs
skip-files:
- ".*\\.pb\\.go$"

output:
format: colored-line-number
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ Flags:
--print-resources-usage Print avg and max memory usage of golangci-lint and total time
-c, --config PATH Read config from file path PATH
--no-config Don't read config
--skip-dirs strings Regexps of directory names to skip
--skip-files strings Regexps of file names to skip
-E, --enable strings Enable specific linter
-D, --disable strings Disable specific linter
--enable-all Enable all linters
Expand Down
13 changes: 12 additions & 1 deletion pkg/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
fs.BoolVar(&rc.PrintResourcesUsage, "print-resources-usage", false, wh("Print avg and max memory usage of golangci-lint and total time"))
fs.StringVarP(&rc.Config, "config", "c", "", wh("Read config from file path `PATH`"))
fs.BoolVar(&rc.NoConfig, "no-config", false, wh("Don't read config"))
fs.StringSliceVar(&rc.SkipDirs, "skip-dirs", nil, wh("Regexps of directory names to skip"))
fs.StringSliceVar(&rc.SkipFiles, "skip-files", nil, wh("Regexps of file names to skip"))

// Linters settings config
lsc := &cfg.LintersSettings
Expand Down Expand Up @@ -194,12 +196,21 @@ func (e *Executor) runAnalysis(ctx context.Context, args []string) (<-chan resul
if lintCtx.Program != nil {
fset = lintCtx.Program.Fset
}

skipFilesProcessor, err := processors.NewSkipFiles(e.cfg.Run.SkipFiles)
if err != nil {
return nil, err
}

runner := lint.SimpleRunner{
Processors: []processors.Processor{
processors.NewPathPrettifier(), // must be before diff processor at least
processors.NewExclude(excludeTotalPattern),
processors.NewCgo(),
skipFilesProcessor,

processors.NewExclude(excludeTotalPattern),
processors.NewNolint(fset),

processors.NewUniqByLine(),
processors.NewDiff(e.cfg.Issues.Diff, e.cfg.Issues.DiffFromRevision, e.cfg.Issues.DiffPatchFilePath),
processors.NewMaxPerFileFromLinter(),
Expand Down
3 changes: 3 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ type Run struct {
AnalyzeTests bool `mapstructure:"tests"`
Deadline time.Duration
PrintVersion bool

SkipFiles []string `mapstructure:"skip-files"`
SkipDirs []string `mapstructure:"skip-dirs"`
}

type LintersSettings struct {
Expand Down
17 changes: 14 additions & 3 deletions pkg/fsutils/fsutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import (
"github.com/sirupsen/logrus"
)

var stdExcludeDirs = []string{"vendor", "testdata", "examples", "Godeps", "builtin"}
var stdExcludeDirRegexps = []string{
"^vendor$", "^third_party$",
"^testdata$", "^examples$",
"^Godeps$",
"^builtin$",
}

func GetProjectRoot() string {
return path.Join(build.Default.GOPATH, "src", "github.com", "golangci", "golangci-worker")
Expand Down Expand Up @@ -101,7 +106,7 @@ func processResolvedPaths(paths *PathResolveResult) (*ProjectPaths, error) {
}, nil
}

func GetPathsForAnalysis(ctx context.Context, inputPaths []string, includeTests bool) (ret *ProjectPaths, err error) {
func GetPathsForAnalysis(ctx context.Context, inputPaths []string, includeTests bool, skipDirRegexps []string) (ret *ProjectPaths, err error) {
defer func(startedAt time.Time) {
if ret != nil {
logrus.Infof("Found paths for analysis for %s: %s", time.Since(startedAt), ret.MixedPaths())
Expand All @@ -114,7 +119,13 @@ func GetPathsForAnalysis(ctx context.Context, inputPaths []string, includeTests
}
}

pr := NewPathResolver(stdExcludeDirs, []string{".go"}, includeTests)
// TODO: don't analyze skipped files also, when be able to do it
excludeDirs := append([]string{}, stdExcludeDirRegexps...)
excludeDirs = append(excludeDirs, skipDirRegexps...)
pr, err := NewPathResolver(excludeDirs, []string{".go"}, includeTests)
if err != nil {
return nil, fmt.Errorf("can't make path resolver: %s", err)
}
paths, err := pr.Resolve(inputPaths...)
if err != nil {
return nil, fmt.Errorf("can't resolve paths %v: %s", inputPaths, err)
Expand Down
24 changes: 18 additions & 6 deletions pkg/fsutils/path_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
)

type PathResolver struct {
excludeDirs map[string]bool
excludeDirs map[string]*regexp.Regexp
allowedFileExtensions map[string]bool
includeTests bool
}
Expand Down Expand Up @@ -57,10 +58,15 @@ func (s pathResolveState) toResult() *PathResolveResult {
return res
}

func NewPathResolver(excludeDirs, allowedFileExtensions []string, includeTests bool) *PathResolver {
excludeDirsMap := map[string]bool{}
func NewPathResolver(excludeDirs, allowedFileExtensions []string, includeTests bool) (*PathResolver, error) {
excludeDirsMap := map[string]*regexp.Regexp{}
for _, dir := range excludeDirs {
excludeDirsMap[dir] = true
re, err := regexp.Compile(dir)
if err != nil {
return nil, fmt.Errorf("can't compile regexp %q: %s", dir, err)
}

excludeDirsMap[dir] = re
}

allowedFileExtensionsMap := map[string]bool{}
Expand All @@ -72,7 +78,7 @@ func NewPathResolver(excludeDirs, allowedFileExtensions []string, includeTests b
excludeDirs: excludeDirsMap,
allowedFileExtensions: allowedFileExtensionsMap,
includeTests: includeTests,
}
}, nil
}

func (pr PathResolver) isIgnoredDir(dir string) bool {
Expand All @@ -87,7 +93,13 @@ func (pr PathResolver) isIgnoredDir(dir string) bool {
return true
}

return pr.excludeDirs[dirName]
for _, dirExludeRe := range pr.excludeDirs {
if dirExludeRe.MatchString(dirName) {
return true
}
}

return false
}

func (pr PathResolver) isAllowedFile(path string) bool {
Expand Down
20 changes: 15 additions & 5 deletions pkg/fsutils/path_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,23 @@ func prepareFS(t *testing.T, paths ...string) *fsPreparer {
}
}

func newPR() *PathResolver {
return NewPathResolver([]string{}, []string{}, false)
func newPR(t *testing.T) *PathResolver {
pr, err := NewPathResolver([]string{}, []string{}, false)
assert.NoError(t, err)

return pr
}

func TestPathResolverNoPaths(t *testing.T) {
_, err := newPR().Resolve()
_, err := newPR(t).Resolve()
assert.EqualError(t, err, "no paths are set")
}

func TestPathResolverNotExistingPath(t *testing.T) {
fp := prepareFS(t)
defer fp.clean()

_, err := newPR().Resolve("a")
_, err := newPR(t).Resolve("a")
assert.EqualError(t, err, "can't find path a: stat a: no such file or directory")
}

Expand Down Expand Up @@ -187,14 +190,21 @@ func TestPathResolverCommonCases(t *testing.T) {
expDirs: []string{".", "a/c"},
expFiles: []string{"a/c/d.go", "e.go"},
},
{
name: "vendor dir is excluded by regexp, not the exact match",
prepare: []string{"vendors/a.go", "novendor/b.go"},
resolve: []string{"./..."},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fp := prepareFS(t, tc.prepare...)
defer fp.clean()

pr := NewPathResolver([]string{"vendor"}, []string{".go"}, tc.includeTests)
pr, err := NewPathResolver([]string{"vendor"}, []string{".go"}, tc.includeTests)
assert.NoError(t, err)

res, err := pr.Resolve(tc.resolve...)
assert.NoError(t, err)

Expand Down
2 changes: 1 addition & 1 deletion pkg/lint/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func LoadContext(ctx context.Context, linters []linter.Config, cfg *config.Confi
args = []string{"./..."}
}

paths, err := fsutils.GetPathsForAnalysis(ctx, args, cfg.Run.AnalyzeTests)
paths, err := fsutils.GetPathsForAnalysis(ctx, args, cfg.Run.AnalyzeTests, cfg.Run.SkipDirs)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/lint/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestASTCacheLoading(t *testing.T) {

inputPaths := []string{"./...", "./", "./load.go", "load.go"}
for _, inputPath := range inputPaths {
paths, err := fsutils.GetPathsForAnalysis(ctx, []string{inputPath}, true)
paths, err := fsutils.GetPathsForAnalysis(ctx, []string{inputPath}, true, nil)
assert.NoError(t, err)
assert.NotEmpty(t, paths.Files)

Expand Down
53 changes: 53 additions & 0 deletions pkg/result/processors/skip_files.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package processors

import (
"fmt"
"path/filepath"
"regexp"

"github.com/golangci/golangci-lint/pkg/result"
)

type SkipFiles struct {
patterns []*regexp.Regexp
}

var _ Processor = SkipFiles{}

func NewSkipFiles(patterns []string) (*SkipFiles, error) {
var patternsRe []*regexp.Regexp
for _, p := range patterns {
patternRe, err := regexp.Compile(p)
if err != nil {
return nil, fmt.Errorf("can't compile regexp %q: %s", p, err)
}
patternsRe = append(patternsRe, patternRe)
}

return &SkipFiles{
patterns: patternsRe,
}, nil
}

func (p SkipFiles) Name() string {
return "skip_files"
}

func (p SkipFiles) Process(issues []result.Issue) ([]result.Issue, error) {
if len(p.patterns) == 0 {
return issues, nil
}

return filterIssues(issues, func(i *result.Issue) bool {
fileName := filepath.Base(i.FilePath())
for _, p := range p.patterns {
if p.MatchString(fileName) {
return false
}
}

return true
}), nil
}

func (p SkipFiles) Finish() {}
43 changes: 43 additions & 0 deletions pkg/result/processors/skip_files_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package processors

import (
"go/token"
"testing"

"github.com/golangci/golangci-lint/pkg/result"
"github.com/stretchr/testify/assert"
)

func newFileIssue(file string) result.Issue {
return result.Issue{
Pos: token.Position{
Filename: file,
},
}
}

func newTestSkipFiles(t *testing.T, patterns ...string) *SkipFiles {
p, err := NewSkipFiles(patterns)
assert.NoError(t, err)
return p
}

func TestSkipFiles(t *testing.T) {
p := newTestSkipFiles(t)
processAssertSame(t, p, newFileIssue("any.go"))

p = newTestSkipFiles(t, "file")
processAssertEmpty(t, p,
newFileIssue("file.go"),
newFileIssue("file"),
newFileIssue("nofile.go"))

p = newTestSkipFiles(t, ".*")
processAssertEmpty(t, p, newFileIssue("any.go"))
}

func TestSkipFilesInvalidPattern(t *testing.T) {
p, err := NewSkipFiles([]string{"\\o"})
assert.Error(t, err)
assert.Nil(t, p)
}