diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5ac8c599..350dc325 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -81,7 +81,7 @@ jobs: name: Go lint runs-on: ubuntu-latest env: - GOLANGCI_LINT_VERSION: v1.64.6 + GOLANGCI_LINT_VERSION: v2.0.0 permissions: contents: read # allow read access to pull request. Use with `only-new-issues` option. @@ -102,7 +102,7 @@ jobs: run: make fake_assets - name: Lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v7 with: # golangci-lint needs to be run separately for every Go module, and # its GitHub Action doesn't provide any way to do that. Have it fetch diff --git a/.golangci.yaml b/.golangci.yaml index e8446eb9..f577960f 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,20 +1,9 @@ -issues: - exclude: - - 'Error return value of .(\w+\.Rollback(.*)). is not checked' +version: "2" linters: - presets: - - bugs - - comment - - format - - performance - - style - - test - - unused + default: all disable: - - intrange # encourages for loops to range over integers like `for i := range(5)` instead of a C-style for - # disabled, but which we should enable with discussion - wrapcheck # checks that errors are wrapped; currently not done anywhere @@ -23,76 +12,103 @@ linters: - testpackage # requires tests in test packages like `river_test` # disabled because they're annoying/bad - - err113 # wants all errors to be defined as variables at the package level; quite obnoxious + - cyclop # screams into the void at "cyclomatic complexity" + - funlen # screams when functions are more than 60 lines long; what are we even doing here guys - interfacebloat # we do in fact want >10 methods on the Adapter interface or wherever we see fit. + - gocognit # yells that "cognitive complexity" is too high; why + - gocyclo # ANOTHER "cyclomatic complexity" checker (see also "cyclop" and "gocyclo") - godox # bans TODO statements; total non-starter at the moment + - err113 # wants all errors to be defined as variables at the package level; quite obnoxious + - maintidx # ANOTHER ANOTHER "cyclomatic complexity" lint (see also "cyclop" and "gocyclo") + - mnd # detects "magic numbers", which it defines as any number; annoying + - nestif # yells when if blocks are nested; what planet do these people come from? - ireturn # bans returning interfaces; questionable as is, but also buggy as hell; very, very annoying - lll # restricts maximum line length; annoying - - mnd # detects "magic numbers", which it defines as any number; annoying - nlreturn # requires a blank line before returns; annoying - wsl # a bunch of style/whitespace stuff; annoying -linters-settings: - depguard: - rules: - all: - files: ["$all"] - allow: - deny: - - desc: "Use `github.com/google/uuid` package for UUIDs instead." - pkg: "github.com/xtgo/uuid" + settings: + depguard: + rules: + all: + files: ["$all"] + deny: + - desc: Use `github.com/google/uuid` package for UUIDs instead. + pkg: github.com/xtgo/uuid - exhaustive: - default-signifies-exhaustive: true + forbidigo: + forbid: + - msg: Use `require` variants instead. + pattern: ^assert\. + - msg: Use `Func` suffix for function variables instead. + pattern: Fn\b + - msg: Use built-in `max` function instead. + pattern: \bmath\.Max\b + - msg: Use built-in `min` function instead. + pattern: \bmath\.Min\b - forbidigo: - forbid: - - msg: "Use `require` variants instead." - p: '^assert\.' - - msg: "Use `Func` suffix for function variables instead." - p: 'Fn\b' - - msg: "Use built-in `max` function instead." - p: '\bmath\.Max\b' - - msg: "Use built-in `min` function instead." - p: '\bmath\.Min\b' + gomoddirectives: + replace-local: true - gci: - sections: - - Standard - - Default - - Prefix(github.com/riverqueue) - - Prefix(riverqueue.com/riverui) + gosec: + excludes: + - G404 # use of non-crypto random; overly broad for our use case - gomoddirectives: - replace-local: true + revive: + rules: + - name: unused-parameter + disabled: true - revive: - rules: + tagliatelle: + case: + rules: + json: snake - tagliatelle: - case: - rules: - json: snake + testifylint: + enable-all: true + disable: + - go-require + + varnamelen: + ignore-names: + - db + - eg + - f + - i + - id + - j + - mu + - r + - rw # common convention for http.ResponseWriter + - sb # common convention for string builder + - t + - tt # common convention for table tests + - tx + - w + - wg + + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - path: (.+)\.go$ + text: Error return value of .(\w+\.Rollback(.*)). is not checked - testifylint: - enable-all: true - disable: - - go-require +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports - varnamelen: - ignore-names: - - db - - eg - - f - - i - - id - - j - - mu - - r # common for http.Request - - rw # common for http.ResponseWriter - - sb # common convention for string builder - - t - - tt # common convention for table tests - - tx - - w # common for http.ResponseWriter - - wg + settings: + gci: + sections: + - Standard + - Default + - Prefix(github.com/riverqueue) + - Prefix(riverqueue.com/riverpro) diff --git a/cmd/riverui/main.go b/cmd/riverui/main.go index 0ef10a01..d7901823 100644 --- a/cmd/riverui/main.go +++ b/cmd/riverui/main.go @@ -15,12 +15,11 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/rs/cors" sloghttp "github.com/samber/slog-http" + "riverqueue.com/riverui" "github.com/riverqueue/apiframe/apimiddleware" "github.com/riverqueue/river" "github.com/riverqueue/river/riverdriver/riverpgxv5" - - "riverqueue.com/riverui" ) func main() { diff --git a/handler_api_endpoint.go b/handler_api_endpoint.go index 298f61f9..1c04d297 100644 --- a/handler_api_endpoint.go +++ b/handler_api_endpoint.go @@ -13,6 +13,9 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" + "riverqueue.com/riverui/internal/dbsqlc" + "riverqueue.com/riverui/internal/querycacher" + "riverqueue.com/riverui/internal/util/pgxutil" "github.com/riverqueue/apiframe/apiendpoint" "github.com/riverqueue/apiframe/apierror" @@ -23,10 +26,6 @@ import ( "github.com/riverqueue/river/rivershared/util/ptrutil" "github.com/riverqueue/river/rivershared/util/sliceutil" "github.com/riverqueue/river/rivertype" - - "riverqueue.com/riverui/internal/dbsqlc" - "riverqueue.com/riverui/internal/querycacher" - "riverqueue.com/riverui/internal/util/pgxutil" ) // A bundle of common utilities needed for many API endpoints. @@ -663,7 +662,7 @@ func (*queueUpdateEndpoint) Meta() *apiendpoint.EndpointMeta { type queueUpdateRequest struct { Concurrency apitype.ExplicitNullable[ConcurrencyConfig] `json:"concurrency"` - Name string `json:"-" validate:"required"` // from ExtractRaw + Name string `json:"-" validate:"required"` // from ExtractRaw } func (req *queueUpdateRequest) ExtractRaw(r *http.Request) error { diff --git a/handler_api_endpoint_test.go b/handler_api_endpoint_test.go index bff84338..367f342d 100644 --- a/handler_api_endpoint_test.go +++ b/handler_api_endpoint_test.go @@ -9,6 +9,8 @@ import ( "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/stretchr/testify/require" + "riverqueue.com/riverui/internal/riverinternaltest" + "riverqueue.com/riverui/internal/riverinternaltest/testfactory" "github.com/riverqueue/apiframe/apiendpoint" "github.com/riverqueue/apiframe/apierror" @@ -20,9 +22,6 @@ import ( "github.com/riverqueue/river/rivershared/startstop" "github.com/riverqueue/river/rivershared/util/ptrutil" "github.com/riverqueue/river/rivertype" - - "riverqueue.com/riverui/internal/riverinternaltest" - "riverqueue.com/riverui/internal/riverinternaltest/testfactory" ) type setupEndpointTestBundle struct { @@ -412,7 +411,7 @@ func TestAPIHandlerProducerList(t *testing.T) { resp, err := apitest.InvokeHandler(ctx, endpoint.Execute, testMountOpts(t), &producerListRequest{QueueName: "queue1"}) require.NoError(t, err) - require.Equal(t, 2, len(resp.Data)) + require.Len(t, resp.Data, 2) require.Equal(t, "client1", resp.Data[0].ClientID) require.Equal(t, 1, resp.Data[0].MaxWorkers) require.Equal(t, int32(0), resp.Data[0].Running) @@ -422,14 +421,14 @@ func TestAPIHandlerProducerList(t *testing.T) { resp, err = apitest.InvokeHandler(ctx, endpoint.Execute, testMountOpts(t), &producerListRequest{QueueName: "queue2"}) require.NoError(t, err) - require.Equal(t, 1, len(resp.Data)) + require.Len(t, resp.Data, 1) require.Equal(t, "client2", resp.Data[0].ClientID) require.Equal(t, 3, resp.Data[0].MaxWorkers) require.Equal(t, int32(5), resp.Data[0].Running) resp, err = apitest.InvokeHandler(ctx, endpoint.Execute, testMountOpts(t), &producerListRequest{QueueName: "queue3"}) require.NoError(t, err) - require.Equal(t, 0, len(resp.Data)) + require.Empty(t, resp.Data) }) } @@ -644,31 +643,31 @@ func TestStateAndCountGetEndpoint(t *testing.T) { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStateAvailable)}) - for i := 0; i < 2; i++ { + for range 2 { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStateCancelled), FinalizedAt: ptrutil.Ptr(time.Now())}) } - for i := 0; i < 3; i++ { + for range 3 { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStateCompleted), FinalizedAt: ptrutil.Ptr(time.Now())}) } - for i := 0; i < 4; i++ { + for range 4 { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStateDiscarded), FinalizedAt: ptrutil.Ptr(time.Now())}) } - for i := 0; i < 5; i++ { + for range 5 { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStatePending)}) } - for i := 0; i < 6; i++ { + for range 6 { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStateRetryable)}) } - for i := 0; i < 7; i++ { + for range 7 { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStateRunning)}) } - for i := 0; i < 8; i++ { + for range 8 { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStateScheduled)}) } @@ -692,7 +691,7 @@ func TestStateAndCountGetEndpoint(t *testing.T) { endpoint, bundle := setupEndpoint(ctx, t, newStateAndCountGetEndpoint) const queryCacheSkipThreshold = 3 - for i := 0; i < queryCacheSkipThreshold+1; i++ { + for range queryCacheSkipThreshold + 1 { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStateAvailable)}) } @@ -712,7 +711,7 @@ func TestStateAndCountGetEndpoint(t *testing.T) { endpoint, bundle := setupEndpoint(ctx, t, newStateAndCountGetEndpoint) const queryCacheSkipThreshold = 3 - for i := 0; i < queryCacheSkipThreshold-1; i++ { + for range queryCacheSkipThreshold - 1 { _ = testfactory.Job(ctx, t, bundle.exec, &testfactory.JobOpts{State: ptrutil.Ptr(rivertype.JobStateAvailable)}) } diff --git a/handler_test.go b/handler_test.go index 89b0dcf0..dc579305 100644 --- a/handler_test.go +++ b/handler_test.go @@ -12,12 +12,11 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" + "riverqueue.com/riverui/internal/riverinternaltest" + "riverqueue.com/riverui/internal/riverinternaltest/testfactory" "github.com/riverqueue/apiframe/apitype" "github.com/riverqueue/river/rivershared/util/ptrutil" - - "riverqueue.com/riverui/internal/riverinternaltest" - "riverqueue.com/riverui/internal/riverinternaltest/testfactory" ) func TestNewHandlerIntegration(t *testing.T) { @@ -128,7 +127,8 @@ func TestNewHandlerIntegration(t *testing.T) { ByKind: true, }, }, - }})) + }, + })) makeAPICall(t, "QueueResume", http.MethodPut, makeURL("/api/queues/%s/resume", queuePaused.Name), nil) makeAPICall(t, "StateAndCountGet", http.MethodGet, makeURL("/api/states"), nil) makeAPICall(t, "WorkflowGet", http.MethodGet, makeURL("/api/workflows/%s", workflowID), nil) diff --git a/internal/querycacher/query_cacher.go b/internal/querycacher/query_cacher.go index 22e612b6..f3d2a28d 100644 --- a/internal/querycacher/query_cacher.go +++ b/internal/querycacher/query_cacher.go @@ -7,10 +7,10 @@ import ( "sync" "time" + "riverqueue.com/riverui/internal/dbsqlc" + "github.com/riverqueue/river/rivershared/baseservice" "github.com/riverqueue/river/rivershared/startstop" - - "riverqueue.com/riverui/internal/dbsqlc" ) // QueryCacher executes a database query periodically and caches the result. The @@ -35,7 +35,7 @@ func NewQueryCacher[TRes any](archetype *baseservice.Archetype, db dbsqlc.DBTX, // +/- 1s random variance to ticker interval. Makes sure that given multiple // query caches running simultaneously, they all start and are scheduled a // little differently to make a thundering herd problem less likely. - randomTickVariance := time.Duration(rand.Float64()*float64(2*time.Second)) - 1*time.Second //nolint:gosec + randomTickVariance := time.Duration(rand.Float64()*float64(2*time.Second)) - 1*time.Second queryCacher := baseservice.Init(archetype, &QueryCacher[TRes]{ db: db, diff --git a/internal/querycacher/query_cacher_test.go b/internal/querycacher/query_cacher_test.go index 7f2d66fc..7ede37a2 100644 --- a/internal/querycacher/query_cacher_test.go +++ b/internal/querycacher/query_cacher_test.go @@ -6,15 +6,14 @@ import ( "time" "github.com/stretchr/testify/require" + "riverqueue.com/riverui/internal/dbsqlc" + "riverqueue.com/riverui/internal/riverinternaltest" + "riverqueue.com/riverui/internal/riverinternaltest/testfactory" "github.com/riverqueue/river/riverdriver" "github.com/riverqueue/river/riverdriver/riverpgxv5" "github.com/riverqueue/river/rivershared/riversharedtest" "github.com/riverqueue/river/rivershared/startstoptest" - - "riverqueue.com/riverui/internal/dbsqlc" - "riverqueue.com/riverui/internal/riverinternaltest" - "riverqueue.com/riverui/internal/riverinternaltest/testfactory" ) func TestQueryCacher(t *testing.T) { diff --git a/internal/riverinternaltest/testfactory/test_factory.go b/internal/riverinternaltest/testfactory/test_factory.go index a443b0f8..5361e9e2 100644 --- a/internal/riverinternaltest/testfactory/test_factory.go +++ b/internal/riverinternaltest/testfactory/test_factory.go @@ -40,7 +40,7 @@ func Job(ctx context.Context, tb testing.TB, exec riverdriver.Executor, opts *Jo return job } -func Job_Build(tb testing.TB, opts *JobOpts) *riverdriver.JobInsertFullParams { //nolint:revive,stylecheck +func Job_Build(tb testing.TB, opts *JobOpts) *riverdriver.JobInsertFullParams { tb.Helper() encodedArgs := opts.EncodedArgs diff --git a/internal/util/pgxutil/db_util_test.go b/internal/util/pgxutil/db_util_test.go index 571b4d12..b8f0fea1 100644 --- a/internal/util/pgxutil/db_util_test.go +++ b/internal/util/pgxutil/db_util_test.go @@ -6,7 +6,6 @@ import ( "github.com/jackc/pgx/v5" "github.com/stretchr/testify/require" - "riverqueue.com/riverui/internal/riverinternaltest" )