diff --git a/.golangci.yml b/.golangci.yml index a7ec44c29f..80590a7e49 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -72,6 +72,7 @@ linters: - tagalign - nolintlint - wrapcheck # We might want to re-enable this if we manually wrap all the existing errors with fmt.Errorf + - protogetter linters-settings: exhaustive: @@ -134,3 +135,6 @@ issues: - unused-parameter - "^loopclosure:" - 'shadow: declaration of "ctx" shadows declaration at' + - "^dot-imports:" + - "fmt.Errorf can be replaced with errors.New" + - "fmt.Sprintf can be replaced with string concatenation" diff --git a/backend/controller/dal/dal_test.go b/backend/controller/dal/dal_test.go index 4b37222e55..677074f6a2 100644 --- a/backend/controller/dal/dal_test.go +++ b/backend/controller/dal/dal_test.go @@ -361,7 +361,7 @@ func TestControllerStateFromProto(t *testing.T) { } func normaliseEvents(events []Event) []Event { - for i := 0; i < len(events); i++ { + for i := range len(events) { event := events[i] re := reflect.Indirect(reflect.ValueOf(event)) f := re.FieldByName("Time") diff --git a/backend/controller/ingress/ingress.go b/backend/controller/ingress/ingress.go index a2242d382f..19f263b707 100644 --- a/backend/controller/ingress/ingress.go +++ b/backend/controller/ingress/ingress.go @@ -158,7 +158,7 @@ func validateValue(fieldType schema.Type, path path, value any, sch *schema.Sche return fmt.Errorf("%s is not a slice", path) } elementType := fieldType.Element - for i := 0; i < rv.Len(); i++ { + for i := range rv.Len() { elemPath := append(path, fmt.Sprintf("[%d]", i)) //nolint:gocritic elem := rv.Index(i).Interface() if err := validateValue(elementType, elemPath, elem, sch); err != nil { diff --git a/backend/controller/scaling/localscaling/local_scaling.go b/backend/controller/scaling/localscaling/local_scaling.go index 98080d9a1c..678c6f1637 100644 --- a/backend/controller/scaling/localscaling/local_scaling.go +++ b/backend/controller/scaling/localscaling/local_scaling.go @@ -57,7 +57,7 @@ func (l *LocalScaling) SetReplicas(ctx context.Context, replicas int, idleRunner if replicasToAdd <= 0 { replicasToRemove := -replicasToAdd - for i := 0; i < replicasToRemove; i++ { + for range replicasToRemove { if len(idleRunners) == 0 { return nil } @@ -74,7 +74,7 @@ func (l *LocalScaling) SetReplicas(ctx context.Context, replicas int, idleRunner } logger.Debugf("Adding %d replicas", replicasToAdd) - for i := 0; i < replicasToAdd; i++ { + for range replicasToAdd { controllerEndpoint := l.controllerAddresses[len(l.runners)%len(l.controllerAddresses)] bind := l.portAllocator.Next() diff --git a/backend/controller/scheduledtask/scheduledtask.go b/backend/controller/scheduledtask/scheduledtask.go index 0f3283a05f..e770197ba4 100644 --- a/backend/controller/scheduledtask/scheduledtask.go +++ b/backend/controller/scheduledtask/scheduledtask.go @@ -137,7 +137,6 @@ func (s *Scheduler) run(ctx context.Context) { if job.next.After(s.clock.Now()) { continue } - job := job hashring := s.hashring.Load() // If the job is singly homed, check that we are the active controller. diff --git a/backend/controller/scheduledtask/scheduledtask_test.go b/backend/controller/scheduledtask/scheduledtask_test.go index 8b325d92c9..6f759a68fb 100644 --- a/backend/controller/scheduledtask/scheduledtask_test.go +++ b/backend/controller/scheduledtask/scheduledtask_test.go @@ -40,7 +40,6 @@ func TestCron(t *testing.T) { clock := clock.NewMock() for _, c := range controllers { - c := c c.cron = NewForTesting(ctx, c.controller.Key, DALFunc(func(ctx context.Context, all bool) ([]dal.Controller, error) { return slices.Map(controllers, func(c *controller) dal.Controller { return c.controller }), nil }), clock) diff --git a/backend/controller/sql/databasetesting/devel.go b/backend/controller/sql/databasetesting/devel.go index 7a22d66ccf..5ab4f63c2c 100644 --- a/backend/controller/sql/databasetesting/devel.go +++ b/backend/controller/sql/databasetesting/devel.go @@ -25,7 +25,7 @@ func CreateForDevel(ctx context.Context, dsn string, recreate bool) (*pgxpool.Po noDBDSN := config.Copy() noDBDSN.Database = "" var conn *pgx.Conn - for i := 0; i < 10; i++ { + for range 10 { conn, err = pgx.ConnectConfig(ctx, noDBDSN) if err == nil { defer conn.Close(ctx) diff --git a/backend/schema/intvalue.go b/backend/schema/intvalue.go index 5a82ea09a9..51eb51a3d3 100644 --- a/backend/schema/intvalue.go +++ b/backend/schema/intvalue.go @@ -1,7 +1,7 @@ package schema import ( - "fmt" + "strconv" "google.golang.org/protobuf/proto" @@ -28,7 +28,7 @@ func (i *IntValue) Position() Position { return i.Pos } func (i *IntValue) schemaChildren() []Node { return nil } func (i *IntValue) String() string { - return fmt.Sprintf("%d", i.Value) + return strconv.Itoa(i.Value) } func (i *IntValue) GetValue() any { return i.Value } diff --git a/backend/schema/strcase/case.go b/backend/schema/strcase/case.go index 2d58850810..bbad861e0b 100644 --- a/backend/schema/strcase/case.go +++ b/backend/schema/strcase/case.go @@ -148,7 +148,7 @@ func split(src string) (entries []string) { } // handle upper case -> lower case sequences, e.g. // "PDFL", "oader" -> "PDF", "Loader" - for i := 0; i < len(runes)-1; i++ { + for i := range len(runes) - 1 { if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) { runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...) runes[i] = runes[i][:len(runes[i])-1] diff --git a/bin/.golangci-lint-1.54.2.pkg b/bin/.golangci-lint-1.57.1.pkg similarity index 100% rename from bin/.golangci-lint-1.54.2.pkg rename to bin/.golangci-lint-1.57.1.pkg diff --git a/bin/golangci-lint b/bin/golangci-lint index 268cdee0f1..32d0089f14 120000 --- a/bin/golangci-lint +++ b/bin/golangci-lint @@ -1 +1 @@ -.golangci-lint-1.54.2.pkg \ No newline at end of file +.golangci-lint-1.57.1.pkg \ No newline at end of file diff --git a/buildengine/deploy.go b/buildengine/deploy.go index 8e86c852f9..653babac2c 100644 --- a/buildengine/deploy.go +++ b/buildengine/deploy.go @@ -27,11 +27,11 @@ type deploymentArtefact struct { } type DeployClient interface { - GetArtefactDiffs(context.Context, *connect.Request[ftlv1.GetArtefactDiffsRequest]) (*connect.Response[ftlv1.GetArtefactDiffsResponse], error) - UploadArtefact(context.Context, *connect.Request[ftlv1.UploadArtefactRequest]) (*connect.Response[ftlv1.UploadArtefactResponse], error) - CreateDeployment(context.Context, *connect.Request[ftlv1.CreateDeploymentRequest]) (*connect.Response[ftlv1.CreateDeploymentResponse], error) - ReplaceDeploy(context.Context, *connect.Request[ftlv1.ReplaceDeployRequest]) (*connect.Response[ftlv1.ReplaceDeployResponse], error) - Status(context.Context, *connect.Request[ftlv1.StatusRequest]) (*connect.Response[ftlv1.StatusResponse], error) + GetArtefactDiffs(ctx context.Context, req *connect.Request[ftlv1.GetArtefactDiffsRequest]) (*connect.Response[ftlv1.GetArtefactDiffsResponse], error) + UploadArtefact(ctx context.Context, req *connect.Request[ftlv1.UploadArtefactRequest]) (*connect.Response[ftlv1.UploadArtefactResponse], error) + CreateDeployment(ctx context.Context, req *connect.Request[ftlv1.CreateDeploymentRequest]) (*connect.Response[ftlv1.CreateDeploymentResponse], error) + ReplaceDeploy(ctx context.Context, req *connect.Request[ftlv1.ReplaceDeployRequest]) (*connect.Response[ftlv1.ReplaceDeployResponse], error) + Status(ctx context.Context, req *connect.Request[ftlv1.StatusRequest]) (*connect.Response[ftlv1.StatusResponse], error) } // Deploy a module to the FTL controller with the given number of replicas. Optionally wait for the deployment to become ready. diff --git a/buildengine/engine.go b/buildengine/engine.go index e559f61984..0ae4c93ae0 100644 --- a/buildengine/engine.go +++ b/buildengine/engine.go @@ -347,7 +347,7 @@ func (e *Engine) buildAndDeploy(ctx context.Context, replicas int32, waitForDepl }) // Process deployment queue. - for i := 0; i < len(projects); i++ { + for range len(projects) { wg.Go(func() error { for { select { diff --git a/buildengine/project.go b/buildengine/project.go index 4bd8bb075f..dfa5257b9f 100644 --- a/buildengine/project.go +++ b/buildengine/project.go @@ -17,7 +17,7 @@ type Project interface { sealed() Config() ProjectConfig - CopyWithDependencies([]string) Project + CopyWithDependencies(deps []string) Project TypeString() string } diff --git a/cmd/ftl/cmd_schema_import.go b/cmd/ftl/cmd_schema_import.go index f01819ec38..07fb5d6703 100644 --- a/cmd/ftl/cmd_schema_import.go +++ b/cmd/ftl/cmd_schema_import.go @@ -6,6 +6,7 @@ import ( "net" "os" "path/filepath" + "strconv" "github.com/tmc/langchaingo/llms" "github.com/tmc/langchaingo/llms/ollama" @@ -142,7 +143,7 @@ func (s *schemaImportCmd) setup(ctx context.Context) error { return err } - port := fmt.Sprintf("%d", s.OllamaPort) + port := strconv.Itoa(s.OllamaPort) if len(output) == 0 { logger.Debugf("Creating docker container '%s' for ollama", ollamaContainerName) @@ -157,7 +158,7 @@ func (s *schemaImportCmd) setup(ctx context.Context) error { err = exec.Command(ctx, log.Debug, "./", "docker", "run", "-d", // run detached so we can follow with other commands "-v", ollamaVolume, - "-p", fmt.Sprintf("%s:11434", port), + "-p", port+":11434", "--name", ollamaContainerName, "ollama/ollama").RunBuffered(ctx) if err != nil { diff --git a/cmd/ftl/cmd_serve.go b/cmd/ftl/cmd_serve.go index 3f68d71644..11ee07aefe 100644 --- a/cmd/ftl/cmd_serve.go +++ b/cmd/ftl/cmd_serve.go @@ -90,7 +90,7 @@ func (s *serveCmd) Run(ctx context.Context) error { } controllerAddresses := make([]*url.URL, 0, s.Controllers) - for i := 0; i < s.Controllers; i++ { + for range s.Controllers { controllerAddresses = append(controllerAddresses, bindAllocator.Next()) } @@ -99,8 +99,7 @@ func (s *serveCmd) Run(ctx context.Context) error { return err } - for i := 0; i < s.Controllers; i++ { - i := i + for i := range s.Controllers { config := controller.Config{ Bind: controllerAddresses[i], Key: model.NewLocalControllerKey(i), @@ -159,7 +158,7 @@ func runInBackground(logger *log.Logger) { return } - if err := os.WriteFile(pidFilePath, []byte(fmt.Sprintf("%d", cmd.Process.Pid)), 0600); err != nil { + if err := os.WriteFile(pidFilePath, []byte(strconv.Itoa(cmd.Process.Pid)), 0600); err != nil { logger.Errorf(err, "failed to write pid file") return } @@ -269,7 +268,7 @@ func (s *serveCmd) setupDB(ctx context.Context) (string, error) { "--user", "postgres", "--restart", "always", "-e", "POSTGRES_PASSWORD=secret", - "-p", fmt.Sprintf("%s:5432", port), + "-p", port+":5432", "--health-cmd=pg_isready", "--health-interval=1s", "--health-timeout=60s", diff --git a/go-runtime/compile/build.go b/go-runtime/compile/build.go index 769cbec520..1fc613b1cb 100644 --- a/go-runtime/compile/build.go +++ b/go-runtime/compile/build.go @@ -9,14 +9,16 @@ import ( "path" "path/filepath" stdreflect "reflect" + "strconv" "strings" - "github.com/TBD54566975/scaffolder" "golang.design/x/reflect" "golang.org/x/mod/modfile" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/proto" + "github.com/TBD54566975/scaffolder" + "github.com/TBD54566975/ftl" "github.com/TBD54566975/ftl/backend/schema" "github.com/TBD54566975/ftl/common/moduleconfig" @@ -257,7 +259,7 @@ var scaffoldFuncs = scaffolder.FuncMap{ case *schema.StringValue: return fmt.Sprintf("%q", t.Value) case *schema.IntValue: - return fmt.Sprintf("%d", t.Value) + return strconv.Itoa(t.Value) } panic(fmt.Sprintf("unsupported value %T", v)) }, diff --git a/go-runtime/compile/schema.go b/go-runtime/compile/schema.go index fbec362da1..49de9d4668 100644 --- a/go-runtime/compile/schema.go +++ b/go-runtime/compile/schema.go @@ -508,7 +508,7 @@ func visitStruct(pctx *parseContext, pos token.Pos, tnode types.Type) (*schema.R Module: destModule, Name: named.Obj().Name(), } - for i := 0; i < named.TypeArgs().Len(); i++ { + for i := range named.TypeArgs().Len() { arg := named.TypeArgs().At(i) typeArg, err := visitType(pctx, pos, arg) if err != nil { @@ -536,7 +536,7 @@ func visitStruct(pctx *parseContext, pos token.Pos, tnode types.Type) (*schema.R Pos: goPosToSchemaPos(pos), Name: out.Name, } - for i := 0; i < named.TypeParams().Len(); i++ { + for i := range named.TypeParams().Len() { param := named.TypeParams().At(i) out.TypeParameters = append(out.TypeParameters, &schema.TypeParameter{ Pos: goPosToSchemaPos(pos), @@ -580,7 +580,7 @@ func visitStruct(pctx *parseContext, pos token.Pos, tnode types.Type) (*schema.R if !ok { return nil, errorf(pos, "expected struct but got %s", named) } - for i := 0; i < s.NumFields(); i++ { + for i := range s.NumFields() { f := s.Field(i) ft, err := visitType(pctx, f.Pos(), f.Type()) if err != nil { diff --git a/go-runtime/compile/schema_test.go b/go-runtime/compile/schema_test.go index 78edb16888..3a93e56f38 100644 --- a/go-runtime/compile/schema_test.go +++ b/go-runtime/compile/schema_test.go @@ -136,20 +136,20 @@ func TestExtractModuleSchemaTwo(t *testing.T) { data Payload { body T } - + data User { name String } - + data UserResponse { user two.User } - - verb callsTwo(two.Payload) two.Payload + + verb callsTwo(two.Payload) two.Payload +calls two.two - + verb returnsUser(Unit) two.UserResponse - + verb two(two.Payload) two.Payload } ` @@ -187,7 +187,6 @@ func TestParseDirectives(t *testing.T) { }}, } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { got, err := directiveParser.ParseString("", tt.input) assert.NoError(t, err) diff --git a/go-runtime/encoding/encoding.go b/go-runtime/encoding/encoding.go index 0dd71dcd33..ef5ce85468 100644 --- a/go-runtime/encoding/encoding.go +++ b/go-runtime/encoding/encoding.go @@ -111,7 +111,7 @@ func encodeValue(v reflect.Value, w *bytes.Buffer) error { func encodeStruct(v reflect.Value, w *bytes.Buffer) error { w.WriteRune('{') afterFirst := false - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { ft := v.Type().Field(i) t := ft.Type fv := v.Field(i) @@ -146,7 +146,7 @@ func encodeBytes(v reflect.Value, w *bytes.Buffer) error { func encodeSlice(v reflect.Value, w *bytes.Buffer) error { w.WriteRune('[') - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { if i > 0 { w.WriteRune(',') } diff --git a/go-runtime/encoding/encoding_test.go b/go-runtime/encoding/encoding_test.go index 18053f466f..f943754c3b 100644 --- a/go-runtime/encoding/encoding_test.go +++ b/go-runtime/encoding/encoding_test.go @@ -4,9 +4,10 @@ import ( "reflect" "testing" + "github.com/alecthomas/assert/v2" + . "github.com/TBD54566975/ftl/go-runtime/encoding" "github.com/TBD54566975/ftl/go-runtime/ftl" - "github.com/alecthomas/assert/v2" ) func TestMarshal(t *testing.T) { diff --git a/go-runtime/ftl/config.go b/go-runtime/ftl/config.go index 419a7730fe..70588e502c 100644 --- a/go-runtime/ftl/config.go +++ b/go-runtime/ftl/config.go @@ -56,7 +56,7 @@ func callerModule() string { return "testing" } if !strings.HasPrefix(module, "ftl/") { - panic(fmt.Sprintf("must be called from an FTL module not %s", module)) + panic("must be called from an FTL module not " + module) } return strings.Split(strings.Split(module, "/")[1], ".")[0] } diff --git a/go-runtime/ftl/observability/traces.go b/go-runtime/ftl/observability/traces.go index b4aa1cafe6..96263bd190 100644 --- a/go-runtime/ftl/observability/traces.go +++ b/go-runtime/ftl/observability/traces.go @@ -18,5 +18,5 @@ func TracerWithVerb(ctx context.Context) trace.Tracer { } func StartSpan(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { - return TracerWithVerb(ctx).Start(ctx, name, opts...) + return TracerWithVerb(ctx).Start(ctx, name, opts...) //nolint:spancheck } diff --git a/internal/exec/circularbuffer.go b/internal/exec/circularbuffer.go index f379fc1c6b..7d9dfe05e0 100644 --- a/internal/exec/circularbuffer.go +++ b/internal/exec/circularbuffer.go @@ -54,7 +54,7 @@ func (cb *CircularBuffer) Bytes() []byte { var buf bytes.Buffer start := cb.r.Move(-cb.size) // Correctly calculate the starting position - for i := 0; i < cb.size; i++ { + for range cb.size { if str, ok := start.Value.(string); ok { buf.WriteString(str) } else {