From e8dc2fce32f2b0cfc6e5e04599bcdab363dbc022 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Feb 2026 13:06:26 +0100 Subject: [PATCH 1/9] modernize: rangeint go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest modernize -rangeint -fix ./... Signed-off-by: Sebastiaan van Stijn --- cli/command/container/run_test.go | 2 +- cli/command/container/tty.go | 2 +- cli/command/formatter/tabwriter/tabwriter_test.go | 10 +++++----- cli/command/idresolver/idresolver_test.go | 2 +- cli/command/service/list_test.go | 2 +- cli/command/service/progress/progress_test.go | 2 +- cmd/docker/docker.go | 2 +- internal/jsonstream/display_test.go | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cli/command/container/run_test.go b/cli/command/container/run_test.go index c0049e8b4547..8cc0b1bdc1a8 100644 --- a/cli/command/container/run_test.go +++ b/cli/command/container/run_test.go @@ -247,7 +247,7 @@ func TestRunPullTermination(t *testing.T) { go func() { id := test.RandomID()[:12] // short-ID progressOutput := streamformatter.NewJSONProgressOutput(server, true) - for i := 0; i < 100; i++ { + for i := range 100 { select { case <-ctx.Done(): assert.NilError(t, server.Close(), "failed to close imageCreateFunc server") diff --git a/cli/command/container/tty.go b/cli/command/container/tty.go index 6d57bf0e3460..89352f789851 100644 --- a/cli/command/container/tty.go +++ b/cli/command/container/tty.go @@ -60,7 +60,7 @@ func initTtySize(ctx context.Context, cli command.Cli, id string, isExec bool, r if err := rTTYfunc(ctx, cli, id, isExec); err != nil { go func() { var err error - for retry := 0; retry < 10; retry++ { + for retry := range 10 { time.Sleep(time.Duration(retry+1) * 10 * time.Millisecond) if err = rTTYfunc(ctx, cli, id, isExec); err == nil { break diff --git a/cli/command/formatter/tabwriter/tabwriter_test.go b/cli/command/formatter/tabwriter/tabwriter_test.go index 5f61d87aae3b..c9fcaf4d3ea0 100644 --- a/cli/command/formatter/tabwriter/tabwriter_test.go +++ b/cli/command/formatter/tabwriter/tabwriter_test.go @@ -25,7 +25,7 @@ func (b *buffer) Write(buf []byte) (written int, err error) { m := len(buf) if n+m <= cap(b.a) { b.a = b.a[0 : n+m] - for i := 0; i < m; i++ { + for i := range m { b.a[n+i] = buf[i] } } else { @@ -669,7 +669,7 @@ func BenchmarkTable(b *testing.B) { for i := 0; i < b.N; i++ { w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings // Write the line h times. - for j := 0; j < h; j++ { + for range h { w.Write(line) } w.Flush() @@ -681,7 +681,7 @@ func BenchmarkTable(b *testing.B) { w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings for i := 0; i < b.N; i++ { // Write the line h times. - for j := 0; j < h; j++ { + for range h { w.Write(line) } w.Flush() @@ -701,7 +701,7 @@ func BenchmarkPyramid(b *testing.B) { for i := 0; i < b.N; i++ { w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings // Write increasing prefixes of that line. - for j := 0; j < x; j++ { + for j := range x { w.Write(line[:j*2]) w.Write([]byte{'\n'}) } @@ -723,7 +723,7 @@ func BenchmarkRagged(b *testing.B) { for i := 0; i < b.N; i++ { w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings // Write the lines in turn h times. - for j := 0; j < h; j++ { + for j := range h { w.Write(lines[j%len(lines)]) w.Write([]byte{'\n'}) } diff --git a/cli/command/idresolver/idresolver_test.go b/cli/command/idresolver/idresolver_test.go index 120d7a8fb2ba..62289d71e157 100644 --- a/cli/command/idresolver/idresolver_test.go +++ b/cli/command/idresolver/idresolver_test.go @@ -60,7 +60,7 @@ func TestResolveWithCache(t *testing.T) { idResolver := New(apiClient, false) ctx := context.Background() - for i := 0; i < 2; i++ { + for range 2 { id, err := idResolver.Resolve(ctx, swarm.Node{}, "nodeID") assert.NilError(t, err) assert.Check(t, is.Equal("node-foo", id)) diff --git a/cli/command/service/list_test.go b/cli/command/service/list_test.go index be60eb4f77d7..8d6462e0bddf 100644 --- a/cli/command/service/list_test.go +++ b/cli/command/service/list_test.go @@ -279,7 +279,7 @@ func generateNodes(t *testing.T, activeNodes uint64) client.NodeListResult { t.Helper() nodes := client.NodeListResult{} var i uint64 - for i = 0; i < activeNodes; i++ { + for i = range activeNodes { nodes.Items = append(nodes.Items, swarm.Node{ ID: fmt.Sprintf("node-ready-%d", i), Status: swarm.NodeStatus{State: swarm.NodeStateReady}, diff --git a/cli/command/service/progress/progress_test.go b/cli/command/service/progress/progress_test.go index 7cd7664e3545..68c77979bccb 100644 --- a/cli/command/service/progress/progress_test.go +++ b/cli/command/service/progress/progress_test.go @@ -856,7 +856,7 @@ func TestGlobalJobProgressUpdaterLarge(t *testing.T) { } activeNodes := map[string]struct{}{} - for i := 0; i < 50; i++ { + for i := range 50 { activeNodes[fmt.Sprintf("node%v", i)] = struct{}{} } diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 5450ea05ec84..72347b9517e6 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -410,7 +410,7 @@ func forceExitAfter3TerminationSignals(ctx context.Context, streams command.Stre signal.Notify(sig, platformsignals.TerminationSignals...) // once we have received a total of 3 signals we force exit the cli - for i := 0; i < 2; i++ { + for range 2 { <-sig } _, _ = fmt.Fprint(streams.Err(), "\ngot 3 SIGTERM/SIGINTs, forcefully exiting\n") diff --git a/internal/jsonstream/display_test.go b/internal/jsonstream/display_test.go index 17f3ac2f3754..b40679422a4d 100644 --- a/internal/jsonstream/display_test.go +++ b/internal/jsonstream/display_test.go @@ -25,7 +25,7 @@ func TestDisplay(t *testing.T) { go func() { id := test.RandomID()[:12] // short-ID progressOutput := streamformatter.NewJSONProgressOutput(server, true) - for i := 0; i < 100; i++ { + for i := range 100 { select { case <-ctx.Done(): assert.NilError(t, server.Close(), "failed to close jsonmessage server") From 85ebca52fd8d711dc0303e6872392bb2b03e0bea Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Feb 2026 13:08:46 +0100 Subject: [PATCH 2/9] modernize: minmax go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest modernize -minmax -fix ./... Signed-off-by: Sebastiaan van Stijn --- cli/command/image/tree.go | 5 +---- cli/command/service/list_test.go | 5 +---- cli/command/service/progress/progress.go | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/cli/command/image/tree.go b/cli/command/image/tree.go index 70b74828679b..6616e9e207a1 100644 --- a/cli/command/image/tree.go +++ b/cli/command/image/tree.go @@ -389,10 +389,7 @@ func generateLegend(out tui.Output, width uint) string { } legend += legendSb371.String() - r := int(width) - tui.Width(legend) - if r < 0 { - r = 0 - } + r := max(int(width)-tui.Width(legend), 0) legend = strings.Repeat(" ", r) + legend return legend } diff --git a/cli/command/service/list_test.go b/cli/command/service/list_test.go index 8d6462e0bddf..78bedbac5143 100644 --- a/cli/command/service/list_test.go +++ b/cli/command/service/list_test.go @@ -191,10 +191,7 @@ func generateServices(t *testing.T, opts clusterOpts) client.ServiceListResult { t.Helper() // Can't have more global tasks than nodes - globalTasks := opts.runningTasks - if globalTasks > opts.activeNodes { - globalTasks = opts.activeNodes - } + globalTasks := min(opts.runningTasks, opts.activeNodes) return client.ServiceListResult{ Items: []swarm.Service{ *builders.Service( diff --git a/cli/command/service/progress/progress.go b/cli/command/service/progress/progress.go index a4de39b8fbdf..94b59d950eea 100644 --- a/cli/command/service/progress/progress.go +++ b/cli/command/service/progress/progress.go @@ -662,10 +662,7 @@ func (u *replicatedJobProgressUpdater) writeOverallProgress(active, completed in }) // actualDesired is the lesser of MaxConcurrent, or the remaining tasks - actualDesired := u.total - completed - if actualDesired > u.concurrent { - actualDesired = u.concurrent - } + actualDesired := min(u.total-completed, u.concurrent) _ = u.progressOut.WriteProgress(progress.Progress{ ID: "active tasks", From 4c7d40cf775c1b611469e62c2038d46b96b69b77 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Feb 2026 13:14:37 +0100 Subject: [PATCH 3/9] modernize: mapsloop go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest modernize -mapsloop -fix ./... Signed-off-by: Sebastiaan van Stijn --- cli/command/context.go | 5 ++--- cli/command/node/update.go | 8 +++++--- cli/command/service/update.go | 13 ++++--------- cli/compose/loader/loader.go | 5 ++--- cli/compose/template/template.go | 5 ++--- cli/compose/types/types.go | 5 ++--- cli/config/configfile/file.go | 8 +++++--- 7 files changed, 22 insertions(+), 27 deletions(-) diff --git a/cli/command/context.go b/cli/command/context.go index 2ca82a5a88f3..b926b303aa1a 100644 --- a/cli/command/context.go +++ b/cli/command/context.go @@ -6,6 +6,7 @@ package command import ( "encoding/json" "errors" + "maps" "github.com/docker/cli/cli/context/store" ) @@ -23,9 +24,7 @@ func (dc DockerContext) MarshalJSON() ([]byte, error) { s["Description"] = dc.Description } if dc.AdditionalFields != nil { - for k, v := range dc.AdditionalFields { - s[k] = v - } + maps.Copy(s, dc.AdditionalFields) } return json.Marshal(s) } diff --git a/cli/command/node/update.go b/cli/command/node/update.go index 1bfd4949e282..38e94bd2fa80 100644 --- a/cli/command/node/update.go +++ b/cli/command/node/update.go @@ -1,9 +1,13 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package node import ( "context" "errors" "fmt" + "maps" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -99,9 +103,7 @@ func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) error { } if flags.Changed(flagLabelAdd) { labels := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetSlice() - for k, v := range opts.ConvertKVStringsToMap(labels) { - spec.Annotations.Labels[k] = v - } + maps.Copy(spec.Annotations.Labels, opts.ConvertKVStringsToMap(labels)) } if flags.Changed(flagLabelRemove) { keys := flags.Lookup(flagLabelRemove).Value.(*opts.ListOpts).GetSlice() diff --git a/cli/command/service/update.go b/cli/command/service/update.go index 54e83d442ea7..bc4ccc1664e1 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "maps" "net/netip" "slices" "sort" @@ -685,9 +686,7 @@ func updateContainerLabels(flags *pflag.FlagSet, field *map[string]string) { } values := flags.Lookup(flagContainerLabelAdd).Value.(*opts.ListOpts).GetSlice() - for key, value := range opts.ConvertKVStringsToMap(values) { - (*field)[key] = value - } + maps.Copy((*field), opts.ConvertKVStringsToMap(values)) } } @@ -704,9 +703,7 @@ func updateLabels(flags *pflag.FlagSet, field *map[string]string) { } values := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetSlice() - for key, value := range opts.ConvertKVStringsToMap(values) { - (*field)[key] = value - } + maps.Copy((*field), opts.ConvertKVStringsToMap(values)) } } @@ -723,9 +720,7 @@ func updateSysCtls(flags *pflag.FlagSet, field *map[string]string) { } values := flags.Lookup(flagSysCtlAdd).Value.(*opts.ListOpts).GetSlice() - for key, value := range opts.ConvertKVStringsToMap(values) { - (*field)[key] = value - } + maps.Copy((*field), opts.ConvertKVStringsToMap(values)) } } diff --git a/cli/compose/loader/loader.go b/cli/compose/loader/loader.go index b8f725cb1a9e..eec1967090bf 100644 --- a/cli/compose/loader/loader.go +++ b/cli/compose/loader/loader.go @@ -6,6 +6,7 @@ package loader import ( "errors" "fmt" + "maps" "path" "path/filepath" "reflect" @@ -249,9 +250,7 @@ func GetDeprecatedProperties(configDicts ...map[string]any) map[string]string { for _, configDict := range configDicts { deprecatedProperties := getProperties(getServices(configDict), types.DeprecatedProperties) - for key, value := range deprecatedProperties { - deprecated[key] = value - } + maps.Copy(deprecated, deprecatedProperties) } return deprecated diff --git a/cli/compose/template/template.go b/cli/compose/template/template.go index b8a77b9bc08e..1a32b90bd973 100644 --- a/cli/compose/template/template.go +++ b/cli/compose/template/template.go @@ -5,6 +5,7 @@ package template import ( "fmt" + "maps" "regexp" "strings" @@ -140,9 +141,7 @@ func recurseExtract(value any, pattern regexper) map[string]string { case map[string]any: for _, elem := range val { submap := recurseExtract(elem, pattern) - for k, v := range submap { - m[k] = v - } + maps.Copy(m, submap) } case []any: diff --git a/cli/compose/types/types.go b/cli/compose/types/types.go index fdc0eb44feda..011353f7f88f 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -6,6 +6,7 @@ package types import ( "encoding/json" "fmt" + "maps" "strconv" "time" @@ -129,9 +130,7 @@ func (c Config) MarshalJSON() ([]byte, error) { if len(c.Configs) > 0 { m["configs"] = c.Configs } - for k, v := range c.Extras { - m[k] = v - } + maps.Copy(m, c.Extras) return json.Marshal(m) } diff --git a/cli/config/configfile/file.go b/cli/config/configfile/file.go index fab3ed4cba13..246f23e983d8 100644 --- a/cli/config/configfile/file.go +++ b/cli/config/configfile/file.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package configfile import ( @@ -6,6 +9,7 @@ import ( "errors" "fmt" "io" + "maps" "os" "path/filepath" "strings" @@ -374,9 +378,7 @@ func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) { auths := make(map[string]types.AuthConfig) addAll := func(from map[string]types.AuthConfig) { - for reg, ac := range from { - auths[reg] = ac - } + maps.Copy(auths, from) } defaultStore := configFile.GetCredentialsStore("") From 2875e480249be07ae0c2b160731a476fa4d52cec Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Feb 2026 13:16:13 +0100 Subject: [PATCH 4/9] modernize: stringscut go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest modernize -stringscut -fix ./... Signed-off-by: Sebastiaan van Stijn --- cli/command/telemetry_utils.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/command/telemetry_utils.go b/cli/command/telemetry_utils.go index dadbd13fd72c..179177c02bcc 100644 --- a/cli/command/telemetry_utils.go +++ b/cli/command/telemetry_utils.go @@ -155,11 +155,11 @@ func (e statusError) Error() string { // Note: The root command's name is excluded. If cmd is the root cmd, return "" func getCommandName(cmd *cobra.Command) string { fullCmdName := getFullCommandName(cmd) - i := strings.Index(fullCmdName, " ") - if i == -1 { + _, after, ok := strings.Cut(fullCmdName, " ") + if !ok { return "" } - return fullCmdName[i+1:] + return after } // getFullCommandName gets the full cobra command name in the format From 7f5bb1e99cc17769d98a343dcaab175947e30f23 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Feb 2026 13:17:36 +0100 Subject: [PATCH 5/9] modernize: testingcontext go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest modernize -testingcontext -fix ./... Signed-off-by: Sebastiaan van Stijn --- cli/command/container/signals_test.go | 3 +-- cmd/docker/builder_test.go | 12 ++++-------- cmd/docker/docker_test.go | 3 +-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/cli/command/container/signals_test.go b/cli/command/container/signals_test.go index f5e24c47dd0b..a8b03d895af2 100644 --- a/cli/command/container/signals_test.go +++ b/cli/command/container/signals_test.go @@ -11,8 +11,7 @@ import ( ) func TestForwardSignals(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() called := make(chan struct{}) apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) { diff --git a/cmd/docker/builder_test.go b/cmd/docker/builder_test.go index f3688e0c8bbc..75b94e3fa919 100644 --- a/cmd/docker/builder_test.go +++ b/cmd/docker/builder_test.go @@ -22,8 +22,7 @@ import ( var pluginFilename = "docker-buildx" func TestBuildWithBuilder(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() + ctx := t.Context() testcases := []struct { name string @@ -131,8 +130,7 @@ func (*fakeClient) Ping(context.Context, client.PingOptions) (client.PingResult, } func TestBuildkitDisabled(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() + ctx := t.Context() t.Setenv("DOCKER_BUILDKIT", "0") @@ -172,8 +170,7 @@ func TestBuildkitDisabled(t *testing.T) { } func TestBuilderBroken(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() + ctx := t.Context() dir := fs.NewDir(t, t.Name(), fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0o777)), @@ -212,8 +209,7 @@ func TestBuilderBroken(t *testing.T) { func TestBuilderBrokenEnforced(t *testing.T) { t.Setenv("DOCKER_BUILDKIT", "1") - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() + ctx := t.Context() dir := fs.NewDir(t, t.Name(), fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0o777)), diff --git a/cmd/docker/docker_test.go b/cmd/docker/docker_test.go index a4529cbb9db3..d3cb536be1c5 100644 --- a/cmd/docker/docker_test.go +++ b/cmd/docker/docker_test.go @@ -38,8 +38,7 @@ func TestDisableFlagsInUseLineIsSet(t *testing.T) { func TestClientDebugEnabled(t *testing.T) { defer debug.Disable() - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() + ctx := t.Context() cli, err := command.NewDockerCli(command.WithBaseContext(ctx)) assert.NilError(t, err) From dd73e2df775ff67b98d8fd74fb995cffc0115cf6 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Feb 2026 13:19:10 +0100 Subject: [PATCH 6/9] modernize: reflecttypefor go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest modernize -reflecttypefor -fix ./... Signed-off-by: Sebastiaan van Stijn --- cli-plugins/manager/candidate_test.go | 2 +- cli/compose/loader/merge_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli-plugins/manager/candidate_test.go b/cli-plugins/manager/candidate_test.go index a0dbb0dd7b56..976aa74bb5fb 100644 --- a/cli-plugins/manager/candidate_test.go +++ b/cli-plugins/manager/candidate_test.go @@ -151,7 +151,7 @@ func TestValidateCandidate(t *testing.T) { assert.ErrorContains(t, err, tc.err) case tc.invalid != "": assert.NilError(t, err) - assert.Assert(t, is.ErrorType(p.Err, reflect.TypeOf(&pluginError{}))) + assert.Assert(t, is.ErrorType(p.Err, reflect.TypeFor[*pluginError]())) assert.ErrorContains(t, p.Err, tc.invalid) default: assert.NilError(t, err) diff --git a/cli/compose/loader/merge_test.go b/cli/compose/loader/merge_test.go index 40445d6c4208..03040cb44416 100644 --- a/cli/compose/loader/merge_test.go +++ b/cli/compose/loader/merge_test.go @@ -1153,7 +1153,7 @@ func TestLoadMultipleServiceVolumes(t *testing.T) { func TestMergeUlimitsConfig(t *testing.T) { specials := &specials{ m: map[reflect.Type]func(dst, src reflect.Value) error{ - reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig, + reflect.TypeFor[*types.UlimitsConfig](): mergeUlimitsConfig, }, } base := map[string]*types.UlimitsConfig{ @@ -1189,7 +1189,7 @@ func TestMergeUlimitsConfig(t *testing.T) { func TestMergeServiceNetworkConfig(t *testing.T) { specials := &specials{ m: map[reflect.Type]func(dst, src reflect.Value) error{ - reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig, + reflect.TypeFor[*types.ServiceNetworkConfig](): mergeServiceNetworkConfig, }, } base := map[string]*types.ServiceNetworkConfig{ From 835d510b78c3e974d20aaa4a0a2288b61ab8036c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Feb 2026 13:25:20 +0100 Subject: [PATCH 7/9] modernize: stringsseq go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest modernize -stringsseq -fix ./... Signed-off-by: Sebastiaan van Stijn --- cli-plugins/manager/hooks.go | 6 ++++-- cli/command/container/opts.go | 2 +- cli/command/container/stats.go | 5 ++++- cli/command/volume/create.go | 7 +++++-- internal/test/strings.go | 7 +++++-- internal/volumespec/volumespec.go | 5 ++++- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/cli-plugins/manager/hooks.go b/cli-plugins/manager/hooks.go index bee212702da4..9d9c4629d106 100644 --- a/cli-plugins/manager/hooks.go +++ b/cli-plugins/manager/hooks.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package manager import ( @@ -141,8 +144,7 @@ func pluginMatch(pluginCfg map[string]string, subCmd string) (string, bool) { return "", false } - commands := strings.Split(configuredPluginHooks, ",") - for _, hookCmd := range commands { + for hookCmd := range strings.SplitSeq(configuredPluginHooks, ",") { if hookMatch(hookCmd, subCmd) { return hookCmd, true } diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index b36d04968c09..9caa830a0d7b 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -902,7 +902,7 @@ func convertToStandardNotation(ports []string) ([]string, error) { for _, publish := range ports { if strings.Contains(publish, "=") { params := map[string]string{"protocol": "tcp"} - for _, param := range strings.Split(publish, ",") { + for param := range strings.SplitSeq(publish, ",") { k, v, ok := strings.Cut(param, "=") if !ok || k == "" { return optsList, fmt.Errorf("invalid publish opts format (should be name=value but got '%s')", param) diff --git a/cli/command/container/stats.go b/cli/command/container/stats.go index ae8cfb465fdd..2fad9cc87d72 100644 --- a/cli/command/container/stats.go +++ b/cli/command/container/stats.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package container import ( @@ -337,7 +340,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions) return err } - for _, line := range strings.Split(statsTextBuffer.String(), "\n") { + for line := range strings.SplitSeq(statsTextBuffer.String(), "\n") { // In case the new text is shorter than the one we are writing over, // we'll append the "erase line" escape sequence to clear the remaining text. _, _ = fmt.Fprintln(&statsTextBuffer, line, "\033[K") diff --git a/cli/command/volume/create.go b/cli/command/volume/create.go index aee5e29a1f56..a0405c97707d 100644 --- a/cli/command/volume/create.go +++ b/cli/command/volume/create.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package volume import ( @@ -166,7 +169,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, options createOptions // each topology takes the form segment=value,segment=value // comma-separated list of equal separated maps segments := map[string]string{} - for _, segment := range strings.Split(top, ",") { + for segment := range strings.SplitSeq(top, ",") { // TODO(dperny): validate topology syntax k, v, _ := strings.Cut(segment, "=") segments[k] = v @@ -181,7 +184,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, options createOptions // each topology takes the form segment=value,segment=value // comma-separated list of equal separated maps segments := map[string]string{} - for _, segment := range strings.Split(top, ",") { + for segment := range strings.SplitSeq(top, ",") { // TODO(dperny): validate topology syntax k, v, _ := strings.Cut(segment, "=") segments[k] = v diff --git a/internal/test/strings.go b/internal/test/strings.go index 2c9ca22d8c3c..2bbae223178b 100644 --- a/internal/test/strings.go +++ b/internal/test/strings.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package test import ( @@ -15,12 +18,12 @@ func CompareMultipleValues(t *testing.T, value, expected string) { // be guaranteed to have the same order as our expected value // We'll create maps and use reflect.DeepEquals to check instead: entriesMap := make(map[string]string) - for _, entry := range strings.Split(value, ",") { + for entry := range strings.SplitSeq(value, ",") { k, v, _ := strings.Cut(entry, "=") entriesMap[k] = v } expMap := make(map[string]string) - for _, exp := range strings.Split(expected, ",") { + for exp := range strings.SplitSeq(expected, ",") { k, v, _ := strings.Cut(exp, "=") expMap[k] = v } diff --git a/internal/volumespec/volumespec.go b/internal/volumespec/volumespec.go index 791805edff38..77dbd14f3a2b 100644 --- a/internal/volumespec/volumespec.go +++ b/internal/volumespec/volumespec.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package volumespec import ( @@ -67,7 +70,7 @@ func populateFieldFromBuffer(char rune, buffer []rune, volume *VolumeConfig) err case char == ':': return errors.New("too many colons") } - for _, option := range strings.Split(strBuffer, ",") { + for option := range strings.SplitSeq(strBuffer, ",") { switch option { case "ro": volume.ReadOnly = true From 6d4b3b5f660953fea1a007735ef4049c45bc7f4b Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Feb 2026 13:35:26 +0100 Subject: [PATCH 8/9] modernize: slicescontains go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest modernize -slicescontains -fix ./... Signed-off-by: Sebastiaan van Stijn --- cli/command/manifest/annotate.go | 10 ++++++---- cli/command/service/update.go | 7 +------ cli/connhelper/connhelper.go | 10 ++++++---- cmd/docker/docker.go | 10 ++++++---- internal/volumespec/volumespec.go | 8 ++------ opts/opts.go | 11 +++++------ opts/swarmopts/port_test.go | 12 +++++------- 7 files changed, 31 insertions(+), 37 deletions(-) diff --git a/cli/command/manifest/annotate.go b/cli/command/manifest/annotate.go index 76385f079c8c..8658022b9094 100644 --- a/cli/command/manifest/annotate.go +++ b/cli/command/manifest/annotate.go @@ -1,9 +1,13 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package manifest import ( "context" "fmt" "path/filepath" + "slices" "github.com/containerd/errdefs" "github.com/docker/cli/cli" @@ -164,10 +168,8 @@ func runManifestAnnotate(dockerCLI command.Cli, opts annotateOptions) error { } func appendIfUnique(list []string, str string) []string { - for _, s := range list { - if s == str { - return list - } + if slices.Contains(list, str) { + return list } return append(list, str) } diff --git a/cli/command/service/update.go b/cli/command/service/update.go index bc4ccc1664e1..be1baca187cc 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -552,12 +552,7 @@ func updateStringToSlice(flags *pflag.FlagSet, flag string, field *[]string) { } func anyChanged(flags *pflag.FlagSet, fields ...string) bool { - for _, flag := range fields { - if flags.Changed(flag) { - return true - } - } - return false + return slices.ContainsFunc(fields, flags.Changed) } func addGenericResources(flags *pflag.FlagSet, spec *swarm.TaskSpec) error { diff --git a/cli/connhelper/connhelper.go b/cli/connhelper/connhelper.go index 25ce7aef022b..38e9a2d72b0b 100644 --- a/cli/connhelper/connhelper.go +++ b/cli/connhelper/connhelper.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + // Package connhelper provides helpers for connecting to a remote daemon host with custom logic. package connhelper @@ -6,6 +9,7 @@ import ( "fmt" "net" "net/url" + "slices" "strings" "github.com/docker/cli/cli/connhelper/commandconn" @@ -89,10 +93,8 @@ func addSSHTimeout(sshFlags []string) []string { // disablePseudoTerminalAllocation disables pseudo-terminal allocation to // prevent SSH from executing as a login shell func disablePseudoTerminalAllocation(sshFlags []string) []string { - for _, flag := range sshFlags { - if flag == "-T" { - return sshFlags - } + if slices.Contains(sshFlags, "-T") { + return sshFlags } return append(sshFlags, "-T") } diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 72347b9517e6..f09b6d74a745 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package main import ( @@ -7,6 +10,7 @@ import ( "os" "os/exec" "os/signal" + "slices" "strings" "syscall" @@ -617,10 +621,8 @@ func findCommand(cmd *cobra.Command, cmds []string) bool { if cmd == nil { return false } - for _, c := range cmds { - if c == cmd.Name() { - return true - } + if slices.Contains(cmds, cmd.Name()) { + return true } return findCommand(cmd.Parent(), cmds) } diff --git a/internal/volumespec/volumespec.go b/internal/volumespec/volumespec.go index 77dbd14f3a2b..37c90ee34adb 100644 --- a/internal/volumespec/volumespec.go +++ b/internal/volumespec/volumespec.go @@ -6,6 +6,7 @@ package volumespec import ( "errors" "fmt" + "slices" "strings" "unicode" "unicode/utf8" @@ -89,12 +90,7 @@ func populateFieldFromBuffer(char rune, buffer []rune, volume *VolumeConfig) err } func isBindOption(option string) bool { - for _, propagation := range mount.Propagations { - if mount.Propagation(option) == propagation { - return true - } - } - return false + return slices.Contains(mount.Propagations, mount.Propagation(option)) } func populateType(volume *VolumeConfig) { diff --git a/opts/opts.go b/opts/opts.go index 0b8979b6b6c6..142740bf77b8 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package opts import ( @@ -7,6 +10,7 @@ import ( "math/big" "net" "path" + "slices" "strings" "github.com/docker/cli/internal/lazyregexp" @@ -104,12 +108,7 @@ func (opts *ListOpts) GetAllOrEmpty() []string { // Get checks the existence of the specified key. func (opts *ListOpts) Get(key string) bool { - for _, k := range *opts.values { - if k == key { - return true - } - } - return false + return slices.Contains(*opts.values, key) } // Len returns the amount of element in the slice. diff --git a/opts/swarmopts/port_test.go b/opts/swarmopts/port_test.go index 5c283f2b7a33..4aa7f702d71d 100644 --- a/opts/swarmopts/port_test.go +++ b/opts/swarmopts/port_test.go @@ -1,8 +1,12 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package swarmopts import ( "bytes" "os" + "slices" "testing" "github.com/docker/go-connections/nat" @@ -371,13 +375,7 @@ func TestConvertPortToPortConfigWithIP(t *testing.T) { func assertContains(t *testing.T, portConfigs []swarm.PortConfig, expected swarm.PortConfig) { t.Helper() - contains := false - for _, portConfig := range portConfigs { - if portConfig == expected { - contains = true - break - } - } + contains := slices.Contains(portConfigs, expected) if !contains { t.Errorf("expected %v to contain %v, did not", portConfigs, expected) } From fddfe63ef964a9a26c13a4722fdf653f81023977 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 11 Feb 2026 13:38:38 +0100 Subject: [PATCH 9/9] modernize: fmtappendf go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest modernize -fmtappendf -fix ./... Signed-off-by: Sebastiaan van Stijn --- cli/compose/types/types.go | 4 ++-- cli/config/credentials/native_store_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/compose/types/types.go b/cli/compose/types/types.go index 011353f7f88f..83d72423fdf4 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -351,7 +351,7 @@ func (u UnitBytes) MarshalYAML() (any, error) { // MarshalJSON makes UnitBytes implement json.Marshaler func (u UnitBytes) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%d"`, u)), nil + return fmt.Appendf(nil, `"%d"`, u), nil } // RestartPolicy the service restart policy @@ -561,7 +561,7 @@ func (e External) MarshalJSON() ([]byte, error) { if e.Name == "" { return []byte(strconv.FormatBool(e.External)), nil } - return []byte(fmt.Sprintf(`{"name": %q}`, e.Name)), nil + return fmt.Appendf(nil, `{"name": %q}`, e.Name), nil } // CredentialSpecConfig for credential spec on Windows diff --git a/cli/config/credentials/native_store_test.go b/cli/config/credentials/native_store_test.go index cb31071e602a..02c85dbcfb7d 100644 --- a/cli/config/credentials/native_store_test.go +++ b/cli/config/credentials/native_store_test.go @@ -73,10 +73,10 @@ func (m *mockCommand) Output() ([]byte, error) { return []byte("program failed"), errCommandExited } case "list": - return []byte(fmt.Sprintf(`{"%s": "%s", "%s": "%s"}`, validServerAddress, "foo", validServerAddress2, "")), nil + return fmt.Appendf(nil, `{"%s": "%s", "%s": "%s"}`, validServerAddress, "foo", validServerAddress2, ""), nil } - return []byte(fmt.Sprintf("unknown argument %q with %q", m.arg, inS)), errCommandExited + return fmt.Appendf(nil, "unknown argument %q with %q", m.arg, inS), errCommandExited } // Input sets the input to send to a remote credentials helper.