Repository: compose-spec/compose-go
Pull request #762 (2025-03-31 06:17:20)

migrate golangci-lint to v2.0.2
bump golangci-lint to v2.0.2
used `golangci-lint migrate` to adapt configuration
fixed a few reported linter issues


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8f65aea..addf4ce 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,9 +37,9 @@ jobs:
         go-version: ${{ matrix.go-version }}
         check-latest: true
         cache: true
-    - uses: golangci/golangci-lint-action@v6
+    - uses: golangci/golangci-lint-action@v7
       with:
-        version: v1.64.2
+        version: v2.0.2
         args: --verbose
         skip-cache: true
     - name: Test
diff --git a/.golangci.yml b/.golangci.yml
index ac48128..085fe91 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,8 +1,6 @@
-issues:
-  max-issues-per-linter: 0
-  max-same-issues: 0
+version: "2"
 linters:
-  disable-all: true
+  default: none
   enable:
     - copyloopvar
     - depguard
@@ -10,11 +8,7 @@ linters:
     - errorlint
     - gocritic
     - gocyclo
-    - gofumpt
-    - goimports
     - gomodguard
-    - revive
-    - gosimple
     - govet
     - ineffassign
     - lll
@@ -24,51 +18,70 @@ linters:
     - revive
     - staticcheck
     - testifylint
-    - typecheck
     - unconvert
     - unparam
     - unused
-linters-settings:
-  depguard:
-    rules:
-      all:
-        deny:
-          - pkg: gopkg.in/yaml.v2
-            desc: 'compose-go uses yaml.v3'
-  gocritic:
-    # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
-    # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
-    enabled-tags:
-      - diagnostic
-      - opinionated
-      - style
-    disabled-checks:
-      - paramTypeCombine
-      - unnamedResult
-      - whyNoLint
-  gomodguard:
-    blocked:
-      modules:
-      - github.com/pkg/errors:
-          recommendations:
-          - errors
-          - fmt
-  lll:
-    line-length: 200
-  testifylint:
-    disable:
-    - float-compare
-    - go-require
-    enable:
-    - bool-compare
-    - compares
-    - empty
-    - error-is-as
-    - error-nil
-    - expected-actual
-    - len
-    - require-error
-    - suite-dont-use-pkg
-    - suite-extra-assert-call
-run:
-  timeout: 5m
+  settings:
+    depguard:
+      rules:
+        all:
+          deny:
+            - pkg: gopkg.in/yaml.v2
+              desc: compose-go uses yaml.v3
+    gocritic:
+      disabled-checks:
+        - paramTypeCombine
+        - unnamedResult
+        - whyNoLint
+      enabled-tags:
+        - diagnostic
+        - opinionated
+        - style
+    gomodguard:
+      blocked:
+        modules:
+          - github.com/pkg/errors:
+              recommendations:
+                - errors
+                - fmt
+    lll:
+      line-length: 200
+    testifylint:
+      enable:
+        - bool-compare
+        - compares
+        - empty
+        - error-is-as
+        - error-nil
+        - expected-actual
+        - len
+        - require-error
+        - suite-dont-use-pkg
+        - suite-extra-assert-call
+      disable:
+        - float-compare
+        - go-require
+  exclusions:
+    generated: lax
+    presets:
+      - comments
+      - common-false-positives
+      - legacy
+      - std-error-handling
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
+issues:
+  max-issues-per-linter: 0
+  max-same-issues: 0
+formatters:
+  enable:
+    - gofumpt
+    - goimports
+  exclusions:
+    generated: lax
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
diff --git a/ci/Dockerfile b/ci/Dockerfile
index 4eb8c7f..cd90a5f 100644
--- a/ci/Dockerfile
+++ b/ci/Dockerfile
@@ -16,7 +16,7 @@ FROM golang:1.24
 
 WORKDIR /go/src
 
-ARG GOLANGCILINT_VERSION=v1.64.5
+ARG GOLANGCILINT_VERSION=v2.0.2
 RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCILINT_VERSION}
 RUN go install github.com/awalterschulze/goderive@v0.5.1
 RUN go install github.com/containerd/ltag@v0.3.0
diff --git a/dotenv/env.go b/dotenv/env.go
index cc472a6..b01f993 100644
--- a/dotenv/env.go
+++ b/dotenv/env.go
@@ -35,7 +35,7 @@ func GetEnvFromFile(currentEnv map[string]string, filenames []string) (map[strin
 
 		s, err := os.Stat(dotEnvFile)
 		if os.IsNotExist(err) {
-			return envMap, fmt.Errorf("Couldn't find env file: %s", dotEnvFile)
+			return envMap, fmt.Errorf("couldn't find env file: %s", dotEnvFile)
 		}
 		if err != nil {
 			return envMap, err
@@ -50,7 +50,7 @@ func GetEnvFromFile(currentEnv map[string]string, filenames []string) (map[strin
 
 		b, err := os.ReadFile(dotEnvFile)
 		if os.IsNotExist(err) {
-			return nil, fmt.Errorf("Couldn't read env file: %s", dotEnvFile)
+			return nil, fmt.Errorf("couldn't read env file: %s", dotEnvFile)
 		}
 		if err != nil {
 			return envMap, err
diff --git a/loader/loader.go b/loader/loader.go
index 3fe9ca0..6b1d13c 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -337,7 +337,7 @@ func LoadModelWithContext(ctx context.Context, configDetails types.ConfigDetails
 // LoadModelWithContext reads a ConfigDetails and returns a fully loaded configuration as a yaml dictionary
 func loadModelWithContext(ctx context.Context, configDetails *types.ConfigDetails, opts *Options) (map[string]any, error) {
 	if len(configDetails.ConfigFiles) < 1 {
-		return nil, errors.New("No files specified")
+		return nil, errors.New("no compose file specified")
 	}
 
 	err := projectName(configDetails, opts)
@@ -432,7 +432,7 @@ func loadYamlFile(ctx context.Context,
 		}
 		cfg, ok := converted.(map[string]interface{})
 		if !ok {
-			return errors.New("Top-level object must be a mapping")
+			return errors.New("top-level object must be a mapping")
 		}
 
 		if opts.Interpolate != nil && !opts.SkipInterpolation {
@@ -877,7 +877,7 @@ func formatInvalidKeyError(keyPrefix string, key interface{}) error {
 	} else {
 		location = fmt.Sprintf("in %s", keyPrefix)
 	}
-	return fmt.Errorf("Non-string key %s: %#v", location, key)
+	return fmt.Errorf("non-string key %s: %#v", location, key)
 }
 
 // Windows path, c:\\my\\path\\shiny, need to be changed to be compatible with
diff --git a/types/project.go b/types/project.go
index 4189f2f..93442ce 100644
--- a/types/project.go
+++ b/types/project.go
@@ -157,9 +157,10 @@ func (p *Project) ServicesWithCapabilities() ([]string, []string, []string) {
 				capabilities = append(capabilities, service.Name)
 			}
 			for _, c := range d.Capabilities {
-				if c == "gpu" {
+				switch c {
+				case "gpu":
 					gpu = append(gpu, service.Name)
-				} else if c == "tpu" {
+				case "tpu":
 					tpu = append(tpu, service.Name)
 				}
 			}


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index e2814f7..1ae9942 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -453,13 +453,13 @@ func TestParseAndLoad(t *testing.T) {
 
 func TestInvalidTopLevelObjectType(t *testing.T) {
 	_, err := loadYAML("1")
-	assert.ErrorContains(t, err, "Top-level object must be a mapping")
+	assert.ErrorContains(t, err, "top-level object must be a mapping")
 
 	_, err = loadYAML("\"hello\"")
-	assert.ErrorContains(t, err, "Top-level object must be a mapping")
+	assert.ErrorContains(t, err, "top-level object must be a mapping")
 
 	_, err = loadYAML("[\"hello\"]")
-	assert.ErrorContains(t, err, "Top-level object must be a mapping")
+	assert.ErrorContains(t, err, "top-level object must be a mapping")
 }
 
 func TestNonStringKeys(t *testing.T) {
@@ -468,7 +468,7 @@ func TestNonStringKeys(t *testing.T) {
   foo:
     image: busybox
 `)
-	assert.ErrorContains(t, err, "Non-string key at top level: 123")
+	assert.ErrorContains(t, err, "non-string key at top level: 123")
 
 	_, err = loadYAML(`
 services:
@@ -477,7 +477,7 @@ services:
   123:
     image: busybox
 `)
-	assert.ErrorContains(t, err, "Non-string key in services: 123")
+	assert.ErrorContains(t, err, "non-string key in services: 123")
 
 	_, err = loadYAML(`
 services:
@@ -489,7 +489,7 @@ networks:
       config:
         - 123: oh dear
 `)
-	assert.ErrorContains(t, err, "Non-string key in networks.default.ipam.config[0]: 123")
+	assert.ErrorContains(t, err, "non-string key in networks.default.ipam.config[0]: 123")
 
 	_, err = loadYAML(`
 services:
@@ -498,7 +498,7 @@ services:
     environment:
       1: FOO
 `)
-	assert.ErrorContains(t, err, "Non-string key in services.dict-env.environment: 1")
+	assert.ErrorContains(t, err, "non-string key in services.dict-env.environment: 1")
 }
 
 func TestV1Unsupported(t *testing.T) {