diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f715372..4ab3bbe 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -18,5 +18,3 @@ jobs: permissions: contents: read uses: FollowTheProcess/ci/.github/workflows/Go.yml@v3 - with: - linter: staticcheck diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..1ba463b --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,162 @@ +version: "2" + +formatters: + enable: + - gofumpt + - goimports + - golines + + settings: + gofumpt: + extra-rules: true + + golines: + max-len: 140 + +linters: + default: all + disable: + - decorder # Don't care about this + - dupl # Basically every table driven test ever triggers this + - dupword # Messes with test cases more often than not + - err113 # Out of date + - exhaustruct # No + - forbidigo # Nothing to forbid + - funlen # Bad metric for complexity + - ginkgolinter # I don't use whatever this is + - gochecknoglobals # Globals are fine sometimes, use common sense + - gocyclo # cyclop does this instead + - godox # "todo" and "fixme" comments are allowed + - goheader # No need + - gosmopolitan # No need + - grouper # Imports take care of themselves, rest is common sense + - ireturn # This is just not necessary or practical in a real codebase + - lll # Auto formatters do this and what they can't do I don't care about + - maintidx # This is just the inverse of complexity... which is cyclop + - nestif # cyclop does this + - nlreturn # Similar to wsl, I think best left to judgement + - noinlineerr # Inline errors are fine + - nonamedreturns # Named returns are often helpful, it's naked returns that are the issue + - paralleltest # I've never had Go tests take longer than a few seconds, it's fine + - unparam # gopls is better and more subtle + - varnamelen # Lots of false positives of things that are fine + - wrapcheck # Not every error must be wrapped + - wsl # Deprecated + + exclusions: + presets: + # See https://golangci-lint.run/usage/false-positives/#exclusion-presets + - std-error-handling + - common-false-positives + rules: + - path: _test\.go + linters: + - prealloc # These kinds of optimisations will make no difference to test code + - gosec # Tests don't need security stuff + - goconst # Sometimes repetition is okay in tests + + settings: + cyclop: + max-complexity: 20 + + depguard: + rules: + main: + deny: + - pkg: io/ioutil + desc: io/ioutil is deprecated, use io instead + + - pkg: "math/rand$" + desc: use math/rand/v2 instead + + errcheck: + check-type-assertions: true + check-blank: true + + exhaustive: + check: + - switch + - map + default-signifies-exhaustive: true + + staticcheck: + checks: + - all + + gosec: + excludes: + - G104 # Errors not checked, handled by errcheck + + govet: + enable-all: true + + nakedret: + max-func-lines: 0 # Disallow any naked returns + + nolintlint: + allow-unused: false + require-explanation: true + require-specific: true + + usetesting: + context-background: true + context-todo: true + os-chdir: true + os-mkdir-temp: true + os-setenv: true + os-create-temp: true + os-temp-dir: true + + revive: + max-open-files: 256 + enable-all-rules: true + rules: + - name: add-constant + disabled: true # goconst does this + + - name: cognitive-complexity + disabled: true # gocognit does this + + - name: comment-spacings + arguments: + - "nolint:" + + - name: cyclomatic + disabled: true # cyclop does this + + - name: exported + arguments: + - checkPrivateReceivers + - checkPublicInterface + + - name: function-length + disabled: true # Bad proxy for complexity + + - name: function-result-limit + arguments: + - 3 + + - name: line-length-limit + disabled: true # gofmt/golines handles this well enough + + - name: max-public-structs + disabled: true # This is a dumb rule + + - name: redefines-builtin-id + disabled: true # predeclared does this + + - name: unhandled-error + arguments: + - fmt\.(Fp|P)rint(ln|f)? + - strings.Builder.Write(String|Byte)? + - bytes.Buffer.Write(String|Byte)? + - go/printer.(Fp|P)rint(ln|f)? + + - name: flag-parameter + disabled: true # As far as I can work out this just doesn't like bools + + - name: unused-parameter + disabled: true # The gopls unused analyzer covers this better + + - name: unused-receiver + disabled: true # As above diff --git a/Taskfile.yml b/Taskfile.yml index f4f9e6b..70a0ec7 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -25,8 +25,9 @@ tasks: desc: Run go fmt on all source files sources: - "**/*.go" + - .golangci.yml cmds: - - go fmt ./... + - golangci-lint fmt ./... demo: desc: Render the demo gifs @@ -46,6 +47,9 @@ tasks: - go.mod - go.sum - "**/testdata/**/*" + env: + # -race needs CGO + CGO_ENABLED: 1 cmds: - go test -race ./... {{ .CLI_ARGS }} @@ -58,17 +62,19 @@ tasks: lint: desc: Run linting + deps: + - fmt sources: - "**/*.go" - - staticcheck.conf + - .golangci.yml preconditions: - - sh: command -v staticcheck - msg: staticcheck not installed, see https://staticcheck.dev/docs/getting-started/ + - sh: command -v golangci-lint + msg: staticcheck not installed, run `brew install golangci-lint` - sh: command -v typos msg: requires typos-cli, run `brew install typos-cli` cmds: - - staticcheck ./... + - golangci-lint run ./... - typos doc: diff --git a/log.go b/log.go index 85121af..ffd4d7a 100644 --- a/log.go +++ b/log.go @@ -164,10 +164,12 @@ func (l *Logger) log(level Level, msg string, kv ...any) { } buf.WriteByte(':') + padding := 2 if level == LevelDebug || level == LevelError { padding = 1 } + buf.WriteString(strings.Repeat(" ", padding)) buf.WriteString(msg) diff --git a/staticcheck.conf b/staticcheck.conf deleted file mode 100644 index 528438b..0000000 --- a/staticcheck.conf +++ /dev/null @@ -1 +0,0 @@ -checks = ["all"]