Skip to content

Commit d6c5b02

Browse files
committed
chore(lint): migrate golangci-lint config to v2 + fix repo-wide findings
CI lint has been red on every branch since the action's 'latest' moved to golangci-lint v2, which rejects the v1 config schema (and the last v1 release can't even run against Go 1.26). Migrated with 'golangci-lint migrate', then tuned: - exhaustive: default-signifies-exhaustive (all flagged switches already had an explicit default) - drop goconst (pure noise: "true", "error", k8s condition strings) - govet: disable shadow ('if err :=' inside an err scope is idiomatic) - gocritic: disable hugeParam (controller-runtime passes specs by value) - gochecknoinits excluded for api/ and cmd/ (kubebuilder scheme registration pattern), gocyclo excluded for Reconcile loops - errcheck back to defaults (check-blank flagged deliberate discards) Code fixes: gofmt x4, misspell x4, QF1008 embedded selector, named results on JWTValidator.Validate, nolint on the scaffolded scheme.Builder. golangci-lint v2.12.2 now reports 0 issues.
1 parent 8ddcae3 commit d6c5b02

10 files changed

Lines changed: 139 additions & 131 deletions

File tree

.golangci.yml

Lines changed: 107 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,26 @@
1-
# golangci-lint configuration
1+
# golangci-lint v2 configuration
22
# https://golangci-lint.run/usage/configuration/
3-
3+
version: "2"
44
run:
5-
timeout: 5m
65
issues-exit-code: 1
76
tests: true
8-
97
output:
108
formats:
11-
- format: colored-line-number
12-
print-issued-lines: true
13-
print-linter-name: true
14-
9+
text:
10+
path: stdout
11+
print-linter-name: true
12+
print-issued-lines: true
1513
linters:
1614
enable:
17-
# Default linters
18-
- errcheck
19-
- gosimple
20-
- govet
21-
- ineffassign
22-
- staticcheck
23-
- unused
24-
# Additional linters
2515
- bodyclose
2616
- dogsled
2717
- dupl
2818
- errorlint
2919
- exhaustive
30-
- exportloopref
3120
- gochecknoinits
32-
- goconst
3321
- gocritic
3422
- gocyclo
3523
- godot
36-
- gofmt
37-
- goimports
3824
- goprintffuncname
3925
- gosec
4026
- misspell
@@ -47,92 +33,113 @@ linters:
4733
- revive
4834
- rowserrcheck
4935
- sqlclosecheck
50-
- stylecheck
36+
- staticcheck
5137
- tparallel
5238
- unconvert
5339
- unparam
5440
- whitespace
55-
56-
linters-settings:
57-
errcheck:
58-
check-type-assertions: true
59-
check-blank: true
60-
61-
govet:
62-
enable-all: true
63-
disable:
64-
- fieldalignment
65-
66-
gocyclo:
67-
min-complexity: 15
68-
69-
goconst:
70-
min-len: 3
71-
min-occurrences: 3
72-
73-
misspell:
74-
locale: US
75-
76-
revive:
41+
settings:
42+
dupl:
43+
threshold: 150
44+
exhaustive:
45+
# A switch with a default branch has made its catch-all explicit;
46+
# don't also require every enum member to be spelled out.
47+
default-signifies-exhaustive: true
48+
gocritic:
49+
disabled-checks:
50+
- dupImport
51+
- ifElseChain
52+
- octalLiteral
53+
- whyNoLint
54+
- wrapperFunc
55+
# controller-runtime passes spec structs by value all over; flagging
56+
# every 160-byte struct copy is noise for an operator codebase.
57+
- hugeParam
58+
enabled-tags:
59+
- diagnostic
60+
- experimental
61+
- opinionated
62+
- performance
63+
- style
64+
gocyclo:
65+
min-complexity: 20
66+
gosec:
67+
excludes:
68+
- G104
69+
- G304
70+
govet:
71+
disable:
72+
- fieldalignment
73+
# `if err := f(); err != nil` inside a scope that already has an err
74+
# is idiomatic Go; shadow flags all of them.
75+
- shadow
76+
enable-all: true
77+
misspell:
78+
locale: US
79+
revive:
80+
rules:
81+
- name: blank-imports
82+
- name: context-as-argument
83+
- name: context-keys-type
84+
- name: dot-imports
85+
- name: error-return
86+
- name: error-strings
87+
- name: error-naming
88+
- name: exported
89+
- name: if-return
90+
- name: increment-decrement
91+
- name: var-naming
92+
- name: var-declaration
93+
- name: package-comments
94+
- name: range
95+
- name: receiver-naming
96+
- name: time-naming
97+
- name: unexported-return
98+
- name: indent-error-flow
99+
- name: errorf
100+
exclusions:
101+
generated: lax
102+
presets:
103+
- comments
104+
- common-false-positives
105+
- legacy
106+
- std-error-handling
77107
rules:
78-
- name: blank-imports
79-
- name: context-as-argument
80-
- name: context-keys-type
81-
- name: dot-imports
82-
- name: error-return
83-
- name: error-strings
84-
- name: error-naming
85-
- name: exported
86-
- name: if-return
87-
- name: increment-decrement
88-
- name: var-naming
89-
- name: var-declaration
90-
- name: package-comments
91-
- name: range
92-
- name: receiver-naming
93-
- name: time-naming
94-
- name: unexported-return
95-
- name: indent-error-flow
96-
- name: errorf
97-
98-
gosec:
99-
excludes:
100-
- G104 # Audit errors not checked
101-
- G304 # File path provided as taint input
102-
103-
dupl:
104-
threshold: 150
105-
106-
gocritic:
107-
enabled-tags:
108-
- diagnostic
109-
- experimental
110-
- opinionated
111-
- performance
112-
- style
113-
disabled-checks:
114-
- dupImport
115-
- ifElseChain
116-
- octalLiteral
117-
- whyNoLint
118-
- wrapperFunc
119-
108+
- linters:
109+
- dogsled
110+
- dupl
111+
- errcheck
112+
- gocyclo
113+
- gosec
114+
path: _test\.go
115+
# kubebuilder scaffolds init() for scheme registration in api packages
116+
# and manager entrypoints — that's the supported pattern, not a smell.
117+
- linters:
118+
- gochecknoinits
119+
path: ^(api|cmd)/
120+
# Reconcile loops are long state machines by design; the complexity
121+
# budget elsewhere stays at the default.
122+
- linters:
123+
- gocyclo
124+
path: ^internal/controller/
125+
- linters:
126+
- all
127+
path: (.*)\.gen\.go
128+
paths:
129+
- third_party$
130+
- builtin$
131+
- examples$
120132
issues:
121-
exclude-rules:
122-
# Exclude some linters from running on tests files
123-
- path: _test\.go
124-
linters:
125-
- gocyclo
126-
- errcheck
127-
- dupl
128-
- gosec
129-
- goconst
130-
131-
# Exclude known issues in generated files
132-
- path: "(.*)\\.gen\\.go"
133-
linters:
134-
- all
135-
136133
max-issues-per-linter: 0
137134
max-same-issues: 0
138135
new: false
136+
formatters:
137+
enable:
138+
- gofmt
139+
- goimports
140+
exclusions:
141+
generated: lax
142+
paths:
143+
- third_party$
144+
- builtin$
145+
- examples$

api/v1alpha1/groupversion_info.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var (
1313
GroupVersion = schema.GroupVersion{Group: "dploy.dev", Version: "v1alpha1"}
1414

1515
// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
16+
//nolint:staticcheck // scheme.Builder is the kubebuilder-scaffolded pattern; SA1019 is aimed at hand-written api packages.
1617
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
1718

1819
// AddToScheme adds the types in this group-version to the given scheme.

cmd/api/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func main() {
148148

149149
// Admin endpoints — gated by MANAGER_ENABLED + the admin claim/value pair.
150150
// 404 when disabled, 403 to non-admin requesters. Shared manager-gate
151-
// middleware so both routes get the same 404 behaviour off-feature.
151+
// middleware so both routes get the same 404 behavior off-feature.
152152
managerGate := func(c *fiber.Ctx) error {
153153
if !cfg.ManagerEnabled {
154154
return c.Status(fiber.StatusNotFound).JSON(models.ErrorResponse{Error: "manager disabled"})

internal/auth/jwt.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@ func NewJWTValidator(jwksURL, issuer, audience, usernameClaim string) *JWTValida
4040
return &JWTValidator{verifier: verifier, usernameClaim: usernameClaim}
4141
}
4242

43-
// Validate verifies the token and returns (sanitizedUsername, raw claims, err).
44-
// All cryptographic and standard-claim checks live inside Verify; the only
45-
// dploy-specific work is pulling the configured username claim and sanitizing
46-
// it for use as a Kubernetes label.
47-
func (v *JWTValidator) Validate(tokenString string) (string, map[string]any, error) {
43+
// Validate verifies the token and returns the sanitized username plus the raw
44+
// claims. All cryptographic and standard-claim checks live inside Verify; the
45+
// only dploy-specific work is pulling the configured username claim and
46+
// sanitizing it for use as a Kubernetes label.
47+
func (v *JWTValidator) Validate(tokenString string) (username string, claims map[string]any, err error) {
4848
idToken, err := v.verifier.Verify(context.Background(), tokenString)
4949
if err != nil {
5050
return "", nil, fmt.Errorf("token parsing failed: %w", err)
5151
}
52-
claims := map[string]any{}
52+
claims = map[string]any{}
5353
if err := idToken.Claims(&claims); err != nil {
5454
return "", nil, fmt.Errorf("decode claims: %w", err)
5555
}

internal/auth/oidc_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package auth
22

33
import "testing"
44

5-
// TestSanitizeRelativePath pins both behaviours in one go: the relative-URL
5+
// TestSanitizeRelativePath pins both behaviors in one go: the relative-URL
66
// safety check (open-redirect surface) and the fragment-stripping canonical
77
// form the SPA's consumeHashToken() relies on. Both come out of one
88
// net/url.Parse pass — no string fiddling.

internal/controller/dployinstance_controller.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -301,15 +301,15 @@ func (r *DployInstanceReconciler) buildData(inst *dployv1alpha1.DployInstance, t
301301
}
302302

303303
return &templating.Data{
304-
Owner: sanitize(owner),
305-
UUID: inst.Status.UUID,
306-
BaseDomain: eff.BaseDomain,
307-
Host: defaultHost(inst.Spec.TemplateRef, inst.Status.UUID, eff.BaseDomain),
308-
Namespace: targetNS,
309-
Template: tmpl,
310-
Params: params,
311-
Claims: claims,
312-
Config: templating.Config{Values: eff.Values},
304+
Owner: sanitize(owner),
305+
UUID: inst.Status.UUID,
306+
BaseDomain: eff.BaseDomain,
307+
Host: defaultHost(inst.Spec.TemplateRef, inst.Status.UUID, eff.BaseDomain),
308+
Namespace: targetNS,
309+
Template: tmpl,
310+
Params: params,
311+
Claims: claims,
312+
Config: templating.Config{Values: eff.Values},
313313
}, nil
314314
}
315315

internal/controller/flux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import (
1212
fluxmeta "github.com/fluxcd/pkg/apis/meta"
1313
sourcev1 "github.com/fluxcd/source-controller/api/v1"
1414
corev1 "k8s.io/api/core/v1"
15-
apimeta "k8s.io/apimachinery/pkg/api/meta"
1615
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
16+
apimeta "k8s.io/apimachinery/pkg/api/meta"
1717
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1818
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1919

internal/kube/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ func (c *Client) ExtendInstance(ctx context.Context, inst *dployv1alpha1.DployIn
284284
return time.Time{}, fmt.Errorf("%w (%d)", ErrMaxExtends, maxExtends)
285285
}
286286

287-
newExpires := inst.Spec.ExpiresAt.Time.Add(time.Duration(extendSeconds) * time.Second)
287+
newExpires := inst.Spec.ExpiresAt.Add(time.Duration(extendSeconds) * time.Second)
288288
patch := client.MergeFrom(inst.DeepCopy())
289289
t := metav1.NewTime(newExpires)
290290
inst.Spec.ExpiresAt = &t

internal/models/responses.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ type AvailableEnvironmentResponse struct {
66
Icon string `json:"icon"`
77
Category string `json:"category,omitempty"`
88
// TTL info
9-
TTL int `json:"ttl"` // Initial TTL in seconds (-1 for unlimited)
10-
ExtendTTL int `json:"extendTTL,omitempty"` // Seconds added per extension (0 = use default)
11-
MaxExtends int `json:"maxExtends,omitempty"` // Max extensions allowed (0 = unlimited)
12-
IsUnlimited bool `json:"isUnlimited"` // True if TTL is unlimited
9+
TTL int `json:"ttl"` // Initial TTL in seconds (-1 for unlimited)
10+
ExtendTTL int `json:"extendTTL,omitempty"` // Seconds added per extension (0 = use default)
11+
MaxExtends int `json:"maxExtends,omitempty"` // Max extensions allowed (0 = unlimited)
12+
IsUnlimited bool `json:"isUnlimited"` // True if TTL is unlimited
1313
}
1414

1515
type UserEnvironmentResponse struct {

internal/operatorconfig/resolver.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,19 @@ const (
2929

3030
// Effective is the merged, ready-to-use operator configuration.
3131
type Effective struct {
32-
DefaultEngine dployv1alpha1.EngineType
33-
FluxNamespace string
34-
FluxServiceAccount string
35-
FluxInterval time.Duration
32+
DefaultEngine dployv1alpha1.EngineType
33+
FluxNamespace string
34+
FluxServiceAccount string
35+
FluxInterval time.Duration
3636
BaseDomain string
3737
ConnectionURLTemplate string
3838
DefaultConnectionType dployv1alpha1.ConnectionType
3939
ConnectionMessageTemplate string
4040
TTLSeconds int64
41-
ExtendSeconds int64
42-
MaxExtends int
43-
MaxInstancesPerUser int
44-
Values map[string]any
41+
ExtendSeconds int64
42+
MaxExtends int
43+
MaxInstancesPerUser int
44+
Values map[string]any
4545
}
4646

4747
// Resolve reads the OperatorConfig named "default" and merges it over the

0 commit comments

Comments
 (0)