Repository: siemens/wfx
Pull request #307: chore(deps): update dependency golangci/golangci-lint to v2
Author: login: renovate[bot]
Created: 2025-04-01T02:13:46Z | Merged: 2025-04-02T08:59:08Z

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [golangci/golangci-lint](https://redirect.github.com/golangci/golangci-lint) | major | `1.64.8` -> `2.0.2` |

---

### Release Notes

<details>
<summary>golangci/golangci-lint (golangci/golangci-lint)</summary>

### [`v2.0.2`](https://redirect.github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v202)

[Compare Source](https://redirect.github.com/golangci/golangci-lint/compare/v2.0.1...v2.0.2)

1.  Misc.
    -   Fixes flags parsing for formatters
    -   Fixes the filepath used by the exclusion `source` option
2.  Documentation
    -   Adds a section about flags migration
    -   Cleaning pages with v1 options

### [`v2.0.1`](https://redirect.github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v201)

[Compare Source](https://redirect.github.com/golangci/golangci-lint/compare/v2.0.0...v2.0.1)

1.  Linters/formatters bug fixes
    -   `golines`: fix settings during linter load
2.  Misc.
    -   Validates the `version` field before the configuration
    -   `forbidigo`: fix migration

### [`v2.0.0`](https://redirect.github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v200)

[Compare Source](https://redirect.github.com/golangci/golangci-lint/compare/v1.64.8...v2.0.0)

1.  Enhancements
    -   🌟 New `golangci-lint fmt` command with dedicated [formatter configuration](https://golangci-lint.run/welcome/quick-start/#formatting)
    -   ♻️ New `golangci-lint migrate` command to help migration from v1 to v2 (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#command-migrate))
    -   ⚠️ New default values (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/))
    -   ⚠️ No exclusions by default (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#issuesexclude-use-default))
    -   ⚠️ New default sort order (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#outputsort-order))
    -   🌟 New option `run.relative-path-mode` (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#runrelative-path-mode))
    -   🌟 New linters configuration (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#linters))
    -   🌟 New output format configuration (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#output))
    -   🌟 New `--fast-only` flag (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#lintersfast))
    -   🌟 New option `linters.exclusions.warn-unused` to log a warning if an exclusion rule is unused.
2.  New linters/formatters
    -   Add `golines` formatter https://github.com/segmentio/golines
3.  Linters new features
    -   ⚠️ Merge `staticcheck`, `stylecheck`, `gosimple` into one linter (`staticcheck`) (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#lintersenablestylecheckgosimplestaticcheck))
    -   `go-critic`: from 0.12.0 to 0.13.0
    -   `gomodguard`: from 1.3.5 to 1.4.1 (block explicit indirect dependencies)
    -   `nilnil`: from 1.0.1 to 1.1.0 (new option: `only-two`)
    -   `perfsprint`: from 0.8.2 to 0.9.1 (checker name in the diagnostic message)
    -   `staticcheck`: new `quickfix` set of rules
    -   `testifylint`: from 1.5.2 to 1.6.0 (new options: `equal-values`, `suite-method-signature`, `require-string-msg`)
    -   `wsl`: from 4.5.0 to 4.6.0 (new option: `allow-cuddle-used-in-block`)
4.  Linters bug fixes
    -   `bidichk`: from 0.3.2 to 0.3.3
    -   `errchkjson`: from 0.4.0 to 0.4.1
    -   `errname`: from 1.0.0 to 1.1.0
    -   `funlen`: fix `ignore-comments` option
    -   `gci`: from 0.13.5 to 0.13.6
    -   `gosmopolitan`: from 1.2.2 to 1.3.0
    -   `inamedparam`: from 0.1.3 to 0.2.0
    -   `intrange`: from 0.3.0 to 0.3.1
    -   `protogetter`: from 0.3.9 to 0.3.12
    -   `unparam`: from [`8a5130c`](https://redirect.github.com/golangci/golangci-lint/commit/8a5130ca722f) to [`0df0534`](https://redirect.github.com/golangci/golangci-lint/commit/0df0534333a4)
5.  Misc.
    -   🧹 Configuration options renaming (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/))
    -   🧹 Remove options (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/))
    -   🧹 Remove flags (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/))
    -   🧹 Remove alternative names (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#alternative-linter-names))
    -   🧹 Remove or replace deprecated elements (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/))
    -   Adds an option to display some commands as JSON:
        -   `golangci-lint config path --json`
        -   `golangci-lint help linters --json`
        -   `golangci-lint help formatters --json`
        -   `golangci-lint linters --json`
        -   `golangci-lint formatters --json`
        -   `golangci-lint version --json`
6.  Documentation
    -   [Migration guide](https://golangci-lint.run/product/migration-guide/)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "* 0-3 1 * *" (UTC), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/siemens/wfx).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIyNy4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->



Diff:
diff --git a/.ci/packages/versions.env b/.ci/packages/versions.env
index d0678886..b48307a3 100644
--- a/.ci/packages/versions.env
+++ b/.ci/packages/versions.env
@@ -1,5 +1,5 @@
 export FLATBUFFERS_VERSION=25.2.10 # github-releases/google/flatbuffers&versioning=semver
-export GOLANGCI_LINT_VERSION=1.64.8 # github-releases/golangci/golangci-lint&versioning=semver
+export GOLANGCI_LINT_VERSION=2.0.2 # github-releases/golangci/golangci-lint&versioning=semver
 export GORELEASER_VERSION=2.8.2 # github-releases/goreleaser/goreleaser&versioning=semver
 export HUGO_VERSION=0.145.0 # github-releases/gohugoio/hugo&versioning=semver
 export JUST_VERSION=1.40.0 # github-releases/casey/just&versioning=semver
diff --git a/.golangci.yml b/.golangci.yml
index f836ca05..481c2569 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -2,6 +2,8 @@
 #
 # SPDX-License-Identifier: Apache-2.0
 ---
+version: "2"
+
 run:
   # timeout for analysis, e.g. 30s, 5m, default is 1m
   timeout: 5m
@@ -33,23 +35,6 @@ run:
   # If false (default) - golangci-lint acquires file lock on start.
   allow-parallel-runners: true
 
-# output configuration options
-output:
-  # print lines of code with issue, default is true
-  print-issued-lines: true
-
-  # print linter name in the end of issue text, default is true
-  print-linter-name: true
-
-  # make issues output unique by line, default is true
-  uniq-by-line: true
-
-  # add a prefix to the output file references; default is no prefix
-  path-prefix: ""
-
-  # sorts results by: filepath, line and column
-  sort-results: false
-
 linters:
   enable:
     - decorder
@@ -61,37 +46,39 @@ linters:
     - gocheckcompilerdirectives
     - goconst
     - gocritic
-    - gofumpt
     - importas
     - misspell
     - nilnil
     - prealloc
     - reassign
-    - revive
-    - stylecheck
     - tparallel
     - usestdlibvars
     - wrapcheck
     - zerologlint
 
-linters-settings:
-  staticcheck:
-    # https://staticcheck.io/docs/options#checks
-    checks: ["all", "-ST1000", "-SA1019"]
-  misspell:
-    locale: US
-  exhaustive:
-    default-signifies-exhaustive: true
-  wrapcheck:
-    ignorePackageGlobs:
-      - github.com/siemens/wfx/internal/errutil
-      - google.golang.org/protobuf/*
-      - io
-  dupword:
-    keywords:
-      - "INSTALLING"
-  stylecheck:
-    checks: [
-        "all",
-        "-ST1003", # allow method names such as GetJobsIdTags
-      ]
+  settings:
+    staticcheck:
+      # https://staticcheck.io/docs/options#checks
+      checks: ["all", "-ST1000", "-SA1019", "-ST1003"]
+    misspell:
+      locale: US
+    exhaustive:
+      default-signifies-exhaustive: true
+    wrapcheck:
+      ignore-package-globs:
+        - github.com/siemens/wfx/internal/errutil
+        - google.golang.org/protobuf/*
+        - io
+    dupword:
+      keywords:
+        - "INSTALLING"
+
+    errcheck:
+      exclude-functions:
+        - (*os.File).Close
+        - fmt.Fprintln
+        - fmt.Fprintf
+
+formatters:
+  enable:
+    - gofumpt
diff --git a/api/southbound_test.go b/api/southbound_test.go
index da5aae24..38f01b2e 100644
--- a/api/southbound_test.go
+++ b/api/southbound_test.go
@@ -31,9 +31,10 @@ func createServerForTesting(t *testing.T, orientation string, db persistence.Sto
 	wfx := NewWfxServer(db)
 	wfx.Start()
 	t.Cleanup(func() { wfx.Stop() })
-	if orientation == "north" {
+	switch orientation {
+	case "north":
 		return NewNorthboundServer(wfx)
-	} else if orientation == "south" {
+	case "south":
 		return NewSouthboundServer(wfx)
 	}
 	panic("invalid orientation")
diff --git a/cmd/wfx-viewer/cmd.go b/cmd/wfx-viewer/cmd.go
index 8bf473f5..f938824b 100644
--- a/cmd/wfx-viewer/cmd.go
+++ b/cmd/wfx-viewer/cmd.go
@@ -95,10 +95,10 @@ Do not use this for confidential information.
 		var inFile, outFile *os.File
 		defer func() {
 			if inFile != nil {
-				inFile.Close()
+				_ = inFile.Close()
 			}
 			if outFile != nil {
-				outFile.Close()
+				_ = outFile.Close()
 			}
 		}()
 		inFile, err = os.OpenFile(src, os.O_RDONLY, 0o644)
diff --git a/cmd/wfx-viewer/cmd_test.go b/cmd/wfx-viewer/cmd_test.go
index 4553bcee..c3e1926d 100644
--- a/cmd/wfx-viewer/cmd_test.go
+++ b/cmd/wfx-viewer/cmd_test.go
@@ -22,8 +22,10 @@ func TestPlantUML_Stdout(t *testing.T) {
 	tmpFile, _ := os.CreateTemp(os.TempDir(), "TestPlantUML.*")
 	b, _ := yaml.Marshal(dau.DirectWorkflow())
 	_, _ = tmpFile.Write(b)
-	tmpFile.Close()
-	defer os.Remove(tmpFile.Name())
+	_ = tmpFile.Close()
+	defer func() {
+		_ = os.Remove(tmpFile.Name())
+	}()
 
 	rootCmd.SetArgs([]string{tmpFile.Name()})
 	err := rootCmd.Execute()
@@ -36,7 +38,9 @@ func TestPlantUML_File(t *testing.T) {
 	f := rootCmd.PersistentFlags()
 
 	outFile, _ := os.CreateTemp(os.TempDir(), "TestPlantUML.*")
-	defer os.Remove(outFile.Name())
+	defer func() {
+		_ = os.Remove(outFile.Name())
+	}()
 
 	_ = f.Set(outputFlag, outFile.Name())
 	_ = f.Set(outputFormatFlag, "plantuml")
@@ -44,8 +48,8 @@ func TestPlantUML_File(t *testing.T) {
 	tmpFile, _ := os.CreateTemp(os.TempDir(), "TestPlantUML.*")
 	b, _ := yaml.Marshal(dau.DirectWorkflow())
 	_, _ = tmpFile.Write(b)
-	tmpFile.Close()
-	defer os.Remove(tmpFile.Name())
+	_ = tmpFile.Close()
+	t.Cleanup(func() { _ = os.Remove(tmpFile.Name()) })
 
 	err := rootCmd.RunE(rootCmd, []string{tmpFile.Name()})
 	require.NoError(t, err)
diff --git a/cmd/wfx-viewer/output/mermaid/mermaid.go b/cmd/wfx-viewer/output/mermaid/mermaid.go
index 2300708f..8843eea1 100644
--- a/cmd/wfx-viewer/output/mermaid/mermaid.go
+++ b/cmd/wfx-viewer/output/mermaid/mermaid.go
@@ -31,7 +31,7 @@ func (g *Generator) Generate(out io.Writer, wf *api.Workflow) error {
 	_, _ = out.Write([]byte("stateDiagram-v2\n"))
 
 	initialState := *workflow.FindInitialState(wf)
-	_, _ = out.Write([]byte(fmt.Sprintf("    [*] --> %s\n", initialState)))
+	_, _ = fmt.Fprintf(out, "    [*] --> %s\n", initialState)
 	for _, transition := range wf.Transitions {
 		_, _ = out.Write([]byte("    "))
 		_, _ = out.Write([]byte(transition.From))
@@ -53,12 +53,12 @@ func (g *Generator) Generate(out io.Writer, wf *api.Workflow) error {
 	cp := colors.NewColorPalette(wf)
 	for _, state := range wf.States {
 		fgColor, bgColor := cp.StateColor(state.Name)
-		_, _ = out.Write([]byte(fmt.Sprintf("    classDef cl_%s color:%s,fill:%s\n", state.Name, fgColor, bgColor)))
-		_, _ = out.Write([]byte(fmt.Sprintf("    class %s cl_%s\n", state.Name, state.Name)))
+		_, _ = fmt.Fprintf(out, "    classDef cl_%s color:%s,fill:%s\n", state.Name, fgColor, bgColor)
+		_, _ = fmt.Fprintf(out, "    class %s cl_%s\n", state.Name, state.Name)
 	}
 
 	// add legend
-	_, _ = out.Write([]byte(fmt.Sprintf("    Note right of %s: <b>Group to Color Mapping</b><br/>", initialState)))
+	_, _ = fmt.Fprintf(out, "    Note right of %s: <b>Group to Color Mapping</b><br/>", initialState)
 	lines := make([]string, 0)
 	for _, group := range wf.Groups {
 		hex := cp.GroupColor(group.Name).ToHEX().String()
diff --git a/cmd/wfx-viewer/output/plantuml/plantuml.go b/cmd/wfx-viewer/output/plantuml/plantuml.go
index fc2f578d..a25eca06 100644
--- a/cmd/wfx-viewer/output/plantuml/plantuml.go
+++ b/cmd/wfx-viewer/output/plantuml/plantuml.go
@@ -37,14 +37,14 @@ func (g *Generator) Generate(out io.Writer, workflow *api.Workflow) error {
 
 	for _, state := range workflow.States {
 		fgColor, bgColor := cp.StateColor(state.Name)
-		_, _ = out.Write([]byte((fmt.Sprintf("state %s as \"<color:%s>%s</color>\" %s: %s\n", state.Name, fgColor, state.Name, bgColor, state.Description))))
+		_, _ = fmt.Fprintf(out, "state %s as \"<color:%s>%s</color>\" %s: %s\n", state.Name, fgColor, state.Name, bgColor, state.Description)
 	}
 
 	// add transitions
 	for _, transition := range workflow.Transitions {
-		_, _ = out.Write([]byte((fmt.Sprintf("%s --> %s: %s", transition.From, transition.To, string(transition.Eligible)))))
+		_, _ = fmt.Fprintf(out, "%s --> %s: %s", transition.From, transition.To, string(transition.Eligible))
 		if transition.Action != nil {
-			_, _ = out.Write([]byte((fmt.Sprintf(" [%s]", string(*transition.Action)))))
+			_, _ = fmt.Fprintf(out, " [%s]", string(*transition.Action))
 		}
 		_, _ = out.Write([]byte("\n"))
 	}
@@ -55,9 +55,9 @@ func (g *Generator) Generate(out io.Writer, workflow *api.Workflow) error {
 	for _, group := range workflow.Groups {
 		color := cp.GroupColor(group.Name)
 		hex := color.ToHEX().String()
-		_, _ = out.Write([]byte((fmt.Sprintf("  | <%s> | %s | %s |\n", hex, group.Name, group.Description))))
+		_, _ = fmt.Fprintf(out, "  | <%s> | %s | %s |\n", hex, group.Name, group.Description)
 	}
-	_, _ = out.Write([]byte((fmt.Sprintf("  | <%s> | %s | %s |\n", colors.DefaultBgColor, "", "The state doesn't belong to any group."))))
+	_, _ = fmt.Fprintf(out, "  | <%s> | %s | %s |\n", colors.DefaultBgColor, "", "The state doesn't belong to any group.")
 	_, _ = out.Write([]byte(("endlegend\n")))
 
 	_, _ = out.Write([]byte(("@enduml\n")))
diff --git a/cmd/wfx-viewer/output/smcat/smcat.go b/cmd/wfx-viewer/output/smcat/smcat.go
index 5cbcca36..2cbc27c3 100644
--- a/cmd/wfx-viewer/output/smcat/smcat.go
+++ b/cmd/wfx-viewer/output/smcat/smcat.go
@@ -43,7 +43,7 @@ func (g *Generator) Generate(out io.Writer, wf *api.Workflow) error {
 	_, _ = out.Write([]byte(";\n\n"))
 
 	initialState := *workflow.FindInitialState(wf)
-	_, _ = out.Write([]byte(fmt.Sprintf("initial => %s;\n", initialState)))
+	_, _ = fmt.Fprintf(out, "initial => %s;\n", initialState)
 	for _, transition := range wf.Transitions {
 		_, _ = out.Write([]byte(transition.From))
 		_, _ = out.Write([]byte(" => "))
diff --git a/cmd/wfx/cmd/root/root_test.go b/cmd/wfx/cmd/root/root_test.go
index 9555bda7..eacf49ca 100644
--- a/cmd/wfx/cmd/root/root_test.go
+++ b/cmd/wfx/cmd/root/root_test.go
@@ -59,13 +59,15 @@ func TestUDS(t *testing.T) {
 	var g errgroup.Group
 	g.Go(cmd.Execute)
 
-	for i := 0; i < 30; i++ {
+	for range 30 {
 		conn, err := net.Dial("unix", clientSocket)
 		if err != nil {
 			time.Sleep(time.Millisecond * 10)
 			continue
 		}
-		defer conn.Close()
+		defer func() {
+			_ = conn.Close()
+		}()
 
 		req, err := http.NewRequest(http.MethodGet, "/version", nil)
 		require.NoError(t, err)
@@ -245,13 +247,15 @@ func TestAPI(t *testing.T) {
 	t.Run("fetch openapiv3 spec", func(t *testing.T) {
 		httpClient := new(http.Client)
 		var spec openapi3.T
-		for i := 0; i < 100; i++ {
+		for range 100 {
 			resp, err := httpClient.Get("http://localhost:8080/api/wfx/v1/openapi.json")
 			if err != nil {
 				time.Sleep(time.Millisecond * 10)
 				continue
 			}
-			defer resp.Body.Close()
+			defer func() {
+				_ = resp.Body.Close()
+			}()
 			// check valid json
 			err = json.NewDecoder(resp.Body).Decode(&spec)
 			require.NoError(t, err)
@@ -263,13 +267,15 @@ func TestAPI(t *testing.T) {
 	t.Run("/health endpoint", func(t *testing.T) {
 		httpClient := new(http.Client)
 		var result api.CheckerResult
-		for i := 0; i < 100; i++ {
+		for range 100 {
 			resp, err := httpClient.Get("http://localhost:8080/health")
 			if err != nil {
 				time.Sleep(time.Millisecond * 10)
 				continue
 			}
-			defer resp.Body.Close()
+			defer func() {
+				_ = resp.Body.Close()
+			}()
 			// check valid json
 			err = json.NewDecoder(resp.Body).Decode(&result)
 			require.NoError(t, err)
@@ -281,13 +287,15 @@ func TestAPI(t *testing.T) {
 	t.Run("/version endpoint", func(t *testing.T) {
 		httpClient := new(http.Client)
 		var result api.GetVersion200JSONResponse
-		for i := 0; i < 100; i++ {
+		for range 100 {
 			resp, err := httpClient.Get("http://localhost:8080/version")
 			if err != nil {
 				time.Sleep(time.Millisecond * 10)
 				continue
 			}
-			defer resp.Body.Close()
+			defer func() {
+				_ = resp.Body.Close()
+			}()
 			// check valid json
 			err = json.NewDecoder(resp.Body).Decode(&result)
 			require.NoError(t, err)
@@ -328,7 +336,7 @@ func TestAPI(t *testing.T) {
 
 		httpClient := new(http.Client)
 		var state string
-		for i := 0; i < 100; i++ {
+		for range 100 {
 			url := fmt.Sprintf("http://localhost:8080/api/wfx/v1/jobs/%s/status", jobID)
 			req, err := http.NewRequest(http.MethodGet, url, nil)
 			require.NoError(t, err)
@@ -338,7 +346,9 @@ func TestAPI(t *testing.T) {
 				time.Sleep(time.Millisecond * 10)
 				continue
 			}
-			defer resp.Body.Close()
+			defer func() {
+				_ = resp.Body.Close()
+			}()
 			b, _ := io.ReadAll(resp.Body)
 			state = strings.TrimSpace(string(b))
 			break
@@ -350,13 +360,15 @@ func TestAPI(t *testing.T) {
 		httpClient := new(http.Client)
 		var statusCode int
 		var body []byte
-		for i := 0; i < 100; i++ {
+		for range 100 {
 			resp, err := httpClient.Get("http://localhost:8080/download/")
 			if err != nil {
 				time.Sleep(time.Millisecond * 10)
 				continue
 			}
-			defer resp.Body.Close()
+			defer func() {
+				_ = resp.Body.Close()
+			}()
 			body, _ = io.ReadAll(resp.Body)
 			statusCode = resp.StatusCode
 
@@ -373,7 +385,7 @@ func TestAPI(t *testing.T) {
 func TestSimpleFileServer(t *testing.T) {
 	dir, err := os.MkdirTemp("", "wfx-fileserver*")
 	require.NoError(t, err)
-	t.Cleanup(func() { os.RemoveAll(dir) })
+	t.Cleanup(func() { _ = os.RemoveAll(dir) })
 
 	testFile := path.Join(dir, "hello.txt")
 	err = os.WriteFile(testFile, []byte("Hello World!"), 0o644)
@@ -397,13 +409,15 @@ func TestSimpleFileServer(t *testing.T) {
 	httpClient := new(http.Client)
 	var statusCode int
 	var body []byte
-	for i := 0; i < 100; i++ {
+	for range 100 {
 		resp, err := httpClient.Get("http://localhost:8080/download/hello.txt")
 		if err != nil {
 			time.Sleep(time.Millisecond * 10)
 			continue
 		}
-		defer resp.Body.Close()
+		defer func() {
+			_ = resp.Body.Close()
+		}()
 		body, _ = io.ReadAll(resp.Body)
 		statusCode = resp.StatusCode
 	}
@@ -419,7 +433,7 @@ func TestSimpleFileServer(t *testing.T) {
 
 func generateTempFilenames(t *testing.T) (string, string) {
 	fnames := make([]string, 0, 2)
-	for i := 0; i < 2; i++ {
+	for range 2 {
 		f, _ := os.CreateTemp(os.TempDir(), fmt.Sprintf("%s.*.sock", t.Name()))
 		f.Close()
 		fname := f.Name()
@@ -443,7 +457,7 @@ func createKeypair(t *testing.T) (string, string) {
 	derBytes, _ := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
 
 	dir, _ := os.MkdirTemp("", "wfx-testkeys.*")
-	t.Cleanup(func() { os.RemoveAll(dir) })
+	t.Cleanup(func() { _ = os.RemoveAll(dir) })
 
 	privkey, pubkey := path.Join(dir, "test.key"), path.Join(dir, "test.crt")
 	certOut, _ := os.Create(pubkey)
diff --git a/cmd/wfxctl/cmd/health/health.go b/cmd/wfxctl/cmd/health/health.go
index 7f2035c9..33c55869 100644
--- a/cmd/wfxctl/cmd/health/health.go
+++ b/cmd/wfxctl/cmd/health/health.go
@@ -116,7 +116,7 @@ func NewCommand() *cobra.Command {
 
 func prettyReport(w io.Writer, useColor bool, allEndpoints []Endpoint) {
 	buf := bufio.NewWriter(w)
-	defer buf.Flush()
+	defer func() { _ = buf.Flush() }()
 	_, _ = buf.WriteString("Health report:\n\n")
 	for _, ep := range allEndpoints {
 		status := api.Down
diff --git a/cmd/wfxctl/cmd/job/events/events.go b/cmd/wfxctl/cmd/job/events/events.go
index b549c969..0371cd98 100644
--- a/cmd/wfxctl/cmd/job/events/events.go
+++ b/cmd/wfxctl/cmd/job/events/events.go
@@ -34,7 +34,7 @@ var validator = func(out io.Writer) sse.ResponseValidator {
 		}
 
 		if r.Body != nil {
-			defer r.Body.Close()
+			defer func() { _ = r.Body.Close() }()
 			b, err := io.ReadAll(r.Body)
 			if err != nil {
 				return fault.Wrap(err)
@@ -59,7 +59,7 @@ type SSETransport struct {
 	out       io.Writer
 }
 
-// Submit implements the runtime.ClientTransport interface.
+// Do implements the runtime.ClientTransport interface.
 func (t SSETransport) Do(req *http.Request) (*http.Response, error) {
 	conn := t.sseClient.NewConnection(req)
 	unsubscribe := conn.SubscribeMessages(func(event sse.Event) {
diff --git a/cmd/wfxctl/cmd/workflow/validate/validate_test.go b/cmd/wfxctl/cmd/workflow/validate/validate_test.go
index 822bbe41..8d5c153d 100644
--- a/cmd/wfxctl/cmd/workflow/validate/validate_test.go
+++ b/cmd/wfxctl/cmd/workflow/validate/validate_test.go
@@ -38,7 +38,7 @@ func TestCommand_Fname(t *testing.T) {
 	tmpFile, _ := os.CreateTemp(os.TempDir(), "workflow")
 	_, _ = tmpFile.Write([]byte(dau.PhasedYAML))
 	_ = tmpFile.Close()
-	defer os.Remove(tmpFile.Name())
+	t.Cleanup(func() { _ = os.Remove(tmpFile.Name()) })
 
 	cmd := NewCommand()
 	cmd.SetArgs([]string{tmpFile.Name()})
diff --git a/cmd/wfxctl/flags/basecmd_test.go b/cmd/wfxctl/flags/basecmd_test.go
index 5f56b612..f8698a6e 100644
--- a/cmd/wfxctl/flags/basecmd_test.go
+++ b/cmd/wfxctl/flags/basecmd_test.go
@@ -50,7 +50,7 @@ CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
 	`)
 	tmpFile, _ := os.CreateTemp("", "wfx-cert-")
 	_, _ = tmpFile.Write(cert)
-	defer os.Remove(tmpFile.Name())
+	t.Cleanup(func() { _ = os.Remove(tmpFile.Name()) })
 
 	b := NewBaseCmd(pflag.NewFlagSet("wfx", pflag.ExitOnError))
 	b.EnableTLS = true
@@ -231,7 +231,7 @@ func TestNewBaseCmd_ConfigFile(t *testing.T) {
 	tmpfile, err := os.CreateTemp("", "test-log-level-*.yaml")
 	require.NoError(t, err)
 	t.Log(tmpfile.Name())
-	defer os.Remove(tmpfile.Name())
+	t.Cleanup(func() { _ = os.Remove(tmpfile.Name()) })
 
 	content := []byte("log-level: trace\n")
 	_, err = tmpfile.Write(content)
diff --git a/contrib/config-deployment/client/worker.go b/contrib/config-deployment/client/worker.go
index 01c54c74..23ae9066 100644
--- a/contrib/config-deployment/client/worker.go
+++ b/contrib/config-deployment/client/worker.go
@@ -47,10 +47,8 @@ func worker() {
 	for !done.Load() {
 		log.Println("\n>> Waiting for new job")
 		var job *api.Job
-		for {
-			if done.Load() {
-				break
-			}
+		for !done.Load() {
+
 			resp, err := client.GetJobsWithResponse(context.Background(), params)
 			if err == nil && resp.JSON200 != nil && len(resp.JSON200.Content) > 0 {
 				job = &resp.JSON200.Content[0]
diff --git a/internal/persistence/entgo/mysql.go b/internal/persistence/entgo/mysql.go
index 159d2dff..9ead97cf 100644
--- a/internal/persistence/entgo/mysql.go
+++ b/internal/persistence/entgo/mysql.go
@@ -75,7 +75,9 @@ func (wrapper *MySQL) Initialize(options string) error {
 		if err != nil {
 			return fault.Wrap(err)
 		}
-		defer conn.Close()
+		defer func() {
+			_ = conn.Close()
+		}()
 
 		m, err := mysql.WithConnection(ctx, conn, &mysql.Config{})
 		if err != nil {
diff --git a/internal/persistence/entgo/postgres.go b/internal/persistence/entgo/postgres.go
index c2e54aad..1c59221d 100644
--- a/internal/persistence/entgo/postgres.go
+++ b/internal/persistence/entgo/postgres.go
@@ -42,8 +42,12 @@ func init() {
 	persistence.RegisterStorage("postgres", &PostgreSQL{})
 }
 
-// example dsn: "user=<username> password=<password> host=localhost port=5432 database=wfx sslmode=disable"
-// see https://github.com/jackc/pgx/blob/master/stdlib/sql.go
+// Initialize sets up the PostgreSQL database connection using the provided DSN (options), runs migrations, and initializes the ent client.
+//
+// Parameters:
+//   - options: PostgreSQL connection string (DSN) in the format:
+//     "user=<username> password=<password> host=localhost port=5432 database=wfx sslmode=disable"
+//     See https://github.com/jackc/pgx/blob/master/stdlib/sql.go for DSN syntax details.
 func (wrapper *PostgreSQL) Initialize(options string) error {
 	connConfig, err := pgx.ParseConfig(options)
 	if err != nil {
diff --git a/internal/server/http_test.go b/internal/server/http_test.go
index 134e0d39..31d99628 100644
--- a/internal/server/http_test.go
+++ b/internal/server/http_test.go
@@ -74,7 +74,7 @@ func TestConfigureTLS(t *testing.T) {
 
 	dir, err := os.MkdirTemp(os.TempDir(), "wfx-tlsca")
 	require.NoError(t, err)
-	defer os.RemoveAll(dir)
+	t.Cleanup(func() { _ = os.RemoveAll(dir) })
 
 	caCrtFile := path.Join(dir, "ca.crt")
 	caKeyFile := path.Join(dir, "ca.key")
diff --git a/middleware/logging/log_test.go b/middleware/logging/log_test.go
index 3ed94a88..c650044e 100644
--- a/middleware/logging/log_test.go
+++ b/middleware/logging/log_test.go
@@ -35,7 +35,7 @@ func TestLog(t *testing.T) {
 	assert.NoError(t, err)
 
 	greeting, err := io.ReadAll(res.Body)
-	defer res.Body.Close()
+	defer func() { _ = res.Body.Close() }()
 	assert.NoError(t, err)
 
 	assert.Equal(t, "Hello, client\n", string(greeting))
@@ -53,7 +53,7 @@ func TestLogDebug(t *testing.T) {
 	assert.NoError(t, err)
 
 	greeting, err := io.ReadAll(res.Body)
-	defer res.Body.Close()
+	defer func() { _ = res.Body.Close() }()
 	assert.NoError(t, err)
 
 	assert.Equal(t, "Hello, client\n", string(greeting))
diff --git a/middleware/logging/writer_test.go b/middleware/logging/writer_test.go
index 6ebf04ea..cb3dff07 100644
--- a/middleware/logging/writer_test.go
+++ b/middleware/logging/writer_test.go
@@ -34,7 +34,7 @@ func TestWriter(t *testing.T) {
 
 	body, err := io.ReadAll(result.Body)
 	require.NoError(t, err)
-	defer result.Body.Close()
+	defer func() { _ = result.Body.Close() }()
 
 	assert.Equal(t, "hello world", string(body))
 	assert.Equal(t, "hello world", w.responseBody.String())
diff --git a/middleware/sse/responder.go b/middleware/sse/responder.go
index d44b9fa0..84ce87bc 100644
--- a/middleware/sse/responder.go
+++ b/middleware/sse/responder.go
@@ -66,7 +66,7 @@ Loop:
 			_, _ = w.Write([]byte("data: "))
 			_, _ = w.Write(b)
 			// must end with two newlines as required by the SSE spec:
-			_, _ = w.Write([]byte(fmt.Sprintf("\nid: %d\n\n", id)))
+			_, _ = fmt.Fprintf(w, "\nid: %d\n\n", id)
 
 			flusher.Flush()
 
diff --git a/persistence/mock.go b/persistence/mock.go
index 98c8ceb5..bb25731f 100644
--- a/persistence/mock.go
+++ b/persistence/mock.go
@@ -18,7 +18,7 @@ import (
 
 func NewHealthyMockStorage(t *testing.T) *MockStorage {
 	m := new(MockStorage)
-	m.Mock.Test(t)
+	m.Test(t)
 	m.On("CheckHealth", mock.Anything).Return(nil)
 	return m
 }
diff --git a/workflow/dfs.go b/workflow/dfs.go
index c1513b82..47e51c35 100644
--- a/workflow/dfs.go
+++ b/workflow/dfs.go
@@ -58,10 +58,11 @@ func (d *dfsData) dfsVisit(g graph.Iterator, v int) {
 	d.Time++
 	d.Discover[v] = d.Time
 	g.Visit(v, func(w int, _ int64) (skip bool) {
-		if d.Color[w] == white {
+		switch d.Color[w] {
+		case white:
 			d.Prev[w] = v
 			d.dfsVisit(g, w)
-		} else if d.Color[w] == gray {
+		case gray:
 			d.Cycles = append(d.Cycles, cycle{From: w, To: v})
 		}
 		return


Reviews:
user: login: stormc
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-02T08:47:00Z
user: login: michaeladler
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T08:52:32Z

Review comments:
diff_hunk: @@ -42,8 +42,12 @@ func init() {
 	persistence.RegisterStorage("postgres", &PostgreSQL{})
 }
 
-// example dsn: "user=<username> password=<password> host=localhost port=5432 database=wfx sslmode=disable"
-// see https://github.com/jackc/pgx/blob/master/stdlib/sql.go
+// Initialize sets up the PostgreSQL database connection using the provided DSN (options), runs migrations, and initializes the ent client.
path: internal/persistence/entgo/postgres.go
user: login: stormc
body: That should read [...] en**d** client, right?
created_at: 2025-04-02T08:46:59Z
author_association: COLLABORATOR
diff_hunk: @@ -42,8 +42,12 @@ func init() {
 	persistence.RegisterStorage("postgres", &PostgreSQL{})
 }
 
-// example dsn: "user=<username> password=<password> host=localhost port=5432 database=wfx sslmode=disable"
-// see https://github.com/jackc/pgx/blob/master/stdlib/sql.go
+// Initialize sets up the PostgreSQL database connection using the provided DSN (options), runs migrations, and initializes the ent client.
path: internal/persistence/entgo/postgres.go
user: login: michaeladler
body: No, the library is called `entgo` and the client is really called `ent.Client` :)
created_at: 2025-04-02T08:52:32Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-04-01T02:16:19Z
body: ## [Codecov](https://app.codecov.io/gh/siemens/wfx/pull/307?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=siemens) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 80.21%. Comparing base [(`a038675`)](https://app.codecov.io/gh/siemens/wfx/commit/a038675a90f26ffa53933cba07cb26671dc496cd?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=siemens) to head [(`6e20491`)](https://app.codecov.io/gh/siemens/wfx/commit/6e20491d3b686436b53609e46c1f6315e5b454a7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=siemens).

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #307   +/-   ##
=======================================
  Coverage   80.21%   80.21%           
=======================================
  Files          93       93           
  Lines        4371     4372    +1     
=======================================
+ Hits         3506     3507    +1     
  Misses        625      625           
  Partials      240      240           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/siemens/wfx/pull/307?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=siemens).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=siemens).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: renovate[bot]
created_at: 2025-04-02T08:25:00Z
body: ### Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

 ⚠️ **Warning**: custom changes will be lost.
author_association: CONTRIBUTOR