...

Source file src/sigs.k8s.io/release-utils/mage/golangci-lint.go

Documentation: sigs.k8s.io/release-utils/mage

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package mage
    18  
    19  import (
    20  	"fmt"
    21  	"log"
    22  	"net/url"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"strings"
    27  
    28  	"github.com/blang/semver/v4"
    29  	"github.com/uwu-tools/magex/pkg"
    30  	"github.com/uwu-tools/magex/pkg/gopath"
    31  	"github.com/uwu-tools/magex/shx"
    32  
    33  	kpath "k8s.io/utils/path"
    34  	"sigs.k8s.io/release-utils/command"
    35  	"sigs.k8s.io/release-utils/env"
    36  )
    37  
    38  const (
    39  	// golangci-lint
    40  	defaultGolangCILintVersion = "v1.55.2"
    41  	golangciCmd                = "golangci-lint"
    42  	golangciConfig             = ".golangci.yml"
    43  	golangciURLBase            = "https://raw.githubusercontent.com/golangci/golangci-lint"
    44  	defaultMinGoVersion        = "1.20"
    45  )
    46  
    47  // Ensure golangci-lint is installed and on the PATH.
    48  func EnsureGolangCILint(version string, forceInstall bool) error {
    49  	found, err := pkg.IsCommandAvailable(golangciCmd, "--version", version)
    50  	if err != nil {
    51  		return fmt.Errorf(
    52  			"checking if %s is available: %w",
    53  			golangciCmd, err,
    54  		)
    55  	}
    56  
    57  	if !found || forceInstall {
    58  		if version == "" {
    59  			log.Printf(
    60  				"A golangci-lint version to install was not specified. Using default version: %s",
    61  				defaultGolangCILintVersion,
    62  			)
    63  
    64  			version = defaultGolangCILintVersion
    65  		}
    66  
    67  		if !strings.HasPrefix(version, "v") {
    68  			return fmt.Errorf(
    69  				"golangci-lint version (%s) must begin with a 'v'",
    70  				version,
    71  			)
    72  		}
    73  
    74  		if _, err := semver.ParseTolerant(version); err != nil {
    75  			return fmt.Errorf(
    76  				"%s was not SemVer-compliant. Cannot continue.: %w",
    77  				version, err,
    78  			)
    79  		}
    80  
    81  		installURL, err := url.Parse(golangciURLBase)
    82  		if err != nil {
    83  			return fmt.Errorf("parsing URL: %w", err)
    84  		}
    85  
    86  		installURL.Path = path.Join(installURL.Path, version, "install.sh")
    87  
    88  		err = gopath.EnsureGopathBin()
    89  		if err != nil {
    90  			return fmt.Errorf("ensuring $GOPATH/bin: %w", err)
    91  		}
    92  
    93  		gopathBin := gopath.GetGopathBin()
    94  
    95  		installCmd := command.New(
    96  			"curl",
    97  			"-sSfL",
    98  			installURL.String(),
    99  		).Pipe(
   100  			"sh",
   101  			"-s",
   102  			"--",
   103  			"-b",
   104  			gopathBin,
   105  			version,
   106  		)
   107  
   108  		err = installCmd.RunSuccess()
   109  		if err != nil {
   110  			return fmt.Errorf("installing golangci-lint: %w", err)
   111  		}
   112  	}
   113  
   114  	return nil
   115  }
   116  
   117  // RunGolangCILint runs all golang linters
   118  func RunGolangCILint(version string, forceInstall bool, args ...string) error {
   119  	if _, err := kpath.Exists(kpath.CheckSymlinkOnly, golangciConfig); err != nil {
   120  		return fmt.Errorf(
   121  			"checking if golangci-lint config file (%s) exists: %w",
   122  			golangciConfig, err,
   123  		)
   124  	}
   125  
   126  	if err := EnsureGolangCILint(version, forceInstall); err != nil {
   127  		return fmt.Errorf("ensuring golangci-lint is installed: %w", err)
   128  	}
   129  
   130  	if err := shx.RunV(golangciCmd, "version"); err != nil {
   131  		return fmt.Errorf("getting golangci-lint version: %w", err)
   132  	}
   133  
   134  	if err := shx.RunV(golangciCmd, "linters"); err != nil {
   135  		return fmt.Errorf("listing golangci-lint linters: %w", err)
   136  	}
   137  
   138  	runArgs := []string{"run"}
   139  	runArgs = append(runArgs, args...)
   140  
   141  	if err := shx.RunV(golangciCmd, runArgs...); err != nil {
   142  		return fmt.Errorf("running golangci-lint linters: %w", err)
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  func TestGo(verbose bool, pkgs ...string) error {
   149  	return testGo(verbose, "", pkgs...)
   150  }
   151  
   152  func TestGoWithTags(verbose bool, tags string, pkgs ...string) error {
   153  	return testGo(verbose, tags, pkgs...)
   154  }
   155  
   156  func testGo(verbose bool, tags string, pkgs ...string) error {
   157  	verboseFlag := ""
   158  	if verbose {
   159  		verboseFlag = "-v"
   160  	}
   161  
   162  	pkgArgs := []string{}
   163  	if len(pkgs) > 0 {
   164  		for _, p := range pkgs {
   165  			pkgArg := fmt.Sprintf("./%s/...", p)
   166  			pkgArgs = append(pkgArgs, pkgArg)
   167  		}
   168  	} else {
   169  		pkgArgs = []string{"./..."}
   170  	}
   171  
   172  	cmdArgs := []string{"test"}
   173  	cmdArgs = append(cmdArgs, verboseFlag)
   174  	if tags != "" {
   175  		cmdArgs = append(cmdArgs, "-tags", tags)
   176  	}
   177  	cmdArgs = append(cmdArgs, pkgArgs...)
   178  
   179  	if err := shx.RunV(
   180  		"go",
   181  		cmdArgs...,
   182  	); err != nil {
   183  		return fmt.Errorf("running go test: %w", err)
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  // VerifyGoMod runs `go mod tidy` and `git diff --exit-code go.*` to ensure
   190  // all module updates have been checked in.
   191  func VerifyGoMod() error {
   192  	minGoVersion := env.Default("MIN_GO_VERSION", defaultMinGoVersion)
   193  	if err := shx.RunV(
   194  		"go", "mod", "tidy", fmt.Sprintf("-compat=%s", minGoVersion),
   195  	); err != nil {
   196  		return fmt.Errorf("running go mod tidy: %w", err)
   197  	}
   198  
   199  	if err := shx.RunV("git", "diff", "--exit-code", "go.*"); err != nil {
   200  		return fmt.Errorf("running go mod tidy: %w", err)
   201  	}
   202  
   203  	return nil
   204  }
   205  
   206  // VerifyBuild builds the project for a chosen set of platforms
   207  func VerifyBuild(scriptDir string) error {
   208  	wd, err := os.Getwd()
   209  	if err != nil {
   210  		return fmt.Errorf("getting working directory: %w", err)
   211  	}
   212  
   213  	scriptDir = filepath.Join(wd, scriptDir)
   214  
   215  	buildScript := filepath.Join(scriptDir, "verify-build.sh")
   216  	if err := shx.RunV(buildScript); err != nil {
   217  		return fmt.Errorf("running go build: %w", err)
   218  	}
   219  
   220  	return nil
   221  }
   222  

View as plain text