mirror of https://github.com/docker/buildx.git
commit
7c91f3d0dd
151
commands/ls.go
151
commands/ls.go
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/store"
|
||||
"github.com/docker/buildx/store/storeutil"
|
||||
|
@ -35,7 +36,8 @@ const (
|
|||
)
|
||||
|
||||
type lsOptions struct {
|
||||
format string
|
||||
format string
|
||||
noTrunc bool
|
||||
}
|
||||
|
||||
func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error {
|
||||
|
@ -72,7 +74,7 @@ func runLs(ctx context.Context, 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")
|
||||
|
@ -107,6 +109,7 @@ 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")
|
||||
|
@ -114,14 +117,15 @@ func lsCmd(dockerCli command.Cli) *cobra.Command {
|
|||
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 {
|
||||
|
@ -138,11 +142,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
|
||||
}
|
||||
|
@ -160,6 +165,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,
|
||||
|
@ -196,6 +202,7 @@ type lsContext struct {
|
|||
Builder *lsBuilder
|
||||
|
||||
format formatter.Format
|
||||
trunc bool
|
||||
node builder.Node
|
||||
}
|
||||
|
||||
|
@ -261,7 +268,11 @@ func (c *lsContext) Platforms() string {
|
|||
if c.node.Name == "" {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(platformutil.FormatInGroups(c.node.Node.Platforms, c.node.Platforms), ", ")
|
||||
pfs := platformutil.FormatInGroups(c.node.Node.Platforms, c.node.Platforms)
|
||||
if c.trunc && c.format.IsTable() {
|
||||
return truncPlatforms(pfs, 4).String()
|
||||
}
|
||||
return strings.Join(pfs, ", ")
|
||||
}
|
||||
|
||||
func (c *lsContext) Error() string {
|
||||
|
@ -272,3 +283,127 @@ func (c *lsContext) Error() string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var truncMajorPlatforms = []string{
|
||||
"linux/amd64",
|
||||
"linux/arm64",
|
||||
"linux/arm",
|
||||
"linux/ppc64le",
|
||||
"linux/s390x",
|
||||
"linux/riscv64",
|
||||
"linux/mips64",
|
||||
}
|
||||
|
||||
type truncatedPlatforms struct {
|
||||
res map[string][]string
|
||||
input []string
|
||||
max int
|
||||
}
|
||||
|
||||
func (tp truncatedPlatforms) List() map[string][]string {
|
||||
return tp.res
|
||||
}
|
||||
|
||||
func (tp truncatedPlatforms) String() string {
|
||||
var out []string
|
||||
var count int
|
||||
|
||||
seen := make(map[string]struct{})
|
||||
for _, mpf := range truncMajorPlatforms {
|
||||
if tpf, ok := tp.res[mpf]; ok {
|
||||
seen[mpf] = struct{}{}
|
||||
if len(tpf) == 1 {
|
||||
out = append(out, fmt.Sprintf("%s", tpf[0]))
|
||||
count++
|
||||
} else {
|
||||
hasPreferredPlatform := false
|
||||
for _, pf := range tpf {
|
||||
if strings.HasSuffix(pf, "*") {
|
||||
hasPreferredPlatform = true
|
||||
break
|
||||
}
|
||||
}
|
||||
mainpf := mpf
|
||||
if hasPreferredPlatform {
|
||||
mainpf += "*"
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%s (+%d)", mainpf, len(tpf)))
|
||||
count += len(tpf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for mpf, pf := range tp.res {
|
||||
if len(out) >= tp.max {
|
||||
break
|
||||
}
|
||||
if _, ok := seen[mpf]; ok {
|
||||
continue
|
||||
}
|
||||
if len(pf) == 1 {
|
||||
out = append(out, fmt.Sprintf("%s", pf[0]))
|
||||
count++
|
||||
} else {
|
||||
hasPreferredPlatform := false
|
||||
for _, pf := range pf {
|
||||
if strings.HasSuffix(pf, "*") {
|
||||
hasPreferredPlatform = true
|
||||
break
|
||||
}
|
||||
}
|
||||
mainpf := mpf
|
||||
if hasPreferredPlatform {
|
||||
mainpf += "*"
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%s (+%d)", mainpf, len(pf)))
|
||||
count += len(pf)
|
||||
}
|
||||
}
|
||||
|
||||
left := len(tp.input) - count
|
||||
if left > 0 {
|
||||
out = append(out, fmt.Sprintf("(%d more)", left))
|
||||
}
|
||||
|
||||
return strings.Join(out, ", ")
|
||||
}
|
||||
|
||||
func truncPlatforms(pfs []string, max int) truncatedPlatforms {
|
||||
res := make(map[string][]string)
|
||||
for _, mpf := range truncMajorPlatforms {
|
||||
for _, pf := range pfs {
|
||||
if len(res) >= max {
|
||||
break
|
||||
}
|
||||
pp, err := platforms.Parse(strings.TrimSuffix(pf, "*"))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if pp.OS+"/"+pp.Architecture == mpf {
|
||||
res[mpf] = append(res[mpf], pf)
|
||||
}
|
||||
}
|
||||
}
|
||||
left := make(map[string][]string)
|
||||
for _, pf := range pfs {
|
||||
if len(res) >= max {
|
||||
break
|
||||
}
|
||||
pp, err := platforms.Parse(strings.TrimSuffix(pf, "*"))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ppf := strings.TrimSuffix(pp.OS+"/"+pp.Architecture, "*")
|
||||
if _, ok := res[ppf]; !ok {
|
||||
left[ppf] = append(left[ppf], pf)
|
||||
}
|
||||
}
|
||||
for k, v := range left {
|
||||
res[k] = v
|
||||
}
|
||||
return truncatedPlatforms{
|
||||
res: res,
|
||||
input: pfs,
|
||||
max: max,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTruncPlatforms(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
platforms []string
|
||||
max int
|
||||
expectedList map[string][]string
|
||||
expectedOut string
|
||||
}{
|
||||
{
|
||||
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"},
|
||||
max: 4,
|
||||
expectedList: map[string][]string{
|
||||
"linux/amd64": {
|
||||
"linux/amd64",
|
||||
"linux/amd64/v2",
|
||||
},
|
||||
"linux/arm": {
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v6",
|
||||
},
|
||||
"linux/arm64": {
|
||||
"linux/arm64*",
|
||||
},
|
||||
"linux/ppc64le": {
|
||||
"linux/ppc64le",
|
||||
},
|
||||
},
|
||||
expectedOut: "linux/amd64 (+2), linux/arm64*, linux/arm (+2), linux/ppc64le, (5 more)",
|
||||
},
|
||||
{
|
||||
name: "riscv64 preferred only",
|
||||
platforms: []string{"linux/riscv64*"},
|
||||
max: 4,
|
||||
expectedList: map[string][]string{
|
||||
"linux/riscv64": {
|
||||
"linux/riscv64*",
|
||||
},
|
||||
},
|
||||
expectedOut: "linux/riscv64*",
|
||||
},
|
||||
{
|
||||
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"},
|
||||
max: 4,
|
||||
expectedList: map[string][]string{
|
||||
"linux/amd64": {
|
||||
"linux/amd64",
|
||||
"linux/amd64/v2",
|
||||
"linux/amd64/v3",
|
||||
},
|
||||
"linux/arm": {
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v6",
|
||||
},
|
||||
"linux/arm64": {
|
||||
"linux/arm64",
|
||||
},
|
||||
"linux/ppc64le": {
|
||||
"linux/ppc64le",
|
||||
}},
|
||||
expectedOut: "linux/amd64 (+3), linux/arm64, linux/arm (+2), linux/ppc64le, (5 more)",
|
||||
},
|
||||
{
|
||||
name: "amd64 no preferred",
|
||||
platforms: []string{"linux/amd64", "linux/386"},
|
||||
max: 4,
|
||||
expectedList: map[string][]string{
|
||||
"linux/386": {
|
||||
"linux/386",
|
||||
},
|
||||
"linux/amd64": {
|
||||
"linux/amd64",
|
||||
},
|
||||
},
|
||||
expectedOut: "linux/amd64, linux/386",
|
||||
},
|
||||
{
|
||||
name: "arm64 no preferred",
|
||||
platforms: []string{"linux/arm64", "linux/arm/v7", "linux/arm/v6"},
|
||||
max: 4,
|
||||
expectedList: map[string][]string{
|
||||
"linux/arm": {
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v6",
|
||||
},
|
||||
"linux/arm64": {
|
||||
"linux/arm64",
|
||||
},
|
||||
},
|
||||
expectedOut: "linux/arm64, linux/arm (+2)",
|
||||
},
|
||||
{
|
||||
name: "all preferred",
|
||||
platforms: []string{"darwin/arm64*", "linux/arm64*", "linux/arm/v5*", "linux/arm/v6*", "linux/arm/v7*", "windows/arm64*"},
|
||||
max: 4,
|
||||
expectedList: map[string][]string{
|
||||
"darwin/arm64": {
|
||||
"darwin/arm64*",
|
||||
},
|
||||
"linux/arm": {
|
||||
"linux/arm/v5*",
|
||||
"linux/arm/v6*",
|
||||
"linux/arm/v7*",
|
||||
},
|
||||
"linux/arm64": {
|
||||
"linux/arm64*",
|
||||
},
|
||||
"windows/arm64": {
|
||||
"windows/arm64*",
|
||||
},
|
||||
},
|
||||
expectedOut: "linux/arm64*, linux/arm* (+3), darwin/arm64*, windows/arm64*",
|
||||
},
|
||||
{
|
||||
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"},
|
||||
max: 4,
|
||||
expectedList: map[string][]string{
|
||||
"linux/amd64": {
|
||||
"linux/amd64/v2*",
|
||||
"linux/amd64",
|
||||
"linux/amd64/v3",
|
||||
},
|
||||
"linux/arm": {
|
||||
"linux/arm/v6*",
|
||||
"linux/arm/v7",
|
||||
},
|
||||
"linux/arm64": {
|
||||
"linux/arm64",
|
||||
},
|
||||
"linux/ppc64le": {
|
||||
"linux/ppc64le",
|
||||
},
|
||||
},
|
||||
expectedOut: "linux/amd64* (+3), linux/arm64, linux/arm* (+2), linux/ppc64le, (5 more)",
|
||||
},
|
||||
{
|
||||
name: "no major with multiple variants",
|
||||
platforms: []string{"linux/arm64", "linux/arm/v7", "linux/arm/v6", "linux/mips64le/softfloat", "linux/mips64le/hardfloat"},
|
||||
max: 4,
|
||||
expectedList: map[string][]string{
|
||||
"linux/arm": {
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v6",
|
||||
},
|
||||
"linux/arm64": {
|
||||
"linux/arm64",
|
||||
},
|
||||
"linux/mips64le": {
|
||||
"linux/mips64le/softfloat",
|
||||
"linux/mips64le/hardfloat",
|
||||
},
|
||||
},
|
||||
expectedOut: "linux/arm64, linux/arm (+2), linux/mips64le (+2)",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tpfs := truncPlatforms(tt.platforms, tt.max)
|
||||
assert.Equal(t, tt.expectedList, tpfs.List())
|
||||
assert.Equal(t, tt.expectedOut, tpfs.String())
|
||||
})
|
||||
}
|
||||
}
|
|
@ -9,10 +9,11 @@ List builder instances
|
|||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:---------|:--------|:---------------------|
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| [`--format`](#format) | `string` | `table` | Format the output |
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:---------|:--------|:----------------------|
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| [`--format`](#format) | `string` | `table` | Format the output |
|
||||
| `--no-trunc` | `bool` | | Don't truncate output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
|
Loading…
Reference in New Issue