login: show auth host instead of the long authorize URL · Entire
login: show auth host instead of the long authorize URL
659f09f→main·
toothbrush·1mo ago·3 files·+29 added/-17 removed
The browser flow now prints "Logging in to:
openBrowser reports failure under test (no usable browser on a CI host, and we mustn't spawn a real one), so the integration test recovers the ephemeral loopback callback URL from that fallback line on stdout — replacing the file-based seam from the previous iteration.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
3ae392c3a1ebView transcript
Changes
3
cmd/entire/cli
integration_test
- Mlogin_test.go+8/-6
Mlogin.go+14/-9
Mlogin_test.go+7/-2
332 unmodified lines
// waitForBrowserPrompt reads login stdout until it finds the "Login URL:"
// line (the browser flow mirrors the device flow's prompt) and returns the
// URL. The browser flow has no "Device code:" line, so this scans for the
// URL alone rather than reusing waitForLoginPrompt.
// waitForBrowserPrompt reads login stdout until it finds the
// "Open this URL in your browser to sign in: <url>" fallback line and
// returns the URL. Under test openBrowser reports failure (no usable
// browser on a headless host), so the browser flow always prints this
// fallback — which is how the test recovers the ephemeral callback URL.
func waitForBrowserPrompt(t *testing.T, stdout *bufio.Reader) string {
t.Helper()
const prefix = "Open this URL in your browser to sign in: "
deadline := time.Now().Add(10 * time.Second)
for time.Now().Before(deadline) {
line, err := stdout.ReadString('\n')
if err != nil {
t.Fatalf("read login output: %v", err)
}
line = strings.TrimSpace(line)
if after, ok := strings.CutPrefix(line, "Login URL:"); ok {
return strings.TrimSpace(after)
}
if after, ok := strings.CutPrefix(line, prefix); ok {
return after
}
}
}
Mcmd/entire/cli/integration_test/login_test.go+8/-6
156 unmodified lines
// Login complete." runBrowserLogin is only reached interactively (see
// shouldUseBrowserLogin), so the Enter prompt is unconditional here.
authURL := flow.AuthorizationURL()
fmt.Fprintf(outW, "Login URL: %s\n\n", authURL)
fmt.Fprintf(outW, "Press Enter to open in browser...")
// Show the auth host, not the full authorize URL — the PKCE challenge +
// loopback redirect make it long and unreadable, and the browser is
// opened for the user anyway. The full URL is only printed below as a
// fallback when the browser can't be opened.
fmt.Fprintf(outW, "Logging in to: %s\n\n", client.BaseURL())
fmt.Fprint(outW, "Press Enter to open in browser...");
// Read from /dev/tty so we get a real keypress and don't consume piped stdin.
if err := waitForEnter(ctx); err != nil {
return fmt.Errorf("wait for input: %w", err)
}
fmt.Fprintln(outW)
if err := openURL(ctx, authURL); err != nil {
fmt.Fprintf(errW, "Warning: failed to open browser: %v\n", err)
fmt.Fprintf(outW, "Open this URL in your browser to sign in: %s\n", authURL)
}
fmt.Fprint(outW, "Waiting for sign-in... ")
fmt.Fprint(outW, "\nWaiting for sign-in... ")
code, err := flow.Wait(ctx)
if err != nil {
return fmt.Errorf("refusing to open non-HTTP URL: %s", browserURL)
}
// Tests force interactive mode (ENTIRE_TEST_TTY=1) to exercise the
// browser flow, but must not actually spawn a browser on the test/CI
// host. Report success so the "Opening..." path runs — the loopback
// listener is what the test drives. URL validation above still applies.
// Under test there's no usable browser, and we must not spawn a real one
// on a dev/CI host. Report failure so the caller takes the "here's the
// URL" fallback — exactly the path a genuinely headless machine hits, and
// what lets an integration test recover the loopback callback URL from
// stdout. URL validation above still applies.
if interactive.UnderTest() {
return nil
}
return errors.New("browser unavailable under test")
}
var command string
Mcmd/entire/cli/login.go+14/-9
393 unmodified lines
if openedURL != flow.authURL {
t.Errorf("opened URL = %q, want %q", openedURL, flow.authURL)
}
if !strings.Contains(out.String(), "Login URL:") || !strings.Contains(out.String(), flow.authURL) {
t.Errorf("output missing login URL:\n%s", out.String())
// Happy path shows the auth host, not the full authorize URL, and
// doesn't print the URL at all (the browser opened fine).
if !strings.Contains(out.String(), "Logging in to:") {
t.Errorf("output missing 'Logging in to:' line:\n%s", out.String())
}
if strings.Contains(out.String(), flow.authURL) {
t.Errorf("happy path should not print the full authorize URL:\n%s", out.String())
}
if !strings.Contains(out.String(), "Press Enter to open in browser...") {
t.Errorf("output missing enter-to-open prompt:\n%s", out.String())
}
Mcmd/entire/cli/login_test.go+7/-2