From b00b991ee01c419b4d41b6bb5ef51281aa95f6ff Mon Sep 17 00:00:00 2001 From: dipto0321 Date: Fri, 3 Jul 2026 09:39:49 +0600 Subject: [PATCH] build(ci): migrate .golangci.yml to v2 schema (closes #62) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pre-fix .golangci.yml targeted the golangci-lint v1 schema (no `version:` field, flat `linters.enable`, `output.formats` as a slice). The Makefile told contributors to `brew install golangci-lint` — which installs the current major version, v2.x, today. Any contributor with a current Homebrew got: Error: can't load config: can't unmarshal config by viper (flags, file): 1 error(s) decoding: * 'output.formats' expected a map, got 'slice' … instead of a lint failure, blocking `make lint` and `make ci` end-to-end. CI was unaffected because the GitHub Action pinned to v1.64.8, but the local dev workflow was effectively broken for anyone following the documented install steps. Migration via `golangci-lint migrate` plus a hand-rewrite to preserve the descriptive comments (migrate strips them). Net schema changes: - `version: "2"` added - `output.formats` is now a map (`text:`), not a list - `linters.disable-all: true` → `linters.default: none` - `gofmt` / `goimports` linters moved into the new `formatters:` section (with `local-prefixes` now a list) - `linters-settings:` → `linters.settings:` - New `linters.exclusions:` block with the same per-rule exclusions the v1 file had (test files, windows-only files, upgrade.go cross-file refs), plus a presets list (`comments`, `common-false-positives`, `legacy`, `std-error-handling`) for v2's broader exclusion grammar gosimple was folded into staticcheck in v2; the linters list is otherwise the same (bodyclose, contextcheck, errcheck, gocritic, govet, ineffassign, misspell, staticcheck, unused). CI bumps: - golangci/golangci-lint-action@v6 → @v8 - version: v1.64.8 → v2.12.2 (matches the version Homebrew currently ships, so local `make lint` agrees byte-for-byte with CI) Makefile `lint` target now checks for the binary and prints the exact `go install github.com/golangci/golangci-lint/v2/... @v2.12.2` command matching the CI pin when it's missing — so a contributor without golangci-lint gets a fix-the-install hint instead of a confusing exec failure. Confirmed locally: `make ci` is now green on a Homebrew v2.12.2 install (lint + vet + test all pass, 0 issues). Refs #62 --- .github/workflows/ci.yml | 10 ++- .golangci.yml | 130 +++++++++++++++++++++++++-------------- CHANGELOG.md | 28 +++++++++ Makefile | 9 ++- 4 files changed, 127 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 981e7e6..601a055 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,11 +55,17 @@ jobs: cache: true - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v8 with: # Pin golangci-lint to a version built against Go >= 1.24. # When go.mod's `go` directive advances, bump this accordingly. - version: v1.64.8 + # + # We track the v2 line (.golangci.yml targets the v2 schema + # via `version: "2"`). v2.12.2 matches the version Homebrew + # currently installs, so a contributor's local `make lint` + # result agrees byte-for-byte with CI. See #62 for why the + # v1 line was abandoned. + version: v2.12.2 args: --timeout=5m - name: commitlint diff --git a/.golangci.yml b/.golangci.yml index 5c55e16..f2faa2a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,16 +3,25 @@ # We run golangci-lint with this file in CI (see .github/workflows/ci.yml) # and recommend developers run it locally via `make lint`. # -# The defaults below are intentionally pragmatic: +# This file targets the v2 schema (`version: "2"`). The migration from +# v1 was done with `golangci-lint migrate` (see #62); the manually +# written comments below describe the lint set and exclusions. +# +# Linter set (linters.enable): # - errcheck: catches unchecked errors. We treat these as bugs. # - govet: standard static checks. # - staticcheck: a curated set of additional checks (formerly part of -# honnef.co/go/tools). +# honnef.co/go/tools). gosimple was folded into staticcheck in v2. # - unused: catches unused vars/funcs/types. -# - gosimple: suggests simplifications. # - ineffassign: catches assignments whose result is never used. # - gocritic: opinionated extra checks (kept light to avoid noise). -# - gofmt / goimports: format consistency. +# - misspell: catches common spelling mistakes (US locale). +# - bodyclose: catches http.Body that wasn't Close()'d. +# - contextcheck: catches ctx not flowing through call chains. +# +# Formatters (formatters.enable): gofmt + goimports. The latter has +# `local-prefixes: github.com/dipto0321/nodeup` so goimports orders +# that path first in import blocks. # # Linters we DELIBERATELY do not enable yet: # - revive / golint: superseded by staticcheck's style checks. @@ -20,69 +29,96 @@ # - exhaustive: useful but noisy for switch statements over manager names. # - funlen / cyclomatic: more useful once the code has stabilized. +version: "2" + run: - timeout: 5m tests: true linters: - disable-all: true + default: none enable: - errcheck - - gosimple - govet - - ineffassign - staticcheck - unused - - gofmt - - goimports + - ineffassign - gocritic - misspell - bodyclose - contextcheck + settings: + gocritic: + enabled-tags: + - diagnostic + - style + - performance + disabled-checks: + - ifElseChain # too noisy on early-return patterns + - hugeParam # not relevant yet + misspell: + locale: US + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + # errcheck / gocritic on test helpers — unchecked errors and + # opinionated suggestions are fine in tests. + - path: _test\.go + linters: + - errcheck + - gocritic + # Windows-only symbols appear unused on linux runners. + - path: internal/detector/.*_windows\.go + linters: + - unused + # Cross-file calls via root.go registration — upgrade.go's + # init symbols are referenced from root.go, not from + # anything in the same file. + - path: internal/cli/upgrade\.go + linters: + - unused + paths: + - docs + - dist + - vendor + - nodeup-npm + - third_party$ + - builtin$ + - examples$ -linters-settings: - gocritic: - enabled-tags: - - diagnostic - - style - - performance - disabled-checks: - - ifElseChain # too noisy on early-return patterns - - hugeParam # not relevant yet - misspell: - locale: US - goimports: - local-prefixes: github.com/dipto0321/nodeup +formatters: + enable: + - gofmt + - goimports + settings: + goimports: + local-prefixes: + - github.com/dipto0321/nodeup + exclusions: + generated: lax + paths: + - docs + - dist + - vendor + - nodeup-npm + - third_party$ + - builtin$ + - examples$ issues: # Don't bail out on first failure — show all problems. max-issues-per-linter: 0 max-same-issues: 0 - # Skip auto-generated / vendored / build-output directories. - # In v1.64.x the `run.exclude-dirs` key was removed from the run-schema - # and consolidated here as `issues.exclude-dirs`. - exclude-dirs: - - docs - - dist - - vendor - - nodeup-npm - # Exclude generated files from lint checks. - exclude-rules: - - path: _test\.go - linters: - - errcheck # unchecked errors in test helpers are fine - - gocritic - - path: internal/detector/.*_windows\.go - linters: - - unused # windows-only symbols appear unused on linux runners - - path: internal/cli/upgrade\.go - linters: - - unused # cross-file calls via root.go registration -# Output configuration. +# Output configuration. v2's `output.formats` is a MAP (not a list +# like v1) — see #62 for the v1→v2 schema migration. output: formats: - - format: colored-line-number + text: path: stdout - print-issued-lines: true - print-linter-name: true \ No newline at end of file + print-issued-lines: true + print-linter-name: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a045f3..2d8a83c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -146,6 +146,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 for the full rationale. ### Fixed +- `.golangci.yml` / `Makefile` / `.github/workflows/ci.yml`: + migrated to the golangci-lint **v2** schema. The pre-fix file + targeted the v1 schema (no `version:` field, flat + `linters.enable`, `output.formats` as a slice), but the + Makefile told contributors to `brew install golangci-lint` — + which installs the current major version, v2.x, today. Any + contributor with a current Homebrew got + `Error: can't load config: ... 'output.formats' expected a + map, got 'slice'` instead of a lint failure, blocking + `make lint` and `make ci` end-to-end. CI was unaffected + because `golangci-lint-action@v6` pinned to `version: + v1.64.8`, but the local dev workflow documented in the + Makefile was effectively broken. We migrated with + `golangci-lint migrate` (preserving the original + exclusions/presets/rationale in hand-written comments), + bumped CI to `golangci/lint-action@v8` pinned to + `v2.12.2` (matching what Homebrew ships today), and updated + the Makefile `lint` target to refuse to run when + golangci-lint isn't installed and point contributors at + the exact `go install` command matching the CI pin. The + same lint set + exclusion rules + presets that v1 ran + with still apply (`bodyclose`, `contextcheck`, `errcheck`, + `gocritic`, `govet`, `ineffassign`, `misspell`, + `staticcheck`, `unused`; `gosimple` is folded into + `staticcheck` in v2; formatters `gofmt` + `goimports` + with `local-prefixes: github.com/dipto0321/nodeup`). + Confirmed locally: `make ci` is now green on a Homebrew + v2.12.2 install. Closes #62. - `internal/detector/n.go`: `N.Current()` no longer shells out to `n current` — an undocumented subcommand that upstream `tj/n` resolves as a label equivalent to "latest" and diff --git a/Makefile b/Makefile index c19de59..33b3381 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,14 @@ test: ## Run unit tests with race + coverage @echo "Coverage report: $(COVERAGE_HTML)" .PHONY: lint -lint: ## Run golangci-lint (must be installed: brew install golangci-lint) +lint: ## Run golangci-lint (must match .github/workflows/ci.yml's pinned version) + @if ! command -v golangci-lint >/dev/null 2>&1; then \ + echo "golangci-lint not installed."; \ + echo "Install the version pinned in .github/workflows/ci.yml with:"; \ + echo " go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2"; \ + echo "(or update both this Makefile and ci.yml together when bumping)."; \ + exit 1; \ + fi golangci-lint run ./... .PHONY: fmt