diff --git a/go.mod b/go.mod index 636e146fc..3acf50372 100644 --- a/go.mod +++ b/go.mod @@ -16,9 +16,9 @@ require ( github.com/creack/pty v1.1.24 github.com/davecgh/go-spew v1.1.1 github.com/distribution/reference v0.6.0 - github.com/docker/cli v28.3.0+incompatible + github.com/docker/cli v28.3.2+incompatible github.com/docker/cli-docs-tool v0.10.0 - github.com/docker/docker v28.3.0+incompatible + github.com/docker/docker v28.3.2+incompatible github.com/docker/go-units v0.5.0 github.com/gofrs/flock v0.12.1 github.com/google/go-dap v0.12.0 diff --git a/go.sum b/go.sum index ff1f48583..08e857c2b 100644 --- a/go.sum +++ b/go.sum @@ -108,15 +108,15 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v28.3.0+incompatible h1:s+ttruVLhB5ayeuf2BciwDVxYdKi+RoUlxmwNHV3Vfo= -github.com/docker/cli v28.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY= +github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli-docs-tool v0.10.0 h1:bOD6mKynPQgojQi3s2jgcUWGp/Ebqy1SeCr9VfKQLLU= github.com/docker/cli-docs-tool v0.10.0/go.mod h1:5EM5zPnT2E7yCLERZmrDA234Vwn09fzRHP4aX1qwp1U= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v28.3.0+incompatible h1:ffS62aKWupCWdvcee7nBU9fhnmknOqDPaJAMtfK0ImQ= -github.com/docker/docker v28.3.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA= +github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= diff --git a/vendor/github.com/docker/cli/cli/config/configfile/file.go b/vendor/github.com/docker/cli/cli/config/configfile/file.go index 396f90034..530c52285 100644 --- a/vendor/github.com/docker/cli/cli/config/configfile/file.go +++ b/vendor/github.com/docker/cli/cli/config/configfile/file.go @@ -56,7 +56,7 @@ type configEnv struct { AuthConfigs map[string]configEnvAuth `json:"auths"` } -// dockerEnvConfig is an environment variable that contains a JSON encoded +// DockerEnvConfigKey is an environment variable that contains a JSON encoded // credential config. It only supports storing the credentials as a base64 // encoded string in the format base64("username:pat"). // @@ -71,7 +71,7 @@ type configEnv struct { // } // } // } -const dockerEnvConfig = "DOCKER_AUTH_CONFIG" +const DockerEnvConfigKey = "DOCKER_AUTH_CONFIG" // ProxyConfig contains proxy configuration settings type ProxyConfig struct { @@ -296,7 +296,7 @@ func (configFile *ConfigFile) GetCredentialsStore(registryHostname string) crede store = newNativeStore(configFile, helper) } - envConfig := os.Getenv(dockerEnvConfig) + envConfig := os.Getenv(DockerEnvConfigKey) if envConfig == "" { return store } diff --git a/vendor/github.com/docker/cli/cli/connhelper/connhelper.go b/vendor/github.com/docker/cli/cli/connhelper/connhelper.go index f4b938859..25ce7aef0 100644 --- a/vendor/github.com/docker/cli/cli/connhelper/connhelper.go +++ b/vendor/github.com/docker/cli/cli/connhelper/connhelper.go @@ -47,14 +47,19 @@ func getConnectionHelper(daemonURL string, sshFlags []string) (*ConnectionHelper } sshFlags = addSSHTimeout(sshFlags) sshFlags = disablePseudoTerminalAllocation(sshFlags) + + remoteCommand := []string{"docker", "system", "dial-stdio"} + socketPath := sp.Path + if strings.Trim(sp.Path, "/") != "" { + remoteCommand = []string{"docker", "--host=unix://" + socketPath, "system", "dial-stdio"} + } + sshArgs, err := sp.Command(sshFlags, remoteCommand...) + if err != nil { + return nil, err + } return &ConnectionHelper{ Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) { - args := []string{"docker"} - if sp.Path != "" { - args = append(args, "--host", "unix://"+sp.Path) - } - args = append(args, "system", "dial-stdio") - return commandconn.New(ctx, "ssh", append(sshFlags, sp.Args(args...)...)...) + return commandconn.New(ctx, "ssh", sshArgs...) }, Host: "http://docker.example.com", }, nil diff --git a/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/LICENSE b/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/LICENSE new file mode 100644 index 000000000..2a5268e5f --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016, Daniel Martí. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/doc.go b/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/doc.go new file mode 100644 index 000000000..32cf60c7c --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/doc.go @@ -0,0 +1,13 @@ +// Package syntax is a fork of [mvdan.cc/sh/v3@v3.10.0/syntax]. +// +// Copyright (c) 2016, Daniel Martí. All rights reserved. +// +// It is a reduced set of the package to only provide the [Quote] function, +// and contains the [LICENSE], [quote.go] and [parser.go] files at the given +// revision. +// +// [quote.go]: https://raw.githubusercontent.com/mvdan/sh/refs/tags/v3.10.0/syntax/quote.go +// [parser.go]: https://raw.githubusercontent.com/mvdan/sh/refs/tags/v3.10.0/syntax/parser.go +// [LICENSE]: https://raw.githubusercontent.com/mvdan/sh/refs/tags/v3.10.0/LICENSE +// [mvdan.cc/sh/v3@v3.10.0/syntax]: https://pkg.go.dev/mvdan.cc/sh/v3@v3.10.0/syntax +package syntax diff --git a/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/parser.go b/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/parser.go new file mode 100644 index 000000000..06b1222f4 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/parser.go @@ -0,0 +1,95 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +// LangVariant describes a shell language variant to use when tokenizing and +// parsing shell code. The zero value is [LangBash]. +type LangVariant int + +const ( + // LangBash corresponds to the GNU Bash language, as described in its + // manual at https://www.gnu.org/software/bash/manual/bash.html. + // + // We currently follow Bash version 5.2. + // + // Its string representation is "bash". + LangBash LangVariant = iota + + // LangPOSIX corresponds to the POSIX Shell language, as described at + // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html. + // + // Its string representation is "posix" or "sh". + LangPOSIX + + // LangMirBSDKorn corresponds to the MirBSD Korn Shell, also known as + // mksh, as described at http://www.mirbsd.org/htman/i386/man1/mksh.htm. + // Note that it shares some features with Bash, due to the shared + // ancestry that is ksh. + // + // We currently follow mksh version 59. + // + // Its string representation is "mksh". + LangMirBSDKorn + + // LangBats corresponds to the Bash Automated Testing System language, + // as described at https://github.com/bats-core/bats-core. Note that + // it's just a small extension of the Bash language. + // + // Its string representation is "bats". + LangBats + + // LangAuto corresponds to automatic language detection, + // commonly used by end-user applications like shfmt, + // which can guess a file's language variant given its filename or shebang. + // + // At this time, [Variant] does not support LangAuto. + LangAuto +) + +func (l LangVariant) String() string { + switch l { + case LangBash: + return "bash" + case LangPOSIX: + return "posix" + case LangMirBSDKorn: + return "mksh" + case LangBats: + return "bats" + case LangAuto: + return "auto" + } + return "unknown shell language variant" +} + +// IsKeyword returns true if the given word is part of the language keywords. +func IsKeyword(word string) bool { + // This list has been copied from the bash 5.1 source code, file y.tab.c +4460 + switch word { + case + "!", + "[[", // only if COND_COMMAND is defined + "]]", // only if COND_COMMAND is defined + "case", + "coproc", // only if COPROCESS_SUPPORT is defined + "do", + "done", + "else", + "esac", + "fi", + "for", + "function", + "if", + "in", + "select", // only if SELECT_COMMAND is defined + "then", + "time", // only if COMMAND_TIMING is defined + "until", + "while", + "{", + "}": + return true + } + return false +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/quote.go b/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/quote.go new file mode 100644 index 000000000..628fa4891 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/internal/syntax/quote.go @@ -0,0 +1,187 @@ +// Copyright (c) 2021, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +type QuoteError struct { + ByteOffset int + Message string +} + +func (e QuoteError) Error() string { + return fmt.Sprintf("cannot quote character at byte %d: %s", e.ByteOffset, e.Message) +} + +const ( + quoteErrNull = "shell strings cannot contain null bytes" + quoteErrPOSIX = "POSIX shell lacks escape sequences" + quoteErrRange = "rune out of range" + quoteErrMksh = "mksh cannot escape codepoints above 16 bits" +) + +// Quote returns a quoted version of the input string, +// so that the quoted version is expanded or interpreted +// as the original string in the given language variant. +// +// Quoting is necessary when using arbitrary literal strings +// as words in a shell script or command. +// Without quoting, one can run into syntax errors, +// as well as the possibility of running unintended code. +// +// An error is returned when a string cannot be quoted for a variant. +// For instance, POSIX lacks escape sequences for non-printable characters, +// and no language variant can represent a string containing null bytes. +// In such cases, the returned error type will be *QuoteError. +// +// The quoting strategy is chosen on a best-effort basis, +// to minimize the amount of extra bytes necessary. +// +// Some strings do not require any quoting and are returned unchanged. +// Those strings can be directly surrounded in single quotes as well. +// +//nolint:gocyclo // ignore "cyclomatic complexity 35 of func `Quote` is high (> 16) (gocyclo)" +func Quote(s string, lang LangVariant) (string, error) { + if s == "" { + // Special case; an empty string must always be quoted, + // as otherwise it expands to zero fields. + return "''", nil + } + shellChars := false + nonPrintable := false + offs := 0 + for rem := s; len(rem) > 0; { + r, size := utf8.DecodeRuneInString(rem) + switch r { + // Like regOps; token characters. + case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`', + // Whitespace; might result in multiple fields. + ' ', '\t', '\r', '\n', + // Escape sequences would be expanded. + '\\', + // Would start a comment unless quoted. + '#', + // Might result in brace expansion. + '{', + // Might result in tilde expansion. + '~', + // Might result in globbing. + '*', '?', '[', + // Might result in an assignment. + '=': + shellChars = true + case '\x00': + return "", &QuoteError{ByteOffset: offs, Message: quoteErrNull} + } + if r == utf8.RuneError || !unicode.IsPrint(r) { + if lang == LangPOSIX { + return "", &QuoteError{ByteOffset: offs, Message: quoteErrPOSIX} + } + nonPrintable = true + } + rem = rem[size:] + offs += size + } + if !shellChars && !nonPrintable && !IsKeyword(s) { + // Nothing to quote; avoid allocating. + return s, nil + } + + // Single quotes are usually best, + // as they don't require any escaping of characters. + // If we have any invalid utf8 or non-printable runes, + // use $'' so that we can escape them. + // Note that we can't use double quotes for those. + var b strings.Builder + if nonPrintable { + b.WriteString("$'") + lastRequoteIfHex := false + offs = 0 + for rem := s; len(rem) > 0; { + nextRequoteIfHex := false + r, size := utf8.DecodeRuneInString(rem) + switch { + case r == '\'', r == '\\': + b.WriteByte('\\') + b.WriteRune(r) + case unicode.IsPrint(r) && r != utf8.RuneError: + if lastRequoteIfHex && isHex(r) { + b.WriteString("'$'") + } + b.WriteRune(r) + case r == '\a': + b.WriteString(`\a`) + case r == '\b': + b.WriteString(`\b`) + case r == '\f': + b.WriteString(`\f`) + case r == '\n': + b.WriteString(`\n`) + case r == '\r': + b.WriteString(`\r`) + case r == '\t': + b.WriteString(`\t`) + case r == '\v': + b.WriteString(`\v`) + case r < utf8.RuneSelf, r == utf8.RuneError && size == 1: + // \xXX, fixed at two hexadecimal characters. + fmt.Fprintf(&b, "\\x%02x", rem[0]) + // Unfortunately, mksh allows \x to consume more hex characters. + // Ensure that we don't allow it to read more than two. + if lang == LangMirBSDKorn { + nextRequoteIfHex = true + } + case r > utf8.MaxRune: + // Not a valid Unicode code point? + return "", &QuoteError{ByteOffset: offs, Message: quoteErrRange} + case lang == LangMirBSDKorn && r > 0xFFFD: + // From the CAVEATS section in R59's man page: + // + // mksh currently uses OPTU-16 internally, which is the same as + // UTF-8 and CESU-8 with 0000..FFFD being valid codepoints. + return "", &QuoteError{ByteOffset: offs, Message: quoteErrMksh} + case r < 0x10000: + // \uXXXX, fixed at four hexadecimal characters. + fmt.Fprintf(&b, "\\u%04x", r) + default: + // \UXXXXXXXX, fixed at eight hexadecimal characters. + fmt.Fprintf(&b, "\\U%08x", r) + } + rem = rem[size:] + lastRequoteIfHex = nextRequoteIfHex + offs += size + } + b.WriteString("'") + return b.String(), nil + } + + // Single quotes without any need for escaping. + if !strings.Contains(s, "'") { + return "'" + s + "'", nil + } + + // The string contains single quotes, + // so fall back to double quotes. + b.WriteByte('"') + for _, r := range s { + switch r { + case '"', '\\', '`', '$': + b.WriteByte('\\') + } + b.WriteRune(r) + } + b.WriteByte('"') + return b.String(), nil +} + +func isHex(r rune) bool { + return (r >= '0' && r <= '9') || + (r >= 'a' && r <= 'f') || + (r >= 'A' && r <= 'F') +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go b/vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go index 2c9d0d61b..2fcb54a98 100644 --- a/vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go +++ b/vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go @@ -5,6 +5,8 @@ import ( "errors" "fmt" "net/url" + + "github.com/docker/cli/cli/connhelper/internal/syntax" ) // ParseURL creates a [Spec] from the given ssh URL. It returns an error if @@ -76,16 +78,106 @@ type Spec struct { Path string } -// Args returns args except "ssh" itself combined with optional additional command args -func (sp *Spec) Args(add ...string) []string { +// Args returns args except "ssh" itself combined with optional additional +// command and args to be executed on the remote host. It attempts to quote +// the given arguments to account for ssh executing the remote command in a +// shell. It returns nil when unable to quote the remote command. +func (sp *Spec) Args(remoteCommandAndArgs ...string) []string { + // Format the remote command to run using the ssh connection, quoting + // values where needed because ssh executes these in a POSIX shell. + remoteCommand, err := quoteCommand(remoteCommandAndArgs...) + if err != nil { + return nil + } + + sshArgs, err := sp.args() + if err != nil { + return nil + } + if remoteCommand != "" { + sshArgs = append(sshArgs, remoteCommand) + } + return sshArgs +} + +func (sp *Spec) args(sshFlags ...string) ([]string, error) { var args []string + if sp.Host == "" { + return nil, errors.New("no host specified") + } if sp.User != "" { - args = append(args, "-l", sp.User) + // Quote user, as it's obtained from the URL. + usr, err := syntax.Quote(sp.User, syntax.LangPOSIX) + if err != nil { + return nil, fmt.Errorf("invalid user: %w", err) + } + args = append(args, "-l", usr) } if sp.Port != "" { - args = append(args, "-p", sp.Port) + // Quote port, as it's obtained from the URL. + port, err := syntax.Quote(sp.Port, syntax.LangPOSIX) + if err != nil { + return nil, fmt.Errorf("invalid port: %w", err) + } + args = append(args, "-p", port) } - args = append(args, "--", sp.Host) - args = append(args, add...) - return args + + // We consider "sshFlags" to be "trusted", and set from code only, + // as they are not parsed from the DOCKER_HOST URL. + args = append(args, sshFlags...) + + host, err := syntax.Quote(sp.Host, syntax.LangPOSIX) + if err != nil { + return nil, fmt.Errorf("invalid host: %w", err) + } + + return append(args, "--", host), nil +} + +// Command returns the ssh flags and arguments to execute a command +// (remoteCommandAndArgs) on the remote host. Where needed, it quotes +// values passed in remoteCommandAndArgs to account for ssh executing +// the remote command in a shell. It returns an error if no remote command +// is passed, or when unable to quote the remote command. +// +// Important: to preserve backward-compatibility, Command does not currently +// perform sanitization or quoting on the sshFlags and callers are expected +// to sanitize this argument. +func (sp *Spec) Command(sshFlags []string, remoteCommandAndArgs ...string) ([]string, error) { + if len(remoteCommandAndArgs) == 0 { + return nil, errors.New("no remote command specified") + } + sshArgs, err := sp.args(sshFlags...) + if err != nil { + return nil, err + } + remoteCommand, err := quoteCommand(remoteCommandAndArgs...) + if err != nil { + return nil, err + } + if remoteCommand != "" { + sshArgs = append(sshArgs, remoteCommand) + } + return sshArgs, nil +} + +// quoteCommand returns the remote command to run using the ssh connection +// as a single string, quoting values where needed because ssh executes +// these in a POSIX shell. +func quoteCommand(commandAndArgs ...string) (string, error) { + var quotedCmd string + for i, arg := range commandAndArgs { + a, err := syntax.Quote(arg, syntax.LangPOSIX) + if err != nil { + return "", fmt.Errorf("invalid argument: %w", err) + } + if i == 0 { + quotedCmd = a + continue + } + quotedCmd += " " + a + } + // each part is quoted appropriately, so now we'll have a full + // shell command to pass off to "ssh" + return quotedCmd, nil } diff --git a/vendor/github.com/docker/cli/cli/debug/debug.go b/vendor/github.com/docker/cli/cli/debug/debug.go index 84002bd00..5ad1b0329 100644 --- a/vendor/github.com/docker/cli/cli/debug/debug.go +++ b/vendor/github.com/docker/cli/cli/debug/debug.go @@ -33,5 +33,8 @@ func IsEnabled() bool { // The default is to log to the debug level which is only // enabled when debugging is enabled. var OTELErrorHandler otel.ErrorHandler = otel.ErrorHandlerFunc(func(err error) { + if err == nil { + return + } logrus.WithError(err).Debug("otel error") }) diff --git a/vendor/github.com/docker/cli/internal/tui/note.go b/vendor/github.com/docker/cli/internal/tui/note.go index 664ba9e8d..d2bc0b9af 100644 --- a/vendor/github.com/docker/cli/internal/tui/note.go +++ b/vendor/github.com/docker/cli/internal/tui/note.go @@ -15,19 +15,39 @@ var InfoHeader = Str{ Fancy: aec.Bold.Apply(aec.LightCyanB.Apply(aec.BlackF.Apply("i")) + " " + aec.LightCyanF.Apply("Info → ")), } -func (o Output) PrintNote(format string, args ...any) { +type options struct { + header Str +} + +type noteOptions func(o *options) + +func withHeader(header Str) noteOptions { + return func(o *options) { + o.header = header + } +} + +func (o Output) printNoteWithOptions(format string, args []any, opts ...noteOptions) { if o.isTerminal { // TODO: Handle all flags format = strings.ReplaceAll(format, "--platform", ColorFlag.Apply("--platform")) } - header := o.Sprint(InfoHeader) + opt := &options{ + header: InfoHeader, + } - _, _ = fmt.Fprint(o, "\n", header) + for _, override := range opts { + override(opt) + } + + h := o.Sprint(opt.header) + + _, _ = fmt.Fprint(o, "\n", h) s := fmt.Sprintf(format, args...) for idx, line := range strings.Split(s, "\n") { if idx > 0 { - _, _ = fmt.Fprint(o, strings.Repeat(" ", Width(header))) + _, _ = fmt.Fprint(o, strings.Repeat(" ", Width(h))) } l := line @@ -37,3 +57,16 @@ func (o Output) PrintNote(format string, args ...any) { _, _ = fmt.Fprintln(o, l) } } + +func (o Output) PrintNote(format string, args ...any) { + o.printNoteWithOptions(format, args, withHeader(InfoHeader)) +} + +var warningHeader = Str{ + Plain: " Warn -> ", + Fancy: aec.Bold.Apply(aec.LightYellowB.Apply(aec.BlackF.Apply("w")) + " " + ColorWarning.Apply("Warn → ")), +} + +func (o Output) PrintWarning(format string, args ...any) { + o.printNoteWithOptions(format, args, withHeader(warningHeader)) +} diff --git a/vendor/github.com/docker/docker/api/swagger.yaml b/vendor/github.com/docker/docker/api/swagger.yaml index 8d6a8f935..3880635db 100644 --- a/vendor/github.com/docker/docker/api/swagger.yaml +++ b/vendor/github.com/docker/docker/api/swagger.yaml @@ -2913,7 +2913,8 @@ definitions: be used. If multiple endpoints have the same priority, endpoints are lexicographically sorted based on their network name, and the one that sorts first is picked. - type: "number" + type: "integer" + format: "int64" example: - 10 diff --git a/vendor/modules.txt b/vendor/modules.txt index e17d1a21d..f830b0f36 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -218,7 +218,7 @@ github.com/davecgh/go-spew/spew # github.com/distribution/reference v0.6.0 ## explicit; go 1.20 github.com/distribution/reference -# github.com/docker/cli v28.3.0+incompatible +# github.com/docker/cli v28.3.2+incompatible ## explicit github.com/docker/cli/cli github.com/docker/cli/cli-plugins/metadata @@ -234,6 +234,7 @@ github.com/docker/cli/cli/config/memorystore github.com/docker/cli/cli/config/types github.com/docker/cli/cli/connhelper github.com/docker/cli/cli/connhelper/commandconn +github.com/docker/cli/cli/connhelper/internal/syntax github.com/docker/cli/cli/connhelper/ssh github.com/docker/cli/cli/context github.com/docker/cli/cli/context/docker @@ -255,7 +256,7 @@ github.com/docker/cli-docs-tool github.com/docker/cli-docs-tool/annotation # github.com/docker/distribution v2.8.3+incompatible ## explicit -# github.com/docker/docker v28.3.0+incompatible +# github.com/docker/docker v28.3.2+incompatible ## explicit github.com/docker/docker/api github.com/docker/docker/api/types