From 4d6ae35be0381609ae24012b7c4cdc1499a7119d Mon Sep 17 00:00:00 2001 From: tcerqueira Date: Mon, 29 Jun 2026 13:58:43 +0100 Subject: [PATCH 1/3] refactor(cli): route success/status output to stderr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generalize the stdout-discipline convention to human mode: stdout carries only data payloads (list/query tables, identity dumps), while success/confirmation/status/empty-state messages go to stderr in both --json and human modes. - create: 'Using the following build configuration', 'Created app', revision-URL and post-create hints -> stderr - whoami: '✔ Authenticated as …' confirmation -> stderr; orgs table stays on stdout as data - logout: 'Successfully logged out' -> stderr - orgs list: empty-state notice -> stderr - deployments list: empty-state notice and pagination hint -> stderr --- deploy/create/mod.ts | 10 +++++----- deploy/deployments.ts | 6 ++++-- deploy/mod.ts | 4 ++-- deploy/orgs.ts | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/deploy/create/mod.ts b/deploy/create/mod.ts index f7fe6e1..add67b1 100644 --- a/deploy/create/mod.ts +++ b/deploy/create/mod.ts @@ -297,8 +297,8 @@ export const createCommand = new Command() const region = required(options.region, "region"); if (!options.json) { - console.log("Using the following build configuration:"); - console.log(renderBuildConfig(buildConfig satisfies BuildConfig)); + console.error("Using the following build configuration:"); + console.error(renderBuildConfig(buildConfig satisfies BuildConfig)); } data = { @@ -417,7 +417,7 @@ export async function createApp( const appUrl = `${context.endpoint}/${data.org}/${data.app}`; if (!context.json) { - console.log(`${green("✔")} Created app, view it at ${appUrl}`); + console.error(`${green("✔")} Created app, view it at ${appUrl}`); } // Local-source apps deploy via publish(), which emits its own JSON envelope @@ -451,7 +451,7 @@ export async function createApp( source: "github", }); } else { - console.log( + console.error( `You can view the revision here:\n ${context.endpoint}/${data.org}/${data.app}/builds/${revisionId}\n`, ); } @@ -459,7 +459,7 @@ export async function createApp( if (wait) { await waitForRevision(context, data.org, data.app, revisionId); } else if (!context.json) { - console.log( + console.error( "To see the deployment, go to the revision page and wait for the build to complete.", ); } diff --git a/deploy/deployments.ts b/deploy/deployments.ts index acaca0c..29d6082 100644 --- a/deploy/deployments.ts +++ b/deploy/deployments.ts @@ -76,7 +76,7 @@ const deploymentsListCommand = new Command() } if (res.items.length === 0) { - console.log("No deployments for this application."); + console.error("No deployments for this application."); return; } @@ -93,7 +93,9 @@ const deploymentsListCommand = new Command() ); if (res.nextCursor) { - console.log(`\nMore results available; pass --cursor ${res.nextCursor}`); + console.error( + `\nMore results available; pass --cursor ${res.nextCursor}`, + ); } })); diff --git a/deploy/mod.ts b/deploy/mod.ts index a5f9b7a..5cacbec 100644 --- a/deploy/mod.ts +++ b/deploy/mod.ts @@ -236,7 +236,7 @@ const logoutCommand = new Command() .description("Revoke the Deno Deploy token if one is present") .action(() => { tokenStorage.remove(); - console.log(`${green("✔")} Successfully logged out`); + console.error(`${green("✔")} Successfully logged out`); }); interface WhoamiOrg { @@ -296,7 +296,7 @@ const whoamiCommand = new Command() ? `@${me.user.githubLogin}` : me.user.email ?? me.user.name ?? me.user.id) : `org-scoped token (${me.tokenType})`; - console.log( + console.error( `${ green("✔") } Authenticated as ${who}. ${orgs.length} reachable organization${ diff --git a/deploy/orgs.ts b/deploy/orgs.ts index 90c60ed..4eb6429 100644 --- a/deploy/orgs.ts +++ b/deploy/orgs.ts @@ -30,7 +30,7 @@ const orgsListCommand = new Command() } if (orgs.length === 0) { - console.log("No organizations accessible with this token."); + console.error("No organizations accessible with this token."); return; } From c5ec7422a1aff96d28ef6a6721c2358c196c9fe6 Mon Sep 17 00:00:00 2001 From: tcerqueira Date: Mon, 29 Jun 2026 14:03:13 +0100 Subject: [PATCH 2/3] fix(auth): route keychain warning to stderr on token removal tokenStorage.remove() (reached via `logout`) printed KEYCHAIN_WARNING via console.log, unlike its siblings in get()/set() which use console.error. Route it to stderr so the warning never pollutes stdout. --- auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth.ts b/auth.ts index 5b185e5..cce751b 100644 --- a/auth.ts +++ b/auth.ts @@ -448,7 +448,7 @@ export const tokenStorage = { } catch { if (!cannotInteractWithKeychain) { cannotInteractWithKeychain = true; - console.log(KEYCHAIN_WARNING); + console.error(KEYCHAIN_WARNING); } } }, From 1c5e9f0fd9f1146644cf63d5bf731ad8feeba953 Mon Sep 17 00:00:00 2001 From: tcerqueira Date: Mon, 29 Jun 2026 15:02:09 +0100 Subject: [PATCH 3/3] test(create): assert build-config echo on stderr The dry-run create tests asserted the 'Using the following build configuration:' echo on stdout, but it now goes to stderr per the stdout-discipline convention. Capture combined stdout+stderr in the `deploy` test helper (matching `deployFail`). --- tests/create.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/create.test.ts b/tests/create.test.ts index c3cd200..695e9b1 100644 --- a/tests/create.test.ts +++ b/tests/create.test.ts @@ -9,7 +9,11 @@ if (!Deno.env.get("DENO_DEPLOY_TOKEN")) { const deploy = async (...args: string[]) => { const escaped = args.map((a) => $.escapeArg(a)).join(" "); console.log(`deno deploy ${escaped}`); - return (await $.raw`deno deploy ${escaped}`.text()).trim(); + // Status output (e.g. the build-config echo) goes to stderr per the CLI's + // stdout-discipline convention, so inspect the combined streams here. + const result = await $.raw`deno deploy ${escaped}` + .stderr("piped").stdout("piped"); + return (result.stdout + result.stderr).trim(); }; const deployFail = async (...args: string[]) => {