Repository: cli/cli

Issue:
Use `golangci-lint` version 2
# Description

Some months ago `golangci-lint` [released version 2](https://ldez.github.io/blog/2025/03/23/golangci-lint-v2/). We should probably just move forward. Also, I think we should use golangci-lint action in our workflows rather than this [weird old way of downloading the binary directly](https://github.com/cli/cli/blob/b83d335f2ffa9be296546f872407778f38a661b0/.github/workflows/lint.yml). See `github-mcp-server` for an example: https://github.com/github/github-mcp-server/blob/c423a52511d6a7c10947b42c5e8c3345aeaf7f96/.github/workflows/lint.yaml

It would probably help us move forward with https://github.com/cli/cli/pull/11015 as well because I don't really 
want to dig into finding a golangci-lint version 1 that is built with 1.24.

## Expected Output

The same linters are applied at minimum.

If I have `golangci-lint` version 2 installed locally, when I run `golangci-lint run` I see no errors.

The workflow uses golangci-lint action.

Discussion:

Solution patch:
diff --git a/.github/secret_scanning.yml b/.github/secret_scanning.yml
new file mode 100644
index 000000000..83ee7b460
--- /dev/null
+++ b/.github/secret_scanning.yml
@@ -0,0 +1,3 @@
+paths-ignore:
+  - 'third-party/**'
+  - 'third-party-licenses.*.md'
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index d74e1c142..37bbb0607 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -32,6 +32,10 @@ jobs:
         with:
           languages: ${{ matrix.language }}
           queries: security-and-quality
+          config: |
+            paths-ignore:
+              - 'third-party/**'
+              - 'third-party-licenses.*.md'
 
       - name: Setup Go
         if: matrix.language == 'go'
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index f1ae1e522..48e8539d1 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -27,17 +27,7 @@ jobs:
         with:
           go-version-file: 'go.mod'
 
-      - name: Verify dependencies
-        run: |
-          go mod verify
-          go mod download
-
-          LINT_VERSION=1.63.4
-          curl -fsSL https://github.com/golangci/golangci-lint/releases/download/v${LINT_VERSION}/golangci-lint-${LINT_VERSION}-linux-amd64.tar.gz | \
-            tar xz --strip-components 1 --wildcards \*/golangci-lint
-          mkdir -p bin && mv golangci-lint bin/
-
-      - name: Run checks
+      - name: Ensure go.mod and go.sum are up to date
         run: |
           STATUS=0
           assert-nothing-changed() {
@@ -49,10 +39,10 @@ jobs:
               STATUS=1
             fi
           }
-
-          assert-nothing-changed go fmt ./...
           assert-nothing-changed go mod tidy
-
-          bin/golangci-lint run --out-format=colored-line-number --timeout=3m || STATUS=$?
-
           exit $STATUS
+
+      - name: golangci-lint
+        uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
+        with:
+          version: v2.1.6
diff --git a/.github/workflows/pr-help-wanted.yml b/.github/workflows/pr-help-wanted.yml
index 5475d2eff..3482ecd08 100644
--- a/.github/workflows/pr-help-wanted.yml
+++ b/.github/workflows/pr-help-wanted.yml
@@ -12,18 +12,16 @@ jobs:
   check-help-wanted:
     runs-on: ubuntu-latest
     steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
       - name: Check for issues without help-wanted label
         env:
           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           PR_AUTHOR: ${{ github.event.pull_request.user.login }}
           PR_AUTHOR_TYPE: ${{ github.event.pull_request.user.type }}
-        if: !github.event.pull_request.draft
+          PR_AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association }}
+        if: "!github.event.pull_request.draft"
         run: |
-          # Skip if PR is from a bot or org member
-          if [ "$PR_AUTHOR_TYPE" = "Bot" ] || "gh api orgs/cli/public_members/${PR_AUTHOR}" --silent 2>/dev/null
-          then
-            exit 0
-          fi
-
           # Run the script to check for issues without help-wanted label
-          bash .github/scripts/check-help-wanted.sh ${{ github.event.pull_request.html_url }}
+          bash .github/workflows/scripts/check-help-wanted.sh ${{ github.event.pull_request.html_url }}
diff --git a/.github/workflows/scripts/check-help-wanted.sh b/.github/workflows/scripts/check-help-wanted.sh
index 59c12fecd..d316e8bde 100755
--- a/.github/workflows/scripts/check-help-wanted.sh
+++ b/.github/workflows/scripts/check-help-wanted.sh
@@ -12,6 +12,12 @@ if [ -z "$PR_URL" ]; then
     exit 1
 fi
 
+# Skip if PR is from a bot or org member
+if [ "$PR_AUTHOR_TYPE" = "Bot" ] || [ "$PR_AUTHOR_ASSOCIATION" = "MEMBER" ] || [ "$PR_AUTHOR_ASSOCIATION" = "OWNER" ]; then
+    echo "Skipping check for PR #$PR_URL as it is from a bot ($PR_AUTHOR_TYPE) or an org member ($PR_AUTHOR_ASSOCIATION: MEMBER/OWNER)"
+    exit 0
+fi
+
 # Extract PR number from URL for logging
 PR_NUM="$(basename "$PR_URL")"
 
diff --git a/.golangci.yml b/.golangci.yml
index ff7f37014..861198dd8 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,7 +1,17 @@
+version: "2"
+
 linters:
   enable:
-  - gofmt
-  - nolintlint
+    - nolintlint
+  disable:
+   # The following linters are disabled purely because this config was migrated to v2 where they are in the default
+   # set, and we should have separate work to enable them if we truly want them.
+   - staticcheck
+   - errcheck
+
+formatters:
+  enable:
+    - gofmt
 
 issues:
   max-issues-per-linter: 0
diff --git a/go.mod b/go.mod
index 289fd83a5..66383962f 100644
--- a/go.mod
+++ b/go.mod
@@ -33,7 +33,7 @@ require (
 	github.com/hashicorp/go-version v1.3.0
 	github.com/henvic/httpretty v0.1.4
 	github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec
-	github.com/in-toto/attestation v1.1.1
+	github.com/in-toto/attestation v1.1.2
 	github.com/joho/godotenv v1.5.1
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
 	github.com/mattn/go-colorable v0.1.14
diff --git a/go.sum b/go.sum
index 95f7ed75d..094016356 100644
--- a/go.sum
+++ b/go.sum
@@ -304,8 +304,8 @@ github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb
 github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
 github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
 github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/in-toto/attestation v1.1.1 h1:QD3d+oATQ0dFsWoNh5oT0udQ3tUrOsZZ0Fc3tSgWbzI=
-github.com/in-toto/attestation v1.1.1/go.mod h1:Dcq1zVwA2V7Qin8I7rgOi+i837wEf/mOZwRm047Sjys=
+github.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E=
+github.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM=
 github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
 github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
diff --git a/pkg/cmd/workflow/run/run.go b/pkg/cmd/workflow/run/run.go
index def3cf9e4..2acd1d4cc 100644
--- a/pkg/cmd/workflow/run/run.go
+++ b/pkg/cmd/workflow/run/run.go
@@ -330,7 +330,7 @@ func runRun(opts *RunOptions) error {
 		fmt.Fprintln(out)
 
 		fmt.Fprintf(out, "To see runs for this workflow, try: %s\n",
-			cs.Boldf("gh run list --workflow=%s", workflow.Base()))
+			cs.Boldf("gh run list --workflow=%q", workflow.Base()))
 	}
 
 	return nil



Test patch:
diff --git a/pkg/cmd/workflow/run/run_test.go b/pkg/cmd/workflow/run/run_test.go
index ee1bc5a1e..b121a573d 100644
--- a/pkg/cmd/workflow/run/run_test.go
+++ b/pkg/cmd/workflow/run/run_test.go
@@ -447,7 +447,7 @@ jobs:
 				"ref": "trunk",
 			},
 			httpStubs: stubs,
-			wantOut:   "✓ Created workflow_dispatch event for workflow.yml at trunk\n\nTo see runs for this workflow, try: gh run list --workflow=workflow.yml\n",
+			wantOut:   "✓ Created workflow_dispatch event for workflow.yml at trunk\n\nTo see runs for this workflow, try: gh run list --workflow=\"workflow.yml\"\n",
 		},
 		{
 			name: "nontty good JSON",
@@ -494,7 +494,7 @@ jobs:
 				"ref": "good-branch",
 			},
 			httpStubs: stubs,
-			wantOut:   "✓ Created workflow_dispatch event for workflow.yml at good-branch\n\nTo see runs for this workflow, try: gh run list --workflow=workflow.yml\n",
+			wantOut:   "✓ Created workflow_dispatch event for workflow.yml at good-branch\n\nTo see runs for this workflow, try: gh run list --workflow=\"workflow.yml\"\n",
 		},
 		{
 			// TODO this test is somewhat silly; it's more of a placeholder in case I decide to handle the API error more elegantly
@@ -634,7 +634,7 @@ jobs:
 				"inputs": map[string]interface{}{},
 				"ref":    "trunk",
 			},
-			wantOut: "✓ Created workflow_dispatch event for minimal.yml at trunk\n\nTo see runs for this workflow, try: gh run list --workflow=minimal.yml\n",
+			wantOut: "✓ Created workflow_dispatch event for minimal.yml at trunk\n\nTo see runs for this workflow, try: gh run list --workflow=\"minimal.yml\"\n",
 		},
 		{
 			name: "prompt",
@@ -682,7 +682,7 @@ jobs:
 				},
 				"ref": "trunk",
 			},
-			wantOut: "✓ Created workflow_dispatch event for workflow.yml at trunk\n\nTo see runs for this workflow, try: gh run list --workflow=workflow.yml\n",
+			wantOut: "✓ Created workflow_dispatch event for workflow.yml at trunk\n\nTo see runs for this workflow, try: gh run list --workflow=\"workflow.yml\"\n",
 		},
 		{
 			name: "prompt, workflow choice input",
@@ -731,7 +731,7 @@ jobs:
 				},
 				"ref": "trunk",
 			},
-			wantOut: "✓ Created workflow_dispatch event for workflow.yml at trunk\n\nTo see runs for this workflow, try: gh run list --workflow=workflow.yml\n",
+			wantOut: "✓ Created workflow_dispatch event for workflow.yml at trunk\n\nTo see runs for this workflow, try: gh run list --workflow=\"workflow.yml\"\n",
 		},
 		{
 			name: "prompt, workflow choice missing input",