Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3247a5aae3 | |||
| 4759615835 | |||
| 511dad69d0 | |||
| 11f24b8458 | |||
| d84396d4eb | |||
| 6751cd1690 | |||
| 8108357bcb | |||
| 3a842587f9 | |||
| eedd9698e9 | |||
| dd2c493825 | |||
| 67cef775fe | |||
| 207bf52c27 | |||
| 2cfd9df568 | |||
| be9e6308f5 | |||
| 88e324150b | |||
| 2ae51e2d69 | |||
| ed281ddf52 | |||
| aa5d00a3a4 | |||
| b66b93130c | |||
| c44e8a0727 | |||
| bff56f0493 |
@ -114,6 +114,16 @@ func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions
|
||||
}
|
||||
}
|
||||
|
||||
format := options.format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCLI.ConfigFile().ImagesFormat) > 0 && !options.quiet && !options.tree {
|
||||
format = dockerCLI.ConfigFile().ImagesFormat
|
||||
useTree = false
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
}
|
||||
|
||||
if useTree {
|
||||
return runTree(ctx, dockerCLI, treeOptions{
|
||||
images: images,
|
||||
@ -123,15 +133,6 @@ func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions
|
||||
})
|
||||
}
|
||||
|
||||
format := options.format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCLI.ConfigFile().ImagesFormat) > 0 && !options.quiet {
|
||||
format = dockerCLI.ConfigFile().ImagesFormat
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
}
|
||||
|
||||
imageCtx := formatter.ImageContext{
|
||||
Context: formatter.Context{
|
||||
Output: dockerCLI.Out(),
|
||||
|
||||
@ -1,2 +1 @@
|
||||
Info -> U In Use
|
||||
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
Info -> U In Use
|
||||
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
|
||||
|
||||
@ -1,2 +1 @@
|
||||
Info -> U In Use
|
||||
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
|
||||
|
||||
@ -1,2 +1 @@
|
||||
Info -> U In Use
|
||||
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
|
||||
|
||||
@ -111,6 +111,13 @@ func runTree(ctx context.Context, dockerCLI command.Cli, opts treeOptions) (int,
|
||||
continue
|
||||
}
|
||||
|
||||
if opts.all && len(sortedTags) == 0 {
|
||||
view.images = append(view.images, topImage{
|
||||
Details: topDetails,
|
||||
Children: children,
|
||||
created: img.Created,
|
||||
})
|
||||
}
|
||||
for _, tag := range sortedTags {
|
||||
view.images = append(view.images, topImage{
|
||||
Names: []string{tag},
|
||||
@ -230,11 +237,11 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
}
|
||||
|
||||
out := tui.NewOutput(outs.Out())
|
||||
isTerm := out.IsTerminal()
|
||||
|
||||
_, width := out.GetTtySize()
|
||||
if width == 0 {
|
||||
width = 80
|
||||
}
|
||||
if width < 20 {
|
||||
limitWidth := width == 0
|
||||
if isTerm && width < 20 {
|
||||
width = 20
|
||||
}
|
||||
|
||||
@ -243,16 +250,18 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
untaggedColor := out.Color(tui.ColorTertiary)
|
||||
titleColor := out.Color(tui.ColorTitle)
|
||||
|
||||
isTerm := out.IsTerminal()
|
||||
|
||||
out.Println(generateLegend(out, width))
|
||||
// Legend is right-aligned, so don't print it if the width is unlimited
|
||||
if !limitWidth {
|
||||
out.Println(generateLegend(out, width))
|
||||
}
|
||||
|
||||
possibleChips := getPossibleChips(view)
|
||||
columns := []imgColumn{
|
||||
{
|
||||
Title: "Image",
|
||||
Align: alignLeft,
|
||||
Width: 0,
|
||||
Title: "Image",
|
||||
Align: alignLeft,
|
||||
Width: 0,
|
||||
NoEllipsis: true,
|
||||
},
|
||||
{
|
||||
Title: "ID",
|
||||
@ -284,7 +293,7 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
Width: func() int {
|
||||
maxChipsWidth := 0
|
||||
for _, chip := range possibleChips {
|
||||
s := chip.String(isTerm)
|
||||
s := out.Sprint(chip)
|
||||
l := tui.Width(s)
|
||||
maxChipsWidth += l
|
||||
}
|
||||
@ -300,9 +309,9 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
var b strings.Builder
|
||||
for _, chip := range possibleChips {
|
||||
if chip.check(d) {
|
||||
b.WriteString(chip.String(isTerm))
|
||||
b.WriteString(out.Sprint(chip))
|
||||
} else {
|
||||
b.WriteString(chipPlaceholder.String(isTerm))
|
||||
b.WriteString(out.Sprint(chipPlaceholder))
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
@ -340,25 +349,27 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
// to display their content.
|
||||
func adjustColumns(width uint, columns []imgColumn, images []topImage) []imgColumn {
|
||||
nameWidth := int(width)
|
||||
for idx, h := range columns {
|
||||
if h.Width == 0 {
|
||||
continue
|
||||
if nameWidth > 0 {
|
||||
for idx, h := range columns {
|
||||
if h.Width == 0 {
|
||||
continue
|
||||
}
|
||||
d := h.Width
|
||||
if idx > 0 {
|
||||
d += columnSpacing
|
||||
}
|
||||
// If the first column gets too short, remove remaining columns
|
||||
if nameWidth-d < 12 {
|
||||
columns = columns[:idx]
|
||||
break
|
||||
}
|
||||
nameWidth -= d
|
||||
}
|
||||
d := h.Width
|
||||
if idx > 0 {
|
||||
d += columnSpacing
|
||||
}
|
||||
// If the first column gets too short, remove remaining columns
|
||||
if nameWidth-d < 12 {
|
||||
columns = columns[:idx]
|
||||
break
|
||||
}
|
||||
nameWidth -= d
|
||||
}
|
||||
|
||||
// Try to make the first column as narrow as possible
|
||||
widest := widestFirstColumnValue(columns, images)
|
||||
if nameWidth > widest {
|
||||
if width == 0 || nameWidth > widest {
|
||||
nameWidth = widest
|
||||
}
|
||||
columns[0].Width = nameWidth
|
||||
@ -426,13 +437,27 @@ func printNames(out tui.Output, headers []imgColumn, img topImage, color, untagg
|
||||
}
|
||||
|
||||
for nameIdx, name := range img.Names {
|
||||
// Don't limit first names to the column width because only the last
|
||||
// name will be printed alongside other columns.
|
||||
if nameIdx < len(img.Names)-1 {
|
||||
_, fullWidth := out.GetTtySize()
|
||||
_, _ = fmt.Fprintln(out, color.Apply(tui.Ellipsis(name, int(fullWidth))))
|
||||
} else {
|
||||
_, _ = fmt.Fprint(out, headers[0].Print(color, name))
|
||||
nameWidth := tui.Width(name)
|
||||
lastName := nameIdx == len(img.Names)-1
|
||||
multiLine := nameWidth > headers[0].Width
|
||||
|
||||
_, _ = fmt.Fprint(out, headers[0].Print(color, name))
|
||||
|
||||
// Print each name on its own line, including the last,
|
||||
// unless the last name fits into the column.
|
||||
//
|
||||
// IMAGE ID ...
|
||||
// anImage 171e65262c80 ...
|
||||
// firstName
|
||||
// lastNameIsALongOne
|
||||
// eade5be814e8 ...
|
||||
// anotherLongName
|
||||
// bb747ca923a5 ...
|
||||
if !lastName || multiLine {
|
||||
_, _ = fmt.Fprintln(out)
|
||||
}
|
||||
if multiLine && lastName {
|
||||
_, _ = fmt.Fprint(out, strings.Repeat(" ", headers[0].Width))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,6 +477,7 @@ type imgColumn struct {
|
||||
|
||||
DetailsValue func(*imageDetails) string
|
||||
Color *aec.ANSI
|
||||
NoEllipsis bool
|
||||
}
|
||||
|
||||
func (h imgColumn) Print(clr aec.ANSI, s string) string {
|
||||
@ -468,12 +494,16 @@ func (h imgColumn) Print(clr aec.ANSI, s string) string {
|
||||
func (h imgColumn) PrintC(clr aec.ANSI, s string) string {
|
||||
ln := tui.Width(s)
|
||||
|
||||
if ln > h.Width {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
}
|
||||
|
||||
fill := h.Width - ln
|
||||
|
||||
if fill < 0 {
|
||||
if h.NoEllipsis {
|
||||
fill = 0
|
||||
} else {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
}
|
||||
}
|
||||
|
||||
l := fill / 2
|
||||
r := fill - l
|
||||
|
||||
@ -482,20 +512,33 @@ func (h imgColumn) PrintC(clr aec.ANSI, s string) string {
|
||||
|
||||
func (h imgColumn) PrintL(clr aec.ANSI, s string) string {
|
||||
ln := tui.Width(s)
|
||||
if ln > h.Width {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
|
||||
fill := h.Width - ln
|
||||
|
||||
if fill < 0 {
|
||||
if h.NoEllipsis {
|
||||
fill = 0
|
||||
} else {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
}
|
||||
}
|
||||
|
||||
return clr.Apply(s) + strings.Repeat(" ", h.Width-ln)
|
||||
return clr.Apply(s) + strings.Repeat(" ", fill)
|
||||
}
|
||||
|
||||
func (h imgColumn) PrintR(clr aec.ANSI, s string) string {
|
||||
ln := tui.Width(s)
|
||||
if ln > h.Width {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
fill := h.Width - ln
|
||||
|
||||
if fill < 0 {
|
||||
if h.NoEllipsis {
|
||||
fill = 0
|
||||
} else {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Repeat(" ", h.Width-ln) + clr.Apply(s)
|
||||
return strings.Repeat(" ", fill) + clr.Apply(s)
|
||||
}
|
||||
|
||||
// widestFirstColumnValue calculates the width needed to fully display the image names and platforms.
|
||||
|
||||
@ -15,6 +15,7 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
|
||||
stdoutTty bool
|
||||
stderrTty bool
|
||||
expectedAnsi bool
|
||||
noColorEnv bool
|
||||
}{
|
||||
{
|
||||
name: "non-terminal",
|
||||
@ -80,6 +81,24 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
|
||||
|
||||
expectedAnsi: false,
|
||||
},
|
||||
{
|
||||
name: "no-color-env",
|
||||
stdinTty: false,
|
||||
stdoutTty: false,
|
||||
stderrTty: false,
|
||||
|
||||
noColorEnv: true,
|
||||
expectedAnsi: false,
|
||||
},
|
||||
{
|
||||
name: "no-color-env-terminal",
|
||||
stdinTty: true,
|
||||
stdoutTty: true,
|
||||
stderrTty: true,
|
||||
|
||||
noColorEnv: true,
|
||||
expectedAnsi: false,
|
||||
},
|
||||
}
|
||||
|
||||
mockView := treeView{
|
||||
@ -115,6 +134,11 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
|
||||
cli.In().SetIsTerminal(tc.stdinTty)
|
||||
cli.Out().SetIsTerminal(tc.stdoutTty)
|
||||
cli.Err().SetIsTerminal(tc.stderrTty)
|
||||
if tc.noColorEnv {
|
||||
t.Setenv("NO_COLOR", "1")
|
||||
} else {
|
||||
t.Setenv("NO_COLOR", "")
|
||||
}
|
||||
|
||||
printImageTree(cli, mockView)
|
||||
|
||||
@ -123,9 +147,9 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
|
||||
|
||||
hasAnsi := strings.Contains(out, "\x1b[")
|
||||
if tc.expectedAnsi {
|
||||
assert.Check(t, hasAnsi, "Output should contain ANSI escape codes")
|
||||
assert.Check(t, hasAnsi, "Output should contain ANSI escape codes, output: %s", out)
|
||||
} else {
|
||||
assert.Check(t, !hasAnsi, "Output should not contain ANSI escape codes")
|
||||
assert.Check(t, !hasAnsi, "Output should not contain ANSI escape codes, output: %s", out)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"","APIVersion":"","MinAPIVersion":"","Os":"","Arch":"","Experimental":false,"Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}]}}
|
||||
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"18.99.5-ce","ApiVersion":"1.30","MinAPIVersion":"1.12","Os":"linux","Arch":"amd64","Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}],"GitCommit":"64ddfa6","GoVersion":"go1.8.7","KernelVersion":"v1.0.0","BuildTime":"2018-07-09T22:38:38.000000000+00:00"}}
|
||||
|
||||
@ -1 +1 @@
|
||||
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"","APIVersion":"","MinAPIVersion":"","Os":"","Arch":"","Experimental":false,"Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}]}}
|
||||
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"18.99.5-ce","ApiVersion":"1.30","MinAPIVersion":"1.12","Os":"linux","Arch":"amd64","Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}],"GitCommit":"64ddfa6","GoVersion":"go1.8.7","KernelVersion":"v1.0.0","BuildTime":"2018-07-09T22:38:38.000000000+00:00"}}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
@ -63,7 +64,7 @@ type versionOptions struct {
|
||||
// versionInfo contains version information of both the Client, and Server
|
||||
type versionInfo struct {
|
||||
Client clientVersion
|
||||
Server *client.ServerVersionResult
|
||||
Server *serverVersion
|
||||
}
|
||||
|
||||
type platformInfo struct {
|
||||
@ -83,6 +84,26 @@ type clientVersion struct {
|
||||
Context string `json:"Context"`
|
||||
}
|
||||
|
||||
// serverVersion contains information about the Docker server host.
|
||||
// it's the client-side presentation of [client.ServerVersionResult].
|
||||
type serverVersion struct {
|
||||
Platform client.PlatformInfo `json:",omitempty"` // Platform is the platform (product name) the server is running on.
|
||||
Version string `json:"Version"` // Version is the version of the daemon.
|
||||
APIVersion string `json:"ApiVersion"` // APIVersion is the highest API version supported by the server.
|
||||
MinAPIVersion string `json:"MinAPIVersion,omitempty"` // MinAPIVersion is the minimum API version the server supports.
|
||||
Os string `json:"Os"` // Os is the operating system the server runs on.
|
||||
Arch string `json:"Arch"` // Arch is the hardware architecture the server runs on.
|
||||
Components []system.ComponentVersion `json:"Components,omitempty"` // Components contains version information for the components making up the server.
|
||||
|
||||
// The following fields are deprecated, they relate to the Engine component and are kept for backwards compatibility
|
||||
|
||||
GitCommit string `json:"GitCommit,omitempty"`
|
||||
GoVersion string `json:"GoVersion,omitempty"`
|
||||
KernelVersion string `json:"KernelVersion,omitempty"`
|
||||
Experimental bool `json:"Experimental,omitempty"`
|
||||
BuildTime string `json:"BuildTime,omitempty"`
|
||||
}
|
||||
|
||||
// newClientVersion constructs a new clientVersion. If a dockerCLI is
|
||||
// passed as argument, additional information is included (API version),
|
||||
// which may invoke an API connection. Pass nil to omit the additional
|
||||
@ -107,6 +128,49 @@ func newClientVersion(contextName string, dockerCli command.Cli) clientVersion {
|
||||
return v
|
||||
}
|
||||
|
||||
func newServerVersion(sv client.ServerVersionResult) *serverVersion {
|
||||
out := &serverVersion{
|
||||
Platform: sv.Platform,
|
||||
Version: sv.Version,
|
||||
APIVersion: sv.APIVersion,
|
||||
MinAPIVersion: sv.MinAPIVersion,
|
||||
Os: sv.Os,
|
||||
Arch: sv.Arch,
|
||||
Experimental: sv.Experimental, //nolint:staticcheck // ignore deprecated field.
|
||||
Components: make([]system.ComponentVersion, 0, len(sv.Components)),
|
||||
}
|
||||
foundEngine := false
|
||||
for _, component := range sv.Components {
|
||||
if component.Name == "Engine" {
|
||||
foundEngine = true
|
||||
buildTime, ok := component.Details["BuildTime"]
|
||||
if ok {
|
||||
component.Details["BuildTime"] = reformatDate(buildTime)
|
||||
}
|
||||
out.GitCommit = component.Details["GitCommit"]
|
||||
out.GoVersion = component.Details["GoVersion"]
|
||||
out.KernelVersion = component.Details["KernelVersion"]
|
||||
out.Experimental = func() bool { b, _ := strconv.ParseBool(component.Details["Experimental"]); return b }()
|
||||
out.BuildTime = buildTime
|
||||
}
|
||||
out.Components = append(out.Components, component)
|
||||
}
|
||||
|
||||
if !foundEngine {
|
||||
out.Components = append(out.Components, system.ComponentVersion{
|
||||
Name: "Engine",
|
||||
Version: sv.Version,
|
||||
Details: map[string]string{
|
||||
"ApiVersion": sv.APIVersion,
|
||||
"MinAPIVersion": sv.MinAPIVersion,
|
||||
"Os": sv.Os,
|
||||
"Arch": sv.Arch,
|
||||
},
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// newVersionCommand creates a new cobra.Command for `docker version`
|
||||
func newVersionCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts versionOptions
|
||||
@ -138,14 +202,14 @@ func reformatDate(buildTime string) string {
|
||||
}
|
||||
|
||||
func arch() string {
|
||||
arch := runtime.GOARCH
|
||||
out := runtime.GOARCH
|
||||
if rosetta.Enabled() {
|
||||
arch += " (rosetta)"
|
||||
out += " (rosetta)"
|
||||
}
|
||||
return arch
|
||||
return out
|
||||
}
|
||||
|
||||
func runVersion(ctx context.Context, dockerCli command.Cli, opts *versionOptions) error {
|
||||
func runVersion(ctx context.Context, dockerCLI command.Cli, opts *versionOptions) error {
|
||||
var err error
|
||||
tmpl, err := newVersionTemplate(opts.format)
|
||||
if err != nil {
|
||||
@ -153,36 +217,13 @@ func runVersion(ctx context.Context, dockerCli command.Cli, opts *versionOptions
|
||||
}
|
||||
|
||||
vd := versionInfo{
|
||||
Client: newClientVersion(dockerCli.CurrentContext(), dockerCli),
|
||||
Client: newClientVersion(dockerCLI.CurrentContext(), dockerCLI),
|
||||
}
|
||||
sv, err := dockerCli.Client().ServerVersion(ctx, client.ServerVersionOptions{})
|
||||
sv, err := dockerCLI.Client().ServerVersion(ctx, client.ServerVersionOptions{})
|
||||
if err == nil {
|
||||
vd.Server = &sv
|
||||
foundEngine := false
|
||||
for _, component := range sv.Components {
|
||||
if component.Name == "Engine" {
|
||||
foundEngine = true
|
||||
buildTime, ok := component.Details["BuildTime"]
|
||||
if ok {
|
||||
component.Details["BuildTime"] = reformatDate(buildTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !foundEngine {
|
||||
vd.Server.Components = append(vd.Server.Components, system.ComponentVersion{
|
||||
Name: "Engine",
|
||||
Version: sv.Version,
|
||||
Details: map[string]string{
|
||||
"ApiVersion": sv.APIVersion,
|
||||
"MinAPIVersion": sv.MinAPIVersion,
|
||||
"Os": sv.Os,
|
||||
"Arch": sv.Arch,
|
||||
},
|
||||
})
|
||||
}
|
||||
vd.Server = newServerVersion(sv)
|
||||
}
|
||||
if err2 := prettyPrintVersion(dockerCli.Out(), vd, tmpl); err2 != nil && err == nil {
|
||||
if err2 := prettyPrintVersion(dockerCLI.Out(), vd, tmpl); err2 != nil && err == nil {
|
||||
err = err2
|
||||
}
|
||||
return err
|
||||
|
||||
@ -47,8 +47,18 @@ func TestVersionFormat(t *testing.T) {
|
||||
BuildTime: "Wed May 30 22:21:05 2018",
|
||||
Context: "my-context",
|
||||
},
|
||||
Server: &client.ServerVersionResult{
|
||||
Platform: client.PlatformInfo{Name: "Docker Enterprise Edition (EE) 2.0"},
|
||||
Server: &serverVersion{
|
||||
Platform: client.PlatformInfo{Name: "Docker Enterprise Edition (EE) 2.0"},
|
||||
Version: "18.99.5-ce",
|
||||
APIVersion: "1.30",
|
||||
MinAPIVersion: "1.12",
|
||||
Os: "linux",
|
||||
Arch: "amd64",
|
||||
GitCommit: "64ddfa6",
|
||||
GoVersion: "go1.8.7",
|
||||
KernelVersion: "v1.0.0",
|
||||
Experimental: false,
|
||||
BuildTime: "2018-07-09T22:38:38.000000000+00:00",
|
||||
Components: []system.ComponentVersion{
|
||||
{
|
||||
Name: "Engine",
|
||||
|
||||
@ -130,6 +130,8 @@ line:
|
||||
| `DOCKER_TLS` | Enable TLS for connections made by the `docker` CLI (equivalent of the `--tls` command-line option). Set to a non-empty value to enable TLS. Note that TLS is enabled automatically if any of the other TLS options are set. |
|
||||
| `DOCKER_TLS_VERIFY` | When set Docker uses TLS and verifies the remote. This variable is used both by the `docker` CLI and the [`dockerd` daemon](https://docs.docker.com/reference/cli/dockerd/) |
|
||||
| `BUILDKIT_PROGRESS` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`) when [building](https://docs.docker.com/reference/cli/docker/image/build/) with [BuildKit backend](https://docs.docker.com/build/buildkit/). Use plain to show container output (default `auto`). |
|
||||
| `NO_COLOR` | Disable any ANSI escape codes in the output in accordance with https://no-color.org/
|
||||
|
|
||||
|
||||
Because Docker is developed using Go, you can also use any environment
|
||||
variables used by the Go runtime. In particular, you may find these useful:
|
||||
@ -317,8 +319,8 @@ be set for each environment:
|
||||
|
||||
These settings are used to configure proxy settings for containers only, and not
|
||||
used as proxy settings for the `docker` CLI or the `dockerd` daemon. Refer to the
|
||||
[environment variables](#environment-variables) and [HTTP/HTTPS proxy](https://docs.docker.com/engine/daemon/proxy/#httphttps-proxy)
|
||||
sections for configuring proxy settings for the CLI and daemon.
|
||||
[environment variables](#environment-variables) section and the [Daemon proxy configuration](https://docs.docker.com/engine/daemon/proxy/)
|
||||
guide for configuring proxy settings for the CLI and daemon.
|
||||
|
||||
> [!WARNING]
|
||||
> Proxy settings may contain sensitive information (for example, if the proxy
|
||||
|
||||
@ -28,7 +28,7 @@ func withHeader(header Str) noteOptions {
|
||||
}
|
||||
|
||||
func (o Output) printNoteWithOptions(format string, args []any, opts ...noteOptions) {
|
||||
if o.isTerminal {
|
||||
if !o.noColor {
|
||||
// TODO: Handle all flags
|
||||
format = strings.ReplaceAll(format, "--platform", ColorFlag.Apply("--platform"))
|
||||
}
|
||||
@ -51,7 +51,7 @@ func (o Output) printNoteWithOptions(format string, args []any, opts ...noteOpti
|
||||
}
|
||||
|
||||
l := line
|
||||
if o.isTerminal {
|
||||
if !o.noColor {
|
||||
l = aec.Italic.Apply(l)
|
||||
}
|
||||
_, _ = fmt.Fprintln(o, l)
|
||||
|
||||
@ -5,6 +5,7 @@ package tui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/morikuni/aec"
|
||||
@ -12,7 +13,7 @@ import (
|
||||
|
||||
type Output struct {
|
||||
*streams.Out
|
||||
isTerminal bool
|
||||
noColor bool
|
||||
}
|
||||
|
||||
type terminalPrintable interface {
|
||||
@ -20,24 +21,28 @@ type terminalPrintable interface {
|
||||
}
|
||||
|
||||
func NewOutput(out *streams.Out) Output {
|
||||
noColor := !out.IsTerminal()
|
||||
if os.Getenv("NO_COLOR") != "" {
|
||||
noColor = true
|
||||
}
|
||||
return Output{
|
||||
Out: out,
|
||||
isTerminal: out.IsTerminal(),
|
||||
Out: out,
|
||||
noColor: noColor,
|
||||
}
|
||||
}
|
||||
|
||||
func (o Output) Color(clr aec.ANSI) aec.ANSI {
|
||||
if o.isTerminal {
|
||||
return clr
|
||||
if o.noColor {
|
||||
return ColorNone
|
||||
}
|
||||
return ColorNone
|
||||
return clr
|
||||
}
|
||||
|
||||
func (o Output) Sprint(all ...any) string {
|
||||
var out []any
|
||||
for _, p := range all {
|
||||
if s, ok := p.(terminalPrintable); ok {
|
||||
out = append(out, s.String(o.isTerminal))
|
||||
out = append(out, s.String(!o.noColor))
|
||||
} else {
|
||||
out = append(out, p)
|
||||
}
|
||||
@ -47,7 +52,7 @@ func (o Output) Sprint(all ...any) string {
|
||||
|
||||
func (o Output) PrintlnWithColor(clr aec.ANSI, args ...any) {
|
||||
msg := o.Sprint(args...)
|
||||
if o.isTerminal {
|
||||
if !o.noColor {
|
||||
msg = clr.Apply(msg)
|
||||
}
|
||||
_, _ = fmt.Fprintln(o.Out, msg)
|
||||
|
||||
Reference in New Issue
Block a user