From 221f923620f28f90898bc6d33f8195445f8b2e40 Mon Sep 17 00:00:00 2001 From: Thomas Stromberg Date: Tue, 29 Jul 2025 06:45:54 -0400 Subject: [PATCH 1/2] Update dependencies --- Makefile.tmpl | 8 ++++---- go.mod | 6 +++--- go.sum | 6 ++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Makefile.tmpl b/Makefile.tmpl index 417c4d1..a4ff787 100644 --- a/Makefile.tmpl +++ b/Makefile.tmpl @@ -21,7 +21,7 @@ LINTERS := FIXERS := {{ if .Shell -}} -SHELLCHECK_VERSION ?= v0.8.0 +SHELLCHECK_VERSION ?= v0.10.0 SHELLCHECK_BIN := $(LINT_ROOT)/out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH) $(SHELLCHECK_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -44,7 +44,7 @@ shellcheck-fix: $(SHELLCHECK_BIN) {{ end -}} {{ if .Dockerfile -}} -HADOLINT_VERSION ?= v2.10.0 +HADOLINT_VERSION ?= v2.12.0 HADOLINT_BIN := $(LINT_ROOT)/out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH) $(HADOLINT_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -61,7 +61,7 @@ hadolint-lint: $(HADOLINT_BIN) {{ if .Go -}} GOLANGCI_LINT_CONFIG := $(LINT_ROOT)/.golangci.yml -GOLANGCI_LINT_VERSION ?= v1.50.0 +GOLANGCI_LINT_VERSION ?= v2.3.0 GOLANGCI_LINT_BIN := $(LINT_ROOT)/out/linters/golangci-lint-$(GOLANGCI_LINT_VERSION)-$(LINT_ARCH) $(GOLANGCI_LINT_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -80,7 +80,7 @@ golangci-lint-fix: $(GOLANGCI_LINT_BIN) {{ end -}} {{ if .YAML -}} -YAMLLINT_VERSION ?= 1.27.1 +YAMLLINT_VERSION ?= 1.37.1 YAMLLINT_ROOT := $(LINT_ROOT)/out/linters/yamllint-$(YAMLLINT_VERSION) YAMLLINT_BIN := $(YAMLLINT_ROOT)/dist/bin/yamllint $(YAMLLINT_BIN): diff --git a/go.mod b/go.mod index 1abee08..92ca282 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.18 require ( github.com/hexops/gotextdiff v1.0.3 - github.com/karrick/godirwalk v1.16.1 - k8s.io/klog/v2 v2.10.0 + github.com/karrick/godirwalk v1.17.0 + k8s.io/klog/v2 v2.130.1 ) -require github.com/go-logr/logr v0.4.0 // indirect +require github.com/go-logr/logr v1.4.3 // indirect diff --git a/go.sum b/go.sum index 440b095..bbe0e76 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,14 @@ github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= +github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= k8s.io/klog/v2 v2.10.0 h1:R2HDMDJsHVTHA2n4RjwbeYXdOcBymXdX/JRb1v0VGhE= k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= From 47f05d256b2571e965ed30a80fabd51a1a0edb78 Mon Sep 17 00:00:00 2001 From: Thomas Stromberg Date: Sun, 3 Aug 2025 11:04:09 -0400 Subject: [PATCH 2/2] update to modern linter versions --- .golangci.yml | 588 +++++++++++++++++++++++++++++++----------------- Makefile | 4 +- go.mod | 2 +- lint-install.go | 110 +++++---- 4 files changed, 450 insertions(+), 254 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index d3416a8..6ecf7b4 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,208 +1,388 @@ -run: - # The default runtime timeout is 1m, which doesn't work well on Github Actions. - timeout: 4m - -# NOTE: This file is populated by the lint-install tool. Local adjustments may be overwritten. -linters-settings: - cyclop: - # NOTE: This is a very high transitional threshold - max-complexity: 37 - package-average: 34.0 - skip-tests: true - - gocognit: - # NOTE: This is a very high transitional threshold - min-complexity: 98 - - dupl: - threshold: 200 - - goconst: - min-len: 4 - min-occurrences: 5 - ignore-tests: true - - gosec: - excludes: - - G107 # Potential HTTP request made with variable url - - G204 # Subprocess launched with function call as argument or cmd arguments - - G404 # Use of weak random number generator (math/rand instead of crypto/rand - - errorlint: - # these are still common in Go: for instance, exit errors. - asserts: false - # Forcing %w in error wrapping forces authors to make errors part of their package APIs. The decision to make - # an error part of a package API should be a concious decision by the author. - # Also see Hyrums Law. - errorf: false - - exhaustive: - default-signifies-exhaustive: true - - nestif: - min-complexity: 8 - - nolintlint: - require-explanation: true - allow-unused: false - require-specific: true - - revive: - ignore-generated-header: true - severity: warning - rules: - - name: atomic - - name: blank-imports - - name: bool-literal-in-expr - - name: confusing-naming - - name: constant-logical-expr - - name: context-as-argument - - name: context-keys-type - - name: deep-exit - - name: defer - - name: range-val-in-closure - - name: range-val-address - - name: dot-imports - - name: error-naming - - name: error-return - - name: error-strings - - name: errorf - - name: exported - - name: identical-branches - - name: if-return - - name: import-shadowing - - name: increment-decrement - - name: indent-error-flow - - name: indent-error-flow - - name: package-comments - - name: range - - name: receiver-naming - - name: redefines-builtin-id - - name: superfluous-else - - name: struct-tag - - name: time-naming - - name: unexported-naming - - name: unexported-return - - name: unnecessary-stmt - - name: unreachable-code - - name: unused-parameter - - name: var-declaration - - name: var-naming - - name: unconditional-recursion - - name: waitgroup-by-value - - staticcheck: - go: "1.18" - - unused: - go: "1.18" - -output: - sort-results: true +# Based on https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322 +# This file is licensed under the terms of the MIT license https://opensource.org/license/mit +# Copyright (c) 2021-2025 Marat Reymers + +## Golden config for golangci-lint v2.3.0 +# +# This is the best config for golangci-lint based on my experience and opinion. +# It is very strict, but not extremely strict. +# Feel free to adapt it to suit your needs. +# If this config helps you, please consider keeping a link to this file (see the next comment). + +# Based on https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322 +version: "2" + +issues: + # Maximum count of issues with the same text. + # Set to 0 to disable. + # Default: 3 + max-same-issues: 50 + +formatters: + enable: + # - goimports # checks if the code and import statements are formatted according to the 'goimports' command + - golines # checks if code is formatted, and fixes long lines + + ## you may want to enable + #- gci # checks if code and import statements are formatted, with additional rules + #- gofmt # checks if the code is formatted according to 'gofmt' command + - gofumpt # enforces a stricter format than 'gofmt', while being backwards compatible + #- swaggo # formats swaggo comments + + # All settings can be found here https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.reference.yml + settings: + golines: + # Target maximum line length. + # Default: 100 + max-len: 120 linters: - disable-all: true enable: - - asciicheck - - bodyclose - - cyclop - - dogsled - - dupl - - durationcheck - - errcheck - - errname - - errorlint - - exhaustive - - exportloopref - - forcetypeassert - - gocognit - - goconst - - gocritic - - godot - - gofmt - - gofumpt - - gosec - - goheader - - goimports - - goprintffuncname - - gosimple - - govet - - importas - - ineffassign - - makezero - - misspell - - nakedret - - nestif - - nilerr - - noctx - - nolintlint - - predeclared - # disabling for the initial iteration of the linting tool - # - promlinter - - revive - # - rowserrcheck - disabled because of generics, https://github.com/golangci/golangci-lint/issues/2649 - # - sqlclosecheck - disabled because of generics, https://github.com/golangci/golangci-lint/issues/2649 - - staticcheck - # - structcheck - disabled because of generics, https://github.com/golangci/golangci-lint/issues/2649 - - stylecheck - - thelper - - tparallel - - typecheck - - unconvert - - unparam - - unused - # - wastedassign - disabled because of generics, https://github.com/golangci/golangci-lint/issues/2649 - - whitespace - - # Disabled linters, due to being misaligned with Go practices - # - exhaustivestruct - # - gochecknoglobals - # - gochecknoinits - # - goconst - # - godox - # - goerr113 - # - gomnd - # - lll - # - nlreturn - # - testpackage - # - wsl - # Disabled linters, due to not being relevant to our code base: - # - maligned - # - prealloc "For most programs usage of prealloc will be a premature optimization." - # Disabled linters due to bad error messages or bugs - # - tagliatelle + - asasalint # checks for pass []any as any in variadic func(...any) + - asciicheck # checks that your code does not contain non-ASCII identifiers + - bidichk # checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - canonicalheader # checks whether net/http.Header uses canonical header + - copyloopvar # detects places where loop variables are copied (Go 1.22+) + - cyclop # checks function and package cyclomatic complexity + - depguard # checks if package imports are in a list of acceptable packages + - dupl # tool for code clone detection + - durationcheck # checks for two durations multiplied together + - embeddedstructfieldcheck # checks embedded types in structs + - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases + - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error + - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 + - exhaustive # checks exhaustiveness of enum switch statements + - exptostd # detects functions from golang.org/x/exp/ that can be replaced by std functions + - fatcontext # detects nested contexts in loops + - forbidigo # forbids identifiers + - funcorder # checks the order of functions, methods, and constructors + - funlen # tool for detection of long functions + - gocheckcompilerdirectives # validates go compiler directive comments (//go:) + # - gochecknoglobals # checks that no global variables exist + - gochecknoinits # checks that no init functions are present in Go code + - gochecksumtype # checks exhaustiveness on Go "sum types" + - gocognit # computes and checks the cognitive complexity of functions + # - goconst # finds repeated strings that could be replaced by a constant + - gocritic # provides diagnostics that check for bugs, performance and style issues + - gocyclo # computes and checks the cyclomatic complexity of functions + - godot # checks if comments end in a period + - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod + - goprintffuncname # checks that printf-like functions are named with f at the end + - gosec # inspects source code for security problems + - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - iface # checks the incorrect use of interfaces, helping developers avoid interface pollution + - ineffassign # detects when assignments to existing variables are not used + - intrange # finds places where for loops could make use of an integer range + - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) + - makezero # finds slice declarations with non-zero initial length + - mirror # reports wrong mirror patterns of bytes/strings usage + - mnd # detects magic numbers + - musttag # enforces field tags in (un)marshaled structs + - nakedret # finds naked returns in functions greater than a specified function length + - nestif # reports deeply nested if statements + - nilerr # finds the code that returns nil even if it checks that the error is not nil + - nilnesserr # reports that it checks for err != nil, but it returns a different nil value error (powered by nilness and nilerr) + - nilnil # checks that there is no simultaneous return of nil error and an invalid value + - noctx # finds sending http request without context.Context + - nolintlint # reports ill-formed or insufficient nolint directives + - nonamedreturns # reports all named returns + - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL + - perfsprint # checks that fmt.Sprintf can be replaced with a faster alternative + - predeclared # finds code that shadows one of Go's predeclared identifiers + - promlinter # checks Prometheus metrics naming via promlint + - protogetter # reports direct reads from proto message fields when getters should be used + - reassign # checks that package variables are not reassigned + - recvcheck # checks for receiver type consistency + - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint + - rowserrcheck # checks whether Err of rows is checked successfully + - sloglint # ensure consistent code style when using log/slog + - spancheck # checks for mistakes with OpenTelemetry/Census spans + - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed + - staticcheck # is a go vet on steroids, applying a ton of static analysis checks + - testableexamples # checks if examples are testable (have an expected output) + - testifylint # checks usage of github.com/stretchr/testify + - testpackage # makes you use a separate _test package + - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes + - unconvert # removes unnecessary type conversions + - unparam # reports unused function parameters + - unused # checks for unused constants, variables, functions and types + - usestdlibvars # detects the possibility to use variables/constants from the Go standard library + - usetesting # reports uses of functions with replacement inside the testing package + - wastedassign # finds wasted assignment statements + - whitespace # detects leading and trailing whitespace -issues: - # Excluding configuration per-path, per-linter, per-text and per-source - exclude-rules: - - path: _test\.go - linters: - - dupl - - errcheck - - forcetypeassert - - gocyclo - - gosec - - noctx - - - path: .*cmd.* - linters: - - noctx - - - path: main\.go - linters: - - noctx - - - path: .*cmd.* - text: "deep-exit" - - - path: main\.go - text: "deep-exit" - - # This check is of questionable value - - linters: - - tparallel - text: "call t.Parallel on the top level as well as its subtests" - - # Don't hide lint issues just because there are many of them - max-same-issues: 0 - max-issues-per-linter: 0 + ## you may want to enable + #- arangolint # opinionated best practices for arangodb client + #- decorder # checks declaration order and count of types, constants, variables and functions + #- exhaustruct # [highly recommend to enable] checks if all structure fields are initialized + #- ginkgolinter # [if you use ginkgo/gomega] enforces standards of using ginkgo and gomega + #- godox # detects usage of FIXME, TODO and other keywords inside comments + #- goheader # checks is file header matches to pattern + - inamedparam # [great idea, but too strict, need to ignore a lot of cases by default] reports interfaces with unnamed method parameters + - interfacebloat # checks the number of methods inside an interface + #- ireturn # accept interfaces, return concrete types + #- noinlineerr # disallows inline error handling `if err := ...; err != nil {` + #- prealloc # [premature optimization, but can be used in some cases] finds slice declarations that could potentially be preallocated + #- tagalign # checks that struct tags are well aligned + #- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope + - wrapcheck # checks that errors returned from external packages are wrapped + #- zerologlint # detects the wrong usage of zerolog that a user forgets to dispatch zerolog.Event + + ## disabled by https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322 + - containedctx # detects struct contained context.Context field + #- contextcheck # [too many false positives] checks the function whether use a non-inherited context + - dogsled # checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) + #- dupword # [useless without config] checks for duplicate words in the source code + #- err113 # [too strict] checks the errors handling expressions + #- errchkjson # [don't see profit + I'm against of omitting errors like in the first example + #- forcetypeassert # [replaced by errcheck] finds forced type assertions + #- gomodguard # [use more powerful depguard] allow and block lists linter for direct Go module dependencies + #- gosmopolitan # reports certain i18n/l10n anti-patterns in your Go codebase + #- grouper # analyzes expression groups + #- importas # enforces consistent import aliases + #- lll # [replaced by golines] reports long lines + #- maintidx # measures the maintainability index of each function + - misspell # [useless] finds commonly misspelled English words in comments + #- nlreturn # [too strict and mostly code is not more readable] checks for a new line before return and branch statements to increase code clarity + #- paralleltest # [too many false positives] detects missing usage of t.Parallel() method in your Go test + #- tagliatelle # checks the struct tags + - thelper # detects golang test helpers without t.Helper() call and checks the consistency of test helpers + #- wsl # [too strict and mostly code is not more readable] whitespace linter forces you to use empty lines + #- wsl_v5 # [too strict and mostly code is not more readable] add or remove empty lines + + # All settings can be found here https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.reference.yml + settings: + cyclop: + # The maximal code complexity to report. + # Default: 10 + max-complexity: 50 + # The maximal average package complexity. + # If it's higher than 0.0 (float) the check is enabled. + # Default: 0.0 + package-average: 10.0 + + depguard: + rules: + "deprecated": + # List of file globs that will match this list of settings to compare against. + # By default, if a path is relative, it is relative to the directory where the golangci-lint command is executed. + # The placeholder '${base-path}' is substituted with a path relative to the mode defined with `run.relative-path-mode`. + # The placeholder '${config-path}' is substituted with a path relative to the configuration file. + # Default: $all + files: + - "$all" + # List of packages that are not allowed. + # Entries can be a variable (starting with $), a string prefix, or an exact match (if ending with $). + # Default: [] + deny: + - pkg: github.com/golang/protobuf + desc: Use google.golang.org/protobuf instead, see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules + - pkg: github.com/satori/go.uuid + desc: Use github.com/google/uuid instead, satori's package is not maintained + - pkg: github.com/gofrs/uuid$ + desc: Use github.com/gofrs/uuid/v5 or later, it was not a go module before v5 + "non-test files": + files: + - "!$test" + deny: + - pkg: math/rand$ + desc: Use math/rand/v2 instead, see https://go.dev/blog/randv2 + - pkg: "github.com/sirupsen/logrus" + desc: not allowed + - pkg: "github.com/pkg/errors" + desc: Should be replaced by standard lib errors package + + embeddedstructfieldcheck: + # Checks that sync.Mutex and sync.RWMutex are not used as embedded fields. + # Default: false + forbid-mutex: true + + errcheck: + # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. + # Such cases aren't reported by default. + # Default: false + check-type-assertions: true + + exhaustive: + # Program elements to check for exhaustiveness. + # Default: [ switch ] + check: + - switch + - map + + fatcontext: + # Check for potential fat contexts in struct pointers. + # May generate false positives. + # Default: false + check-struct-pointers: true + + funcorder: + # Checks if the exported methods of a structure are placed before the non-exported ones. + # Default: true + struct-method: false + + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + # Default: 60 + lines: 120 + # Checks the number of statements in a function. + # If lower than 0, disable the check. + # Default: 40 + statements: 60 + + gochecksumtype: + # Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed. + # Default: true + default-signifies-exhaustive: false + + gocognit: + # Minimal code complexity to report. + # Default: 30 (but we recommend 10-20) + min-complexity: 49 + + gocritic: + enable-all: true + disabled-checks: + - paramTypeCombine + # Settings passed to gocritic. + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be found at https://go-critic.com/overview. + settings: + captLocal: + # Whether to restrict checker to params only. + # Default: true + paramsOnly: false + underef: + # Whether to skip (*x).method() calls where x is a pointer receiver. + # Default: true + skipRecvDeref: false + hugeParam: + # Default: 80 + sizeThreshold: 200 + + govet: + # Enable all analyzers. + # Default: false + enable-all: true + # Disable analyzers by name. + # Run `GL_DEBUG=govet golangci-lint run --enable=govet` to see default, all available analyzers, and enabled analyzers. + # Default: [] + #disable: + # - fieldalignment # too strict + + inamedparam: + # Skips check for interface methods with only a single parameter. + # Default: false + skip-single-param: true + + mnd: + # List of function patterns to exclude from analysis. + # Values always ignored: `time.Date`, + # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`, + # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`. + # Default: [] + ignored-functions: + - args.Error + - flag.Arg + - flag.Duration.* + - flag.Float.* + - flag.Int.* + - flag.Uint.* + - os.Chmod + - os.Mkdir.* + - os.OpenFile + - os.WriteFile + - prometheus.ExponentialBuckets.* + - prometheus.LinearBuckets + + nakedret: + # Make an issue if func has more lines of code than this setting, and it has naked returns. + # Default: 30 + max-func-lines: 1 + + nestif: + min-complexity: 6 + + nolintlint: + # Exclude following linters from requiring an explanation. + # Default: [] + allow-no-explanation: [funlen, gocognit, golines] + # Enable to require an explanation of nonzero length after each nolint directive. + # Default: false + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. + # Default: false + require-specific: true + + perfsprint: + # Optimizes into strings concatenation. + # Default: true + strconcat: false + + reassign: + # Patterns for global variable names that are checked for reassignment. + # See https://github.com/curioswitch/go-reassign#usage + # Default: ["EOF", "Err.*"] + patterns: + - ".*" + + rowserrcheck: + # database/sql is always checked. + # Default: [] + packages: + - github.com/jmoiron/sqlx + + sloglint: + # Enforce not using global loggers. + # Values: + # - "": disabled + # - "all": report all global loggers + # - "default": report only the default slog logger + # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global + # Default: "" + no-global: all + # Enforce using methods that accept a context. + # Values: + # - "": disabled + # - "all": report all contextless calls + # - "scope": report only if a context exists in the scope of the outermost function + # https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only + # Default: "" + context: scope + + staticcheck: + # SAxxxx checks in https://staticcheck.dev/docs/configuration/options/#checks + # Example (to disable some checks): [ "all", "-SA1000", "-SA1001"] + # Default: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"] + checks: + - all + + usetesting: + # Enable/disable `os.TempDir()` detections. + # Default: false + os-temp-dir: true + + exclusions: + # Log a warning if an exclusion rule is unused. + # Default: false + warn-unused: true + # Predefined exclusion rules. + # Default: [] + presets: + - common-false-positives + # Excluding configuration per-path, per-linter, per-text and per-source. + rules: + - text: 'shadow: declaration of "err" shadows declaration at' + linters: [govet] + - path: '_test\.go' + linters: + - bodyclose + - dupl + - errcheck + - funlen + - goconst + - gosec + - noctx + - wrapcheck diff --git a/Makefile b/Makefile index 16649e6..381984e 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ FIXERS += shellcheck-fix shellcheck-fix: $(SHELLCHECK_BIN) $(SHELLCHECK_BIN) $(shell find . -name "*.sh") -f diff | { read -t 1 line || exit 0; { echo "$$line" && cat; } | git apply -p2; } -HADOLINT_VERSION ?= v2.10.0 +HADOLINT_VERSION ?= v2.12.1-beta HADOLINT_BIN := $(LINT_ROOT)/out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH) $(HADOLINT_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -54,7 +54,7 @@ hadolint-lint: $(HADOLINT_BIN) $(HADOLINT_BIN) $(shell find . -name "*Dockerfile") GOLANGCI_LINT_CONFIG := $(LINT_ROOT)/.golangci.yml -GOLANGCI_LINT_VERSION ?= v1.50.0 +GOLANGCI_LINT_VERSION ?= v2.3.1 GOLANGCI_LINT_BIN := $(LINT_ROOT)/out/linters/golangci-lint-$(GOLANGCI_LINT_VERSION)-$(LINT_ARCH) $(GOLANGCI_LINT_BIN): mkdir -p $(LINT_ROOT)/out/linters diff --git a/go.mod b/go.mod index 92ca282..b3b5257 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/tinkerbell/lint-install -go 1.18 +go 1.22 require ( github.com/hexops/gotextdiff v1.0.3 diff --git a/lint-install.go b/lint-install.go index cbc1a3e..4191c3d 100644 --- a/lint-install.go +++ b/lint-install.go @@ -1,3 +1,4 @@ +// Package main provides a tool to automatically install and configure linters in Go projects. package main import ( @@ -45,14 +46,14 @@ const ( ) type Config struct { + LintCommands map[string]string + FixCommands map[string]string Makefile string Args string Go string Dockerfile string Shell string YAML string - LintCommands map[string]string - FixCommands map[string]string } // applicableLinters returns a list of languages with known linters within a given directory. @@ -61,7 +62,7 @@ func applicableLinters(root string) (map[Language]bool, error) { found := map[Language]bool{} err := godirwalk.Walk(root, &godirwalk.Options{ - Callback: func(path string, de *godirwalk.Dirent) error { + Callback: func(path string, _ *godirwalk.Dirent) error { switch { case strings.HasSuffix(path, ".go"): found[Go] = true @@ -77,8 +78,10 @@ func applicableLinters(root string) (map[Language]bool, error) { }, Unsorted: true, }) - - return found, err + if err != nil { + return found, fmt.Errorf("walk: %w", err) + } + return found, nil } // updateMakefile updates the Makefile within a project with lint rules. @@ -91,7 +94,7 @@ func updateMakefile(root string, cfg Config, dryRun bool) (string, error) { klog.Infof("Found existing %s", dest) existing, err = os.ReadFile(dest) if err != nil { - return "", err + return "", fmt.Errorf("read file: %w", err) } } @@ -142,7 +145,7 @@ func updateMakefile(root string, cfg Config, dryRun bool) (string, error) { change := gotextdiff.ToUnified(filepath.Base(dest), filepath.Base(dest), string(existing), edits) if !dryRun { if err := os.WriteFile(dest, proposed, 0o600); err != nil { - return "", err + return "", fmt.Errorf("write file: %w", err) } } return fmt.Sprint(change), nil @@ -159,7 +162,7 @@ func updateFile(root string, basename string, content []byte, dryRun bool) (stri klog.Infof("Found existing %s", dest) existing, err = os.ReadFile(dest) if err != nil { - return "", err + return "", fmt.Errorf("read file: %w", err) } } @@ -167,20 +170,21 @@ func updateFile(root string, basename string, content []byte, dryRun bool) (stri edits := myers.ComputeEdits(span.URI(basename), string(existing), proposed) change := gotextdiff.ToUnified(basename, basename, string(existing), edits) - if !dryRun { - var err error - if content == nil { - // must be broken up, can't be conten == nil && f != nil because then - // the else branch will be taken if the file does *not* exist and - // an empty file will be written - if f != nil { - err = os.Remove(dest) - } - } else { - err = os.WriteFile(dest, content, 0o600) + if dryRun { + return fmt.Sprint(change), nil + } + + // Handle file operations when not in dry-run mode + if content == nil && f != nil { + if err := os.Remove(dest); err != nil { + return "", fmt.Errorf("remove file: %w", err) } - if err != nil { - return "", err + return fmt.Sprint(change), nil + } + + if content != nil { + if err := os.WriteFile(dest, content, 0o600); err != nil { + return "", fmt.Errorf("write file: %w", err) } } @@ -197,7 +201,7 @@ func updateGitignore(root string, dryRun bool) (string, error) { klog.Infof("Found existing %s", dest) existing, err = os.ReadFile(dest) if err != nil { - return "", err + return "", fmt.Errorf("read file: %w", err) } } proposed := []byte{} @@ -222,7 +226,7 @@ func updateGitignore(root string, dryRun bool) (string, error) { change := gotextdiff.ToUnified(filepath.Base(dest), filepath.Base(dest), string(existing), edits) if !dryRun { if err := os.WriteFile(dest, proposed, 0o600); err != nil { - return "", err + return "", fmt.Errorf("write file: %w", err) } } return fmt.Sprint(change), nil @@ -234,7 +238,7 @@ func goLintCmd(root string, level string, fix bool) string { found := []string{} err := godirwalk.Walk(root, &godirwalk.Options{ - Callback: func(path string, de *godirwalk.Dirent) error { + Callback: func(path string, _ *godirwalk.Dirent) error { if strings.HasSuffix(path, "go.mod") { found = append(found, filepath.Dir(path)) } @@ -254,7 +258,10 @@ func goLintCmd(root string, level string, fix bool) string { } klog.Infof("found %d modules within %s: %s", len(found), root, found) - return fmt.Sprintf(`find . -name go.mod -execdir "$(GOLANGCI_LINT_BIN)" run -c "$(GOLANGCI_LINT_CONFIG)"%s \;`, suffix) + return fmt.Sprintf( + `find . -name go.mod -execdir "$(GOLANGCI_LINT_BIN)" run -c "$(GOLANGCI_LINT_CONFIG)"%s \;`, + suffix, + ) } // shellLintCmd returns the appropriate shell lint command for a project. @@ -294,6 +301,35 @@ func yamlLintCmd(_ string, level string) string { return fmt.Sprintf(`PYTHONPATH=$(YAMLLINT_ROOT)/dist $(YAMLLINT_ROOT)/dist/bin/yamllint .%s`, suffix) } +// configureGoLinter sets up Go linting configuration and commands. +func configureGoLinter(root string, cfg *Config, dryRun bool) error { + cfg.Go = *goFlag + cfg.LintCommands["golangci-lint"] = goLintCmd(root, cfg.Go, false) + cfg.FixCommands["golangci-lint"] = goLintCmd(root, cfg.Go, true) + + diff, err := updateFile(root, ".golangci.yml", goLintConfig, dryRun) + if err != nil { + return fmt.Errorf("update go lint config: %w", err) + } + if diff != "" { + klog.Infof("go lint config changes:\n%s", diff) + } else { + klog.Infof("go lint config has no changes") + } + + // Remove non-standard config files + for _, name := range []string{".golangci.json", ".golangci.toml", ".golangci.yaml"} { + diff, err := updateFile(root, name, nil, dryRun) + if err != nil { + return fmt.Errorf("delete %s: %w", name, err) + } + if diff != "" { + klog.Infof("standardizing on golangci.yml, deleting %s", name) + } + } + return nil +} + // main creates peanut butter & jelly sandwiches with utmost precision. func main() { klog.InitFlags(nil) @@ -320,28 +356,8 @@ func main() { } if needs[Go] { - cfg.Go = *goFlag - cfg.LintCommands["golangci-lint"] = goLintCmd(root, cfg.Go, false) - cfg.FixCommands["golangci-lint"] = goLintCmd(root, cfg.Go, true) - - diff, err := updateFile(root, ".golangci.yml", goLintConfig, *dryRunFlag) - if err != nil { - klog.Exitf("update go lint config failed: %v", err) - } - if diff != "" { - klog.Infof("go lint config changes:\n%s", diff) - } else { - klog.Infof("go lint config has no changes") - } - - for _, name := range []string{".golangci.json", ".golangci.toml", ".golangci.yaml"} { - diff, err := updateFile(root, name, nil, *dryRunFlag) - if err != nil { - klog.Exitf("deleting non-standardized go lint config failed: %v", err) - } - if diff != "" { - klog.Infof("standardizing on golangci.yml, deleting %s", name) - } + if err := configureGoLinter(root, &cfg, *dryRunFlag); err != nil { + klog.Exitf("configure go linter: %v", err) } } if needs[Dockerfile] {