Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ls: no-trunc opt #2138

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 78 additions & 8 deletions commands/ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const (
)

type lsOptions struct {
format string
format string
noTrunc bool
}

func runLs(dockerCli command.Cli, in lsOptions) error {
Expand Down Expand Up @@ -75,7 +76,7 @@ func runLs(dockerCli command.Cli, in lsOptions) error {
return err
}

if hasErrors, err := lsPrint(dockerCli, current, builders, in.format); err != nil {
if hasErrors, err := lsPrint(dockerCli, current, builders, in); err != nil {
return err
} else if hasErrors {
_, _ = fmt.Fprintf(dockerCli.Err(), "\n")
Expand Down Expand Up @@ -110,21 +111,23 @@ func lsCmd(dockerCli command.Cli) *cobra.Command {

flags := cmd.Flags()
flags.StringVar(&options.format, "format", formatter.TableFormatKey, "Format the output")
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")

// hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")

return cmd
}

func lsPrint(dockerCli command.Cli, current *store.NodeGroup, builders []*builder.Builder, format string) (hasErrors bool, _ error) {
if format == formatter.TableFormatKey {
format = lsDefaultTableFormat
func lsPrint(dockerCli command.Cli, current *store.NodeGroup, builders []*builder.Builder, in lsOptions) (hasErrors bool, _ error) {
if in.format == formatter.TableFormatKey {
in.format = lsDefaultTableFormat
}

ctx := formatter.Context{
Output: dockerCli.Out(),
Format: formatter.Format(format),
Format: formatter.Format(in.format),
Trunc: !in.noTrunc,
}

sort.SliceStable(builders, func(i, j int) bool {
Expand All @@ -141,11 +144,12 @@ func lsPrint(dockerCli command.Cli, current *store.NodeGroup, builders []*builde
render := func(format func(subContext formatter.SubContext) error) error {
for _, b := range builders {
if err := format(&lsContext{
format: ctx.Format,
trunc: ctx.Trunc,
Builder: &lsBuilder{
Builder: b,
Current: b.Name == current.Name,
},
format: ctx.Format,
}); err != nil {
return err
}
Expand All @@ -163,6 +167,7 @@ func lsPrint(dockerCli command.Cli, current *store.NodeGroup, builders []*builde
}
if err := format(&lsContext{
format: ctx.Format,
trunc: ctx.Trunc,
Builder: &lsBuilder{
Builder: b,
Current: b.Name == current.Name,
Expand Down Expand Up @@ -199,6 +204,7 @@ type lsContext struct {
Builder *lsBuilder

format formatter.Format
trunc bool
node builder.Node
}

Expand Down Expand Up @@ -264,7 +270,17 @@ func (c *lsContext) Platforms() string {
if c.node.Name == "" {
return ""
}
return strings.Join(platformutil.FormatInGroups(c.node.Node.Platforms, c.node.Platforms), ", ")
platforms := platformutil.FormatInGroups(c.node.Node.Platforms, c.node.Platforms)
if c.trunc && c.format.IsTable() {
tplatforms := truncPlatforms(platforms, 4)
left := len(platforms) - len(tplatforms)
out := strings.Join(tplatforms, ", ")
if left > 0 {
out += fmt.Sprintf(", (+%d)", left)
}
return out
}
return strings.Join(platforms, ", ")
}

func (c *lsContext) Error() string {
Expand All @@ -275,3 +291,57 @@ func (c *lsContext) Error() string {
}
return ""
}

func truncPlatforms(platforms []string, max int) []string {
popularPlatforms := []string{
"linux/amd64",
"linux/arm64",
"linux/arm/v7",
"linux/ppc64le",
"linux/s390x",
"linux/riscv64",
"linux/mips64",
}
var res []string
m := map[string]struct{}{}
for _, prefp := range popularPlatforms {
for _, p := range platforms {
if len(res) >= max {
break
}
pp := strings.TrimSuffix(p, "*")
if _, ok := m[pp]; ok {
continue
}
if pp == prefp {
res = append(res, p)
m[pp] = struct{}{}
}
}
}
var left []string
for _, p := range platforms {
pp := strings.TrimSuffix(p, "*")
if _, ok := m[pp]; !ok {
left = append(left, p)
}
}
if len(left) > 0 && len(res) < max {
cl := max - len(res)
if cl > len(left) {
cl = len(left)
}
res = append(res, left[:cl]...)
}
sort.SliceStable(res, func(i, j int) bool {
ipref := strings.HasSuffix(res[i], "*")
jpref := strings.HasSuffix(res[j], "*")
if ipref && !jpref {
return true
} else if !ipref && jpref {
return false
}
return i < j
})
return res
}
65 changes: 65 additions & 0 deletions commands/ls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package commands

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestTruncPlatforms(t *testing.T) {
tests := []struct {
name string
platforms []string
expected []string
max int
}{
{
name: "arm64 preferred and emulated",
platforms: []string{"linux/arm64*", "linux/amd64", "linux/amd64/v2", "linux/riscv64", "linux/ppc64le", "linux/s390x", "linux/386", "linux/mips64le", "linux/mips64", "linux/arm/v7", "linux/arm/v6"},
expected: []string{"linux/arm64*", "linux/amd64", "linux/arm/v7", "linux/ppc64le"},
Comment on lines +18 to +19
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the priority over popular platforms to iterate over them until finding a match instead of using a map so we add some weight from the list.

I think this truncated output makes sense based on popular CPU architectures. I will take a look about stripping the variant.

max: 4,
},
{
name: "riscv64 preferred only",
platforms: []string{"linux/riscv64*"},
expected: []string{"linux/riscv64*"},
max: 4,
},
{
name: "amd64 no preferred and emulated",
platforms: []string{"linux/amd64", "linux/amd64/v2", "linux/amd64/v3", "linux/386", "linux/arm64", "linux/riscv64", "linux/ppc64le", "linux/s390x", "linux/mips64le", "linux/mips64", "linux/arm/v7", "linux/arm/v6"},
expected: []string{"linux/amd64", "linux/arm64", "linux/arm/v7", "linux/ppc64le"},
max: 4,
},
{
name: "amd64 no preferred",
platforms: []string{"linux/amd64", "linux/386"},
expected: []string{"linux/amd64", "linux/386"},
max: 4,
},
{
name: "arm64 no preferred",
platforms: []string{"linux/arm64", "linux/arm/v7", "linux/arm/v6"},
expected: []string{"linux/arm64", "linux/arm/v7", "linux/arm/v6"},
max: 4,
},
{
name: "all preferred",
platforms: []string{"darwin/arm64*", "linux/arm64*", "linux/arm/v5*", "linux/arm/v6*", "linux/arm/v7*", "windows/arm64*"},
expected: []string{"linux/arm64*", "linux/arm/v7*", "darwin/arm64*", "linux/arm/v5*"},
max: 4,
},
{
name: "no major preferred",
platforms: []string{"linux/amd64/v2*", "linux/arm/v6*", "linux/mips64le*", "linux/amd64", "linux/amd64/v3", "linux/386", "linux/arm64", "linux/riscv64", "linux/ppc64le", "linux/s390x", "linux/mips64", "linux/arm/v7"},
expected: []string{"linux/amd64", "linux/arm64", "linux/arm/v7", "linux/ppc64le"},
max: 4,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expected, truncPlatforms(tt.platforms, tt.max))
})
}
}
7 changes: 4 additions & 3 deletions docs/reference/buildx_ls.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ List builder instances

### Options

| Name | Type | Default | Description |
|:----------------------|:---------|:--------|:------------------|
| [`--format`](#format) | `string` | `table` | Format the output |
| Name | Type | Default | Description |
|:----------------------|:---------|:--------|:----------------------|
| [`--format`](#format) | `string` | `table` | Format the output |
| `--no-trunc` | | | Don't truncate output |


<!---MARKER_GEN_END-->
Expand Down