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:
commit
a37c6e62f3
@ -5,6 +5,10 @@ run:
|
||||
tests: true
|
||||
build-tags:
|
||||
- mytag
|
||||
skip-dirs:
|
||||
- external_libs
|
||||
skip-files:
|
||||
- ".*\\.pb\\.go$"
|
||||
|
||||
output:
|
||||
format: colored-line-number
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
53
pkg/result/processors/skip_files.go
Normal file
53
pkg/result/processors/skip_files.go
Normal 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() {}
|
43
pkg/result/processors/skip_files_test.go
Normal file
43
pkg/result/processors/skip_files_test.go
Normal 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)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user