speed up CI and golangci-lint (#1070)
Run CI on mac os only with go1.13 and on windows only on go1.14. Speed up tests. Introduce --allow-parallel-runners. Block on parallel run lock 5s instead of 60s. Don't invalidate analysis cache for minor config changes.
This commit is contained in:
parent
f0012d3248
commit
cb58d1f82e
13
.github/workflows/pr.yml
vendored
13
.github/workflows/pr.yml
vendored
@ -23,17 +23,12 @@ jobs:
|
|||||||
tests-on-windows:
|
tests-on-windows:
|
||||||
needs: golangci-lint # run after golangci-lint action to not produce duplicated errors
|
needs: golangci-lint # run after golangci-lint action to not produce duplicated errors
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
golang:
|
|
||||||
- 1.13
|
|
||||||
- 1.14
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.golang }}
|
go-version: 1.14 # test only the latest go version to speed up CI
|
||||||
- name: Run tests on Windows
|
- name: Run tests on Windows
|
||||||
run: make.exe test
|
run: make.exe test
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
@ -47,7 +42,11 @@ jobs:
|
|||||||
- 1.14
|
- 1.14
|
||||||
os:
|
os:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
- macos-latest
|
include:
|
||||||
|
- os: macos-latest
|
||||||
|
# test only the one go version on Mac OS to speed up CI
|
||||||
|
# TODO: use the latet go version after https://github.com/golang/go/issues/38824
|
||||||
|
golang: 1.13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
|
@ -53,6 +53,10 @@ run:
|
|||||||
# the dependency descriptions in go.mod.
|
# the dependency descriptions in go.mod.
|
||||||
modules-download-mode: readonly|release|vendor
|
modules-download-mode: readonly|release|vendor
|
||||||
|
|
||||||
|
# Allow multiple parallel golangci-lint instances running.
|
||||||
|
# If false (default) - golangci-lint acquires file lock on start.
|
||||||
|
allow-parallel-runners: false
|
||||||
|
|
||||||
|
|
||||||
# output configuration options
|
# output configuration options
|
||||||
output:
|
output:
|
||||||
|
4
Makefile
4
Makefile
@ -29,9 +29,7 @@ clean:
|
|||||||
test: export GOLANGCI_LINT_INSTALLED = true
|
test: export GOLANGCI_LINT_INSTALLED = true
|
||||||
test: build
|
test: build
|
||||||
GL_TEST_RUN=1 ./golangci-lint run -v
|
GL_TEST_RUN=1 ./golangci-lint run -v
|
||||||
GL_TEST_RUN=1 ./golangci-lint run --fast --no-config -v --skip-dirs 'test/testdata_etc,internal/(cache|renameio|robustio)'
|
GL_TEST_RUN=1 go test -v -parallel 2 ./...
|
||||||
GL_TEST_RUN=1 ./golangci-lint run --no-config -v --skip-dirs 'test/testdata_etc,internal/(cache|renameio|robustio)'
|
|
||||||
GL_TEST_RUN=1 go test -v ./...
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
|
|
||||||
test_race: build_race
|
test_race: build_race
|
||||||
|
@ -546,6 +546,7 @@ Flags:
|
|||||||
- (^|/)builtin($|/)
|
- (^|/)builtin($|/)
|
||||||
(default true)
|
(default true)
|
||||||
--skip-files strings Regexps of files to skip
|
--skip-files strings Regexps of files to skip
|
||||||
|
--allow-parallel-runners Allow multiple parallel golangci-lint instances running. If false (default) - golangci-lint acquires file lock on start.
|
||||||
-E, --enable strings Enable specific linter
|
-E, --enable strings Enable specific linter
|
||||||
-D, --disable strings Disable specific linter
|
-D, --disable strings Disable specific linter
|
||||||
--disable-all Disable all linters
|
--disable-all Disable all linters
|
||||||
@ -679,6 +680,10 @@ run:
|
|||||||
# the dependency descriptions in go.mod.
|
# the dependency descriptions in go.mod.
|
||||||
modules-download-mode: readonly|release|vendor
|
modules-download-mode: readonly|release|vendor
|
||||||
|
|
||||||
|
# Allow multiple parallel golangci-lint instances running.
|
||||||
|
# If false (default) - golangci-lint acquires file lock on start.
|
||||||
|
allow-parallel-runners: false
|
||||||
|
|
||||||
|
|
||||||
# output configuration options
|
# output configuration options
|
||||||
output:
|
output:
|
||||||
|
6
internal/cache/cache_test.go
vendored
6
internal/cache/cache_test.go
vendored
@ -20,6 +20,8 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
func TestBasic(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "cachetest-")
|
dir, err := ioutil.TempDir("", "cachetest-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -65,6 +67,8 @@ func TestBasic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGrowth(t *testing.T) {
|
func TestGrowth(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "cachetest-")
|
dir, err := ioutil.TempDir("", "cachetest-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -151,6 +155,8 @@ func dummyID(x int) [HashSize]byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCacheTrim(t *testing.T) {
|
func TestCacheTrim(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "cachetest-")
|
dir, err := ioutil.TempDir("", "cachetest-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -35,22 +35,32 @@ func (e *Executor) initConfig() {
|
|||||||
cmd.AddCommand(pathCmd)
|
cmd.AddCommand(pathCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) getUsedConfig() string {
|
||||||
|
usedConfigFile := viper.ConfigFileUsed()
|
||||||
|
if usedConfigFile == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
prettyUsedConfigFile, err := fsutils.ShortestRelPath(usedConfigFile, "")
|
||||||
|
if err != nil {
|
||||||
|
e.log.Warnf("Can't pretty print config file path: %s", err)
|
||||||
|
return usedConfigFile
|
||||||
|
}
|
||||||
|
|
||||||
|
return prettyUsedConfigFile
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) executePathCmd(_ *cobra.Command, args []string) {
|
func (e *Executor) executePathCmd(_ *cobra.Command, args []string) {
|
||||||
if len(args) != 0 {
|
if len(args) != 0 {
|
||||||
e.log.Fatalf("Usage: golangci-lint config path")
|
e.log.Fatalf("Usage: golangci-lint config path")
|
||||||
}
|
}
|
||||||
|
|
||||||
usedConfigFile := viper.ConfigFileUsed()
|
usedConfigFile := e.getUsedConfig()
|
||||||
if usedConfigFile == "" {
|
if usedConfigFile == "" {
|
||||||
e.log.Warnf("No config file detected")
|
e.log.Warnf("No config file detected")
|
||||||
os.Exit(exitcodes.NoConfigFileDetected)
|
os.Exit(exitcodes.NoConfigFileDetected)
|
||||||
}
|
}
|
||||||
|
|
||||||
usedConfigFile, err := fsutils.ShortestRelPath(usedConfigFile, "")
|
|
||||||
if err != nil {
|
|
||||||
e.log.Warnf("Can't pretty print config file path: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(usedConfigFile)
|
fmt.Println(usedConfigFile)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
@ -31,8 +31,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Executor struct {
|
type Executor struct {
|
||||||
rootCmd *cobra.Command
|
rootCmd *cobra.Command
|
||||||
runCmd *cobra.Command
|
runCmd *cobra.Command
|
||||||
|
lintersCmd *cobra.Command
|
||||||
|
|
||||||
exitCode int
|
exitCode int
|
||||||
version, commit, date string
|
version, commit, date string
|
||||||
@ -55,6 +56,7 @@ type Executor struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewExecutor(version, commit, date string) *Executor {
|
func NewExecutor(version, commit, date string) *Executor {
|
||||||
|
startedAt := time.Now()
|
||||||
e := &Executor{
|
e := &Executor{
|
||||||
cfg: config.NewDefault(),
|
cfg: config.NewDefault(),
|
||||||
version: version,
|
version: version,
|
||||||
@ -66,9 +68,6 @@ func NewExecutor(version, commit, date string) *Executor {
|
|||||||
|
|
||||||
e.debugf("Starting execution...")
|
e.debugf("Starting execution...")
|
||||||
e.log = report.NewLogWrapper(logutils.NewStderrLog(""), &e.reportData)
|
e.log = report.NewLogWrapper(logutils.NewStderrLog(""), &e.reportData)
|
||||||
if ok := e.acquireFileLock(); !ok {
|
|
||||||
e.log.Fatalf("Parallel golangci-lint is running")
|
|
||||||
}
|
|
||||||
|
|
||||||
// to setup log level early we need to parse config from command line extra time to
|
// to setup log level early we need to parse config from command line extra time to
|
||||||
// find `-v` option
|
// find `-v` option
|
||||||
@ -121,6 +120,7 @@ func NewExecutor(version, commit, date string) *Executor {
|
|||||||
|
|
||||||
// Slice options must be explicitly set for proper merging of config and command-line options.
|
// Slice options must be explicitly set for proper merging of config and command-line options.
|
||||||
fixSlicesFlags(e.runCmd.Flags())
|
fixSlicesFlags(e.runCmd.Flags())
|
||||||
|
fixSlicesFlags(e.lintersCmd.Flags())
|
||||||
|
|
||||||
e.EnabledLintersSet = lintersdb.NewEnabledSet(e.DBManager,
|
e.EnabledLintersSet = lintersdb.NewEnabledSet(e.DBManager,
|
||||||
lintersdb.NewValidator(e.DBManager), e.log.Child("lintersdb"), e.cfg)
|
lintersdb.NewValidator(e.DBManager), e.log.Child("lintersdb"), e.cfg)
|
||||||
@ -139,7 +139,7 @@ func NewExecutor(version, commit, date string) *Executor {
|
|||||||
if err = e.initHashSalt(version); err != nil {
|
if err = e.initHashSalt(version); err != nil {
|
||||||
e.log.Fatalf("Failed to init hash salt: %s", err)
|
e.log.Fatalf("Failed to init hash salt: %s", err)
|
||||||
}
|
}
|
||||||
e.debugf("Initialized executor")
|
e.debugf("Initialized executor in %s", time.Since(startedAt))
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,27 +191,39 @@ func computeBinarySalt(version string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func computeConfigSalt(cfg *config.Config) ([]byte, error) {
|
func computeConfigSalt(cfg *config.Config) ([]byte, error) {
|
||||||
configBytes, err := json.Marshal(cfg)
|
// We don't hash all config fields to reduce meaningless cache
|
||||||
|
// invalidations. At least, it has a huge impact on tests speed.
|
||||||
|
|
||||||
|
lintersSettingsBytes, err := json.Marshal(cfg.LintersSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to json marshal config")
|
return nil, errors.Wrap(err, "failed to json marshal config linter settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var configData bytes.Buffer
|
||||||
|
configData.WriteString("linters-settings=")
|
||||||
|
configData.Write(lintersSettingsBytes)
|
||||||
|
configData.WriteString("\nbuild-tags=%s" + strings.Join(cfg.Run.BuildTags, ","))
|
||||||
|
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
if n, err := h.Write(configBytes); n != len(configBytes) {
|
h.Write(configData.Bytes()) //nolint:errcheck
|
||||||
return nil, fmt.Errorf("failed to hash config bytes: wrote %d/%d bytes, error: %s", n, len(configBytes), err)
|
|
||||||
}
|
|
||||||
return h.Sum(nil), nil
|
return h.Sum(nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) acquireFileLock() bool {
|
func (e *Executor) acquireFileLock() bool {
|
||||||
|
if e.cfg.Run.AllowParallelRunners {
|
||||||
|
e.debugf("Parallel runners are allowed, no locking")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
lockFile := filepath.Join(os.TempDir(), "golangci-lint.lock")
|
lockFile := filepath.Join(os.TempDir(), "golangci-lint.lock")
|
||||||
e.debugf("Locking on file %s...", lockFile)
|
e.debugf("Locking on file %s...", lockFile)
|
||||||
f := flock.New(lockFile)
|
f := flock.New(lockFile)
|
||||||
ctx, finish := context.WithTimeout(context.Background(), time.Minute)
|
const totalTimeout = 5 * time.Second
|
||||||
|
const retryDelay = time.Second
|
||||||
|
ctx, finish := context.WithTimeout(context.Background(), totalTimeout)
|
||||||
defer finish()
|
defer finish()
|
||||||
|
|
||||||
timeout := time.Second * 3
|
if ok, _ := f.TryLockContext(ctx, retryDelay); !ok {
|
||||||
if ok, _ := f.TryLockContext(ctx, timeout); !ok {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +232,10 @@ func (e *Executor) acquireFileLock() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) releaseFileLock() {
|
func (e *Executor) releaseFileLock() {
|
||||||
|
if e.cfg.Run.AllowParallelRunners {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := e.flock.Unlock(); err != nil {
|
if err := e.flock.Unlock(); err != nil {
|
||||||
e.debugf("Failed to unlock on file: %s", err)
|
e.debugf("Failed to unlock on file: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (e *Executor) initLinters() {
|
func (e *Executor) initLinters() {
|
||||||
lintersCmd := &cobra.Command{
|
e.lintersCmd = &cobra.Command{
|
||||||
Use: "linters",
|
Use: "linters",
|
||||||
Short: "List current linters configuration",
|
Short: "List current linters configuration",
|
||||||
Run: e.executeLinters,
|
Run: e.executeLinters,
|
||||||
}
|
}
|
||||||
e.rootCmd.AddCommand(lintersCmd)
|
e.rootCmd.AddCommand(e.lintersCmd)
|
||||||
e.initRunConfiguration(lintersCmd)
|
e.initRunConfiguration(e.lintersCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) executeLinters(_ *cobra.Command, args []string) {
|
func (e *Executor) executeLinters(_ *cobra.Command, args []string) {
|
||||||
|
@ -73,7 +73,6 @@ func (e *Executor) persistentPostRun(_ *cobra.Command, _ []string) {
|
|||||||
trace.Stop()
|
trace.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
e.releaseFileLock()
|
|
||||||
os.Exit(e.exitCode)
|
os.Exit(e.exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +106,10 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager, is
|
|||||||
fs.BoolVar(&rc.UseDefaultSkipDirs, "skip-dirs-use-default", true, getDefaultDirectoryExcludeHelp())
|
fs.BoolVar(&rc.UseDefaultSkipDirs, "skip-dirs-use-default", true, getDefaultDirectoryExcludeHelp())
|
||||||
fs.StringSliceVar(&rc.SkipFiles, "skip-files", nil, wh("Regexps of files to skip"))
|
fs.StringSliceVar(&rc.SkipFiles, "skip-files", nil, wh("Regexps of files to skip"))
|
||||||
|
|
||||||
|
const allowParallelDesc = "Allow multiple parallel golangci-lint instances running. " +
|
||||||
|
"If false (default) - golangci-lint acquires file lock on start."
|
||||||
|
fs.BoolVar(&rc.AllowParallelRunners, "allow-parallel-runners", false, wh(allowParallelDesc))
|
||||||
|
|
||||||
// Linters settings config
|
// Linters settings config
|
||||||
lsc := &cfg.LintersSettings
|
lsc := &cfg.LintersSettings
|
||||||
|
|
||||||
@ -251,6 +255,14 @@ func (e *Executor) initRun() {
|
|||||||
Use: "run",
|
Use: "run",
|
||||||
Short: welcomeMessage,
|
Short: welcomeMessage,
|
||||||
Run: e.executeRun,
|
Run: e.executeRun,
|
||||||
|
PreRun: func(_ *cobra.Command, _ []string) {
|
||||||
|
if ok := e.acquireFileLock(); !ok {
|
||||||
|
e.log.Fatalf("Parallel golangci-lint is running")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PostRun: func(_ *cobra.Command, _ []string) {
|
||||||
|
e.releaseFileLock()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
e.rootCmd.AddCommand(e.runCmd)
|
e.rootCmd.AddCommand(e.runCmd)
|
||||||
|
|
||||||
|
@ -150,6 +150,8 @@ type Run struct {
|
|||||||
SkipFiles []string `mapstructure:"skip-files"`
|
SkipFiles []string `mapstructure:"skip-files"`
|
||||||
SkipDirs []string `mapstructure:"skip-dirs"`
|
SkipDirs []string `mapstructure:"skip-dirs"`
|
||||||
UseDefaultSkipDirs bool `mapstructure:"skip-dirs-use-default"`
|
UseDefaultSkipDirs bool `mapstructure:"skip-dirs-use-default"`
|
||||||
|
|
||||||
|
AllowParallelRunners bool `mapstructure:"allow-parallel-runners"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LintersSettings struct {
|
type LintersSettings struct {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package lintersdb
|
package lintersdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ func NewEnabledSet(m *Manager, v *Validator, log logutils.Log, cfg *config.Confi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []*linter.Config) map[string]*linter.Config {
|
func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []*linter.Config) map[string]*linter.Config {
|
||||||
|
es.debugf("Linters config: %#v", lcfg)
|
||||||
resultLintersSet := map[string]*linter.Config{}
|
resultLintersSet := map[string]*linter.Config{}
|
||||||
switch {
|
switch {
|
||||||
case len(lcfg.Presets) != 0:
|
case len(lcfg.Presets) != 0:
|
||||||
@ -82,7 +84,11 @@ func (es EnabledSet) GetEnabledLintersMap() (map[string]*linter.Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return es.build(&es.cfg.Linters, es.m.GetAllEnabledByDefaultLinters()), nil
|
enabledLinters := es.build(&es.cfg.Linters, es.m.GetAllEnabledByDefaultLinters())
|
||||||
|
if os.Getenv("GL_TEST_RUN") == "1" {
|
||||||
|
es.verbosePrintLintersStatus(enabledLinters)
|
||||||
|
}
|
||||||
|
return enabledLinters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters
|
// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters
|
||||||
|
@ -170,9 +170,12 @@ func TestEnabledLinters(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runner := testshared.NewLintRunner(t)
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
c := c
|
c := c
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
runArgs := []string{"-v"}
|
runArgs := []string{"-v"}
|
||||||
if !c.noImplicitFast {
|
if !c.noImplicitFast {
|
||||||
runArgs = append(runArgs, "--fast")
|
runArgs = append(runArgs, "--fast")
|
||||||
@ -180,8 +183,7 @@ func TestEnabledLinters(t *testing.T) {
|
|||||||
if c.args != "" {
|
if c.args != "" {
|
||||||
runArgs = append(runArgs, strings.Split(c.args, " ")...)
|
runArgs = append(runArgs, strings.Split(c.args, " ")...)
|
||||||
}
|
}
|
||||||
runArgs = append(runArgs, minimalPkg)
|
r := runner.RunCommandWithYamlConfig(c.cfg, "linters", runArgs...)
|
||||||
r := testshared.NewLintRunner(t).RunWithYamlConfig(c.cfg, runArgs...)
|
|
||||||
sort.StringSlice(c.el).Sort()
|
sort.StringSlice(c.el).Sort()
|
||||||
|
|
||||||
expectedLine := fmt.Sprintf("Active %d linters: [%s]", len(c.el), strings.Join(c.el, " "))
|
expectedLine := fmt.Sprintf("Active %d linters: [%s]", len(c.el), strings.Join(c.el, " "))
|
||||||
|
@ -19,7 +19,7 @@ func runGoErrchk(c *exec.Cmd, files []string, t *testing.T) {
|
|||||||
output, err := c.CombinedOutput()
|
output, err := c.CombinedOutput()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
_, ok := err.(*exec.ExitError)
|
_, ok := err.(*exec.ExitError)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok, err)
|
||||||
|
|
||||||
// TODO: uncomment after deprecating go1.11
|
// TODO: uncomment after deprecating go1.11
|
||||||
// assert.Equal(t, exitcodes.IssuesFound, exitErr.ExitCode())
|
// assert.Equal(t, exitcodes.IssuesFound, exitErr.ExitCode())
|
||||||
@ -48,9 +48,9 @@ func testSourcesFromDir(t *testing.T, dir string) {
|
|||||||
|
|
||||||
for _, s := range sources {
|
for _, s := range sources {
|
||||||
s := s
|
s := s
|
||||||
t.Run(filepath.Base(s), func(t *testing.T) {
|
t.Run(filepath.Base(s), func(subTest *testing.T) {
|
||||||
t.Parallel()
|
subTest.Parallel()
|
||||||
testOneSource(t, s)
|
testOneSource(subTest, s)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,6 +101,7 @@ func saveConfig(t *testing.T, cfg map[string]interface{}) (cfgPath string, finis
|
|||||||
func testOneSource(t *testing.T, sourcePath string) {
|
func testOneSource(t *testing.T, sourcePath string) {
|
||||||
args := []string{
|
args := []string{
|
||||||
"run",
|
"run",
|
||||||
|
"--allow-parallel-runners",
|
||||||
"--disable-all",
|
"--disable-all",
|
||||||
"--print-issued-lines=false",
|
"--print-issued-lines=false",
|
||||||
"--print-linter-name=false",
|
"--print-linter-name=false",
|
||||||
|
@ -99,9 +99,10 @@ func TestCgoOk(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCgoWithIssues(t *testing.T) {
|
func TestCgoWithIssues(t *testing.T) {
|
||||||
testshared.NewLintRunner(t).Run("--no-config", "--disable-all", "-Egovet", getTestDataDir("cgo_with_issues")).
|
r := testshared.NewLintRunner(t)
|
||||||
|
r.Run("--no-config", "--disable-all", "-Egovet", getTestDataDir("cgo_with_issues")).
|
||||||
ExpectHasIssue("Printf format %t has arg cs of wrong type")
|
ExpectHasIssue("Printf format %t has arg cs of wrong type")
|
||||||
testshared.NewLintRunner(t).Run("--no-config", "--disable-all", "-Estaticcheck", getTestDataDir("cgo_with_issues")).
|
r.Run("--no-config", "--disable-all", "-Estaticcheck", getTestDataDir("cgo_with_issues")).
|
||||||
ExpectHasIssue("SA5009: Printf format %t has arg #1 of wrong type")
|
ExpectHasIssue("SA5009: Printf format %t has arg #1 of wrong type")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,31 +139,32 @@ func TestLineDirectiveProcessedFilesFullLoading(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLintFilesWithLineDirective(t *testing.T) {
|
func TestLintFilesWithLineDirective(t *testing.T) {
|
||||||
testshared.NewLintRunner(t).Run("-Edupl", "--disable-all", "--config=testdata/linedirective/dupl.yml", getTestDataDir("linedirective")).
|
r := testshared.NewLintRunner(t)
|
||||||
|
r.Run("-Edupl", "--disable-all", "--config=testdata/linedirective/dupl.yml", getTestDataDir("linedirective")).
|
||||||
ExpectHasIssue("21-23 lines are duplicate of `testdata/linedirective/hello.go:25-27` (dupl)")
|
ExpectHasIssue("21-23 lines are duplicate of `testdata/linedirective/hello.go:25-27` (dupl)")
|
||||||
testshared.NewLintRunner(t).Run("-Egofmt", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
r.Run("-Egofmt", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
||||||
ExpectHasIssue("File is not `gofmt`-ed with `-s` (gofmt)")
|
ExpectHasIssue("File is not `gofmt`-ed with `-s` (gofmt)")
|
||||||
testshared.NewLintRunner(t).Run("-Egoimports", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
r.Run("-Egoimports", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
||||||
ExpectHasIssue("File is not `goimports`-ed (goimports)")
|
ExpectHasIssue("File is not `goimports`-ed (goimports)")
|
||||||
testshared.NewLintRunner(t).
|
r.
|
||||||
Run("-Egomodguard", "--disable-all", "--config=testdata/linedirective/gomodguard.yml", getTestDataDir("linedirective")).
|
Run("-Egomodguard", "--disable-all", "--config=testdata/linedirective/gomodguard.yml", getTestDataDir("linedirective")).
|
||||||
ExpectHasIssue("import of package `github.com/ryancurrah/gomodguard` is blocked because the module is not " +
|
ExpectHasIssue("import of package `github.com/ryancurrah/gomodguard` is blocked because the module is not " +
|
||||||
"in the allowed modules list. (gomodguard)")
|
"in the allowed modules list. (gomodguard)")
|
||||||
testshared.NewLintRunner(t).Run("-Eineffassign", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
r.Run("-Eineffassign", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
||||||
ExpectHasIssue("ineffectual assignment to `x` (ineffassign)")
|
ExpectHasIssue("ineffectual assignment to `x` (ineffassign)")
|
||||||
testshared.NewLintRunner(t).Run("-Elll", "--disable-all", "--config=testdata/linedirective/lll.yml", getTestDataDir("linedirective")).
|
r.Run("-Elll", "--disable-all", "--config=testdata/linedirective/lll.yml", getTestDataDir("linedirective")).
|
||||||
ExpectHasIssue("line is 57 characters (lll)")
|
ExpectHasIssue("line is 57 characters (lll)")
|
||||||
testshared.NewLintRunner(t).Run("-Emisspell", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
r.Run("-Emisspell", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
||||||
ExpectHasIssue("is a misspelling of `language` (misspell)")
|
ExpectHasIssue("is a misspelling of `language` (misspell)")
|
||||||
testshared.NewLintRunner(t).Run("-Ewsl", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
r.Run("-Ewsl", "--disable-all", "--no-config", getTestDataDir("linedirective")).
|
||||||
ExpectHasIssue("block should not start with a whitespace (wsl)")
|
ExpectHasIssue("block should not start with a whitespace (wsl)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSkippedDirsNoMatchArg(t *testing.T) {
|
func TestSkippedDirsNoMatchArg(t *testing.T) {
|
||||||
dir := getTestDataDir("skipdirs", "skip_me", "nested")
|
dir := getTestDataDir("skipdirs", "skip_me", "nested")
|
||||||
r := testshared.NewLintRunner(t).Run("--print-issued-lines=false", "--no-config", "--skip-dirs", dir, "-Egolint", dir)
|
res := testshared.NewLintRunner(t).Run("--print-issued-lines=false", "--no-config", "--skip-dirs", dir, "-Egolint", dir)
|
||||||
|
|
||||||
r.ExpectExitCode(exitcodes.IssuesFound).
|
res.ExpectExitCode(exitcodes.IssuesFound).
|
||||||
ExpectOutputEq("testdata/skipdirs/skip_me/nested/with_issue.go:8:9: `if` block ends with " +
|
ExpectOutputEq("testdata/skipdirs/skip_me/nested/with_issue.go:8:9: `if` block ends with " +
|
||||||
"a `return` statement, so drop this `else` and outdent its block (golint)\n")
|
"a `return` statement, so drop this `else` and outdent its block (golint)\n")
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
@ -14,10 +16,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LintRunner struct {
|
type LintRunner struct {
|
||||||
t assert.TestingT
|
t assert.TestingT
|
||||||
log logutils.Log
|
log logutils.Log
|
||||||
env []string
|
env []string
|
||||||
installed bool
|
installOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLintRunner(t assert.TestingT, environ ...string) *LintRunner {
|
func NewLintRunner(t assert.TestingT, environ ...string) *LintRunner {
|
||||||
@ -31,17 +33,14 @@ func NewLintRunner(t assert.TestingT, environ ...string) *LintRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *LintRunner) Install() {
|
func (r *LintRunner) Install() {
|
||||||
if r.installed {
|
r.installOnce.Do(func() {
|
||||||
return
|
if os.Getenv("GOLANGCI_LINT_INSTALLED") == "true" {
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if os.Getenv("GOLANGCI_LINT_INSTALLED") == "true" {
|
cmd := exec.Command("make", "-C", "..", "build")
|
||||||
return
|
assert.NoError(r.t, cmd.Run(), "Can't go install golangci-lint")
|
||||||
}
|
})
|
||||||
|
|
||||||
cmd := exec.Command("make", "-C", "..", "build")
|
|
||||||
assert.NoError(r.t, cmd.Run(), "Can't go install golangci-lint")
|
|
||||||
r.installed = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RunResult struct {
|
type RunResult struct {
|
||||||
@ -82,10 +81,18 @@ func (r *RunResult) ExpectHasIssue(issueText string) *RunResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *LintRunner) Run(args ...string) *RunResult {
|
func (r *LintRunner) Run(args ...string) *RunResult {
|
||||||
|
newArgs := append([]string{"--allow-parallel-runners"}, args...)
|
||||||
|
return r.RunCommand("run", newArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LintRunner) RunCommand(command string, args ...string) *RunResult {
|
||||||
r.Install()
|
r.Install()
|
||||||
|
|
||||||
runArgs := append([]string{"run"}, args...)
|
runArgs := append([]string{command}, args...)
|
||||||
r.log.Infof("../golangci-lint %s", strings.Join(runArgs, " "))
|
defer func(startedAt time.Time) {
|
||||||
|
r.log.Infof("ran [../golangci-lint %s] in %s", strings.Join(runArgs, " "), time.Since(startedAt))
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
cmd := exec.Command("../golangci-lint", runArgs...)
|
cmd := exec.Command("../golangci-lint", runArgs...)
|
||||||
cmd.Env = append(os.Environ(), r.env...)
|
cmd.Env = append(os.Environ(), r.env...)
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
@ -114,6 +121,11 @@ func (r *LintRunner) Run(args ...string) *RunResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *LintRunner) RunWithYamlConfig(cfg string, args ...string) *RunResult {
|
func (r *LintRunner) RunWithYamlConfig(cfg string, args ...string) *RunResult {
|
||||||
|
newArgs := append([]string{"--allow-parallel-runners"}, args...)
|
||||||
|
return r.RunCommandWithYamlConfig(cfg, "run", newArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LintRunner) RunCommandWithYamlConfig(cfg, command string, args ...string) *RunResult {
|
||||||
f, err := ioutil.TempFile("", "golangci_lint_test")
|
f, err := ioutil.TempFile("", "golangci_lint_test")
|
||||||
assert.NoError(r.t, err)
|
assert.NoError(r.t, err)
|
||||||
f.Close()
|
f.Close()
|
||||||
@ -133,5 +145,5 @@ func (r *LintRunner) RunWithYamlConfig(cfg string, args ...string) *RunResult {
|
|||||||
assert.NoError(r.t, err)
|
assert.NoError(r.t, err)
|
||||||
|
|
||||||
pargs := append([]string{"-c", cfgPath}, args...)
|
pargs := append([]string{"-c", cfgPath}, args...)
|
||||||
return r.Run(pargs...)
|
return r.RunCommand(command, pargs...)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user