Skip to content
Merged
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
3 changes: 3 additions & 0 deletions cli/command/registry/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ func runLogin(ctx context.Context, dockerCLI command.Cli, opts loginOptions) err
if err := verifyLoginOptions(dockerCLI, &opts); err != nil {
return err
}

maybePrintEnvAuthWarning(dockerCLI)
Copy link

Copilot AI Jul 7, 2025

Choose a reason for hiding this comment

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

The call to maybePrintEnvAuthWarning is passing a command.Cli instead of command.Streams. Either extract and pass the streams (dockerCLI.Streams()) or adjust the helper to accept the CLI interface.

Suggested change
maybePrintEnvAuthWarning(dockerCLI)
maybePrintEnvAuthWarning(dockerCLI.Streams())

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

FWIW this comment could make sense to be explicit about what we pass.

We could even consider making it accept a stream.Out, but that's a concrete type (I don't think we have an interface for those)

cli/cli/streams/out.go

Lines 11 to 14 in 28f19a9

// 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 {

Copy link
Member

Choose a reason for hiding this comment

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

I should probably outline my motivation; we have various places where (out of convenience) we just pass DockerCLI around; there's many places where we don't need a full CLI, but because we do, it's not always clear "what's used". Changing the maybePrintEnvAuthWarning to accept a shallower interface already helps, but explicitly passing only what's needed helps as well to discover places where we only need a subset of the CLI's functionality (and can help reduce the "ripple effect" of some of those.


var (
serverAddress string
msg string
Expand Down
2 changes: 2 additions & 0 deletions cli/command/registry/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ func NewLogoutCommand(dockerCli command.Cli) *cobra.Command {
}

func runLogout(ctx context.Context, dockerCLI command.Cli, serverAddress string) error {
maybePrintEnvAuthWarning(dockerCLI)

var isDefaultRegistry bool

if serverAddress == "" {
Expand Down
18 changes: 18 additions & 0 deletions cli/command/registry/warning.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package registry

import (
"os"

"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/internal/tui"
)

// maybePrintEnvAuthWarning if the `DOCKER_AUTH_CONFIG` environment variable is
// set this function will output a warning to stdErr
func maybePrintEnvAuthWarning(out command.Streams) {
if os.Getenv(configfile.DockerEnvConfigKey) != "" {
tui.NewOutput(out.Err()).
PrintWarning("%[1]s is set and takes precedence.\nUnset %[1]s to restore the CLI auth behaviour.\n", configfile.DockerEnvConfigKey)
}
}
6 changes: 3 additions & 3 deletions cli/config/configfile/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -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").
//
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down
41 changes: 37 additions & 4 deletions internal/tui/note.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

for _, override := range opts {
override(opt)
}

h := o.Sprint(opt.header)

_, _ = fmt.Fprint(o, "\n", 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
Expand All @@ -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))
}
Loading