vendor: github.com/docker/cli v29.4.0

full diff: https://github.com/docker/cli/compare/v29.3.1...v29.4.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2026-04-07 11:52:07 +02:00
parent 2da27cedb5
commit 0b0843233b
33 changed files with 264 additions and 148 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package command
+22 -11
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package formatter
@@ -141,25 +141,36 @@ func (c *ContainerContext) ID() string {
// Names returns a comma-separated string of the container's names, with their
// slash (/) prefix stripped. Additional names for the container (related to the
// legacy `--link` feature) are omitted.
// legacy `--link` feature) are omitted when formatting "truncated".
func (c *ContainerContext) Names() string {
names := StripNamePrefix(c.c.Names)
if c.trunc {
for _, name := range names {
if len(strings.Split(name, "/")) == 1 {
names = []string{name}
break
var b strings.Builder
for i, n := range c.c.Names {
name := strings.TrimPrefix(n, "/")
if c.trunc {
// When printing truncated, we only print a single name.
//
// Pick the first name that's not a legacy link (does not have
// slashes inside the name itself (e.g., "/other-container/link")).
// Normally this would be the first name found.
if strings.IndexByte(name, '/') == -1 {
return name
}
continue
}
if i > 0 {
b.WriteByte(',')
}
b.WriteString(name)
}
return strings.Join(names, ",")
return b.String()
}
// StripNamePrefix removes prefix from string, typically container names as returned by `ContainersList` API
// StripNamePrefix removes any "/" prefix from container names returned
// by the "ContainersList" API.
func StripNamePrefix(ss []string) []string {
sss := make([]string, len(ss))
for i, s := range ss {
sss[i] = s[1:]
sss[i] = strings.TrimPrefix(s, "/")
}
return sss
}
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package formatter
-3
View File
@@ -46,7 +46,6 @@ func (ctx *DiskUsageContext) startSubsection(format Format) (*template.Template,
ctx.buffer = &bytes.Buffer{}
ctx.header = ""
ctx.Format = format
ctx.preFormat()
return ctx.parseFormat()
}
@@ -88,7 +87,6 @@ func (ctx *DiskUsageContext) Write() (err error) {
return ctx.verboseWrite()
}
ctx.buffer = &bytes.Buffer{}
ctx.preFormat()
tmpl, err := ctx.parseFormat()
if err != nil {
@@ -213,7 +211,6 @@ func (ctx *DiskUsageContext) verboseWrite() error {
return ctx.verboseWriteTable(duc)
}
ctx.preFormat()
tmpl, err := ctx.parseFormat()
if err != nil {
return err
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package formatter
+43 -35
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package formatter
@@ -33,7 +33,7 @@ func (f Format) IsTable() bool {
return strings.HasPrefix(string(f), TableFormatKey)
}
// IsJSON returns true if the format is the json format
// IsJSON returns true if the format is the JSON format
func (f Format) IsJSON() bool {
return string(f) == JSONFormatKey
}
@@ -43,6 +43,31 @@ func (f Format) Contains(sub string) bool {
return strings.Contains(string(f), sub)
}
// templateString pre-processes the format and returns it as a string
// for templating.
func (f Format) templateString() string {
out := string(f)
switch out {
case TableFormatKey:
// A bare "--format table" should already be handled before we
// hit this; a literal "table" here means a custom "table" format
// without template.
return ""
case JSONFormatKey:
// "--format json" only; not JSON formats ("--format '{{json .Field}}'").
return JSONFormat
}
// "--format 'table {{.Field}}\t{{.Field}}'" -> "{{.Field}}\t{{.Field}}"
if after, isTable := strings.CutPrefix(out, TableFormatKey); isTable {
out = after
}
out = strings.Trim(out, " ") // trim spaces, but preserve other whitespace.
out = strings.NewReplacer(`\t`, "\t", `\n`, "\n").Replace(out)
return out
}
// Context contains information required by the formatter to print the output as desired.
type Context struct {
// Output is the output stream to which the formatted string is written.
@@ -53,28 +78,12 @@ type Context struct {
Trunc bool
// internal element
finalFormat string
header any
buffer *bytes.Buffer
}
func (c *Context) preFormat() {
c.finalFormat = string(c.Format)
// TODO: handle this in the Format type
switch {
case c.Format.IsTable():
c.finalFormat = c.finalFormat[len(TableFormatKey):]
case c.Format.IsJSON():
c.finalFormat = JSONFormat
}
c.finalFormat = strings.Trim(c.finalFormat, " ")
r := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
c.finalFormat = r.Replace(c.finalFormat)
header any
buffer *bytes.Buffer
}
func (c *Context) parseFormat() (*template.Template, error) {
tmpl, err := templates.Parse(c.finalFormat)
tmpl, err := templates.Parse(c.Format.templateString())
if err != nil {
return nil, fmt.Errorf("template parsing error: %w", err)
}
@@ -82,20 +91,21 @@ func (c *Context) parseFormat() (*template.Template, error) {
}
func (c *Context) postFormat(tmpl *template.Template, subContext SubContext) {
if c.Output == nil {
c.Output = io.Discard
out := c.Output
if out == nil {
out = io.Discard
}
if c.Format.IsTable() {
t := tabwriter.NewWriter(c.Output, 10, 1, 3, ' ', 0)
buffer := bytes.NewBufferString("")
tmpl.Funcs(templates.HeaderFunctions).Execute(buffer, subContext.FullHeader())
buffer.WriteTo(t)
t.Write([]byte("\n"))
c.buffer.WriteTo(t)
t.Flush()
} else {
c.buffer.WriteTo(c.Output)
if !c.Format.IsTable() {
_, _ = c.buffer.WriteTo(out)
return
}
// Write column-headers and rows to the tab-writer buffer, then flush the output.
tw := tabwriter.NewWriter(out, 10, 1, 3, ' ', 0)
_ = tmpl.Funcs(templates.HeaderFunctions).Execute(tw, subContext.FullHeader())
_, _ = tw.Write([]byte{'\n'})
_, _ = c.buffer.WriteTo(tw)
_ = tw.Flush()
}
func (c *Context) contextFormat(tmpl *template.Template, subContext SubContext) error {
@@ -115,8 +125,6 @@ type SubFormat func(func(SubContext) error) error
// Write the template to the buffer using this Context
func (c *Context) Write(sub SubContext, f SubFormat) error {
c.buffer = &bytes.Buffer{}
c.preFormat()
tmpl, err := c.parseFormat()
if err != nil {
return err
+2 -2
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package formatter
@@ -24,7 +24,7 @@ func MarshalJSON(x any) ([]byte, error) {
// marshalMap marshals x to map[string]any
func marshalMap(x any) (map[string]any, error) {
val := reflect.ValueOf(x)
if val.Kind() != reflect.Ptr {
if val.Kind() != reflect.Pointer {
return nil, fmt.Errorf("expected a pointer to a struct, got %v", val.Kind())
}
if val.IsNil() {
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package configfile
+8 -3
View File
@@ -99,9 +99,14 @@ func (c *fileStore) Store(authConfig types.AuthConfig) error {
return nil
}
// ConvertToHostname converts a registry url which has http|https prepended
// to just an hostname.
// Copied from github.com/docker/docker/registry.ConvertToHostname to reduce dependencies.
// ConvertToHostname normalizes a registry URL which has http|https prepended
// to just its hostname. It is used to match credentials, which may be either
// stored as hostname or as hostname including scheme (in legacy configuration
// files).
//
// It's the equivalent to [registry.ConvertToHostname] in the daemon.
//
// [registry.ConvertToHostname]: https://pkg.go.dev/github.com/moby/moby/v2@v2.0.0-beta.7/daemon/pkg/registry#ConvertToHostname
func ConvertToHostname(maybeURL string) string {
stripped := maybeURL
if strings.Contains(stripped, "://") {
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package memorystore
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
// Package connhelper provides helpers for connecting to a remote daemon host with custom logic.
package connhelper
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package store
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package store
+1 -1
View File
@@ -1,5 +1,5 @@
// 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
//go:build go1.25
package store
+48 -27
View File
@@ -4,7 +4,6 @@ import (
"errors"
"io"
"os"
"runtime"
"github.com/moby/term"
)
@@ -12,8 +11,29 @@ import (
// In is an input stream to read user input. It implements [io.ReadCloser]
// with additional utilities, such as putting the terminal in raw mode.
type In struct {
commonStream
in io.ReadCloser
cs commonStream
}
// NewIn returns a new [In] from an [io.ReadCloser]. If in is an [*os.File],
// a reference is kept to the file, and accessible through [In.File].
func NewIn(in io.ReadCloser) *In {
return &In{
in: in,
cs: newCommonStream(in),
}
}
// FD returns the file descriptor number for this stream.
func (i *In) FD() uintptr {
return i.cs.fd
}
// File returns the underlying *os.File if the stream was constructed from one.
// If the stream was created from a non-file (e.g., a pipe, buffer, or wrapper),
// the returned boolean will be false.
func (i *In) File() (*os.File, bool) {
return i.cs.file()
}
// Read implements the [io.Reader] interface.
@@ -26,36 +46,37 @@ func (i *In) Close() error {
return i.in.Close()
}
// IsTerminal returns whether this stream is connected to a terminal.
func (i *In) IsTerminal() bool {
return i.cs.isTerminal()
}
// SetRawTerminal sets raw mode on the input terminal. It is a no-op if In
// is not a TTY, or if the "NORAW" environment variable is set to a non-empty
// value.
func (i *In) SetRawTerminal() (err error) {
if !i.isTerminal || os.Getenv("NORAW") != "" {
func (i *In) SetRawTerminal() error {
return i.cs.setRawTerminal(term.SetRawTerminal)
}
// RestoreTerminal restores the terminal state if SetRawTerminal succeeded earlier.
func (i *In) RestoreTerminal() {
i.cs.restoreTerminal()
}
// CheckTty reports an error when stdin is requested for a TTY-enabled
// container, but the client stdin is not itself a terminal (for example,
// when input is piped or redirected).
func (i *In) CheckTty(attachStdin, ttyMode bool) error {
// TODO(thaJeztah): consider inlining this code and deprecating the method.
if !ttyMode || !attachStdin || i.cs.isTerminal() {
return nil
}
i.state, err = term.SetRawTerminal(i.fd)
return err
return errors.New("cannot attach stdin to a TTY-enabled container because stdin is not a terminal")
}
// CheckTty checks if we are trying to attach to a container TTY
// from a non-TTY client input stream, and if so, returns an error.
func (i *In) CheckTty(attachStdin, ttyMode bool) error {
// In order to attach to a container tty, input stream for the client must
// be a tty itself: redirecting or piping the client standard input is
// incompatible with `docker run -t`, `docker exec -t` or `docker attach`.
if ttyMode && attachStdin && !i.isTerminal {
const eText = "the input device is not a TTY"
if runtime.GOOS == "windows" {
return errors.New(eText + ". If you are using mintty, try prefixing the command with 'winpty'")
}
return errors.New(eText)
}
return nil
}
// NewIn returns a new [In] from an [io.ReadCloser].
func NewIn(in io.ReadCloser) *In {
i := &In{in: in}
i.fd, i.isTerminal = term.GetFdInfo(in)
return i
// SetIsTerminal overrides whether a terminal is connected. It is used to
// override this property in unit-tests, and should not be depended on for
// other purposes.
func (i *In) SetIsTerminal(isTerminal bool) {
i.cs.setIsTerminal(isTerminal)
}
+41 -24
View File
@@ -5,54 +5,71 @@ import (
"os"
"github.com/moby/term"
"github.com/sirupsen/logrus"
)
// Out is an output stream to write normal program output. It implements
// an [io.Writer], with additional utilities for detecting whether a terminal
// is connected, getting the TTY size, and putting the terminal in raw mode.
type Out struct {
commonStream
out io.Writer
cs commonStream
}
// NewOut returns a new [Out] from an [io.Writer]. If out is an [*os.File],
// a reference is kept to the file, and accessible through [Out.File].
func NewOut(out io.Writer) *Out {
return &Out{
out: out,
cs: newCommonStream(out),
}
}
// FD returns the file descriptor number for this stream.
func (o *Out) FD() uintptr {
return o.cs.FD()
}
// File returns the underlying *os.File if the stream was constructed from one.
// If the stream was created from a non-file (e.g., a pipe, buffer, or wrapper),
// the returned boolean will be false.
func (o *Out) File() (*os.File, bool) {
return o.cs.file()
}
// Write writes to the output stream.
func (o *Out) Write(p []byte) (int, error) {
return o.out.Write(p)
}
// IsTerminal returns whether this stream is connected to a terminal.
func (o *Out) IsTerminal() bool {
return o.cs.isTerminal()
}
// SetRawTerminal puts the output of the terminal connected to the stream
// into raw mode.
//
// On UNIX, this does nothing. On Windows, it disables LF -> CRLF/ translation.
// It is a no-op if Out is not a TTY, or if the "NORAW" environment variable is
// set to a non-empty value.
func (o *Out) SetRawTerminal() (err error) {
if !o.isTerminal || os.Getenv("NORAW") != "" {
return nil
}
o.state, err = term.SetRawTerminalOutput(o.fd)
return err
func (o *Out) SetRawTerminal() error {
return o.cs.setRawTerminal(term.SetRawTerminalOutput)
}
// RestoreTerminal restores the terminal state if SetRawTerminal succeeded earlier.
func (o *Out) RestoreTerminal() {
o.cs.restoreTerminal()
}
// GetTtySize returns the height and width in characters of the TTY, or
// zero for both if no TTY is connected.
func (o *Out) GetTtySize() (height uint, width uint) {
if !o.isTerminal {
return 0, 0
}
ws, err := term.GetWinsize(o.fd)
if err != nil {
logrus.WithError(err).Debug("Error getting TTY size")
if ws == nil {
return 0, 0
}
}
return uint(ws.Height), uint(ws.Width)
return o.cs.terminalSize()
}
// NewOut returns a new [Out] from an [io.Writer].
func NewOut(out io.Writer) *Out {
o := &Out{out: out}
o.fd, o.isTerminal = term.GetFdInfo(out)
return o
// SetIsTerminal overrides whether a terminal is connected. It is used to
// override this property in unit-tests, and should not be depended on for
// other purposes.
func (o *Out) SetIsTerminal(isTerminal bool) {
o.cs.setIsTerminal(isTerminal)
}
+59 -17
View File
@@ -1,35 +1,77 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.25
package streams
import (
"os"
"github.com/moby/term"
"github.com/sirupsen/logrus"
)
func newCommonStream(stream any) commonStream {
var f *os.File
if v, ok := stream.(*os.File); ok {
f = v
}
fd, tty := term.GetFdInfo(stream)
return commonStream{
f: f,
fd: fd,
tty: tty,
}
}
type commonStream struct {
fd uintptr
isTerminal bool
state *term.State
f *os.File
fd uintptr
tty bool
state *term.State
}
// FD returns the file descriptor number for this stream.
func (s *commonStream) FD() uintptr {
return s.fd
}
func (s *commonStream) FD() uintptr { return s.fd }
// IsTerminal returns true if this stream is connected to a terminal.
func (s *commonStream) IsTerminal() bool {
return s.isTerminal
}
// file returns the underlying *os.File if the stream was constructed from one.
func (s *commonStream) file() (*os.File, bool) { return s.f, s.f != nil }
// RestoreTerminal restores normal mode to the terminal.
func (s *commonStream) RestoreTerminal() {
// isTerminal returns whether this stream is connected to a terminal.
func (s *commonStream) isTerminal() bool { return s.tty }
// setIsTerminal overrides whether a terminal is connected for testing.
func (s *commonStream) setIsTerminal(isTerminal bool) { s.tty = isTerminal }
// restoreTerminal restores the terminal state if SetRawTerminal succeeded earlier.
func (s *commonStream) restoreTerminal() {
if s.state != nil {
_ = term.RestoreTerminal(s.fd, s.state)
}
}
// SetIsTerminal overrides whether a terminal is connected. It is used to
// override this property in unit-tests, and should not be depended on for
// other purposes.
func (s *commonStream) SetIsTerminal(isTerminal bool) {
s.isTerminal = isTerminal
func (s *commonStream) setRawTerminal(setter func(uintptr) (*term.State, error)) error {
if !s.tty || os.Getenv("NORAW") != "" {
return nil
}
state, err := setter(s.fd)
if err != nil {
return err
}
s.state = state
return nil
}
func (s *commonStream) terminalSize() (height uint, width uint) {
if !s.tty {
return 0, 0
}
ws, err := term.GetWinsize(s.fd)
if err != nil {
logrus.WithError(err).Debug("Error getting TTY size")
if ws == nil {
return 0, 0
}
}
return uint(ws.Height), uint(ws.Width)
}