Skip to content

Commit 26ce213

Browse files
Add tangled workflows and update linter config
1 parent c45a396 commit 26ce213

12 files changed

Lines changed: 203 additions & 141 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
!.github/**/*.yaml
3434
!.github/renovate.json
3535

36+
# Tangled
37+
!.tangled/**/*.yml
38+
3639
# Docs
3740
!docs/**/*.png
3841
!docs/**/*.gif

.golangci.yml

Lines changed: 51 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -11,57 +11,51 @@ formatters:
1111
extra-rules: true
1212

1313
golines:
14-
max-len: 140
15-
shorten-comments: true
16-
reformat-tags: true
14+
max-len: 120
1715

1816
linters:
1917
default: all
2018
disable:
21-
- cyclop # I prefer cognitive complexity
22-
- decorder # Don't care about this
23-
- dupl # Basically every table driven test ever triggers this
24-
- dupword # Messes with test cases more often than not
25-
- err113 # Opaque errors are fine
26-
- exhaustruct # No
27-
- forbidigo # Nothing to forbid
28-
- funlen # Bad metric for complexity
29-
- gocyclo # I prefer cognitive complexity
30-
- godox # "todo" and "fixme" comments are allowed
31-
- goheader # No need
32-
- gomodguard # Deprecated in v2.12.0, replaced by gomodguard_v2 which is enabled by default
33-
- gosmopolitan # No need
34-
- grouper # Imports take care of themselves, rest is common sense
35-
- ireturn # Returning an interface is sometimes a good abstraction
36-
- lll # Auto formatters do this and what they can't do I don't care about
37-
- nlreturn # Similar to wsl
38-
- noinlineerr # Inline errors are fine
39-
- nonamedreturns # Used sparingly in short functions these are fine
40-
- paralleltest # This makes go test -v ./... much more difficult to read
41-
- thelper # Lots of false positives in here due to the way I do table tests
42-
- unparam # gopls is better and more subtle
43-
- varnamelen # Lots of false positives of things that are fine
44-
- wrapcheck # Not every error must be wrapped
45-
- wsl # Deprecated
19+
- cyclop # gocognit is the single complexity linter, cognitive beats cyclomatic
20+
- decorder # Don't care about this
21+
- err113 # Not everything needs this
22+
- exhaustruct # This is just a bad idea
23+
- forbidigo # Nothing to forbid
24+
- funlen # Bad metric for complexity
25+
- gocyclo # Prefer cognitive complexity over naive metrics
26+
- godox # "todo" and "fixme" comments are allowed
27+
- gomodguard # Deprecated
28+
- ireturn # The Arg and Flag interfaces are intentionally opaque
29+
- lll # Auto formatters do this and what they can't do I don't care about
30+
- maintidx # Prefer cognitive
31+
- mnd # Annoying and too sensitive
32+
- nestif # Nesting depth is the dominant factor in gocognit's score already
33+
- noinlineerr # Inline errors are fine
34+
- nonamedreturns # Named returns are often helpful documentation, it's naked returns that are the issue
35+
- paralleltest # I've never had Go tests take longer than a few seconds, it's fine
36+
- unparam # gopls is better and more subtle
37+
- varnamelen # Lots of false positives of things that are fine
38+
- wrapcheck # Not every error must be wrapped
39+
- wsl # Deprecated
4640

4741
exclusions:
4842
presets:
4943
# See https://golangci-lint.run/usage/false-positives/#exclusion-presets
50-
- std-error-handling
5144
- common-false-positives
45+
- std-error-handling
5246
rules:
5347
- path: _test\.go
5448
linters:
55-
- prealloc # These kinds of optimisations will make no difference to test code
56-
- gosec # Tests don't need security stuff
57-
- gochecknoglobals # e.g. global test flags
58-
- goconst # Table driven tests naturally repeat string literals
59-
- maintidx # Flags table driven tests
49+
- dupl # Common false positives with table driven tests
50+
- dupword # Messes with test cases more often than not
51+
- gochecknoglobals # Flags and env vars
52+
- goconst # Sometimes repetition is okay in tests
53+
- gosec # Tests don't need security stuff
54+
- gosmopolitan # Testing unicode flags etc.
55+
- thelper # Lots of false positives because of how I've done CLI tests
56+
- prealloc # These kinds of optimisations will make no difference to test code
6057

6158
settings:
62-
cyclop:
63-
max-complexity: 20
64-
6559
depguard:
6660
rules:
6761
main:
@@ -73,105 +67,50 @@ linters:
7367
desc: use math/rand/v2 instead
7468

7569
errcheck:
76-
check-type-assertions: true
7770
check-blank: true
71+
check-type-assertions: true
7872

7973
exhaustive:
8074
check:
81-
- switch
8275
- map
76+
- switch
8377
default-signifies-exhaustive: true
8478

85-
staticcheck:
86-
checks:
87-
- all
88-
8979
gosec:
9080
excludes:
91-
- G104 # Errors not checked, handled by errcheck
81+
- G104 # Errors not checked, handled by errcheck
9282

9383
govet:
9484
enable-all: true
9585

9686
nakedret:
97-
max-func-lines: 0 # Disallow any naked returns
87+
max-func-lines: 0 # Disallow any naked returns
9888

9989
nolintlint:
10090
allow-unused: false
10191
require-explanation: true
10292
require-specific: true
10393

94+
revive:
95+
# every other revive rule overlaps one or more of the other
96+
# linters. These are the ones nothing else covers.
97+
enable-all-rules: false
98+
rules:
99+
- name: context-as-argument # ctx must be the first parameter
100+
- name: deep-exit # os.Exit / log.Fatal outside main and init
101+
- name: modifies-parameter # don't mutate value parameters
102+
- name: modifies-value-receiver # value receiver mutations are lost
103+
- name: unexported-return # exported func returning an unexported type
104+
105+
staticcheck:
106+
checks:
107+
- all
108+
104109
usetesting:
105110
context-background: true
106111
context-todo: true
107112
os-chdir: true
113+
os-create-temp: true
108114
os-mkdir-temp: true
109115
os-setenv: true
110-
os-create-temp: true
111116
os-temp-dir: true
112-
113-
revive:
114-
max-open-files: 256
115-
enable-all-rules: true
116-
rules:
117-
- name: add-constant
118-
disabled: true # goconst does this
119-
120-
- name: argument-limit
121-
arguments:
122-
- 5
123-
124-
- name: cognitive-complexity
125-
disabled: true # gocognit does this
126-
127-
- name: comment-spacings
128-
arguments:
129-
- "nolint:"
130-
131-
- name: cyclomatic
132-
disabled: true # cyclop does this
133-
134-
- name: enforce-switch-style
135-
disabled: true # exhaustive handles this
136-
137-
- name: exported
138-
arguments:
139-
- checkPrivateReceivers
140-
- checkPublicInterface
141-
142-
- name: function-length
143-
disabled: true # Bad proxy for complexity
144-
145-
- name: function-result-limit
146-
arguments:
147-
- 3
148-
149-
- name: import-shadowing
150-
disabled: true # predeclared does this
151-
152-
- name: line-length-limit
153-
disabled: true # gofmt/golines handles this well enough
154-
155-
- name: max-public-structs
156-
disabled: true # This is a dumb rule
157-
158-
- name: redefines-builtin-id
159-
disabled: true # predeclared does this
160-
161-
- name: unhandled-error
162-
disabled: true # errcheck handles this
163-
164-
- name: flag-parameter
165-
disabled: true # As far as I can work out this just doesn't like bools
166-
167-
- name: unused-parameter
168-
disabled: true # The gopls unused analyzer covers this better
169-
170-
- name: unused-receiver
171-
disabled: true # As above
172-
173-
- name: var-naming
174-
disabled: true
175-
176-
- name: package-naming
177-
disabled: true

.tangled/workflows/CI.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
when:
2+
- event: [push]
3+
branch: [main]
4+
- event: [pull_request]
5+
branch: [main]
6+
7+
engine: nixery
8+
9+
dependencies:
10+
nixpkgs:
11+
- go
12+
- golangci-lint
13+
- gcc # Needed for CGO_ENABLED=1 (go test -race)
14+
15+
steps:
16+
- name: Test
17+
environment:
18+
CGO_ENABLED: "1"
19+
command: go test -race ./...
20+
21+
- name: Format
22+
command: golangci-lint fmt ./... --diff-colored
23+
24+
- name: Lint
25+
command: golangci-lint run ./...

command_test.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,17 @@ func TestHelp(t *testing.T) {
420420
name: "with named arguments",
421421
options: []cli.Option{
422422
cli.OverrideArgs([]string{"--help"}),
423-
cli.Arg(new(string), "src", "The file to copy"), // This one is required
424-
cli.Arg(new(string), "dest", "Destination to copy to", cli.ArgDefault("default.txt")), // This one is optional
423+
cli.Arg(
424+
new(string),
425+
"src",
426+
"The file to copy",
427+
), // This one is required
428+
cli.Arg(
429+
new(string),
430+
"dest",
431+
"Destination to copy to",
432+
cli.ArgDefault("default.txt"),
433+
), // This one is optional
425434
cli.Arg(new(int), "other", "Something else", cli.ArgDefault(0)),
426435
cli.Run(func(ctx context.Context, cmd *cli.Command) error { return nil }),
427436
},
@@ -431,8 +440,17 @@ func TestHelp(t *testing.T) {
431440
name: "with verbosity count",
432441
options: []cli.Option{
433442
cli.OverrideArgs([]string{"--help"}),
434-
cli.Arg(new(string), "src", "The file to copy"), // This one is required
435-
cli.Arg(new(string), "dest", "Destination to copy to", cli.ArgDefault("destination.txt")), // This one is optional
443+
cli.Arg(
444+
new(string),
445+
"src",
446+
"The file to copy",
447+
), // This one is required
448+
cli.Arg(
449+
new(string),
450+
"dest",
451+
"Destination to copy to",
452+
cli.ArgDefault("destination.txt"),
453+
), // This one is optional
436454
cli.Flag(new(flag.Count), "verbosity", 'v', "Increase the verbosity level"),
437455
cli.Run(func(ctx context.Context, cmd *cli.Command) error { return nil }),
438456
},
@@ -945,6 +963,7 @@ func TestEnvFlag(t *testing.T) {
945963
cli.OverrideArgs(tt.args),
946964
cli.Run(func(ctx context.Context, cmd *cli.Command) error {
947965
fmt.Fprintf(cmd.Stdout(), "force: %v\n", force)
966+
948967
return nil
949968
}),
950969
)

examples/subcommands/cli.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,23 @@ func buildDoCommand() (*cli.Command, error) {
8888
cli.Example("Do it for a specific duration", "demo do something --duration 1m30s"),
8989
cli.Version("do version"),
9090
cli.Arg(&thing, "thing", "Thing to do"),
91-
cli.Flag(&options.count, "count", 'c', "Number of times to do the thing", cli.FlagDefault(1), cli.Env[int]("DEMO_COUNT")),
91+
cli.Flag(
92+
&options.count,
93+
"count",
94+
'c',
95+
"Number of times to do the thing",
96+
cli.FlagDefault(1),
97+
cli.Env[int]("DEMO_COUNT"),
98+
),
9299
cli.Flag(&options.fast, "fast", 'f', "Do the thing quickly"),
93100
cli.Flag(&options.verbosity, "verbosity", 'v', "Increase the verbosity level"),
94-
cli.Flag(&options.duration, "duration", 'd', "Do the thing for a specific duration", cli.FlagDefault(1*time.Second)),
101+
cli.Flag(
102+
&options.duration,
103+
"duration",
104+
'd',
105+
"Do the thing for a specific duration",
106+
cli.FlagDefault(1*time.Second),
107+
),
95108
cli.Run(func(ctx context.Context, cmd *cli.Command) error {
96109
if options.fast {
97110
fmt.Fprintf(

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ require (
1616
require (
1717
go.followtheprocess.codes/diff v0.2.0 // indirect
1818
go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect
19-
golang.org/x/sys v0.45.0 // indirect
20-
golang.org/x/term v0.43.0 // indirect
19+
golang.org/x/sys v0.46.0 // indirect
20+
golang.org/x/term v0.44.0 // indirect
2121
)

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ go.followtheprocess.codes/test v1.4.0 h1:LBZYn2MqOW20HLe1AJm3lMLplSWH+zycfC7mmS9
88
go.followtheprocess.codes/test v1.4.0/go.mod h1:/Lq3YrwTqU/tb1wbO+Kt7Gs1I3qzFu/o/CUykOavoVA=
99
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
1010
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
11-
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
12-
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
13-
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
14-
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
11+
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
12+
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
13+
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
14+
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
1515
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
1616
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=

internal/flag/flag.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ func (f *Flag[T]) isZeroIsh() bool {
752752
return len(*parse.Cast[[]string](f.value)) == 0
753753
case kind.Time:
754754
var zero time.Time
755+
755756
return parse.Cast[time.Time](f.value).Equal(zero)
756757
case kind.Duration:
757758
return *parse.Cast[time.Duration](f.value) == 0

0 commit comments

Comments
 (0)