From 09c65fcf425a96380621813c8b1eda1f69618901 Mon Sep 17 00:00:00 2001
From: Denis Isaev <denis@golangci.com>
Date: Sat, 22 Dec 2018 13:28:35 +0300
Subject: [PATCH] rework modules download mode option

---
 .golangci.example.yml | 10 ++++++++++
 README.md             | 11 ++++++++++-
 pkg/commands/run.go   |  1 -
 pkg/config/config.go  |  4 ++--
 pkg/lint/load.go      | 42 ++++++++++++++++++++++++++++++++----------
 5 files changed, 54 insertions(+), 14 deletions(-)

diff --git a/.golangci.example.yml b/.golangci.example.yml
index 2aa9c0e1..50a88a03 100644
--- a/.golangci.example.yml
+++ b/.golangci.example.yml
@@ -36,6 +36,16 @@ run:
     - ".*\\.my\\.go$"
     - lib/bad.go
 
+  # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
+  # If invoked with -mod=readonly, the go command is disallowed from the implicit
+  # automatic updating of go.mod described above. Instead, it fails when any changes
+  # to go.mod are needed. This setting is most useful to check that go.mod does
+  # not need updates, such as in a continuous integration and testing system.
+  # If invoked with -mod=vendor, the go command assumes that the vendor
+  # directory holds the correct copies of dependencies and ignores
+  # the dependency descriptions in go.mod.
+  modules-download-mode: readonly|release|vendor
+
 
 # output configuration options
 output:
diff --git a/README.md b/README.md
index 70271df3..024df052 100644
--- a/README.md
+++ b/README.md
@@ -432,7 +432,6 @@ Flags:
       --print-linter-name           Print linter name in issue line (default true)
       --issues-exit-code int        Exit code when issues were found (default 1)
       --build-tags strings          Build tags
-      --mod string                  module download mode to use: readonly or vendor (passed to go list)
       --deadline duration           Deadline for total work (default 1m0s)
       --tests                       Analyze tests (*_test.go) (default true)
       --print-resources-usage       Print avg and max memory usage of golangci-lint and total time
@@ -552,6 +551,16 @@ run:
     - ".*\\.my\\.go$"
     - lib/bad.go
 
+  # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
+  # If invoked with -mod=readonly, the go command is disallowed from the implicit
+  # automatic updating of go.mod described above. Instead, it fails when any changes
+  # to go.mod are needed. This setting is most useful to check that go.mod does
+  # not need updates, such as in a continuous integration and testing system.
+  # If invoked with -mod=vendor, the go command assumes that the vendor
+  # directory holds the correct copies of dependencies and ignores
+  # the dependency descriptions in go.mod.
+  modules-download-mode: readonly|release|vendor
+
 
 # output configuration options
 output:
diff --git a/pkg/commands/run.go b/pkg/commands/run.go
index dc893efe..46bfa29e 100644
--- a/pkg/commands/run.go
+++ b/pkg/commands/run.go
@@ -63,7 +63,6 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager) {
 	fs.IntVar(&rc.ExitCodeIfIssuesFound, "issues-exit-code",
 		exitcodes.IssuesFound, wh("Exit code when issues were found"))
 	fs.StringSliceVar(&rc.BuildTags, "build-tags", nil, wh("Build tags"))
-	fs.StringVar(&rc.Mod, "mod", "", wh("module download mode to use: readonly or vendor (passed to go list)"))
 	fs.DurationVar(&rc.Deadline, "deadline", time.Minute, wh("Deadline for total work"))
 	fs.BoolVar(&rc.AnalyzeTests, "tests", true, wh("Analyze tests (*_test.go)"))
 	fs.BoolVar(&rc.PrintResourcesUsage, "print-resources-usage", false,
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 8ec2b077..3d1f28a8 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -109,8 +109,8 @@ type Run struct {
 
 	Args []string
 
-	BuildTags []string `mapstructure:"build-tags"`
-	Mod       string   `mapstructure:"mod"`
+	BuildTags           []string `mapstructure:"build-tags"`
+	ModulesDownloadMode string   `mapstructure:"modules-download-mode"`
 
 	ExitCodeIfIssuesFound int  `mapstructure:"issues-exit-code"`
 	AnalyzeTests          bool `mapstructure:"tests"`
diff --git a/pkg/lint/load.go b/pkg/lint/load.go
index f8cbb4aa..c3cc7217 100644
--- a/pkg/lint/load.go
+++ b/pkg/lint/load.go
@@ -193,6 +193,35 @@ func (cl ContextLoader) buildArgs() []string {
 	return retArgs
 }
 
+func (cl ContextLoader) makeBuildFlags() ([]string, error) {
+	var buildFlags []string
+
+	if len(cl.cfg.Run.BuildTags) != 0 {
+		// go help build
+		buildFlags = append(buildFlags, "-tags", strings.Join(cl.cfg.Run.BuildTags, " "))
+	}
+
+	mod := cl.cfg.Run.ModulesDownloadMode
+	if mod != "" {
+		// go help modules
+		allowedMods := []string{"release", "readonly", "vendor"}
+		var ok bool
+		for _, am := range allowedMods {
+			if am == mod {
+				ok = true
+				break
+			}
+		}
+		if !ok {
+			return nil, fmt.Errorf("invalid modules download path %s, only (%s) allowed", mod, strings.Join(allowedMods, "|"))
+		}
+
+		buildFlags = append(buildFlags, fmt.Sprintf("-mod=%s", cl.cfg.Run.ModulesDownloadMode))
+	}
+
+	return buildFlags, nil
+}
+
 func (cl ContextLoader) loadPackages(ctx context.Context, loadMode packages.LoadMode) ([]*packages.Package, error) {
 	defer func(startedAt time.Time) {
 		cl.log.Infof("Go packages loading at mode %s took %s", stringifyLoadMode(loadMode), time.Since(startedAt))
@@ -200,16 +229,9 @@ func (cl ContextLoader) loadPackages(ctx context.Context, loadMode packages.Load
 
 	cl.prepareBuildContext()
 
-	var buildFlags []string
-
-	if len(cl.cfg.Run.BuildTags) != 0 {
-		// go help build
-		buildFlags = []string{"-tags", strings.Join(cl.cfg.Run.BuildTags, " ")}
-	}
-
-	if cl.cfg.Run.Mod != "" {
-		// go help module
-		buildFlags = append(buildFlags, fmt.Sprintf("-mod=%s", cl.cfg.Run.Mod))
+	buildFlags, err := cl.makeBuildFlags()
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to make build flags for go list")
 	}
 
 	conf := &packages.Config{