From e54eb133cbc4ade4410d2bd59c3e50286ecd4c4a Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 2 Sep 2026 02:24:46 +0200 Subject: [PATCH 1/2] update naturalsort sorting with slices Signed-off-by: Sebastiaan van Stijn --- cli-plugins/manager/manager.go | 9 ++++++--- cli/cobra.go | 9 ++++++--- cli/command/config/ls.go | 10 +++++++--- cli/command/container/port.go | 9 +++++---- cli/command/context/list.go | 6 +++--- cli/command/network/list.go | 10 +++++++--- cli/command/node/list.go | 10 +++++++--- cli/command/plugin/list.go | 10 +++++++--- cli/command/secret/ls.go | 10 +++++++--- cli/command/service/formatter.go | 5 ++--- cli/command/stack/list.go | 9 ++++++--- cli/command/stack/services.go | 10 +++++++--- cli/command/system/prune.go | 6 ++---- cli/command/task/print.go | 31 +++++++++++------------------- cli/command/volume/list.go | 10 +++++++--- cli/context/store/metadatastore.go | 6 +++--- 16 files changed, 93 insertions(+), 67 deletions(-) diff --git a/cli-plugins/manager/manager.go b/cli-plugins/manager/manager.go index bdbed4e023d2..bf398533aa22 100644 --- a/cli-plugins/manager/manager.go +++ b/cli-plugins/manager/manager.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.26 + package manager import ( @@ -6,7 +9,7 @@ import ( "os" "os/exec" "path/filepath" - "sort" + slices "slices" "strings" "sync" @@ -164,8 +167,8 @@ func ListPlugins(dockerCli config.Provider, rootcmd *cobra.Command) ([]Plugin, e return nil, err } - sort.Slice(plugins, func(i, j int) bool { - return sortorder.NaturalLess(plugins[i].Name, plugins[j].Name) + slices.SortFunc(plugins, func(a, b Plugin) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) return plugins, nil diff --git a/cli/cobra.go b/cli/cobra.go index 4ec721f88772..bfcb1bdcc8f2 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -1,9 +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.26 + package cli import ( "fmt" "os" - "sort" + "slices" "strings" "github.com/docker/cli/cli-plugins/metadata" @@ -273,8 +276,8 @@ func topCommands(cmd *cobra.Command) []*cobra.Command { cmds = append(cmds, sub) } } - sort.SliceStable(cmds, func(i, j int) bool { - return sortorder.NaturalLess(cmds[i].Annotations["category-top"], cmds[j].Annotations["category-top"]) + slices.SortStableFunc(cmds, func(a, b *cobra.Command) int { + return sortorder.NaturalCompare(a.Annotations["category-top"], b.Annotations["category-top"]) }) return cmds } diff --git a/cli/command/config/ls.go b/cli/command/config/ls.go index cbed96200ee9..94541fcb575d 100644 --- a/cli/command/config/ls.go +++ b/cli/command/config/ls.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package config import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -62,8 +66,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er } } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Spec.Name, res.Items[j].Spec.Name) + slices.SortFunc(res.Items, func(a, b swarm.Config) int { + return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name) }) configCtx := formatter.Context{ diff --git a/cli/command/container/port.go b/cli/command/container/port.go index 534ddd1d34c0..839e40ca2f96 100644 --- a/cli/command/container/port.go +++ b/cli/command/container/port.go @@ -1,10 +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.26 + package container import ( "context" "fmt" "net" - "sort" + "slices" "strings" "github.com/docker/cli/cli" @@ -80,9 +83,7 @@ func runPort(ctx context.Context, dockerCli command.Cli, opts *portOptions) erro } if len(out) > 0 { - sort.Slice(out, func(i, j int) bool { - return sortorder.NaturalLess(out[i], out[j]) - }) + slices.SortFunc(out, sortorder.NaturalCompare) _, _ = fmt.Fprintln(dockerCli.Out(), strings.Join(out, "\n")) } diff --git a/cli/command/context/list.go b/cli/command/context/list.go index 67e9b9c66011..3507ae9c2205 100644 --- a/cli/command/context/list.go +++ b/cli/command/context/list.go @@ -6,7 +6,7 @@ package context import ( "fmt" "os" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -101,8 +101,8 @@ func runList(dockerCli command.Cli, opts *listOptions) error { Error: errMsg, }) } - sort.Slice(contexts, func(i, j int) bool { - return sortorder.NaturalLess(contexts[i].Name, contexts[j].Name) + slices.SortFunc(contexts, func(a, b *formatter.ClientContext) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) if err := format(dockerCli, opts, contexts); err != nil { return err diff --git a/cli/command/network/list.go b/cli/command/network/list.go index 70d94ec78655..9f2c90f4598a 100644 --- a/cli/command/network/list.go +++ b/cli/command/network/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package network import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/network" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -61,8 +65,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er } } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Name, res.Items[j].Name) + slices.SortFunc(res.Items, func(a, b network.Summary) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) networksCtx := formatter.Context{ diff --git a/cli/command/node/list.go b/cli/command/node/list.go index 526ec655878f..48e5641f2999 100644 --- a/cli/command/node/list.go +++ b/cli/command/node/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package node import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -73,8 +77,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er Output: dockerCLI.Out(), Format: newFormat(format, options.quiet), } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Description.Hostname, res.Items[j].Description.Hostname) + slices.SortFunc(res.Items, func(a, b swarm.Node) int { + return sortorder.NaturalCompare(a.Description.Hostname, b.Description.Hostname) }) return formatWrite(nodesCtx, res, info) } diff --git a/cli/command/plugin/list.go b/cli/command/plugin/list.go index fb651ab25abe..3606ac5bbfb4 100644 --- a/cli/command/plugin/list.go +++ b/cli/command/plugin/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package plugin import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/plugin" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -54,8 +58,8 @@ func runList(ctx context.Context, dockerCli command.Cli, options listOptions) er return err } - sort.Slice(resp.Items, func(i, j int) bool { - return sortorder.NaturalLess(resp.Items[i].Name, resp.Items[j].Name) + slices.SortFunc(resp.Items, func(a, b plugin.Plugin) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) format := options.format diff --git a/cli/command/secret/ls.go b/cli/command/secret/ls.go index d68b52165c25..999ea53e38b3 100644 --- a/cli/command/secret/ls.go +++ b/cli/command/secret/ls.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package secret import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -59,8 +63,8 @@ func runSecretList(ctx context.Context, dockerCLI command.Cli, options listOptio } } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Spec.Name, res.Items[j].Spec.Name) + slices.SortFunc(res.Items, func(a, b swarm.Secret) int { + return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name) }) secretCtx := formatter.Context{ diff --git a/cli/command/service/formatter.go b/cli/command/service/formatter.go index 5b77bbb1a5ee..4440f6165dce 100644 --- a/cli/command/service/formatter.go +++ b/cli/command/service/formatter.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "slices" - "sort" "strconv" "strings" "time" @@ -620,8 +619,8 @@ func NewListFormat(source string, quiet bool) formatter.Format { // ListFormatWrite writes the context func ListFormatWrite(ctx formatter.Context, services client.ServiceListResult) error { render := func(format func(subContext formatter.SubContext) error) error { - sort.Slice(services.Items, func(i, j int) bool { - return sortorder.NaturalLess(services.Items[i].Spec.Name, services.Items[j].Spec.Name) + slices.SortFunc(services.Items, func(a, b swarm.Service) int { + return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name) }) for _, service := range services.Items { serviceCtx := &serviceContext{service: service} diff --git a/cli/command/stack/list.go b/cli/command/stack/list.go index 6461b0902076..8463bd551571 100644 --- a/cli/command/stack/list.go +++ b/cli/command/stack/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package stack import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -52,8 +55,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, opts listOptions) error Output: dockerCLI.Out(), Format: format, } - sort.Slice(stacks, func(i, j int) bool { - return sortorder.NaturalLess(stacks[i].Name, stacks[j].Name) + slices.SortFunc(stacks, func(a, b stackSummary) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) return stackWrite(stackCtx, stacks) } diff --git a/cli/command/stack/services.go b/cli/command/stack/services.go index 26d252d98e93..49bf573da7c1 100644 --- a/cli/command/stack/services.go +++ b/cli/command/stack/services.go @@ -1,9 +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.26 + package stack import ( "context" "fmt" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -12,6 +15,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" cliopts "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -68,8 +72,8 @@ func formatWrite(dockerCLI command.Cli, services client.ServiceListResult, opts _, _ = fmt.Fprintln(dockerCLI.Err(), "Nothing found in stack:", opts.namespace) return nil } - sort.Slice(services.Items, func(i, j int) bool { - return sortorder.NaturalLess(services.Items[i].Spec.Name, services.Items[j].Spec.Name) + slices.SortFunc(services.Items, func(a, b swarm.Service) int { + return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name) }) f := opts.format diff --git a/cli/command/system/prune.go b/cli/command/system/prune.go index 698b6d1bb365..33544bf5b52c 100644 --- a/cli/command/system/prune.go +++ b/cli/command/system/prune.go @@ -8,7 +8,7 @@ import ( "context" "errors" "fmt" - "sort" + "slices" "text/template" "github.com/containerd/errdefs" @@ -172,9 +172,7 @@ func dryRun(ctx context.Context, dockerCli command.Cli, options pruneOptions) (s filters = append(filters, name+"="+v) } } - sort.Slice(filters, func(i, j int) bool { - return sortorder.NaturalLess(filters[i], filters[j]) - }) + slices.SortFunc(filters, sortorder.NaturalCompare) } var buffer bytes.Buffer diff --git a/cli/command/task/print.go b/cli/command/task/print.go index ae27d42d0133..846a293cdcce 100644 --- a/cli/command/task/print.go +++ b/cli/command/task/print.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.26 + package task import ( + "cmp" "context" "fmt" - "sort" + "slices" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/formatter" @@ -14,24 +18,6 @@ import ( "github.com/moby/moby/client" ) -type tasksSortable []swarm.Task - -func (t tasksSortable) Len() int { - return len(t) -} - -func (t tasksSortable) Swap(i, j int) { - t[i], t[j] = t[j], t[i] -} - -func (t tasksSortable) Less(i, j int) bool { - if t[i].Name != t[j].Name { - return sortorder.NaturalLess(t[i].Name, t[j].Name) - } - // Sort tasks for the same service and slot by most recent. - return t[j].Meta.CreatedAt.Before(t[i].CreatedAt) -} - // Print task information in a format. // Besides this, command `docker node ps ` // and `docker stack ps` will call this, too. @@ -44,7 +30,12 @@ func Print(ctx context.Context, dockerCli command.Cli, tasks client.TaskListResu // First sort tasks, so that all tasks (including previous ones) of the same // service and slot are together. This must be done first, to print "previous" // tasks indented - sort.Stable(tasksSortable(tasks.Items)) + slices.SortStableFunc(tasks.Items, func(a, b swarm.Task) int { + return cmp.Or( + sortorder.NaturalCompare(a.Name, b.Name), + b.Meta.CreatedAt.Compare(a.Meta.CreatedAt), + ) + }) names := map[string]string{} nodes := map[string]string{} diff --git a/cli/command/volume/list.go b/cli/command/volume/list.go index 1e0df1f84b59..bab03f9f44f5 100644 --- a/cli/command/volume/list.go +++ b/cli/command/volume/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package volume import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/volume" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -85,8 +89,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er } } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Name, res.Items[j].Name) + slices.SortFunc(res.Items, func(a, b volume.Volume) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) volumeCtx := formatter.Context{ diff --git a/cli/context/store/metadatastore.go b/cli/context/store/metadatastore.go index 8bada79fe51d..1751e820690b 100644 --- a/cli/context/store/metadatastore.go +++ b/cli/context/store/metadatastore.go @@ -10,7 +10,7 @@ import ( "os" "path/filepath" "reflect" - "sort" + "slices" "github.com/fvbommel/sortorder" "github.com/moby/sys/atomicwriter" @@ -122,8 +122,8 @@ func (s *metadataStore) list() ([]Metadata, error) { } res = append(res, c) } - sort.Slice(res, func(i, j int) bool { - return sortorder.NaturalLess(res[i].Name, res[j].Name) + slices.SortFunc(res, func(a, b Metadata) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) return res, nil } From 36e01adcfa55166fd30a7b44c4b96d9d28a72774 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 2 Sep 2026 02:54:58 +0200 Subject: [PATCH 2/2] cmd/docker-trust: update naturalsort sorting with slices Signed-off-by: Sebastiaan van Stijn --- cmd/docker-trust/trust/common.go | 5 ++--- cmd/docker-trust/trust/inspect_pretty.go | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/docker-trust/trust/common.go b/cmd/docker-trust/trust/common.go index f839bd26bd8c..c743d49722ce 100644 --- a/cmd/docker-trust/trust/common.go +++ b/cmd/docker-trust/trust/common.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "fmt" "slices" - "sort" "strings" "github.com/docker/cli/cli/command" @@ -163,8 +162,8 @@ func matchReleasedSignatures(allTargets []client.TargetSignedStruct) []trustTagR for targetKey, signers := range releasedTargetRows { signatureRows = append(signatureRows, trustTagRow{targetKey, signers}) } - sort.Slice(signatureRows, func(i, j int) bool { - return sortorder.NaturalLess(signatureRows[i].SignedTag, signatureRows[j].SignedTag) + slices.SortFunc(signatureRows, func(a, b trustTagRow) int { + return sortorder.NaturalCompare(a.SignedTag, b.SignedTag) }) return signatureRows } diff --git a/cmd/docker-trust/trust/inspect_pretty.go b/cmd/docker-trust/trust/inspect_pretty.go index 68e717be71e9..4336f0872a24 100644 --- a/cmd/docker-trust/trust/inspect_pretty.go +++ b/cmd/docker-trust/trust/inspect_pretty.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "slices" - "sort" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/formatter" @@ -85,15 +84,15 @@ func printSignerInfo(out io.Writer, roleToKeyIDs map[string][]string) error { Format: defaultSignerInfoTableFormat, Trunc: true, } - formattedSignerInfo := []signerInfo{} + formattedSignerInfo := make([]signerInfo, 0, len(roleToKeyIDs)) for name, keyIDs := range roleToKeyIDs { formattedSignerInfo = append(formattedSignerInfo, signerInfo{ Name: name, Keys: keyIDs, }) } - sort.Slice(formattedSignerInfo, func(i, j int) bool { - return sortorder.NaturalLess(formattedSignerInfo[i].Name, formattedSignerInfo[j].Name) + slices.SortFunc(formattedSignerInfo, func(a, b signerInfo) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) return signerInfoWrite(signerInfoCtx, formattedSignerInfo) }