Merge pull request #74 from golangci/feature/support-skip-files-and-dirs

#30: support --skip-files and --skip-dirs options: they skip files an…
This commit is contained in:
Isaev Denis 2018-06-07 09:52:53 +03:00 committed by GitHub
commit a37c6e62f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 166 additions and 17 deletions

View File

@ -5,6 +5,10 @@ run:
tests: true
build-tags:
- mytag
skip-dirs:
- external_libs
skip-files:
- ".*\\.pb\\.go$"
output:
format: colored-line-number

View File

@ -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

View File

@ -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
@ -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(),

View File

@ -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 {

View File

@ -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")
@ -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())
@ -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)

View File

@ -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
}
@ -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{}
@ -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 {
@ -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 {

View File

@ -53,12 +53,15 @@ 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")
}
@ -66,7 +69,7 @@ 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")
}
@ -187,6 +190,11 @@ 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 {
@ -194,7 +202,9 @@ func TestPathResolverCommonCases(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)

View File

@ -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
}

View File

@ -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)

View File

@ -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() {}

View File

@ -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)
}