Skip to content

Commit eee7e73

Browse files
committed
#30: support --skip-files and --skip-dirs options: they skip files and dirs by regexps
1 parent 110f584 commit eee7e73

File tree

11 files changed

+166
-17
lines changed

11 files changed

+166
-17
lines changed

Diff for: .golangci.example.yml

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ run:
55
tests: true
66
build-tags:
77
- mytag
8+
skip-dirs:
9+
- external_libs
10+
skip-files:
11+
- ".*\\.pb\\.go$"
812

913
output:
1014
format: colored-line-number

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ Flags:
232232
--print-resources-usage Print avg and max memory usage of golangci-lint and total time
233233
-c, --config PATH Read config from file path PATH
234234
--no-config Don't read config
235+
--skip-dirs strings Regexps of directory names to skip
236+
--skip-files strings Regexps of file names to skip
235237
-E, --enable strings Enable specific linter
236238
-D, --disable strings Disable specific linter
237239
--enable-all Enable all linters

Diff for: pkg/commands/run.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
7171
fs.BoolVar(&rc.PrintResourcesUsage, "print-resources-usage", false, wh("Print avg and max memory usage of golangci-lint and total time"))
7272
fs.StringVarP(&rc.Config, "config", "c", "", wh("Read config from file path `PATH`"))
7373
fs.BoolVar(&rc.NoConfig, "no-config", false, wh("Don't read config"))
74+
fs.StringSliceVar(&rc.SkipDirs, "skip-dirs", nil, wh("Regexps of directory names to skip"))
75+
fs.StringSliceVar(&rc.SkipFiles, "skip-files", nil, wh("Regexps of file names to skip"))
7476

7577
// Linters settings config
7678
lsc := &cfg.LintersSettings
@@ -194,12 +196,21 @@ func (e *Executor) runAnalysis(ctx context.Context, args []string) (<-chan resul
194196
if lintCtx.Program != nil {
195197
fset = lintCtx.Program.Fset
196198
}
199+
200+
skipFilesProcessor, err := processors.NewSkipFiles(e.cfg.Run.SkipFiles)
201+
if err != nil {
202+
return nil, err
203+
}
204+
197205
runner := lint.SimpleRunner{
198206
Processors: []processors.Processor{
199207
processors.NewPathPrettifier(), // must be before diff processor at least
200-
processors.NewExclude(excludeTotalPattern),
201208
processors.NewCgo(),
209+
skipFilesProcessor,
210+
211+
processors.NewExclude(excludeTotalPattern),
202212
processors.NewNolint(fset),
213+
203214
processors.NewUniqByLine(),
204215
processors.NewDiff(e.cfg.Issues.Diff, e.cfg.Issues.DiffFromRevision, e.cfg.Issues.DiffPatchFilePath),
205216
processors.NewMaxPerFileFromLinter(),

Diff for: pkg/config/config.go

+3
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ type Run struct {
101101
AnalyzeTests bool `mapstructure:"tests"`
102102
Deadline time.Duration
103103
PrintVersion bool
104+
105+
SkipFiles []string `mapstructure:"skip-files"`
106+
SkipDirs []string `mapstructure:"skip-dirs"`
104107
}
105108

106109
type LintersSettings struct {

Diff for: pkg/fsutils/fsutils.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ import (
1313
"github.com/sirupsen/logrus"
1414
)
1515

16-
var stdExcludeDirs = []string{"vendor", "testdata", "examples", "Godeps", "builtin"}
16+
var stdExcludeDirRegexps = []string{
17+
"^vendor$", "^third_party$",
18+
"^testdata$", "^examples$",
19+
"^Godeps$",
20+
"^builtin$",
21+
}
1722

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

104-
func GetPathsForAnalysis(ctx context.Context, inputPaths []string, includeTests bool) (ret *ProjectPaths, err error) {
109+
func GetPathsForAnalysis(ctx context.Context, inputPaths []string, includeTests bool, skipDirRegexps []string) (ret *ProjectPaths, err error) {
105110
defer func(startedAt time.Time) {
106111
if ret != nil {
107112
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
114119
}
115120
}
116121

117-
pr := NewPathResolver(stdExcludeDirs, []string{".go"}, includeTests)
122+
// TODO: don't analyze skipped files also, when be able to do it
123+
excludeDirs := append([]string{}, stdExcludeDirRegexps...)
124+
excludeDirs = append(excludeDirs, skipDirRegexps...)
125+
pr, err := NewPathResolver(excludeDirs, []string{".go"}, includeTests)
126+
if err != nil {
127+
return nil, fmt.Errorf("can't make path resolver: %s", err)
128+
}
118129
paths, err := pr.Resolve(inputPaths...)
119130
if err != nil {
120131
return nil, fmt.Errorf("can't resolve paths %v: %s", inputPaths, err)

Diff for: pkg/fsutils/path_resolver.go

+18-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"regexp"
78
"sort"
89
"strings"
910
)
1011

1112
type PathResolver struct {
12-
excludeDirs map[string]bool
13+
excludeDirs map[string]*regexp.Regexp
1314
allowedFileExtensions map[string]bool
1415
includeTests bool
1516
}
@@ -57,10 +58,15 @@ func (s pathResolveState) toResult() *PathResolveResult {
5758
return res
5859
}
5960

60-
func NewPathResolver(excludeDirs, allowedFileExtensions []string, includeTests bool) *PathResolver {
61-
excludeDirsMap := map[string]bool{}
61+
func NewPathResolver(excludeDirs, allowedFileExtensions []string, includeTests bool) (*PathResolver, error) {
62+
excludeDirsMap := map[string]*regexp.Regexp{}
6263
for _, dir := range excludeDirs {
63-
excludeDirsMap[dir] = true
64+
re, err := regexp.Compile(dir)
65+
if err != nil {
66+
return nil, fmt.Errorf("can't compile regexp %q: %s", dir, err)
67+
}
68+
69+
excludeDirsMap[dir] = re
6470
}
6571

6672
allowedFileExtensionsMap := map[string]bool{}
@@ -72,7 +78,7 @@ func NewPathResolver(excludeDirs, allowedFileExtensions []string, includeTests b
7278
excludeDirs: excludeDirsMap,
7379
allowedFileExtensions: allowedFileExtensionsMap,
7480
includeTests: includeTests,
75-
}
81+
}, nil
7682
}
7783

7884
func (pr PathResolver) isIgnoredDir(dir string) bool {
@@ -87,7 +93,13 @@ func (pr PathResolver) isIgnoredDir(dir string) bool {
8793
return true
8894
}
8995

90-
return pr.excludeDirs[dirName]
96+
for _, dirExludeRe := range pr.excludeDirs {
97+
if dirExludeRe.MatchString(dirName) {
98+
return true
99+
}
100+
}
101+
102+
return false
91103
}
92104

93105
func (pr PathResolver) isAllowedFile(path string) bool {

Diff for: pkg/fsutils/path_resolver_test.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,23 @@ func prepareFS(t *testing.T, paths ...string) *fsPreparer {
5353
}
5454
}
5555

56-
func newPR() *PathResolver {
57-
return NewPathResolver([]string{}, []string{}, false)
56+
func newPR(t *testing.T) *PathResolver {
57+
pr, err := NewPathResolver([]string{}, []string{}, false)
58+
assert.NoError(t, err)
59+
60+
return pr
5861
}
5962

6063
func TestPathResolverNoPaths(t *testing.T) {
61-
_, err := newPR().Resolve()
64+
_, err := newPR(t).Resolve()
6265
assert.EqualError(t, err, "no paths are set")
6366
}
6467

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

69-
_, err := newPR().Resolve("a")
72+
_, err := newPR(t).Resolve("a")
7073
assert.EqualError(t, err, "can't find path a: stat a: no such file or directory")
7174
}
7275

@@ -187,14 +190,21 @@ func TestPathResolverCommonCases(t *testing.T) {
187190
expDirs: []string{".", "a/c"},
188191
expFiles: []string{"a/c/d.go", "e.go"},
189192
},
193+
{
194+
name: "vendor dir is excluded by regexp, not the exact match",
195+
prepare: []string{"vendors/a.go", "novendor/b.go"},
196+
resolve: []string{"./..."},
197+
},
190198
}
191199

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

197-
pr := NewPathResolver([]string{"vendor"}, []string{".go"}, tc.includeTests)
205+
pr, err := NewPathResolver([]string{"vendor"}, []string{".go"}, tc.includeTests)
206+
assert.NoError(t, err)
207+
198208
res, err := pr.Resolve(tc.resolve...)
199209
assert.NoError(t, err)
200210

Diff for: pkg/lint/load.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func LoadContext(ctx context.Context, linters []linter.Config, cfg *config.Confi
154154
args = []string{"./..."}
155155
}
156156

157-
paths, err := fsutils.GetPathsForAnalysis(ctx, args, cfg.Run.AnalyzeTests)
157+
paths, err := fsutils.GetPathsForAnalysis(ctx, args, cfg.Run.AnalyzeTests, cfg.Run.SkipDirs)
158158
if err != nil {
159159
return nil, err
160160
}

Diff for: pkg/lint/load_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func TestASTCacheLoading(t *testing.T) {
1919

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

Diff for: pkg/result/processors/skip_files.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package processors
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
"regexp"
7+
8+
"github.com/golangci/golangci-lint/pkg/result"
9+
)
10+
11+
type SkipFiles struct {
12+
patterns []*regexp.Regexp
13+
}
14+
15+
var _ Processor = SkipFiles{}
16+
17+
func NewSkipFiles(patterns []string) (*SkipFiles, error) {
18+
var patternsRe []*regexp.Regexp
19+
for _, p := range patterns {
20+
patternRe, err := regexp.Compile(p)
21+
if err != nil {
22+
return nil, fmt.Errorf("can't compile regexp %q: %s", p, err)
23+
}
24+
patternsRe = append(patternsRe, patternRe)
25+
}
26+
27+
return &SkipFiles{
28+
patterns: patternsRe,
29+
}, nil
30+
}
31+
32+
func (p SkipFiles) Name() string {
33+
return "skip_files"
34+
}
35+
36+
func (p SkipFiles) Process(issues []result.Issue) ([]result.Issue, error) {
37+
if len(p.patterns) == 0 {
38+
return issues, nil
39+
}
40+
41+
return filterIssues(issues, func(i *result.Issue) bool {
42+
fileName := filepath.Base(i.FilePath())
43+
for _, p := range p.patterns {
44+
if p.MatchString(fileName) {
45+
return false
46+
}
47+
}
48+
49+
return true
50+
}), nil
51+
}
52+
53+
func (p SkipFiles) Finish() {}

Diff for: pkg/result/processors/skip_files_test.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package processors
2+
3+
import (
4+
"go/token"
5+
"testing"
6+
7+
"github.com/golangci/golangci-lint/pkg/result"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func newFileIssue(file string) result.Issue {
12+
return result.Issue{
13+
Pos: token.Position{
14+
Filename: file,
15+
},
16+
}
17+
}
18+
19+
func newTestSkipFiles(t *testing.T, patterns ...string) *SkipFiles {
20+
p, err := NewSkipFiles(patterns)
21+
assert.NoError(t, err)
22+
return p
23+
}
24+
25+
func TestSkipFiles(t *testing.T) {
26+
p := newTestSkipFiles(t)
27+
processAssertSame(t, p, newFileIssue("any.go"))
28+
29+
p = newTestSkipFiles(t, "file")
30+
processAssertEmpty(t, p,
31+
newFileIssue("file.go"),
32+
newFileIssue("file"),
33+
newFileIssue("nofile.go"))
34+
35+
p = newTestSkipFiles(t, ".*")
36+
processAssertEmpty(t, p, newFileIssue("any.go"))
37+
}
38+
39+
func TestSkipFilesInvalidPattern(t *testing.T) {
40+
p, err := NewSkipFiles([]string{"\\o"})
41+
assert.Error(t, err)
42+
assert.Nil(t, p)
43+
}

0 commit comments

Comments
 (0)