diff --git a/go.mod b/go.mod index a7895a7b3..07e81d85c 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.20.0 golang.org/x/sys v0.46.0 + golang.org/x/term v0.43.0 golang.org/x/time v0.15.0 google.golang.org/api v0.280.0 gopkg.in/ini.v1 v1.67.3 diff --git a/pkg/apk/auth/auth.go b/pkg/apk/auth/auth.go index 8be624274..423344b3b 100644 --- a/pkg/apk/auth/auth.go +++ b/pkg/apk/auth/auth.go @@ -12,6 +12,7 @@ import ( "github.com/chainguard-dev/clog" "golang.org/x/oauth2" + "golang.org/x/term" "golang.org/x/time/rate" ) @@ -102,7 +103,14 @@ func (c CGRAuth) AddAuth(ctx context.Context, req *http.Request) error { sometimes.Do(func() { cmd := exec.CommandContext(ctx, "chainctl", "auth", "token", "--audience", host) //#nosec G702 -- host is from env/default, passed as argv (no shell) - cmd.Stderr = io.Discard // Don't pollute logs when things fail + // If stderr is a terminal, let chainctl write to it so it can prompt + // interactively (e.g. emit a device-login URL when running headless). + // Otherwise discard it to avoid polluting logs when things fail. + if term.IsTerminal(int(os.Stderr.Fd())) { + cmd.Stderr = os.Stderr + } else { + cmd.Stderr = io.Discard + } out, err := cmd.Output() if err != nil { // Document that automatic auth failed and how to reproduce.