Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ linters:
- whitespace
- wrapcheck
settings:
dupl:
# Token threshold for duplication detection
# Lower = more sensitive (catches smaller duplicates)
# Default is 150. We use 75 for lint (blocks CI), `mise run dup` uses 50 (advisory)
threshold: 75
errcheck:
check-type-assertions: true
check-blank: true
Expand Down
21 changes: 21 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ Integration tests use the `//go:build integration` build tag and are located in
mise run fmt && mise run lint
```

### Code Duplication Prevention

Before implementing Go code, use `/go:discover-related` to find existing utilities and patterns that might be reusable.

**Check for duplication:**
```bash
mise run dup # Comprehensive check (threshold 50) with summary
mise run dup:staged # Check only staged files
mise run lint # Normal lint includes dupl at threshold 75 (new issues only)
mise run lint:full # All issues at threshold 75
```

**Tiered thresholds:**
- **75 tokens** (lint/CI) - Blocks on serious duplication (~20+ lines)
- **50 tokens** (dup) - Advisory, catches smaller patterns (~10+ lines)

When duplication is found:
1. Check if a helper already exists in `common.go` or nearby utility files
2. If not, consider extracting the duplicated logic to a shared helper
3. If duplication is intentional (e.g., test setup), add a `//nolint:dupl` comment with explanation

## Code Patterns

### Error Handling
Expand Down
54 changes: 53 additions & 1 deletion mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,56 @@ mkdir completions
for sh in bash zsh fish; do
go run ./cmd/entire/main.go completion "$sh" >"completions/entire.$sh"
done
"""
"""

[tasks.dup]
description = "Check for code duplication (threshold 50, with summary)"
run = """
#!/usr/bin/env bash
set -euo pipefail

# Create temp files with proper extensions (works on both Linux and macOS)
tmpdir=$(mktemp -d)
config="$tmpdir/config.yaml"
json_out="$tmpdir/output.json"

cat > "$config" << 'YAML'
version: "2"
linters:
default: none
enable: [dupl]
settings:
dupl:
threshold: 50
YAML

# Run with JSON output for summary, text output for details
golangci-lint run -c "$config" --new=false --max-issues-per-linter=0 --max-same-issues=0 \
--output.json.path="$json_out" --output.text.path=/dev/stderr ./... 2>&1 || true

# Print summary grouped by file
echo ""
echo "=== Duplication Summary (by file) ==="
if command -v jq &>/dev/null && [ -s "$json_out" ]; then
jq -r '.Issues // [] | group_by(.Pos.Filename) | map({file: (.[0].Pos.Filename | split("/") | .[-1]), count: length}) | sort_by(-.count) | .[] | " " + (.count|tostring) + " " + .file' "$json_out" 2>/dev/null || echo " (no issues)"
else
echo " (install jq for summary)"
fi

rm -rf "$tmpdir"
"""

[tasks."dup:staged"]
description = "Check duplication in staged files only (threshold 75, same as CI)"
run = """
#!/usr/bin/env bash
set -euo pipefail

# Get staged Go files, preserving paths with spaces using null delimiters throughout
if ! git diff --cached --name-only -z --diff-filter=ACM | grep -z '\\.go$' | grep -zq .; then
echo "No staged Go files to check"
exit 0
fi
echo "Checking staged files for duplication..."
git diff --cached --name-only -z --diff-filter=ACM | grep -z '\\.go$' | xargs -0 golangci-lint run --enable-only dupl --new=false --max-issues-per-linter=0 --max-same-issues=0
"""