diff --git a/.custom-gcl.yml b/.custom-gcl.yml new file mode 100644 index 0000000000..04280ce0eb --- /dev/null +++ b/.custom-gcl.yml @@ -0,0 +1,6 @@ +version: v2.5.0 +name: custom-golangci-lint +plugins: + # partitiontest plugin from local source + - module: 'github.com/algorand/go-algorand/cmd/partitiontest_linter' + path: ./cmd/partitiontest_linter diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index 5294531394..09734550a6 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -1,6 +1,6 @@ name: "ReviewDog workflow" env: - GOLANGCI_LINT_VERSION: "v1.64.8" + GOLANGCI_LINT_VERSION: "v2.5.0" on: push: branches: @@ -23,52 +23,17 @@ jobs: # move go out of the way temporarily to avoid "go list ./..." from installing modules - name: Make libsodium.a run: sudo mv /usr/bin/go /usr/bin/go.bak && make libsodium && sudo mv /usr/bin/go.bak /usr/bin/go - - name: Add bin to PATH - run: | - echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH - echo "$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)/bin" >> $GITHUB_PATH - - name: Set up Go - uses: ./.github/actions/setup-go - - name: Create folders for golangci-lint - run: mkdir -p cicdtmp/golangci-lint - - name: Check if custom golangci-lint is already built - id: cache-golangci-lint - uses: actions/cache@v4 + - name: reviewdog-golangci-lint + uses: reviewdog/action-golangci-lint@v2.8.0 with: - path: cicdtmp/golangci-lint/golangci-lint-cgo - key: cicd-golangci-lint-cgo-v0.0.3-${{ env.GO_VERSION }}-${{ env.GOLANGCI_LINT_VERSION }} - - - name: Build custom golangci-lint with CGO_ENABLED - if: steps.cache-golangci-lint.outputs.cache-hit != 'true' - run: | - cd cicdtmp/golangci-lint - git clone https://github.com/golangci/golangci-lint.git . - git checkout tags/${GOLANGCI_LINT_VERSION} - CGO_ENABLED=true go build -trimpath -o golangci-lint-cgo ./cmd/golangci-lint - ./golangci-lint-cgo --version - cd ../../ - - name: Install reviewdog - run: | - curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/v0.20.3/install.sh | sh -s -- v0.20.3 - reviewdog --version - - name: Run golangci-lint with reviewdog - env: - REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - - ./cicdtmp/golangci-lint/golangci-lint-cgo run \ - --out-format line-number \ - -c .golangci.yml \ - --allow-parallel-runners > temp_golangci-lint-errors.txt - - cat temp_golangci-lint-errors.txt | reviewdog \ - -f=golangci-lint \ - -name="Lint Errors" \ - -reporter=github-pr-check \ - -filter-mode=nofilter \ - -fail-level=any \ - -level=error + go_version_file: go.mod + golangci_lint_version: ${{ env.GOLANGCI_LINT_VERSION }} + golangci_lint_flags: "-c .golangci.yml --allow-parallel-runners" + reporter: "github-pr-check" + tool_name: "Lint Errors" + level: "error" + fail_level: any + filter_mode: "nofilter" # Non-Blocking Warnings Section reviewdog-warnings: runs-on: ubuntu-latest @@ -91,47 +56,34 @@ jobs: echo "$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)/bin" >> $GITHUB_PATH - name: Set up Go uses: ./.github/actions/setup-go - - name: Create folders for golangci-lint - run: mkdir -p cicdtmp/golangci-lint - - name: Check if custom golangci-lint is already built - id: cache-golangci-lint + - name: Check if custom golangci-lint with partitiontest plugin is already built + id: cache-custom-golangci-lint uses: actions/cache@v4 with: - path: cicdtmp/golangci-lint/golangci-lint-cgo - key: cicd-golangci-lint-cgo-v0.0.3-${{ env.GO_VERSION }}-${{ env.GOLANGCI_LINT_VERSION }} - - - name: Build custom golangci-lint with CGO_ENABLED - if: steps.cache-golangci-lint.outputs.cache-hit != 'true' + path: custom-golangci-lint + key: custom-golangci-lint-${{ env.GO_VERSION }}-${{ env.GOLANGCI_LINT_VERSION }}-${{ hashFiles('cmd/partitiontest_linter/**', '.custom-gcl.yml') }} + - name: Build custom golangci-lint with partitiontest plugin + if: steps.cache-custom-golangci-lint.outputs.cache-hit != 'true' run: | - cd cicdtmp/golangci-lint - git clone https://github.com/golangci/golangci-lint.git . - git checkout tags/${GOLANGCI_LINT_VERSION} - CGO_ENABLED=true go build -trimpath -o golangci-lint-cgo ./cmd/golangci-lint - ./golangci-lint-cgo --version - cd ../../ + go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${{ env.GOLANGCI_LINT_VERSION }} custom -v + ./custom-golangci-lint --version - name: Install reviewdog run: | - curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/v0.20.3/install.sh | sh -s -- v0.20.3 + curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/v0.21.0/install.sh | sh -s -- v0.21.0 reviewdog --version - - name: Build custom linters - run: | - cd cmd/partitiontest_linter/ - CGO_ENABLED=true go build -buildmode=plugin -trimpath plugin/plugin.go - cd ../../ - ls -la cmd/partitiontest_linter/ - name: Run golangci-lint with reviewdog env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -e - ./cicdtmp/golangci-lint/golangci-lint-cgo run \ - --out-format line-number \ + ./custom-golangci-lint run \ + --output.text.path stdout \ -c .golangci-warnings.yml \ --issues-exit-code 0 \ - --allow-parallel-runners > temp_golangci-lint-cgo.txt + --allow-parallel-runners > temp_golangci-lint-warnings.txt - cat temp_golangci-lint-cgo.txt | reviewdog \ + cat temp_golangci-lint-warnings.txt | reviewdog \ -f=golangci-lint \ -name="Lint Warnings" \ -reporter=github-pr-check \ diff --git a/.gitignore b/.gitignore index b553f63bce..2845f0a7f1 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ cmd/algod/algod cmd/goal/goal cmd/updater/updater +# custom golangci-lint binary with plugins +custom-golangci-lint + # Exclude our local temp directory tmp/ diff --git a/.golangci-warnings.yml b/.golangci-warnings.yml index 8ab68f9faf..d94ffb0d54 100644 --- a/.golangci-warnings.yml +++ b/.golangci-warnings.yml @@ -1,66 +1,35 @@ +version: "2" run: - timeout: 5m tests: true - linters: - disable-all: true + default: none enable: - gosec - partitiontest - -linters-settings: - gosec: # Go 1.22 makes G601 irrelevant - excludes: [G101, G103, G104, G107, G115, G202, G301, G302, G303, G304, G306, G307, G404, G601] - custom: - partitiontest: - path: cmd/partitiontest_linter/plugin.so - description: This custom linter checks files that end in '_test.go', specifically functions that start with 'Test' and have testing argument, for a line 'partitiontest.ParitionTest()' - original-url: github.com/algorand/go-algorand/cmd/partitiontest_linter - -severity: - default-severity: warning - + settings: + gosec: + excludes: [G101, G103, G104, G107, G112, G114, G115, G202, G204, G301, G302, G303, G304, G306, G307, G404] + custom: + partitiontest: + type: "module" + description: This custom linter ensures test functions call 'partitiontest.PartitionTest(t)' + exclusions: + generated: lax + rules: + # be more lenient with test code + - linters: + - gosec + path: _test\.go + - linters: + - partitiontest + path: crypto/secp256k1/secp256_test\.go issues: - # use these new lint checks on code since #2574 - new-from-rev: eb019291beed556ec6ac1ceb4a15114ce4df0c57 - - # Disable default exclude rules listed in `golangci-lint run --help` (selectively re-enable some below) - exclude-use-default: false - # Maximum issues count per one linter. Set to 0 to disable. Default is 50. max-issues-per-linter: 0 - # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. max-same-issues: 0 - - exclude: - # ignore govet false positive fixed in https://github.com/golang/go/issues/45043 - - "sigchanyzer: misuse of unbuffered os.Signal channel as argument to signal.Notify" - # ignore issues about the way we use _struct fields to define encoding settings - - "`_struct` is unused" - - # Enable some golangci-lint default exception rules: - # "EXC0001 errcheck: Almost all programs ignore errors on these functions and in most cases it's ok" - - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked - # "EXC0005 staticcheck: Developers tend to write in C-style with an explicit 'break' in a 'switch', so it's ok to ignore" - - ineffective break statement. Did you mean to break out of the outer loop - - exclude-rules: - # be more lenient with test code - - path: _test\.go - linters: - - deadcode - - gosec - - structcheck - - varcheck - - unused - # Add all linters here -- Comment this block out for testing linters - - path: test/linttest/lintissues\.go - linters: - - deadcode - - structcheck - - varcheck - - unused - - path: crypto/secp256k1/secp256_test\.go - linters: - - partitiontest +severity: + default: warning +formatters: + exclusions: + generated: lax diff --git a/.golangci.yml b/.golangci.yml index 4c09369e1f..ccfdc51bb7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,15 +1,12 @@ +version: "2" run: - timeout: 5m tests: true - + linters: - # default: deadcode, errcheck, gosimple, govet, ineffassign, staticcheck, typecheck, unused, varcheck - disable-all: true + default: none enable: - - errcheck - copyloopvar - - gofmt - - gosimple + - errcheck - govet - ineffassign - misspell @@ -18,63 +15,126 @@ linters: - paralleltest - revive - staticcheck - - typecheck - unused + + settings: + errcheck: + exclude-functions: + # We do this 121 times and never check the error. + - (*github.com/spf13/cobra.Command).MarkFlagRequired + - (*github.com/spf13/pflag.FlagSet).MarkDeprecated + - (*github.com/spf13/pflag.FlagSet).MarkShorthandDeprecated + govet: + # Enables these linters in addition to the default ones. + enable: + - shadow + settings: + printf: + # Comma-separated list of print function names to check (in addition to default, see `go tool vet help printf`). + # Default: [] + funcs: + - (github.com/algorand/go-algorand/logging.Logger).Debugf + - (github.com/algorand/go-algorand/logging.Logger).Infof + - (github.com/algorand/go-algorand/logging.Logger).Warnf + - (github.com/algorand/go-algorand/logging.Logger).Errorf + - (github.com/algorand/go-algorand/logging.Logger).Fatalf + - (github.com/algorand/go-algorand/logging.Logger).Panicf + - (github.com/algorand/go-algorand/logging.Logger).Debugln + - (github.com/algorand/go-algorand/logging.Logger).Infoln + - (github.com/algorand/go-algorand/logging.Logger).Warnln + - (github.com/algorand/go-algorand/logging.Logger).Errorln + - (github.com/algorand/go-algorand/logging.Logger).Fatalln + - (github.com/algorand/go-algorand/logging.Logger).Panicln + - (github.com/algorand/go-algorand/logging.Logger).Debug + - (github.com/algorand/go-algorand/logging.Logger).Info + - (github.com/algorand/go-algorand/logging.Logger).Warn + - (github.com/algorand/go-algorand/logging.Logger).Error + - (github.com/algorand/go-algorand/logging.Logger).Fatal + - (github.com/algorand/go-algorand/logging.Logger).Panic + - (github.com/algorand/go-algorand/cmd/goal/main).reportInfof + - (github.com/algorand/go-algorand/cmd/goal/main).reportInfoln + - (github.com/algorand/go-algorand/cmd/goal/main).reportWarnf + - (github.com/algorand/go-algorand/cmd/goal/main).reportWarnln + - (github.com/algorand/go-algorand/cmd/goal/main).reportWarnRawf + - (github.com/algorand/go-algorand/cmd/goal/main).reportWarnRawln + - (github.com/algorand/go-algorand/cmd/goal/main).reportErrorf + - (github.com/algorand/go-algorand/cmd/goal/main).reportErrorln + shadow: + # explanation of strict vs non-strict: + # https://github.com/golang/tools/blob/v0.7.0/go/analysis/passes/shadow/shadow.go#L104-L122 + strict: false + nolintlint: + # require naming a specific linter X using //nolint:X + require-specific: true + # require comments like "//nolint:errcheck // Explanation of why we are ignoring linter here..." + require-explanation: true + staticcheck: + checks: + - all + - '-ST1000' # don't require package comments + - '-ST1003' # don't require currentDBRound vs currentDbRound + - '-ST1016' # OK to have mismatched receiver names + - '-SA3001' # we assign to b.N in several benchmarks + - '-SA1019' # TODO should fix, in Go 1.24 rand.Seed() is a no-op + - '-QF1008' # ignore suggestions to remove embedded fields (e.g., txn.SignedTxn.Txn -> txn.Txn) + - '-ST1005' # ignore "error strings should not be capitalized" + - '-QF1001' # ignore De Morgan's law suggestions + - '-QF1003' # ignore suggestions to replace if/else chain with switch -severity: - default-severity: error + exclusions: + generated: lax + rules: + # exclude all issues from several linters from test code (TODO should fix these issues) + - path: _test\.go + linters: + - errcheck + - ineffassign + - misspell + - nolintlint + - staticcheck + - unused + - linters: govet + path: _test\.go + text: shadows declaration at line # allow shadowing in test code + - linters: paralleltest # Ignore missing t.Parallel calls in the following packages + path: ^(agreement|catchup|config|crypto|daemon|data|gen|ledger|logging|network|node|protocol|rpcs|stateproof|test|tools|util).*_test\.go -linters-settings: - nolintlint: - # require naming a specific linter X using //nolint:X - require-specific: true - # require comments like "//nolint:errcheck // Explanation of why we are ignoring linter here..." - require-explanation: true - errcheck: - exclude-functions: - # We do this 121 times and never check the error. - - (*github.com/spf13/cobra.Command).MarkFlagRequired - - (*github.com/spf13/pflag.FlagSet).MarkDeprecated - - (*github.com/spf13/pflag.FlagSet).MarkShorthandDeprecated - govet: - # Enables these linters in addition to the default ones. - enable: - - shadow - settings: - shadow: - # explanation of strict vs non-strict: - # https://github.com/golang/tools/blob/v0.7.0/go/analysis/passes/shadow/shadow.go#L104-L122 - strict: false - printf: - # Comma-separated list of print function names to check (in addition to default, see `go tool vet help printf`). - # Default: [] - funcs: - - (github.com/algorand/go-algorand/logging.Logger).Debugf - - (github.com/algorand/go-algorand/logging.Logger).Infof - - (github.com/algorand/go-algorand/logging.Logger).Warnf - - (github.com/algorand/go-algorand/logging.Logger).Errorf - - (github.com/algorand/go-algorand/logging.Logger).Fatalf - - (github.com/algorand/go-algorand/logging.Logger).Panicf - - (github.com/algorand/go-algorand/logging.Logger).Debugln - - (github.com/algorand/go-algorand/logging.Logger).Infoln - - (github.com/algorand/go-algorand/logging.Logger).Warnln - - (github.com/algorand/go-algorand/logging.Logger).Errorln - - (github.com/algorand/go-algorand/logging.Logger).Fatalln - - (github.com/algorand/go-algorand/logging.Logger).Panicln - - (github.com/algorand/go-algorand/logging.Logger).Debug - - (github.com/algorand/go-algorand/logging.Logger).Info - - (github.com/algorand/go-algorand/logging.Logger).Warn - - (github.com/algorand/go-algorand/logging.Logger).Error - - (github.com/algorand/go-algorand/logging.Logger).Fatal - - (github.com/algorand/go-algorand/logging.Logger).Panic - - (github.com/algorand/go-algorand/cmd/goal/main).reportInfof - - (github.com/algorand/go-algorand/cmd/goal/main).reportInfoln - - (github.com/algorand/go-algorand/cmd/goal/main).reportWarnf - - (github.com/algorand/go-algorand/cmd/goal/main).reportWarnln - - (github.com/algorand/go-algorand/cmd/goal/main).reportWarnRawf - - (github.com/algorand/go-algorand/cmd/goal/main).reportWarnRawln - - (github.com/algorand/go-algorand/cmd/goal/main).reportErrorf - - (github.com/algorand/go-algorand/cmd/goal/main).reportErrorln + # Enable default golangci-lint exclusion: "Almost all programs ignore errors on these functions and in most cases it's ok" + - linters: errcheck + text: Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked + - linters: revive + path: _test\.go + text: 'dot-imports: should not use dot imports' # dot imports OK for tests + - linters: revive + path: util/ + text: 'var-naming: avoid meaningless package names' # util package is OK + - linters: unused + text: 'field _struct is unused' # we use _struct field tags for msgp/json encoding settings + - linters: revive + text: '^unused-parameter: parameter' + - linters: revive + text: '^package-comments: should have a package comment' + - linters: revive + text: '^unexported-return: exported func .* returns unexported type .*, which can be annoying' + - linters: revive + text: '^redefines-builtin-id: redefinition of the built-in (func|type)' + - linters: revive + text: '^var-declaration: should omit type .* from declaration of var .*; it will be inferred from the right-hand side' + - linters: revive + text: '^var-declaration: should drop .* from declaration of var .*; it is the zero value' + - linters: revive + text: '^empty-block: this block is empty, you can remove it' + - linters: revive + text: '^superfluous-else: if block ends with .* so drop this else and outdent its block' + # Comment this block out for testing linters + - linters: + - errcheck + - govet + - ineffassign + - misspell + - revive + - unused + path: test/linttest/lintissues\.go issues: # Work our way back over time to be clean against all these @@ -82,142 +142,18 @@ issues: # run the linter and dig in. new-from-rev: eb019291beed556ec6ac1ceb4a15114ce4df0c57~25 - # Disable default exclude rules listed in `golangci-lint run --help` (selectively re-enable some below) - exclude-use-default: false - # Maximum issues count per one linter. Set to 0 to disable. Default is 50. max-issues-per-linter: 0 # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. max-same-issues: 0 +severity: + default: error - exclude: - # ignore govet false positive fixed in https://github.com/golang/go/issues/45043 - - "sigchanyzer: misuse of unbuffered os.Signal channel as argument to signal.Notify" - # ignore golint false positive fixed in https://github.com/golang/lint/pull/487 - - "exported method (.*).Unwrap` should have comment or be unexported" - # ignore issues about the way we use _struct fields to define encoding settings - - "`_struct` is unused" - - # Enable some golangci-lint default exception rules: - # "EXC0001 errcheck: Almost all programs ignore errors on these functions and in most cases it's ok" - - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked - # "EXC0005 staticcheck: Developers tend to write in C-style with an explicit 'break' in a 'switch', so it's ok to ignore" - - ineffective break statement. Did you mean to break out of the outer loop - # revive: irrelevant error about naming - - "^var-naming: don't use leading k in Go names" - # revive: ignore unused-paramter, package-comments, unexported-return, redefines-builtin-id, var-declaration, empty-block, superfluous-else - - "^unused-parameter: parameter" - - "^package-comments: should have a package comment" - - "^unexported-return: " - - "^redefines-builtin-id: redefinition of" - - "^var-declaration: should" - - "^empty-block: this block is empty, you can remove it" - - "^superfluous-else: if block ends with" - - exclude-rules: - - path: cmd/algofix/ - linters: unused - - path: cmd/algocfg/ - linters: unused - - path: cmd/catchpointdump/ - linters: unused - - path: tools/ - linters: unused - - path: daemon/kmd/lib/kmdapi/ - linters: unused - - path: _test\.go - linters: - - errcheck - # - gofmt - - gosimple - # - govet - - ineffassign - - misspell - # - nilerr - - nolintlint - # - revive - # - staticcheck - - typecheck - - unused - - path: _test\.go - linters: - - staticcheck - text: "SA4006: this value" # of X is never used - - linters: - - staticcheck - text: "(SA3001|SA1019):" - - path: _test\.go - linters: - - revive - text: "dot-imports: should not use dot imports" - - linters: - - staticcheck - text: "SA1019: rand*" - # allow shadowing in test code - - path: _test\.go - linters: - - govet - text: "shadows declaration at line" - # Ignore missing parallel tests in existing packages - - path: ^agreement.*_test\.go - linters: - - paralleltest - - path: ^catchup.*_test\.go - linters: - - paralleltest - - path: ^config.*_test\.go - linters: - - paralleltest - - path: ^crypto.*_test\.go - linters: - - paralleltest - - path: ^daemon.*_test\.go - linters: - - paralleltest - - path: ^data.*_test\.go - linters: - - paralleltest - - path: ^gen.*_test\.go - linters: - - paralleltest - - path: ^ledger.*_test\.go - linters: - - paralleltest - - path: ^logging.*_test\.go - linters: - - paralleltest - - path: ^network.*_test\.go - linters: - - paralleltest - - path: ^node.*_test\.go - linters: - - paralleltest - - path: ^protocol.*_test\.go - linters: - - paralleltest - - path: ^rpcs.*_test\.go - linters: - - paralleltest - - path: ^stateproof.*_test\.go - linters: - - paralleltest - - path: ^test.*_test\.go - linters: - - paralleltest - - path: ^tools.*_test\.go - linters: - - paralleltest - - path: ^util.*_test\.go - linters: - - paralleltest - # Add all linters here -- Comment this block out for testing linters - - path: test/linttest/lintissues\.go - linters: - - errcheck - - gofmt - - revive - - govet - - ineffassign - - misspell - - unused +formatters: + enable: + - gofmt + exclusions: + generated: lax + paths: + - test/linttest/lintissues\.go diff --git a/Makefile b/Makefile index 066d61731d..89053064b9 100644 --- a/Makefile +++ b/Makefile @@ -424,5 +424,5 @@ archive: aws s3 cp tmp/node_pkgs s3://algorand-internal/channel/$(CHANNEL)/$(FULLBUILDNUMBER) --recursive --exclude "*" --include "*$(FULLBUILDNUMBER)*" build_custom_linters: - cd $(SRCPATH)/cmd/partitiontest_linter/ && go build -buildmode=plugin -trimpath plugin/plugin.go && ls plugin.so - cd $(SRCPATH) + golangci-lint custom -v + ./custom-golangci-lint --version diff --git a/agreement/player.go b/agreement/player.go index c56f8b1ccf..6336df9825 100644 --- a/agreement/player.go +++ b/agreement/player.go @@ -275,7 +275,7 @@ func (p *player) issueFastVote(r routerHandle) (actions []action) { func (p *player) handleCheckpointEvent(r routerHandle, e checkpointEvent) []action { return []action{ - checkpointAction{ //nolint:gosimple // explicit assignment for clarity + checkpointAction{ //nolint:staticcheck // explicit assignment for clarity Round: e.Round, Period: e.Period, Step: e.Step, diff --git a/cmd/goal/node.go b/cmd/goal/node.go index af98f8a9c6..5c423b1772 100644 --- a/cmd/goal/node.go +++ b/cmd/goal/node.go @@ -198,7 +198,7 @@ var catchupCmd = &cobra.Command{ fmt.Printf(nodeConfirmImplicitCatchpoint, catchpoint) reader := bufio.NewReader(os.Stdin) text, _ := reader.ReadString('\n') - text = strings.Replace(text, "\n", "", -1) + text = strings.ReplaceAll(text, "\n", "") if text != "yes" { reportErrorf(errorAbortedPerUserRequest) } diff --git a/cmd/nodecfg/download.go b/cmd/nodecfg/download.go index e87077e73a..85c554e7ef 100644 --- a/cmd/nodecfg/download.go +++ b/cmd/nodecfg/download.go @@ -23,7 +23,6 @@ import ( "path/filepath" "github.com/algorand/go-algorand/util/s3" - "github.com/algorand/go-algorand/util/tar" ) func downloadAndExtractConfigPackage(channel string, targetDir string, configBucket string) (err error) { @@ -74,7 +73,7 @@ func downloadConfigPackage(channelName string, targetDir string, configBucket st } func extractConfigPackage(packageFile string, targetDir string) (err error) { - err = tar.UncompressFile(packageFile, targetDir) + err = UncompressFile(packageFile, targetDir) if err != nil { return } diff --git a/util/tar/untar.go b/cmd/nodecfg/untar.go similarity index 75% rename from util/tar/untar.go rename to cmd/nodecfg/untar.go index e04af188a3..2339bbe00d 100644 --- a/util/tar/untar.go +++ b/cmd/nodecfg/untar.go @@ -14,14 +14,16 @@ // You should have received a copy of the GNU Affero General Public License // along with go-algorand. If not, see . -package tar +package main import ( "archive/tar" "compress/gzip" + "fmt" "io" "os" "path/filepath" + "strings" ) // UncompressFile takes the name of a tar/gz archive file and expands @@ -45,6 +47,7 @@ func Uncompress(r io.Reader, dst string) error { defer gzr.Close() tr := tar.NewReader(gzr) + baseDir := filepath.Clean(dst) for { header, err := tr.Next() @@ -65,7 +68,10 @@ func Uncompress(r io.Reader, dst string) error { } // the target location where the dir/file should be created - target := filepath.Join(dst, header.Name) + target, err := resolveEntryPath(baseDir, header.Name) + if err != nil { + return err + } // the following switch could also be done using fi.Mode(), not sure if there // a benefit of using one vs. the other. @@ -90,7 +96,7 @@ func Uncompress(r io.Reader, dst string) error { } // copy over contents - if _, err := io.Copy(f, tr); err != nil { + if _, err := io.Copy(f, tr); err != nil { //nolint:gosec // only used with trusted testing config data return err } @@ -100,3 +106,24 @@ func Uncompress(r io.Reader, dst string) error { } } } + +func resolveEntryPath(destination, headerName string) (string, error) { + cleanDest := filepath.Clean(destination) + cleanName := filepath.Clean(headerName) + + if filepath.IsAbs(cleanName) { + return "", fmt.Errorf("tar entry %q: absolute paths are not supported", headerName) + } + + target := filepath.Join(cleanDest, cleanName) + rel, err := filepath.Rel(cleanDest, target) + if err != nil { + return "", fmt.Errorf("tar entry %q: %w", headerName, err) + } + + if rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { + return "", fmt.Errorf("tar entry %q: invalid path", headerName) + } + + return target, nil +} diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index 8e4cf3df84..8b2243879f 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -256,7 +256,7 @@ func opToMarkdown(out io.Writer, op *logic.OpSpec, groupDocWritten map[string]bo } func opsToMarkdown(out io.Writer, version uint64) error { - _, err := out.Write([]byte(fmt.Sprintf("# v%d Opcodes\n\nOps have a 'cost' of 1 unless otherwise specified.\n\n", version))) + _, err := fmt.Fprintf(out, "# v%d Opcodes\n\nOps have a 'cost' of 1 unless otherwise specified.\n\n", version) if err != nil { return err } diff --git a/cmd/partitiontest_linter/go.mod b/cmd/partitiontest_linter/go.mod index ca958e24bf..e4d7f8eafe 100644 --- a/cmd/partitiontest_linter/go.mod +++ b/cmd/partitiontest_linter/go.mod @@ -6,7 +6,10 @@ toolchain go1.25.3 require ( golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.12.0 // indirect + golang.org/x/sync v0.13.0 // indirect ) -require golang.org/x/tools v0.31.0 +require ( + github.com/golangci/plugin-module-register v0.1.2 + golang.org/x/tools v0.32.0 +) diff --git a/cmd/partitiontest_linter/go.sum b/cmd/partitiontest_linter/go.sum index 09ebfb8a2a..e2328570de 100644 --- a/cmd/partitiontest_linter/go.sum +++ b/cmd/partitiontest_linter/go.sum @@ -1,8 +1,10 @@ +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= diff --git a/cmd/partitiontest_linter/linter.go b/cmd/partitiontest_linter/linter.go index 05af0ae6b9..6807e8c2ed 100644 --- a/cmd/partitiontest_linter/linter.go +++ b/cmd/partitiontest_linter/linter.go @@ -20,6 +20,7 @@ import ( "go/ast" "strings" + "github.com/golangci/plugin-module-register/register" "golang.org/x/tools/go/analysis" ) @@ -30,10 +31,10 @@ const functionNamePrefix string = "Test" const parameterType string = "T" const parameterName string = "t" -// Analyzer initilization +// Analyzer initialization var Analyzer = &analysis.Analyzer{ - Name: "lint", - Doc: "This custom linter checks inside files that end in '_test.go', and inside functions that start with 'Test' and have testing argument, for a line 'partitiontest.ParitionTest()'", + Name: "partitiontest", + Doc: "This custom linter checks inside files that end in '_test.go', and inside functions that start with 'Test' and have testing argument, for a line 'partitiontest.PartitionTest()'", Run: run, } @@ -58,7 +59,7 @@ func run(pass *analysis.Pass) (interface{}, error) { if !isTestParameterInFunction(fn.Type.Params.List[0].Type, parameterType) { continue } - if !isSearchLineInFunction(fn) { + if !hasPartitionInvocation(f, fn) { pass.Reportf(fn.Pos(), "%s: Add missing partition call to top of test. To disable partitioning, add it as a comment: %s.%s(%s)", fn.Name.Name, packageName, functionName, parameterName) } @@ -83,17 +84,26 @@ func isTestParameterInFunction(typ ast.Expr, wantType string) bool { return false } +func hasPartitionInvocation(file *ast.File, fn *ast.FuncDecl) bool { + if isSearchLineInFunction(fn) { + return true + } + return hasPartitionComment(file, fn) +} + func isSearchLineInFunction(fn *ast.FuncDecl) bool { for _, oneline := range fn.Body.List { if exprStmt, ok := oneline.(*ast.ExprStmt); ok { if call, ok := exprStmt.X.(*ast.CallExpr); ok { - if fun, ok := call.Fun.(*ast.SelectorExpr); ok { - if !doesPackageNameMatch(fun) { - continue - } - if !doesFunctionNameMatch(fun) { - continue - } + fun, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + continue + } + if !doesPackageNameMatch(fun) { + continue + } + if !doesFunctionNameMatch(fun) { + continue } if !doesParameterNameMatch(call, fn) { @@ -107,6 +117,20 @@ func isSearchLineInFunction(fn *ast.FuncDecl) bool { return false } +func hasPartitionComment(file *ast.File, fn *ast.FuncDecl) bool { + for _, commentGroup := range file.Comments { + if commentGroup.Pos() < fn.Pos() || commentGroup.Pos() > fn.End() { + continue + } + for _, comment := range commentGroup.List { + if strings.Contains(comment.Text, "partitiontest.PartitionTest(") { + return true + } + } + } + return false +} + func doesPackageNameMatch(fun *ast.SelectorExpr) bool { if packageobject, ok := fun.X.(*ast.Ident); ok { if packageobject.Name == packageName { @@ -131,3 +155,24 @@ func doesParameterNameMatch(call *ast.CallExpr, fn *ast.FuncDecl) bool { } return false } + +// V2 module plugin registration + +func init() { + register.Plugin("partitiontest", New) +} + +// PartitionTestPlugin implements the golangci-lint v2 module plugin interface +type PartitionTestPlugin struct{} + +func New(_ any) (register.LinterPlugin, error) { + return &PartitionTestPlugin{}, nil +} + +func (p *PartitionTestPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { + return []*analysis.Analyzer{Analyzer}, nil +} + +func (p *PartitionTestPlugin) GetLoadMode() string { + return register.LoadModeSyntax +} diff --git a/cmd/partitiontest_linter/plugin/plugin.go b/cmd/partitiontest_linter/plugin/plugin.go deleted file mode 100644 index a8f248c65c..0000000000 --- a/cmd/partitiontest_linter/plugin/plugin.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2025 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package main - -import ( - linter "github.com/algorand/go-algorand/cmd/partitiontest_linter" - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/singlechecker" -) - -type analyzerPlugin struct{} - -// This must be implemented -func (*analyzerPlugin) GetAnalyzers() []*analysis.Analyzer { - return []*analysis.Analyzer{ - linter.Analyzer, - } -} - -// AnalyzerPlugin must be defined and named 'AnalyzerPlugin' -var AnalyzerPlugin analyzerPlugin - -func main() { - singlechecker.Main(linter.Analyzer) -} diff --git a/cmd/partitiontest_linter/testdata/linter_testdata_test.go b/cmd/partitiontest_linter/testdata/linter_testdata_test.go index b50d313b3e..2e06e15dfd 100644 --- a/cmd/partitiontest_linter/testdata/linter_testdata_test.go +++ b/cmd/partitiontest_linter/testdata/linter_testdata_test.go @@ -42,13 +42,13 @@ func notTestFunctionWithCorrectParamWrongLine(t *testing.T) { println("something") } -func TestFunctionWithCorrectParamOnly(t *testing.T) {} // want "function is missing partitiontest.PartitionTest" +func TestFunctionWithCorrectParamOnly(t *testing.T) {} // want "Add missing partition call to top of test" func TestFunctionWithCorrectParamCorrectLine(t *testing.T) { partitiontest.PartitionTest(t) } -func TestFunctionWithCorrectParamBadLine(t *testing.T) { // want "function is missing partitiontest.PartitionTest" +func TestFunctionWithCorrectParamBadLine(t *testing.T) { // want "Add missing partition call to top of test" println("something") } @@ -56,6 +56,8 @@ func TestFunctionWithDifferentName(n *testing.T) { partitiontest.PartitionTest(n) } +func helperFunction(t *testing.T) {} + func TestFunctionWithCorrectParamNotFirstCorrectLine(t *testing.T) { println("something") partitiontest.PartitionTest(t) @@ -71,3 +73,12 @@ func TestFunctionWithCorrectParamMiddleCorrectLine(t *testing.T) { partitiontest.PartitionTest(t) println("something") } + +func TestFunctionWithCorrectParamLeadingDifferentCall(t *testing.T) { // want "Add missing partition call to top of test" + helperFunction(t) +} + +func TestFunctionWithPartitionComment(t *testing.T) { + // partitiontest.PartitionTest(t) + println("something") +} diff --git a/config/dnsbootstrap.go b/config/dnsbootstrap.go index d6780384b3..b01876cc3b 100644 --- a/config/dnsbootstrap.go +++ b/config/dnsbootstrap.go @@ -107,7 +107,7 @@ func parseDNSBootstrap(dnsBootstrapID string, network protocol.NetworkID, defaul } // Normalize the dnsBootstrapID and insert the network - dnsBootstrapID = strings.Replace(strings.TrimSpace(strings.ToLower(dnsBootstrapID)), "", string(network), -1) + dnsBootstrapID = strings.ReplaceAll(strings.TrimSpace(strings.ToLower(dnsBootstrapID)), "", string(network)) if dnsBootstrapID == "" { return nil, errors.New(bootstrapErrorEmpty) diff --git a/crypto/gobatchverifier_test.go b/crypto/gobatchverifier_test.go index 36ed799fab..884e65eb44 100644 --- a/crypto/gobatchverifier_test.go +++ b/crypto/gobatchverifier_test.go @@ -148,6 +148,8 @@ func TestBatchVerifierLibsodiumTestData(t *testing.T) { // based on TestEd25519Vectors from go/src/crypto/ed25519/ed25519vectors_test.go // which uses test vectors from filippo.io/mostly-harmless/ed25519vectors func TestBatchVerifierFilippoVectors(t *testing.T) { + partitiontest.PartitionTest(t) + var vectors []struct { A, R, S, M string Flags []string diff --git a/daemon/algod/server.go b/daemon/algod/server.go index 8fb650078e..72c2beba0c 100644 --- a/daemon/algod/server.go +++ b/daemon/algod/server.go @@ -23,7 +23,7 @@ import ( "io" "net" "net/http" - _ "net/http/pprof" // net/http/pprof is for registering the pprof URLs with the web server, so http://localhost:8080/debug/pprof/ works. + _ "net/http/pprof" //nolint:gosec // registers handlers on http.DefaultServeMux, but we only route to it when Config.EnableProfiler is true "net/url" "os" "os/signal" diff --git a/data/basics/serr.go b/data/basics/serr.go index cf65e9d911..4e767a8bde 100644 --- a/data/basics/serr.go +++ b/data/basics/serr.go @@ -54,7 +54,7 @@ func (e *SError) Error() string { } // imperfect because we replace \%A as well if strings.Contains(e.Msg, "%A") { - return strings.Replace(e.Msg, "%A", e.AttributesAsString(), -1) + return strings.ReplaceAll(e.Msg, "%A", e.AttributesAsString()) } return e.Msg } diff --git a/gen/generate_test.go b/gen/generate_test.go index 11d595eddc..36c3bd4a07 100644 --- a/gen/generate_test.go +++ b/gen/generate_test.go @@ -42,6 +42,7 @@ import ( ) func TestLoadMultiRootKeyConcurrent(t *testing.T) { + partitiontest.PartitionTest(t) t.Skip() // skip in auto-test mode a := require.New(t) tempDir := t.TempDir() @@ -81,6 +82,7 @@ func TestLoadMultiRootKeyConcurrent(t *testing.T) { } func TestLoadSingleRootKeyConcurrent(t *testing.T) { + partitiontest.PartitionTest(t) t.Skip() // skip in auto-test mode a := require.New(t) tempDir := t.TempDir() diff --git a/ledger/lrukv.go b/ledger/lrukv.go index 5b5c1a2bd2..75f01cb67d 100644 --- a/ledger/lrukv.go +++ b/ledger/lrukv.go @@ -131,10 +131,8 @@ func (m *lruKV) prune(newSize int) (removed int) { if m.kvs == nil { return } - for { - if len(m.kvs) <= newSize { - break - } + for len(m.kvs) > newSize { + back := m.kvList.Back() delete(m.kvs, back.Value.key) m.kvList.Remove(back) diff --git a/ledger/lruonlineaccts.go b/ledger/lruonlineaccts.go index 90bd69ebc1..47e39d6b9a 100644 --- a/ledger/lruonlineaccts.go +++ b/ledger/lruonlineaccts.go @@ -120,10 +120,8 @@ func (m *lruOnlineAccounts) prune(newSize int) (removed int) { if m.accounts == nil { return } - for { - if len(m.accounts) <= newSize { - break - } + for len(m.accounts) > newSize { + back := m.accountsList.Back() delete(m.accounts, back.Value.Addr) m.accountsList.Remove(back) diff --git a/ledger/lruresources.go b/ledger/lruresources.go index b869e23b07..303dd86e66 100644 --- a/ledger/lruresources.go +++ b/ledger/lruresources.go @@ -178,10 +178,8 @@ func (m *lruResources) prune(newSize int) (removed int) { if m.resources == nil { return } - for { - if len(m.resources) <= newSize { - break - } + for len(m.resources) > newSize { + back := m.resourcesList.Back() delete(m.resources, accountCreatable{address: back.Value.address, index: back.Value.Aidx}) m.resourcesList.Remove(back) diff --git a/logging/telemetryspec/metric.go b/logging/telemetryspec/metric.go index a466f0615c..941e0c7b33 100644 --- a/logging/telemetryspec/metric.go +++ b/logging/telemetryspec/metric.go @@ -112,37 +112,37 @@ func (m AssembleBlockMetrics) Identifier() Metric { } func (m AssembleBlockStats) String() string { b := &bytes.Buffer{} - b.WriteString(fmt.Sprintf("StartCount:%d, ", m.StartCount)) - b.WriteString(fmt.Sprintf("IncludedCount:%d, ", m.IncludedCount)) - b.WriteString(fmt.Sprintf("InvalidCount:%d, ", m.InvalidCount)) - b.WriteString(fmt.Sprintf("MinFeeErrorCount:%d, ", m.MinFeeErrorCount)) - b.WriteString(fmt.Sprintf("LogicErrorCount:%d, ", m.LogicErrorCount)) - b.WriteString(fmt.Sprintf("ExpiredCount:%d, ", m.ExpiredCount)) - b.WriteString(fmt.Sprintf("ExpiredLongLivedCount:%d, ", m.ExpiredLongLivedCount)) - b.WriteString(fmt.Sprintf("LeaseErrorCount:%d, ", m.LeaseErrorCount)) - b.WriteString(fmt.Sprintf("MinFee:%d, ", m.MinFee)) - b.WriteString(fmt.Sprintf("MaxFee:%d, ", m.MaxFee)) - b.WriteString(fmt.Sprintf("AverageFee:%d, ", m.AverageFee)) - b.WriteString(fmt.Sprintf("MinLength:%d, ", m.MinLength)) - b.WriteString(fmt.Sprintf("MaxLength:%d, ", m.MaxLength)) - b.WriteString(fmt.Sprintf("MinPriority:%d, ", m.MinPriority)) - b.WriteString(fmt.Sprintf("MaxPriority:%d, ", m.MaxPriority)) - b.WriteString(fmt.Sprintf("CommittedCount:%d, ", m.CommittedCount)) - b.WriteString(fmt.Sprintf("StopReason:%s, ", m.StopReason)) - b.WriteString(fmt.Sprintf("TotalLength:%d, ", m.TotalLength)) - b.WriteString(fmt.Sprintf("EarlyCommittedCount:%d, ", m.EarlyCommittedCount)) - b.WriteString(fmt.Sprintf("Nanoseconds:%d, ", m.Nanoseconds)) - b.WriteString(fmt.Sprintf("ProcessingTime:%v, ", m.ProcessingTime)) - b.WriteString(fmt.Sprintf("BlockGenerationDuration:%d, ", m.BlockGenerationDuration)) - b.WriteString(fmt.Sprintf("TransactionsLoopStartTime:%d, ", m.TransactionsLoopStartTime)) - b.WriteString(fmt.Sprintf("StateProofNextRound:%d, ", m.StateProofNextRound)) + fmt.Fprintf(b, "StartCount:%d, ", m.StartCount) + fmt.Fprintf(b, "IncludedCount:%d, ", m.IncludedCount) + fmt.Fprintf(b, "InvalidCount:%d, ", m.InvalidCount) + fmt.Fprintf(b, "MinFeeErrorCount:%d, ", m.MinFeeErrorCount) + fmt.Fprintf(b, "LogicErrorCount:%d, ", m.LogicErrorCount) + fmt.Fprintf(b, "ExpiredCount:%d, ", m.ExpiredCount) + fmt.Fprintf(b, "ExpiredLongLivedCount:%d, ", m.ExpiredLongLivedCount) + fmt.Fprintf(b, "LeaseErrorCount:%d, ", m.LeaseErrorCount) + fmt.Fprintf(b, "MinFee:%d, ", m.MinFee) + fmt.Fprintf(b, "MaxFee:%d, ", m.MaxFee) + fmt.Fprintf(b, "AverageFee:%d, ", m.AverageFee) + fmt.Fprintf(b, "MinLength:%d, ", m.MinLength) + fmt.Fprintf(b, "MaxLength:%d, ", m.MaxLength) + fmt.Fprintf(b, "MinPriority:%d, ", m.MinPriority) + fmt.Fprintf(b, "MaxPriority:%d, ", m.MaxPriority) + fmt.Fprintf(b, "CommittedCount:%d, ", m.CommittedCount) + fmt.Fprintf(b, "StopReason:%s, ", m.StopReason) + fmt.Fprintf(b, "TotalLength:%d, ", m.TotalLength) + fmt.Fprintf(b, "EarlyCommittedCount:%d, ", m.EarlyCommittedCount) + fmt.Fprintf(b, "Nanoseconds:%d, ", m.Nanoseconds) + fmt.Fprintf(b, "ProcessingTime:%v, ", m.ProcessingTime) + fmt.Fprintf(b, "BlockGenerationDuration:%d, ", m.BlockGenerationDuration) + fmt.Fprintf(b, "TransactionsLoopStartTime:%d, ", m.TransactionsLoopStartTime) + fmt.Fprintf(b, "StateProofNextRound:%d, ", m.StateProofNextRound) emptySPStats := StateProofStats{} if m.StateProofStats != emptySPStats { - b.WriteString(fmt.Sprintf("ProvenWeight:%d, ", m.StateProofStats.ProvenWeight)) - b.WriteString(fmt.Sprintf("SignedWeight:%d, ", m.StateProofStats.SignedWeight)) - b.WriteString(fmt.Sprintf("NumReveals:%d, ", m.StateProofStats.NumReveals)) - b.WriteString(fmt.Sprintf("NumPosToReveal:%d, ", m.StateProofStats.NumPosToReveal)) - b.WriteString(fmt.Sprintf("TxnSize:%d", m.StateProofStats.TxnSize)) + fmt.Fprintf(b, "ProvenWeight:%d, ", m.StateProofStats.ProvenWeight) + fmt.Fprintf(b, "SignedWeight:%d, ", m.StateProofStats.SignedWeight) + fmt.Fprintf(b, "NumReveals:%d, ", m.StateProofStats.NumReveals) + fmt.Fprintf(b, "NumPosToReveal:%d, ", m.StateProofStats.NumPosToReveal) + fmt.Fprintf(b, "TxnSize:%d", m.StateProofStats.TxnSize) } return b.String() } diff --git a/network/addr.go b/network/addr.go index 8fcc140883..402f1e4c76 100644 --- a/network/addr.go +++ b/network/addr.go @@ -34,6 +34,6 @@ func (wn *WebsocketNetwork) addrToGossipAddr(a string) (string, error) { if parsedURL.Scheme == "" { parsedURL.Scheme = "ws" } - parsedURL.Path = strings.Replace(path.Join(parsedURL.Path, GossipNetworkPath), "{genesisID}", wn.GetGenesisID(), -1) + parsedURL.Path = strings.ReplaceAll(path.Join(parsedURL.Path, GossipNetworkPath), "{genesisID}", wn.GetGenesisID()) return parsedURL.String(), nil } diff --git a/network/gossipNode.go b/network/gossipNode.go index cfd43e48fa..10bbf2d27d 100644 --- a/network/gossipNode.go +++ b/network/gossipNode.go @@ -257,5 +257,5 @@ func Propagate(msg IncomingMessage) OutgoingMessage { // SubstituteGenesisID substitutes the "{genesisID}" with their network-specific genesisID. func SubstituteGenesisID(net GossipNode, rawURL string) string { - return strings.Replace(rawURL, "{genesisID}", net.GetGenesisID(), -1) + return strings.ReplaceAll(rawURL, "{genesisID}", net.GetGenesisID()) } diff --git a/network/hybridNetwork.go b/network/hybridNetwork.go index 1f2545b149..687d1271c5 100644 --- a/network/hybridNetwork.go +++ b/network/hybridNetwork.go @@ -49,8 +49,8 @@ func NewHybridP2PNetwork(log logging.Logger, cfg config.Local, datadir string, p p2pcfg.IncomingConnectionsLimit = cfg.P2PHybridIncomingConnectionsLimit identityTracker := NewIdentityTracker() - var childWsNetMeshCreator MeshCreator = meshCreator - var childP2PNetMeshCreator MeshCreator = meshCreator + var childWsNetMeshCreator = meshCreator + var childP2PNetMeshCreator = meshCreator var hybridMeshCreator MeshCreator = noopMeshCreator{} _, isHybridMeshCreator := meshCreator.(hybridRelayMeshCreator) if meshCreator == nil && cfg.IsHybridServer() || isHybridMeshCreator { diff --git a/network/p2p/peerstore/peerstore.go b/network/p2p/peerstore/peerstore.go index 5d0b2b24ca..137f7a924f 100644 --- a/network/p2p/peerstore/peerstore.go +++ b/network/p2p/peerstore/peerstore.go @@ -194,7 +194,7 @@ func (ps *PeerStore) UpdateConnectionTime(addrOrPeerID string, provisionalTime t // Find the provisionalTime and update it entry := ad.recentConnectionTimes for indx, val := range entry { - if provisionalTime == val { + if provisionalTime.Equal(val) { entry[indx] = time.Now() return true } diff --git a/network/wsNetwork.go b/network/wsNetwork.go index 746536da74..356cad1561 100644 --- a/network/wsNetwork.go +++ b/network/wsNetwork.go @@ -1560,11 +1560,8 @@ func (wn *WebsocketNetwork) meshThreadInner() bool { // as long as the call to checkExistingConnectionsNeedDisconnecting is deleting existing connections, we want to // kick off the creation of new connections. - for { - if wn.checkNewConnectionsNeeded() { - // new connections were created. - break - } + for !wn.checkNewConnectionsNeeded() { + if !wn.checkExistingConnectionsNeedDisconnecting() { // no connection were removed. break diff --git a/network/wsPeer.go b/network/wsPeer.go index ff8723be0b..4b5bc9955f 100644 --- a/network/wsPeer.go +++ b/network/wsPeer.go @@ -386,7 +386,7 @@ func (wp *wsPeer) RoutingAddr() []byte { // (Implements TCPInfoUnicastPeer) func (wp *wsPeer) GetUnderlyingConnTCPInfo() (*util.TCPInfo, error) { // unwrap websocket.Conn, requestTrackedConnection, rejectingLimitListenerConn - var uconn net.Conn = wp.conn.UnderlyingConn() + var uconn = wp.conn.UnderlyingConn() for i := 0; i < 10; i++ { wconn, ok := uconn.(wrappedConn) if !ok { diff --git a/rpcs/ledgerService.go b/rpcs/ledgerService.go index 60459d9f98..3265ba3781 100644 --- a/rpcs/ledgerService.go +++ b/rpcs/ledgerService.go @@ -241,7 +241,7 @@ func (ls *LedgerService) ServeHTTP(response http.ResponseWriter, request *http.R return } defer decompressedGzip.Close() - written, err := io.Copy(response, decompressedGzip) + written, err := io.Copy(response, decompressedGzip) //nolint:gosec // writing to the network from a local file, no "decompression bomb" if err != nil { logging.Base().Infof("LedgerService.ServeHTTP : unable to write decompressed catchpoint file for round %d, written bytes %d : %v", round, written, err) } else { diff --git a/scripts/buildtools/install_buildtools.sh b/scripts/buildtools/install_buildtools.sh index a06f5b6bbb..d4f5b0c848 100755 --- a/scripts/buildtools/install_buildtools.sh +++ b/scripts/buildtools/install_buildtools.sh @@ -91,4 +91,4 @@ install_go_module golang.org/x/tools golang.org/x/tools/cmd/stringer install_go_module github.com/algorand/go-swagger github.com/algorand/go-swagger/cmd/swagger install_go_module github.com/algorand/msgp install_go_module gotest.tools/gotestsum -install_go_module github.com/golangci/golangci-lint/cmd/golangci-lint +install_go_module github.com/golangci/golangci-lint/v2/cmd/golangci-lint diff --git a/scripts/buildtools/versions b/scripts/buildtools/versions index 4185f1953c..04ae69bf2b 100644 --- a/scripts/buildtools/versions +++ b/scripts/buildtools/versions @@ -3,4 +3,4 @@ golang.org/x/tools v0.27.0 github.com/algorand/msgp v1.1.61 github.com/algorand/go-swagger v0.0.0-20251018003531-2ea7c750dcac gotest.tools/gotestsum v1.13.0 -github.com/golangci/golangci-lint/cmd/golangci-lint v1.64.8 +github.com/golangci/golangci-lint/v2/cmd/golangci-lint v2.5.0 diff --git a/shared/pingpong/pingpong.go b/shared/pingpong/pingpong.go index ab2a2a2fbb..4bf6ba44af 100644 --- a/shared/pingpong/pingpong.go +++ b/shared/pingpong/pingpong.go @@ -16,7 +16,7 @@ // Package pingpong provides a transaction generating utility for performance testing. // -//nolint:unused,structcheck,deadcode,varcheck // ignore unused pingpong code +//nolint:unused // ignore unused pingpong code package pingpong import ( @@ -327,10 +327,8 @@ func (pps *WorkerState) schedule(n int) { if n > 1 { nextSendTime = nextSendTime.Add(timePerStep * time.Duration(n-1)) } - for { - if now.After(nextSendTime) { - break - } + for !now.After(nextSendTime) { + dur := nextSendTime.Sub(now) if dur < durationEpsilon { break diff --git a/test/e2e-go/features/accountPerf/sixMillion_test.go b/test/e2e-go/features/accountPerf/sixMillion_test.go index e0a94c9eb3..03b05d8823 100644 --- a/test/e2e-go/features/accountPerf/sixMillion_test.go +++ b/test/e2e-go/features/accountPerf/sixMillion_test.go @@ -195,22 +195,26 @@ func signerGrpTxn( // create 6M unique assets by a different 6,000 accounts, and have a single account opted in, and owning all of them func Test5MAssetsScenario1(t *testing.T) { + // partitiontest.PartitionTest(t) // partition handled inside test5MAssets test5MAssets(t, 1) } // create 6M unique assets, all created by a single account. func Test5MAssetsScenario2(t *testing.T) { + // partitiontest.PartitionTest(t) // partition handled inside test5MAssets test5MAssets(t, 2) } // create 6M unique apps by a different 6,000 accounts, and have a single account opted-in all of them. // Make an app call to each of them, and make sure the app store some information into the local storage. func Test5MAssetsScenario3(t *testing.T) { + // partitiontest.PartitionTest(t) // partition handled inside test5MAssets test5MAssets(t, 3) } // create 6M unique apps by a single account. Opt-into all the applications and make sure the app stores information to both the local and global storage. func Test5MAssetsScenario4(t *testing.T) { + // partitiontest.PartitionTest(t) // partition handled inside test5MAssets test5MAssets(t, 4) } diff --git a/test/netperf-go/puppeteer/promMetricFetcher_test.go b/test/netperf-go/puppeteer/promMetricFetcher_test.go index f654f683df..62726c985b 100644 --- a/test/netperf-go/puppeteer/promMetricFetcher_test.go +++ b/test/netperf-go/puppeteer/promMetricFetcher_test.go @@ -19,9 +19,12 @@ package main import ( "fmt" "testing" + + "github.com/algorand/go-algorand/test/partitiontest" ) func TestMetricsFetcher(t *testing.T) { + partitiontest.PartitionTest(t) // this test function was meant for local development test and not as an official unit test. t.Skip() //host := "3.81.68.74" diff --git a/test/netperf-go/puppeteer/puppeteer_test.go b/test/netperf-go/puppeteer/puppeteer_test.go index 937f0de88a..3b68efa0b2 100644 --- a/test/netperf-go/puppeteer/puppeteer_test.go +++ b/test/netperf-go/puppeteer/puppeteer_test.go @@ -18,9 +18,12 @@ package main import ( "testing" + + "github.com/algorand/go-algorand/test/partitiontest" ) func TestMetricsPrintout(t *testing.T) { + partitiontest.PartitionTest(t) // this test function was meant for local development test and not as an official unit test. t.Skip() puppets := []*puppet{ diff --git a/tools/debug/transplanter/main.go b/tools/debug/transplanter/main.go index 2433fecbef..5c0de8b0eb 100644 --- a/tools/debug/transplanter/main.go +++ b/tools/debug/transplanter/main.go @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with go-algorand. If not, see . +//nolint:unused // old debug program package main import ( diff --git a/util/cmdUtils.go b/util/cmdUtils.go index 87d62b15c1..32f88a86f9 100644 --- a/util/cmdUtils.go +++ b/util/cmdUtils.go @@ -42,7 +42,6 @@ func RunFuncWithSpinningCursor(asyncFunc func()) { case <-doneChan: finished = true ticker.Stop() - break case <-ticker.C: fmt.Print(progressStrings[i]) fmt.Print("\b") diff --git a/util/db/dbutil_test.go b/util/db/dbutil_test.go index 03b6308b90..be8cdfc2be 100644 --- a/util/db/dbutil_test.go +++ b/util/db/dbutil_test.go @@ -481,11 +481,13 @@ func TestReadingWhileWriting(t *testing.T) { // using Write-Ahead Logging (WAL) func TestLockingTableWhileWritingWAL(t *testing.T) { + // partitiontest.PartitionTest(t) // partition handled inside testLockingTableWhileWriting testLockingTableWhileWriting(t, true) } // using the default Rollback Journal func TestLockingTableWhileWritingJournal(t *testing.T) { + // partitiontest.PartitionTest(t) // partition handled inside testLockingTableWhileWriting testLockingTableWhileWriting(t, false) } diff --git a/util/metrics/reporter.go b/util/metrics/reporter.go index db9ae5f964..5eeeb55c59 100644 --- a/util/metrics/reporter.go +++ b/util/metrics/reporter.go @@ -206,7 +206,7 @@ func (reporter *MetricReporter) tryInvokeNodeExporter(ctx context.Context) { var err error if nil == reporter.neSync { // try to create it. - if reporter.neSync, err = net.Listen("tcp", nodeExporterSyncAddr); err != nil { + if reporter.neSync, err = net.Listen("tcp", nodeExporterSyncAddr); err != nil { //nolint:gosec // OK to bind to all interfaces // we couldn't get a hold of this port number; that's an expected behaviour for any algod instance that isn't the first one.. return } diff --git a/util/tar/tar.go b/util/tar/tar.go deleted file mode 100644 index 012ead433f..0000000000 --- a/util/tar/tar.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (C) 2019-2025 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package tar - -import ( - "archive/tar" - "compress/gzip" - "fmt" - "io" - "os" - "path/filepath" - "strings" -) - -// CompressFolder takes the name of a folder to compress, and the name of a file in which to store it. -func CompressFolder(src, filename string) error { - f, err := os.Create(filename) - if err != nil { - return err - } - return Compress(src, f) -} - -// Compress takes a source and variable writers and walks 'source' writing each file -// found to the tar writer; the purpose for accepting multiple writers is to allow -// for multiple outputs (for example a file, or md5 hash) -// Copied with minor modifications from https://medium.com/@skdomino/taring-untaring-files-in-go-6b07cf56bc07 -func Compress(src string, writers ...io.Writer) error { - - // ensure the src actually exists before trying to tar it - if _, err := os.Stat(src); err != nil { - return fmt.Errorf("Unable to tar files - %v", err.Error()) - } - - mw := io.MultiWriter(writers...) - - gzw := gzip.NewWriter(mw) - defer gzw.Close() - - tw := tar.NewWriter(gzw) - defer tw.Close() - - // walk path - return filepath.Walk(src, func(file string, fi os.FileInfo, err error) error { - - // return on any error - if err != nil { - return err - } - - // create a new dir/file header - header, err := tar.FileInfoHeader(fi, fi.Name()) - if err != nil { - return err - } - - // update the name to correctly reflect the desired destination when untaring - header.Name = strings.TrimPrefix(strings.Replace(file, src, "", -1), string(filepath.Separator)) - - // write the header - if err1 := tw.WriteHeader(header); err1 != nil { - return err1 - } - - // return on non-regular files (thanks to [kumo](https://medium.com/@komuw/just-like-you-did-fbdd7df829d3) for this suggested update) - if !header.FileInfo().Mode().IsRegular() { - return nil - } - - // open files for taring - f, err := os.Open(file) - if err != nil { - return err - } - - // copy file data into tar writer - if _, err := io.Copy(tw, f); err != nil { - return err - } - - // manually close here after each file operation; defering would cause each file close - // to wait until all operations have completed. - f.Close() - - return nil - }) -}