Skip to content

Commit ea19c17

Browse files
chore(lint): add golangci-lint v2 with CI workflow
Configure golangci-lint v2.12.2 with a curated linter set (bodyclose, errorlint, gocritic, gosec, revive, etc.) plus gofumpt + goimports formatters. Add a GitHub Actions workflow that runs lint on push and PR. Tune rule suppressions for CLI idioms (tablewriter result discard, PAGER subprocess, urfave/cli int casts, proto Id/Url naming). Apply golangci-lint --fix across 59 files for whitespace, gofumpt, and staticcheck rewrites; tests still pass.
1 parent cc4b003 commit ea19c17

61 files changed

Lines changed: 266 additions & 88 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/lint.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Lint
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- '**'
10+
11+
permissions:
12+
contents: read
13+
pull-requests: read
14+
15+
jobs:
16+
golangci-lint:
17+
name: golangci-lint
18+
runs-on: ubuntu-latest
19+
timeout-minutes: 5
20+
steps:
21+
- uses: actions/checkout@v6
22+
- uses: actions/setup-go@v6
23+
with:
24+
go-version: stable
25+
cache: false
26+
- name: golangci-lint
27+
uses: golangci/golangci-lint-action@v9
28+
with:
29+
version: v2.12.2

.golangci.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
version: "2"
2+
3+
run:
4+
timeout: 5m
5+
modules-download-mode: readonly
6+
7+
linters:
8+
default: standard
9+
enable:
10+
- bodyclose
11+
- copyloopvar
12+
- errorlint
13+
- gocritic
14+
- gosec
15+
- misspell
16+
- nolintlint
17+
- revive
18+
- unconvert
19+
- unparam
20+
- usestdlibvars
21+
- whitespace
22+
23+
settings:
24+
errcheck:
25+
exclude-functions:
26+
- (*github.com/olekukonko/tablewriter.Table).Bulk
27+
- (*github.com/olekukonko/tablewriter.Table).Render
28+
- (*os/exec.Cmd).Wait
29+
- github.com/joho/godotenv.Load
30+
gocritic:
31+
enabled-tags:
32+
- diagnostic
33+
- performance
34+
- style
35+
disabled-checks:
36+
- appendCombine
37+
- commentedOutCode
38+
- hugeParam
39+
- ifElseChain
40+
- importShadow
41+
- paramTypeCombine
42+
- rangeValCopy
43+
- unlambda # test seams use this pattern
44+
- unnamedResult
45+
gosec:
46+
excludes:
47+
- G104 # duplicated by errcheck
48+
- G115 # int->int32 narrowing for proto/API fields is intentional
49+
revive:
50+
rules:
51+
- name: blank-imports
52+
- name: context-as-argument
53+
- name: context-keys-type
54+
- name: error-return
55+
- name: error-strings
56+
- name: error-naming
57+
- name: increment-decrement
58+
- name: range
59+
- name: receiver-naming
60+
- name: redefines-builtin-id
61+
- name: superfluous-else
62+
- name: time-naming
63+
- name: unexported-return
64+
disabled: true # CLI internals return unexported result types
65+
- name: unreachable-code
66+
- name: unused-parameter
67+
disabled: true
68+
- name: var-naming
69+
disabled: true # conflicts with generated proto identifiers (Id, Url, …)
70+
71+
exclusions:
72+
generated: lax
73+
presets:
74+
- comments
75+
- common-false-positives
76+
- legacy
77+
- std-error-handling
78+
rules:
79+
- path: _test\.go
80+
linters:
81+
- errcheck
82+
- gocritic
83+
- gosec
84+
- unparam
85+
- path: internal/cli/pager\.go
86+
linters:
87+
- gosec # G204: PAGER env var is the standard CLI pattern
88+
# int(cmd.Int(...)) idiom from urfave/cli/v3 and int64() casts of proto ints
89+
- linters:
90+
- unconvert
91+
text: unnecessary conversion
92+
93+
formatters:
94+
enable:
95+
- gofumpt
96+
- goimports
97+
settings:
98+
goimports:
99+
local-prefixes:
100+
- github.com/openstatusHQ/cli

cmd/docs/docs.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ package main
33
import (
44
"os"
55

6-
cmd "github.com/openstatusHQ/cli/internal/cmd"
76
docs "github.com/urfave/cli-docs/v3"
7+
8+
cmd "github.com/openstatusHQ/cli/internal/cmd"
89
)
910

1011
func main() {

cmd/openstatus/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package main
22

33
import (
4+
"log"
5+
46
"github.com/joho/godotenv"
7+
58
cmd "github.com/openstatusHQ/cli/internal/cmd"
6-
"log"
79
)
810

911
func main() {

internal/api/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"connectrpc.com/connect"
11+
1112
output "github.com/openstatusHQ/cli/internal/cli"
1213
)
1314

internal/auth/auth.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import (
55
"os"
66
"strings"
77

8-
"github.com/openstatusHQ/cli/internal/config"
98
clilib "github.com/urfave/cli/v3"
9+
10+
"github.com/openstatusHQ/cli/internal/config"
1011
)
1112

1213
// ResolveAccessToken extracts the access token from CLI flags or falls back to saved token.
@@ -40,7 +41,7 @@ func SaveToken(token string) error {
4041
if err != nil {
4142
return fmt.Errorf("failed to determine config directory: %w", err)
4243
}
43-
if err := os.MkdirAll(dir, 0700); err != nil {
44+
if err := os.MkdirAll(dir, 0o700); err != nil {
4445
return fmt.Errorf("failed to create config directory: %w", err)
4546
}
4647

@@ -64,7 +65,7 @@ func SaveToken(token string) error {
6465
os.Remove(tmpPath)
6566
return fmt.Errorf("failed to close temp file: %w", err)
6667
}
67-
if err := os.Chmod(tmpPath, 0600); err != nil {
68+
if err := os.Chmod(tmpPath, 0o600); err != nil {
6869
os.Remove(tmpPath)
6970
return fmt.Errorf("failed to set token file permissions: %w", err)
7071
}

internal/cli/output.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ func IsDebug() bool { return debugMode.Load() }
2626
func IsTerminal() bool {
2727
return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
2828
}
29+
2930
func IsStdinTerminal() bool {
3031
return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
3132
}
33+
3234
func IsStderrTerminal() bool {
3335
return isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())
3436
}

internal/cmd/app.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"os/signal"
77
"syscall"
88

9+
"github.com/urfave/cli/v3"
10+
911
output "github.com/openstatusHQ/cli/internal/cli"
1012
"github.com/openstatusHQ/cli/internal/login"
1113
"github.com/openstatusHQ/cli/internal/maintenance"
@@ -16,7 +18,6 @@ import (
1618
"github.com/openstatusHQ/cli/internal/statusreport"
1719
"github.com/openstatusHQ/cli/internal/terraform"
1820
"github.com/openstatusHQ/cli/internal/whoami"
19-
"github.com/urfave/cli/v3"
2021
)
2122

2223
func NewApp() *cli.Command {

internal/config/config_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"testing"
77

88
"github.com/google/go-cmp/cmp"
9+
910
"github.com/openstatusHQ/cli/internal/config"
1011
)
1112

@@ -24,7 +25,7 @@ func Test_ReadConfig(t *testing.T) {
2425
t.Fatal(err)
2526
}
2627

27-
if _, err := f.Write([]byte(configFile)); err != nil {
28+
if _, err := f.WriteString(configFile); err != nil {
2829
t.Fatal(err)
2930
}
3031
if err := f.Close(); err != nil {
@@ -60,7 +61,7 @@ func Test_ReadConfig(t *testing.T) {
6061
t.Fatal(err)
6162
}
6263

63-
if _, err := f.Write([]byte("invalid: yaml: content: [")); err != nil {
64+
if _, err := f.WriteString("invalid: yaml: content: ["); err != nil {
6465
t.Fatal(err)
6566
}
6667
if err := f.Close(); err != nil {
@@ -78,12 +79,12 @@ func Test_ReadConfig_NoStatePollution(t *testing.T) {
7879
dir := t.TempDir()
7980

8081
file1 := filepath.Join(dir, "config1.yaml")
81-
if err := os.WriteFile(file1, []byte("tests:\n ids:\n - 1\n - 2\n - 3\n"), 0600); err != nil {
82+
if err := os.WriteFile(file1, []byte("tests:\n ids:\n - 1\n - 2\n - 3\n"), 0o600); err != nil {
8283
t.Fatal(err)
8384
}
8485

8586
file2 := filepath.Join(dir, "config2.yaml")
86-
if err := os.WriteFile(file2, []byte("tests:\n ids:\n - 4\n - 5\n - 6\n"), 0600); err != nil {
87+
if err := os.WriteFile(file2, []byte("tests:\n ids:\n - 4\n - 5\n - 6\n"), 0o600); err != nil {
8788
t.Fatal(err)
8889
}
8990

internal/config/lock.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,15 @@ type Lock struct {
1717
type MonitorsLock map[string]Lock
1818

1919
func ReadLockFile(filename string) (MonitorsLock, error) {
20-
2120
var out MonitorsLock
2221
if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
2322
return MonitorsLock{}, nil
2423
}
2524

2625
file := file.Provider(filename)
27-
var k = koanf.New(".")
26+
k := koanf.New(".")
2827

2928
err := k.Load(file, yaml.Parser())
30-
3129
if err != nil {
3230
return nil, err
3331
}
@@ -41,5 +39,4 @@ func ReadLockFile(filename string) (MonitorsLock, error) {
4139
}
4240

4341
return out, nil
44-
4542
}

0 commit comments

Comments
 (0)