From fe588daddac35d2718e7888f07badeaff572dff3 Mon Sep 17 00:00:00 2001 From: Kaj Kowalski Date: Thu, 6 Nov 2025 15:56:29 +0100 Subject: [PATCH] chore(ci): add linting and refine workflow dependencies Adds a golangci-lint job to the CI pipeline to enforce code quality and style. The test job is now dependent on the new linting job. The final image build job is also updated to depend on the successful completion of the test, docker-test, and dependency-review jobs, ensuring more checks pass before publishing. Additionally, Go 1.25 is added to the testing matrix. --- .github/workflows/autofix.yml | 40 +++- .github/workflows/ci.yml | 270 ++++++++++++++---------- .gitignore | 2 + .golangci.yml | 381 ++++++++++++++++++++++++++++++++++ 4 files changed, 568 insertions(+), 125 deletions(-) create mode 100644 .golangci.yml diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 8d24ab3..3e5eed3 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -10,16 +10,36 @@ jobs: autofix: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: actions/setup-go@v6 - with: - go-version-file: 'go.mod' + - name: Checkout code + uses: actions/checkout@v5 - # goimports works like gofmt, but also fixes imports. - # see https://pkg.go.dev/golang.org/x/tools/cmd/goimports - - run: go install golang.org/x/tools/cmd/goimports@latest - - run: goimports -w . - # of course we can also do just this instead: - # - run: gofmt -w . + - name: Install Task + uses: go-task/setup-task@v1 + + - uses: actions/setup-go@v6 + with: { go-version-file: 'go.mod' } + + - name: Setup go deps + run: | + # Install golangci-lint + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin + + # Install go-task dependencies + go install golang.org/x/tools/cmd/goimports@latest + + - name: Run goimports + run: goimports -w . + + - name: Run golangci-lint autofix + run: golangci-lint run --fix + + - name: Run golangci-lint format + run: golangci-lint format + + - name: Run go mod tidy + run: go mod tidy + + - name: Run gopls modernize + run: task modernize - uses: autofix-ci/action@v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7ac4c0..ec26478 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,6 @@ on: push: branches: ['master', 'develop'] pull_request: - branches: ['master', 'develop', 'feature/*'] env: REGISTRY: ghcr.io @@ -15,8 +14,24 @@ concurrency: cancel-in-progress: true jobs: + golangci: + name: lint + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version: stable + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: { version: latest } + test: name: Test + needs: [golangci] runs-on: ubuntu-latest permissions: contents: write @@ -27,6 +42,7 @@ jobs: - 1.22.x - 1.23.x - 1.24.x + - 1.25.x steps: - uses: actions/checkout@v5 @@ -52,11 +68,13 @@ jobs: - name: Run tests with enhanced reporting id: test run: | - echo "## ๐Ÿ”ง Test Environment" >> $GITHUB_STEP_SUMMARY - echo "- **Go Version:** ${{ matrix.go }}" >> $GITHUB_STEP_SUMMARY - echo "- **OS:** ubuntu-latest" >> $GITHUB_STEP_SUMMARY - echo "- **Timestamp:** $(date -u)" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << EOF + ## ๐Ÿ”ง Test Environment + - **Go Version:** ${{ matrix.go }} + - **OS:** ubuntu-latest + - **Timestamp:** $(date -u) + + EOF echo "Running tests with coverage..." task test:coverage 2>&1 | tee test-output.log @@ -69,30 +87,30 @@ jobs: SKIPPED_TESTS=$(grep -c "--- SKIP:" test-output.log || echo "0") # Generate test summary - echo "## ๐Ÿงช Test Results (Go ${{ matrix.go }})" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY - echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Total Tests | $TOTAL_TESTS |" >> $GITHUB_STEP_SUMMARY - echo "| Passed | โœ… $PASSED_TESTS |" >> $GITHUB_STEP_SUMMARY - echo "| Failed | โŒ $FAILED_TESTS |" >> $GITHUB_STEP_SUMMARY - echo "| Skipped | โญ๏ธ $SKIPPED_TESTS |" >> $GITHUB_STEP_SUMMARY - echo "| Status | $([ $TEST_STATUS -eq 0 ] && echo "โœ… PASSED" || echo "โŒ FAILED") |" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << EOF + ## ๐Ÿงช Test Results (Go ${{ matrix.go }}) - # Add package breakdown - echo "### ๐Ÿ“ฆ Package Test Results" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Package | Status |" >> $GITHUB_STEP_SUMMARY - echo "|---------|--------|" >> $GITHUB_STEP_SUMMARY + | Metric | Value | + | ----------- | ----------------------------------------------------------- | + | Total Tests | $TOTAL_TESTS | + | Passed | $PASSED_TESTS | + | Failed | $FAILED_TESTS | + | Skipped | $SKIPPED_TESTS | + | Status | $([ $TEST_STATUS -eq 0 ] && echo "PASSED" || echo "FAILED") | + + ### ๐Ÿ“ฆ Package Test Results + + | Package | Status | + |---------|--------| + EOF # Extract package results - grep "^ok\|^FAIL" test-output.log | while read line; do + grep "^ok\|^FAIL" test-output.log | while read -r line; do if [[ $line == ok* ]]; then - pkg=$(echo $line | awk '{print $2}') + pkg=$(echo "$line" | awk '{print $2}') echo "| $pkg | โœ… PASS |" >> $GITHUB_STEP_SUMMARY elif [[ $line == FAIL* ]]; then - pkg=$(echo $line | awk '{print $2}') + pkg=$(echo "$line" | awk '{print $2}') echo "| $pkg | โŒ FAIL |" >> $GITHUB_STEP_SUMMARY fi done @@ -101,19 +119,24 @@ jobs: # Add detailed results if tests failed if [ $TEST_STATUS -ne 0 ]; then - echo "### โŒ Failed Tests Details" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOF' + ### โŒ Failed Tests Details + ``` + EOF grep -A 10 "--- FAIL:" test-output.log | head -100 >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOF' + ``` + + EOF fi # Set outputs for other steps - echo "test-status=$TEST_STATUS" >> $GITHUB_OUTPUT - echo "total-tests=$TOTAL_TESTS" >> $GITHUB_OUTPUT - echo "passed-tests=$PASSED_TESTS" >> $GITHUB_OUTPUT - echo "failed-tests=$FAILED_TESTS" >> $GITHUB_OUTPUT + cat >> $GITHUB_OUTPUT << EOF + test-status=$TEST_STATUS + total-tests=$TOTAL_TESTS + passed-tests=$PASSED_TESTS + failed-tests=$FAILED_TESTS + EOF # Exit with the original test status exit $TEST_STATUS @@ -124,33 +147,31 @@ jobs: if [ -f coverage/coverage.out ]; then COVERAGE=$(go tool cover -func=coverage/coverage.out | grep total | awk '{print $3}') - echo "## ๐Ÿ“Š Code Coverage (Go ${{ matrix.go }})" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Total Coverage: $COVERAGE**" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << EOF + ## ๐Ÿ“Š Code Coverage (Go ${{ matrix.go }}) - # Add coverage by package - echo "
" >> $GITHUB_STEP_SUMMARY - echo "Click to expand ๐Ÿ“‹ Coverage by Package details" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Package | Coverage |" >> $GITHUB_STEP_SUMMARY - echo "|---------|----------|" >> $GITHUB_STEP_SUMMARY + **Total Coverage: $COVERAGE** + +
+ Click to expand ๐Ÿ“‹ Coverage by Package details + + | Package | Coverage | + | ------- | -------- | + EOF # Create temporary file for package coverage aggregation temp_coverage=$(mktemp) # Extract package-level coverage data - go tool cover -func=coverage/coverage.out | grep -v total | while read line; do + go tool cover -func=coverage/coverage.out | grep -v total | while read -r line; do if [[ $line == *".go:"* ]]; then # Extract package path from file path (everything before the filename) filepath=$(echo "$line" | awk '{print $1}') - pkg_path=$(dirname "$filepath" | sed 's|github.com/kjanat/articulate-parser/||' | sed 's|^\./||') + pkg_path=$(dirname "$filepath" | sed 's|github.com/kjanat/articulate-parser/||; s|^\./||') coverage=$(echo "$line" | awk '{print $3}' | sed 's/%//') # Use root package if no subdirectory - if [[ "$pkg_path" == "." || "$pkg_path" == "" ]]; then - pkg_path="root" - fi + [[ "$pkg_path" == "." || "$pkg_path" == "" ]] && pkg_path="root" echo "$pkg_path $coverage" >> "$temp_coverage" fi @@ -158,7 +179,7 @@ jobs: # Aggregate coverage by package (average) awk '{ - packages[$1] += $2; + packages[$1] += $2 counts[$1]++ } END { @@ -166,17 +187,21 @@ jobs: avg = packages[pkg] / counts[pkg] printf "| %s | %.1f%% |\n", pkg, avg } - }' $temp_coverage | sort >> $GITHUB_STEP_SUMMARY + }' "$temp_coverage" | sort >> $GITHUB_STEP_SUMMARY - rm -f $temp_coverage + rm -f "$temp_coverage" - echo "
" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOF' - echo "" >> $GITHUB_STEP_SUMMARY +
+ + EOF else - echo "## โš ๏ธ Coverage Report" >> $GITHUB_STEP_SUMMARY - echo "No coverage file generated" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOF' + ## โš ๏ธ Coverage Report + No coverage file generated + + EOF fi - name: Upload test artifacts @@ -191,8 +216,11 @@ jobs: - name: Run linters run: | - echo "## ๐Ÿ” Static Analysis (Go ${{ matrix.go }})" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + # Initialize summary + cat >> $GITHUB_STEP_SUMMARY << EOF + ## ๐Ÿ” Static Analysis (Go ${{ matrix.go }}) + + EOF # Run go vet VET_OUTPUT=$(task lint:vet 2>&1 || echo "") @@ -201,11 +229,13 @@ jobs: if [ $VET_STATUS -eq 0 ]; then echo "โœ… **go vet:** No issues found" >> $GITHUB_STEP_SUMMARY else - echo "โŒ **go vet:** Issues found" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOF' + โŒ **go vet:** Issues found + + ``` + EOF echo "$VET_OUTPUT" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY fi echo "" >> $GITHUB_STEP_SUMMARY @@ -216,32 +246,33 @@ jobs: if [ $FMT_STATUS -eq 0 ]; then echo "โœ… **go fmt:** All files properly formatted" >> $GITHUB_STEP_SUMMARY else - echo "โŒ **go fmt:** Files need formatting" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOF' + โŒ **go fmt:** Files need formatting + + ``` + EOF echo "$FMT_OUTPUT" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY fi # Exit with error if any linter failed - if [ $VET_STATUS -ne 0 ] || [ $FMT_STATUS -ne 0 ]; then - exit 1 - fi + [ $VET_STATUS -eq 0 ] && [ $FMT_STATUS -eq 0 ] || exit 1 - name: Job Summary if: always() run: | - echo "## ๐Ÿ“‹ Job Summary (Go ${{ matrix.go }})" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Step | Status |" >> $GITHUB_STEP_SUMMARY - echo "|------|--------|" >> $GITHUB_STEP_SUMMARY - echo "| Dependencies | โœ… Success |" >> $GITHUB_STEP_SUMMARY - echo "| Build | โœ… Success |" >> $GITHUB_STEP_SUMMARY - echo "| Tests | ${{ steps.test.outcome == 'success' && 'โœ… Success' || 'โŒ Failed' }} |" >> $GITHUB_STEP_SUMMARY - echo "| Coverage | ${{ job.status == 'success' && 'โœ… Generated' || 'โš ๏ธ Partial' }} |" >> $GITHUB_STEP_SUMMARY - echo "| Static Analysis | ${{ job.status == 'success' && 'โœ… Clean' || 'โŒ Issues' }} |" >> $GITHUB_STEP_SUMMARY - echo "| Code Formatting | ${{ job.status == 'success' && 'โœ… Clean' || 'โŒ Issues' }} |" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOF' + ## ๐Ÿ“‹ Job Summary (Go ${{ matrix.go }}) + + | Step | Status | + | --------------- | --------------------------------------------------------------- | + | Dependencies | Success | + | Build | Success | + | Tests | ${{ steps.test.outcome == 'success' && 'Success' || 'Failed' }} | + | Coverage | ${{ job.status == 'success' && 'Generated' || 'Partial' }} | + | Static Analysis | ${{ job.status == 'success' && 'Clean' || 'Issues' }} | + | Code Formatting | ${{ job.status == 'success' && 'Clean' || 'Issues' }} | + EOF - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v5 @@ -285,16 +316,18 @@ jobs: - name: Test Docker image using Task run: | - echo "## ๐Ÿงช Docker Image Tests" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOF' + ## ๐Ÿงช Docker Image Tests + + EOF # Run Task docker test task docker:test echo "**Testing help command:**" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo '```terminaloutput' >> $GITHUB_STEP_SUMMARY docker run --rm articulate-parser:latest --help >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY # Test image size @@ -324,8 +357,11 @@ jobs: permissions: contents: read packages: write - needs: ['test'] - if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/feature/docker')) + needs: [test, docker-test, dependency-review] + if: | + github.event_name == 'push' && (github.ref == 'refs/heads/master' || + github.ref == 'refs/heads/develop' || + startsWith(github.ref, 'refs/heads/feature/docker')) steps: - name: Checkout repository uses: actions/checkout@v5 @@ -400,35 +436,39 @@ jobs: - name: Generate Docker summary run: | - echo "## ๐Ÿณ Docker Build Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Image:** \`ghcr.io/${{ github.repository }}\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Tags built:**" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Features:**" >> $GITHUB_STEP_SUMMARY - echo "- **Platforms:** linux/amd64, linux/arm64, linux/arm/v7, linux/386, linux/ppc64le, linux/s390x" >> $GITHUB_STEP_SUMMARY - echo "- **Architecture optimization:** โœ… Native compilation for each platform" >> $GITHUB_STEP_SUMMARY - echo "- **Multi-arch image description:** โœ… Enabled" >> $GITHUB_STEP_SUMMARY - echo "- **SBOM (Software Bill of Materials):** โœ… Generated" >> $GITHUB_STEP_SUMMARY - echo "- **Provenance attestation:** โœ… Generated" >> $GITHUB_STEP_SUMMARY - echo "- **Security scanning:** Ready for vulnerability analysis" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Usage:**" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY - echo "# Pull the image" >> $GITHUB_STEP_SUMMARY - echo "docker pull ghcr.io/${{ github.repository }}:latest" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "# Run with help" >> $GITHUB_STEP_SUMMARY - echo "docker run --rm ghcr.io/${{ github.repository }}:latest --help" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "# Process a local file (mount current directory)" >> $GITHUB_STEP_SUMMARY - echo "docker run --rm -v \$(pwd):/workspace ghcr.io/${{ github.repository }}:latest /workspace/input.json markdown /workspace/output.md" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOF' + ## ๐Ÿณ Docker Build Summary + + **Image:** `ghcr.io/${{ github.repository }}` + + **Tags built:** + + ```text + ${{ steps.meta.outputs.tags }} + ``` + + **Features:** + - **Platforms:** linux/amd64, linux/arm64, linux/arm/v7, linux/386, linux/ppc64le, linux/s390x + - **Architecture optimization:** Native compilation for each platform + - **Multi-arch image description:** Enabled + - **SBOM (Software Bill of Materials):** Generated + - **Provenance attestation:** Generated + - **Security scanning:** Ready for vulnerability analysis + + **Usage:** + + ```bash + # Pull the image + docker pull ghcr.io/${{ github.repository }}:latest + + # Run with help + docker run --rm ghcr.io/${{ github.repository }}:latest --help + + # Process a local file (mount current directory) + docker run --rm -v $(pwd):/workspace ghcr.io/${{ github.repository }}:latest /workspace/input.json markdown /workspace/output.md + ``` + + EOF # Security and quality analysis workflows codeql-analysis: diff --git a/.gitignore b/.gitignore index 1aadfdb..0627806 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,5 @@ main_coverage **/*.local.* .claude/ + +NUL diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..8c52b25 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,381 @@ +# golangci-lint configuration for articulate-parser +# https://golangci-lint.run/usage/configuration/ + +version: "2" + +# Options for analysis running +run: + # Timeout for total work + timeout: 5m + + # Skip directories (not allowed in config v2, will use issues exclude instead) + + # Go version + go: "1.24" + + # Include test files + tests: true + + # Use Go module mode + modules-download-mode: readonly + +# Output configuration +output: + # Format of output + formats: + text: + print-issued-lines: true + print-linter-name: true + + # Sort results + sort-order: + - linter + - severity + - file + + # Show statistics + show-stats: true + +# Issues configuration +issues: + # Maximum issues count per one linter + max-issues-per-linter: 0 + + # Maximum count of issues with the same text + max-same-issues: 3 + + # Show only new issues + new: false + + # Fix found issues (if linter supports) + fix: false + +# Formatters configuration +formatters: + enable: + - gofmt + - goimports + - gofumpt + settings: + # gofmt settings + gofmt: + simplify: true + + # goimports settings + goimports: + local-prefixes: + - github.com/kjanat/articulate-parser + + # gofumpt settings + gofumpt: + module-path: github.com/kjanat/articulate-parser + extra-rules: true + +# Linters configuration +linters: + # Default set of linters + default: none + + # Enable specific linters + enable: + # Default/standard linters + - errcheck # Check for unchecked errors + - govet # Go vet + - ineffassign # Detect ineffectual assignments + - staticcheck # Staticcheck + - unused # Find unused code + + # Code quality + - revive # Fast, configurable linter + - gocritic # Opinionated Go source code linter + - godot # Check comment periods + - godox # Detect TODO/FIXME comments + - gocognit # Cognitive complexity + - gocyclo # Cyclomatic complexity + - funlen # Function length + - maintidx # Maintainability index + + # Security + - gosec # Security problems + + # Performance + - prealloc # Find slice preallocation opportunities + - bodyclose # Check HTTP response body closed + + # Style and formatting + - goconst # Find repeated strings + - misspell # Find misspellings + - whitespace # Find unnecessary blank lines + - unconvert # Remove unnecessary type conversions + - dupword # Check for duplicate words + + # Error handling + - errorlint # Error handling improvements + - wrapcheck # Check error wrapping + + # Testing + - testifylint # Testify usage + - tparallel # Detect improper t.Parallel() usage + - thelper # Detect test helpers without t.Helper() + + # Best practices + - exhaustive # Check exhaustiveness of enum switches + - nolintlint # Check nolint directives + - nakedret # Find naked returns + - nilnil # Check for redundant nil checks + - noctx # Check sending HTTP requests without context + - contextcheck # Check context propagation + - asciicheck # Check for non-ASCII identifiers + - bidichk # Check for dangerous unicode sequences + - durationcheck # Check for multiplied durations + - makezero # Find slice declarations with non-zero length + - nilerr # Find code returning nil with non-nil error + - predeclared # Find code shadowing predeclared identifiers + - promlinter # Check Prometheus metrics naming + - reassign # Check reassignment of package variables + - usestdlibvars # Use variables from stdlib + - wastedassign # Find wasted assignments + + # Documentation + - godoclint # Check godoc comments + + # New + - modernize # Suggest simplifications using new Go features + + # Exclusion rules for linters + exclusions: + rules: + # Exclude some linters from test files + - path: _test\.go + linters: + - gosec + - dupl + - errcheck + - goconst + - funlen + - goerr113 + + # Exclude benchmarks from some linters + - path: _bench_test\.go + linters: + - gosec + - dupl + - errcheck + - goconst + - funlen + - goerr113 + - wrapcheck + + # Exclude example tests + - path: _example_test\.go + linters: + - gosec + - errcheck + - funlen + - goerr113 + - wrapcheck + - revive + + # Exclude linters for main.go + - path: ^main\.go$ + linters: + - forbidigo + + # Exclude certain linters for generated files + - path: internal/version/version.go + linters: + - gochecknoglobals + - gochecknoinits + + # Allow fmt.Print* in main package + - path: ^main\.go$ + text: "use of fmt.Print" + linters: + - forbidigo + + # Exclude common false positives + - text: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked" + linters: + - errcheck + + # Exclude error wrapping suggestions for well-known errors + - text: "non-wrapping format verb for fmt.Errorf" + linters: + - errorlint + + # Linters settings + settings: + # errcheck settings + errcheck: + check-type-assertions: true + check-blank: false + + # govet settings + govet: + enable-all: true + disable: + - fieldalignment # Too many false positives + - shadow # Can be noisy + + # goconst settings + goconst: + min-len: 3 + min-occurrences: 3 + + # godot settings + godot: + scope: toplevel + exclude: + - "^fixme:" + - "^todo:" + capital: true + period: true + + # godox settings + godox: + keywords: + - TODO + - FIXME + - HACK + - BUG + - XXX + + # misspell settings + misspell: + locale: US + + # funlen settings + funlen: + lines: 100 + statements: 50 + + # gocognit settings + gocognit: + min-complexity: 20 + + # gocyclo settings + gocyclo: + min-complexity: 15 + + # gocritic settings + gocritic: + enabled-tags: + - diagnostic + - style + - performance + - experimental + disabled-checks: + - ifElseChain + - singleCaseSwitch + - commentedOutCode + settings: + hugeParam: + sizeThreshold: 512 + rangeValCopy: + sizeThreshold: 512 + + # gosec settings + gosec: + severity: medium + confidence: medium + excludes: + - G104 # Handled by errcheck + - G304 # File path provided as taint input + + # revive settings + revive: + severity: warning + rules: + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: empty-block + - name: error-naming + - name: error-return + - name: error-strings + - name: errorf + - name: exported + - name: if-return + - name: increment-decrement + - name: indent-error-flow + - name: package-comments + - name: range + - name: receiver-naming + - name: time-naming + - name: unexported-return + - name: var-declaration + - name: var-naming + + # errorlint settings + errorlint: + errorf: true + errorf-multi: true + asserts: true + comparison: true + + # wrapcheck settings + wrapcheck: + ignore-sigs: + - .Errorf( + - errors.New( + - errors.Unwrap( + - errors.Join( + - .WithMessage( + - .WithMessagef( + - .WithStack( + ignore-package-globs: + - github.com/kjanat/articulate-parser/* + + # exhaustive settings + exhaustive: + check: + - switch + - map + default-signifies-exhaustive: true + + # nolintlint settings + nolintlint: + allow-unused: false + require-explanation: true + require-specific: true + + # stylecheck settings + staticcheck: + checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"] + + # maintidx settings + maintidx: + under: 20 + + # testifylint settings + testifylint: + enable-all: true + disable: + - float-compare + + # thelper settings + thelper: + test: + first: true + name: true + begin: true + benchmark: + first: true + name: true + begin: true + +# Severity rules +severity: + default: warning + rules: + - linters: + - gosec + severity: error + - linters: + - errcheck + - staticcheck + severity: error + - linters: + - godox + severity: info