diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b540e20..454315d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,10 +31,10 @@ jobs: run: go build ./... - name: Lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v9 with: install-mode: goinstall - version: v1.64.8 + version: v2.8.0 args: --timeout=5m - name: Test diff --git a/.golangci.yml b/.golangci.yml index 627b1f6..3d9214a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,65 +1,75 @@ +version: "2" run: - timeout: 5m tests: true - -issues: - exclude-dirs: - - vendor - exclude-files: - - ".*\\.pb\\.go" - - ".*\\.gen\\.go" - exclude-use-default: false - max-issues-per-linter: 0 - max-same-issues: 0 - exclude-rules: - - path: _test\.go - linters: - - goconst - - errorlint - - errcheck - - linters: - - goconst - source: "case \".*\":|return \".*\"|== \".*\"" - - linters: - - staticcheck - text: "SA1019.*deprecated" - path: "custom/.*/.*\\.go" - linters: - disable-all: true + default: none enable: + - bodyclose - errcheck - - gosimple + - errorlint + - goconst + - gosec - govet - ineffassign + - misspell + - noctx - staticcheck - - typecheck + - unconvert - unused + settings: + goconst: + min-len: 5 + min-occurrences: 5 + gosec: + excludes: + - G104 + - G204 + - G304 + - G115 + - G301 + - G101 + - G306 + exclusions: + generated: lax + rules: + - linters: + - errcheck + - errorlint + - goconst + path: _test\.go + - linters: + - goconst + source: case ".*":|return ".*"|== ".*" + - linters: + - staticcheck + path: custom/.*/.*\.go + text: SA1019.*deprecated + paths: + - .*\.pb\.go + - .*\.gen\.go + - vendor + - third_party$ + - builtin$ + - examples$ +issues: + max-issues-per-linter: 0 + max-same-issues: 0 +formatters: + enable: - gofmt - goimports - - misspell - - errorlint - - unconvert - - goconst - # Security linters - - gosec - - bodyclose - - noctx - -linters-settings: - goimports: - local-prefixes: github.com/clawscli/claws - gofmt: - simplify: true - goconst: - min-len: 5 - min-occurrences: 5 - gosec: - excludes: - - G104 # Audit errors not checked (covered by errcheck) - - G204 # Subprocess with variable - intentional for CLI actions - - G304 # File path from variable - intentional for log/config - - G115 # int->int32 conversion - pageSize always < 1000 - - G301 # Directory permissions - test only - - G101 # Hardcoded credentials - test examples only - - G306 # WriteFile permissions 0644 - standard for generated Go source + settings: + gofmt: + simplify: true + goimports: + local-prefixes: + - github.com/clawscli/claws + exclusions: + generated: lax + paths: + - .*\.pb\.go + - .*\.gen\.go + - vendor + - third_party$ + - builtin$ + - examples$ diff --git a/custom/apprunner/services/render.go b/custom/apprunner/services/render.go index eb978a7..0fde7c3 100644 --- a/custom/apprunner/services/render.go +++ b/custom/apprunner/services/render.go @@ -109,11 +109,12 @@ func (r *ServiceRenderer) RenderDetail(resource dao.Resource) string { if sourceType := svc.SourceType(); sourceType != "" { d.Section("Source Configuration") d.Field("Source Type", sourceType) - if sourceType == "IMAGE_REPOSITORY" { + switch sourceType { + case "IMAGE_REPOSITORY": if img := svc.ImageIdentifier(); img != "" { d.Field("Image", img) } - } else if sourceType == "CODE_REPOSITORY" { + case "CODE_REPOSITORY": if repo := svc.RepositoryUrl(); repo != "" { d.Field("Repository URL", repo) } diff --git a/custom/s3/buckets/dao.go b/custom/s3/buckets/dao.go index 724cf00..2063f00 100644 --- a/custom/s3/buckets/dao.go +++ b/custom/s3/buckets/dao.go @@ -218,7 +218,7 @@ func (d *BucketDAO) fetchTags(ctx context.Context, client *s3.Client, bucket str tags[*tag.Key] = *tag.Value } } - r.BaseResource.Tags = tags + r.Tags = tags } func (d *BucketDAO) Delete(ctx context.Context, id string) error { diff --git a/internal/action/exec_with_header.go b/internal/action/exec_with_header.go index a0a696b..d84593e 100644 --- a/internal/action/exec_with_header.go +++ b/internal/action/exec_with_header.go @@ -2,6 +2,7 @@ package action import ( "cmp" + "context" "fmt" "io" "os" @@ -66,7 +67,7 @@ func (e *SimpleExec) Run() error { stderr = os.Stderr } - cmd := exec.Command("/bin/sh", "-c", e.Command) + cmd := exec.CommandContext(context.Background(), "/bin/sh", "-c", e.Command) cmd.Stdin = stdin cmd.Stdout = stdout cmd.Stderr = stderr @@ -164,7 +165,7 @@ func (e *ExecWithHeader) Run() error { return ErrEmptyCommand } - cmd := exec.Command("/bin/sh", "-c", e.Command) + cmd := exec.CommandContext(context.Background(), "/bin/sh", "-c", e.Command) cmd.Stdin = stdin cmd.Stdout = stdout cmd.Stderr = stderr diff --git a/internal/app/pty_test.go b/internal/app/pty_test.go index d3a3bbe..2cd0bb9 100644 --- a/internal/app/pty_test.go +++ b/internal/app/pty_test.go @@ -2,6 +2,7 @@ package app import ( "bytes" + "context" "os" "os/exec" "strings" @@ -14,14 +15,14 @@ import ( // TestPTYEscHandling runs the actual app in a PTY and tests esc handling func TestPTYEscHandling(t *testing.T) { // Build the app first - buildCmd := exec.Command("go", "build", "-o", "/tmp/claws-test", "../../cmd/claws") + buildCmd := exec.CommandContext(context.Background(), "go", "build", "-o", "/tmp/claws-test", "../../cmd/claws") if out, err := buildCmd.CombinedOutput(); err != nil { t.Fatalf("Failed to build: %v\n%s", err, out) } defer func() { _ = os.Remove("/tmp/claws-test") }() // Run the app in a PTY - cmd := exec.Command("/tmp/claws-test") + cmd := exec.CommandContext(context.Background(), "/tmp/claws-test") ptmx, err := pty.Start(cmd) if err != nil { t.Fatalf("Failed to start PTY: %v", err) diff --git a/internal/view/chat_overlay.go b/internal/view/chat_overlay.go index e221001..7081178 100644 --- a/internal/view/chat_overlay.go +++ b/internal/view/chat_overlay.go @@ -519,7 +519,7 @@ func (c *ChatOverlay) handleToolExecute(msg chatToolExecuteMsg) (tea.Model, tea. for _, tu := range msg.toolUses { // Check tool call limit before executing each tool if c.toolCallCount >= maxCalls { - c.err = fmt.Errorf("Tool call limit reached (%d calls). Start new query to continue.", maxCalls) + c.err = fmt.Errorf("tool call limit reached (%d calls), start new query to continue", maxCalls) c.isStreaming = false c.updateViewport() return c, nil diff --git a/internal/view/dashboard_view.go b/internal/view/dashboard_view.go index 7f43f29..202879a 100644 --- a/internal/view/dashboard_view.go +++ b/internal/view/dashboard_view.go @@ -487,13 +487,14 @@ func (d *DashboardView) ViewString() string { opsFocusRow := -1 secFocusRow := -1 optFocusRow := -1 - if d.focusedPanel == panelCost { + switch d.focusedPanel { + case panelCost: costFocusRow = d.focusedRow - } else if d.focusedPanel == panelOperations { + case panelOperations: opsFocusRow = d.focusedRow - } else if d.focusedPanel == panelSecurity { + case panelSecurity: secFocusRow = d.focusedRow - } else if d.focusedPanel == panelOptimization { + case panelOptimization: optFocusRow = d.focusedRow } diff --git a/internal/view/dashboard_view_panels.go b/internal/view/dashboard_view_panels.go index a27b115..0ad1ba8 100644 --- a/internal/view/dashboard_view_panels.go +++ b/internal/view/dashboard_view_panels.go @@ -177,9 +177,10 @@ func (d *DashboardView) renderSecurityContent(contentWidth, contentHeight int, f } else if len(d.secItems) > 0 { var critical, high int for _, item := range d.secItems { - if item.severity == "CRITICAL" { + switch item.severity { + case "CRITICAL": critical++ - } else if item.severity == "HIGH" { + case "HIGH": high++ } } diff --git a/internal/view/profile_selector.go b/internal/view/profile_selector.go index 4213c92..922337f 100644 --- a/internal/view/profile_selector.go +++ b/internal/view/profile_selector.go @@ -1,6 +1,7 @@ package view import ( + "context" "fmt" "io" "os/exec" @@ -231,7 +232,7 @@ type ssoLoginCmd struct { } func (s *ssoLoginCmd) Run() error { - cmd := exec.Command("aws", "sso", "login", "--profile", s.profileName) + cmd := exec.CommandContext(context.Background(), "aws", "sso", "login", "--profile", s.profileName) cmd.Stdin = s.stdin cmd.Stdout = s.stdout cmd.Stderr = s.stderr diff --git a/internal/view/table_style.go b/internal/view/table_style.go index 91b27ab..e3b134d 100644 --- a/internal/view/table_style.go +++ b/internal/view/table_style.go @@ -33,10 +33,10 @@ func NewTableStyleFunc(widths []int, cursor int) func(row, col int) lipgloss.Sty if col >= numCols { return ui.NoStyle() } - switch { - case row == table.HeaderRow: + switch row { + case table.HeaderRow: return headerStyles[col] - case row == cursor: + case cursor: return selectedStyles[col] default: return normalStyles[col] diff --git a/scripts/gen-imports/main.go b/scripts/gen-imports/main.go index 1f02458..76deb1d 100644 --- a/scripts/gen-imports/main.go +++ b/scripts/gen-imports/main.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "fmt" "go/format" "os" @@ -63,7 +64,7 @@ func main() { } func verifyBuild(projectRoot string) error { - cmd := exec.Command("go", "build", "./cmd/claws") + cmd := exec.CommandContext(context.Background(), "go", "build", "./cmd/claws") cmd.Dir = projectRoot cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr