Skip to content

Commit 6191e86

Browse files
committed
fix(deploy): remove gutter tone plumbing
1 parent d043690 commit 6191e86

6 files changed

Lines changed: 32 additions & 125 deletions

File tree

packages/cli-core/src/commands/deploy/index.test.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,6 @@ describe("deploy", () => {
579579
.map((call: unknown[]) => String(call[0]))
580580
.join("");
581581
expect(terminalOutput).toContain("Cancelled");
582-
expect(terminalOutput).toContain("\x1b[31m└");
583582
expect(terminalOutput).not.toContain("Done");
584583
});
585584

@@ -598,7 +597,6 @@ describe("deploy", () => {
598597
.map((call: unknown[]) => String(call[0]))
599598
.join("");
600599
expect(terminalOutput).toContain("Cancelled");
601-
expect(terminalOutput).toContain("\x1b[31m└");
602600
expect(terminalOutput).not.toContain("Done");
603601
});
604602

@@ -696,7 +694,6 @@ describe("deploy", () => {
696694
.map((call: unknown[]) => String(call[0]))
697695
.join("");
698696
expect(terminalOutput).toContain("Paused");
699-
expect(terminalOutput).toContain("\x1b[33m└");
700697
expect(terminalOutput).not.toContain("Done");
701698
});
702699

@@ -880,7 +877,7 @@ describe("deploy", () => {
880877
expect(mockFetchInstanceConfig).not.toHaveBeenCalled();
881878
});
882879

883-
test("--test-fail-production-instance-check prints one Failed status in interactive output", async () => {
880+
test("--test-fail-production-instance-check prints Failed in interactive output", async () => {
884881
await linkedProject();
885882
mockIsAgent.mockReturnValue(false);
886883
stderrSpy = spyOn(process.stderr, "write").mockImplementation(() => true);
@@ -909,7 +906,7 @@ describe("deploy", () => {
909906
const terminalOutput = stripAnsi(
910907
stderrSpy.mock.calls.map((call: unknown[]) => String(call[0])).join(""),
911908
);
912-
expect(terminalOutput.match(/\bFailed\b/g) ?? []).toHaveLength(1);
909+
expect(terminalOutput).toContain("Failed");
913910
});
914911

915912
test("--test-fail-domain-lookup simulates production domain lookup failure", async () => {
@@ -1121,7 +1118,6 @@ describe("deploy", () => {
11211118
.map((call: unknown[]) => String(call[0]))
11221119
.join("");
11231120
expect(terminalOutput).toContain("Paused");
1124-
expect(terminalOutput).toContain("\x1b[33m└");
11251121
expect(terminalOutput).not.toContain("Done");
11261122
});
11271123

packages/cli-core/src/commands/deploy/index.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { isAgent } from "../../mode.ts";
22
import { NEXT_STEPS } from "../../lib/next-steps.ts";
3-
import { isInsideGutter, log, setPrefixTone, type PrefixTone } from "../../lib/log.ts";
3+
import { isInsideGutter, log } from "../../lib/log.ts";
44
import { sleep } from "../../lib/sleep.ts";
55
import { bar, intro, outro, withSpinner } from "../../lib/spinner.ts";
66
import {
@@ -165,30 +165,29 @@ export async function deploy(options: DeployOptions = {}) {
165165
setLogLevel("debug");
166166
}
167167

168-
intro("clerk deploy", { tone: "active" });
168+
intro("clerk deploy");
169169
try {
170170
const ctx = await resolveDeployContext(options);
171171
await runDeploy(ctx);
172172
} catch (error) {
173173
if (error instanceof DeployPausedError && isInsideGutter()) {
174-
closeDeployGutter("error", "Paused");
174+
closeDeployGutter("Paused");
175175
}
176176
if (isPromptExitError(error) && isInsideGutter()) {
177-
closeDeployGutter("cancel", "Cancelled");
177+
closeDeployGutter("Cancelled");
178178
throw new UserAbortError();
179179
}
180180
throw error;
181181
} finally {
182182
// Successful and paused paths call outro themselves. This balances the
183183
// intro gutter if an unexpected error escapes.
184184
if (isInsideGutter()) {
185-
closeDeployGutter("error", "Failed");
185+
closeDeployGutter("Failed");
186186
}
187187
}
188188
}
189189

190-
function closeDeployGutter(tone: PrefixTone, messageOrSteps: string | readonly string[]): void {
191-
setPrefixTone(tone);
190+
function closeDeployGutter(messageOrSteps: string | readonly string[]): void {
192191
outro(messageOrSteps);
193192
}
194193

@@ -318,7 +317,7 @@ async function runDeploy(ctx: DeployContext): Promise<void> {
318317
"No Clerk project linked to this directory. Run `clerk link`, then rerun `clerk deploy`.",
319318
);
320319
log.blank();
321-
closeDeployGutter("error", "Link required");
320+
closeDeployGutter("Link required");
322321
return;
323322
}
324323

@@ -357,7 +356,7 @@ async function startNewDeploy(ctx: DeployContext): Promise<void> {
357356
const proceed = await confirmProceed();
358357
if (!proceed) {
359358
log.info("No changes were made.");
360-
closeDeployGutter("cancel", "Cancelled");
359+
closeDeployGutter("Cancelled");
361360
return;
362361
}
363362

@@ -415,7 +414,7 @@ async function reconcileExistingDeploy(ctx: DeployContext): Promise<void> {
415414
log.info("A production instance exists, but Clerk did not return a production domain yet.");
416415
log.info("Run `clerk deploy` again after the domain is available from the API.");
417416
log.blank();
418-
closeDeployGutter("neutral", "No deploy actions available");
417+
closeDeployGutter("No deploy actions available");
419418
return;
420419
}
421420

@@ -708,7 +707,7 @@ async function confirmProductionInstanceCreation(
708707
log.blank();
709708
log.info("No production instance was created.");
710709
log.blank();
711-
closeDeployGutter("cancel", "Cancelled");
710+
closeDeployGutter("Cancelled");
712711
return false;
713712
}
714713

@@ -736,7 +735,7 @@ async function runDnsSetup(
736735
log.blank();
737736
log.info(pausedOperationNotice());
738737
log.blank();
739-
closeDeployGutter("error", "Paused");
738+
closeDeployGutter("Paused");
740739
return false;
741740
}
742741
return await runDnsVerification(ctx, { ...state, cnameTargets });
@@ -857,7 +856,7 @@ async function runOAuthSetup(
857856
log.blank();
858857
log.info(pausedOperationNotice());
859858
log.blank();
860-
closeDeployGutter("error", "Paused");
859+
closeDeployGutter("Paused");
861860
return [...completed];
862861
}
863862
} catch (error) {
@@ -950,7 +949,7 @@ async function finishDeploy(
950949
log.blank();
951950
printNextSteps();
952951
log.blank();
953-
closeDeployGutter("success", NEXT_STEPS.DEPLOY);
952+
closeDeployGutter(NEXT_STEPS.DEPLOY);
954953
}
955954

956955
function printNextSteps(): void {

packages/cli-core/src/lib/log.test.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
getLogLevel,
88
pushPrefix,
99
popPrefix,
10-
setPrefixTone,
1110
type LogLevel,
1211
} from "./log.ts";
1312

@@ -256,25 +255,6 @@ describe("blank", () => {
256255
expect(cap.stderr.length).toBe(1);
257256
expect(cap.stderr[0]).toContain("│");
258257
});
259-
260-
test("colors pipe prefix from the active gutter tone", () => {
261-
const cap = createCapture();
262-
263-
withCapturedLogs(cap, () => {
264-
pushPrefix("active");
265-
log.info("working");
266-
setPrefixTone("error");
267-
log.info("needs attention");
268-
setPrefixTone("cancel");
269-
log.info("cancelled");
270-
popPrefix();
271-
});
272-
273-
expect(cap.stderr).toHaveLength(3);
274-
expect(cap.stderr[0]).toContain("\x1b[36m│");
275-
expect(cap.stderr[1]).toContain("\x1b[33m│");
276-
expect(cap.stderr[2]).toContain("\x1b[31m│");
277-
});
278258
});
279259

280260
describe("raw", () => {

packages/cli-core/src/lib/log.ts

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AsyncLocalStorage } from "node:async_hooks";
2-
import { cyan, dim, green, red, yellow } from "./color.ts";
2+
import { dim, green, red, yellow } from "./color.ts";
33

44
// ── Log level ────────────────────────────────────────────────────────────
55

@@ -30,50 +30,24 @@ function isLevelEnabled(level: LogLevel): boolean {
3030
// ── Pipe prefix state (for intro/outro flow) ──────────────────────────────
3131

3232
const S_BAR = "│";
33-
export type PrefixTone = "neutral" | "active" | "error" | "cancel" | "success";
33+
let prefixDepth = 0;
3434

35-
const prefixTones: PrefixTone[] = [];
36-
37-
export function pushPrefix(tone: PrefixTone = "neutral") {
38-
prefixTones.push(tone);
35+
export function pushPrefix() {
36+
prefixDepth++;
3937
}
4038

4139
export function popPrefix() {
42-
prefixTones.pop();
43-
}
44-
45-
export function setPrefixTone(tone: PrefixTone) {
46-
if (prefixTones.length === 0) return;
47-
prefixTones[prefixTones.length - 1] = tone;
48-
}
49-
50-
export function getPrefixTone(): PrefixTone {
51-
return prefixTones[prefixTones.length - 1] ?? "neutral";
52-
}
53-
54-
export function formatPrefixSymbol(symbol: string, tone: PrefixTone = getPrefixTone()): string {
55-
switch (tone) {
56-
case "active":
57-
return cyan(symbol);
58-
case "error":
59-
return yellow(symbol);
60-
case "cancel":
61-
return red(symbol);
62-
case "success":
63-
return green(symbol);
64-
case "neutral":
65-
return dim(symbol);
66-
}
40+
prefixDepth = Math.max(0, prefixDepth - 1);
6741
}
6842

6943
/** True while an intro/outro block is active and stderr output is gutter-prefixed. */
7044
export function isInsideGutter(): boolean {
71-
return prefixTones.length > 0;
45+
return prefixDepth > 0;
7246
}
7347

7448
function applyPrefix(msg: string): string {
75-
if (prefixTones.length === 0) return msg;
76-
const bar = formatPrefixSymbol(S_BAR);
49+
if (prefixDepth === 0) return msg;
50+
const bar = dim(S_BAR);
7751
if (!msg) return bar;
7852
return msg
7953
.split("\n")

packages/cli-core/src/lib/spinner.test.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

packages/cli-core/src/lib/spinner.ts

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
import { isHuman } from "../mode.ts";
22
import { dim, cyan, green, red } from "./color.ts";
3-
import {
4-
formatPrefixSymbol,
5-
getPrefixTone,
6-
popPrefix,
7-
pushPrefix,
8-
setPrefixTone,
9-
type PrefixTone,
10-
} from "./log.ts";
3+
import { pushPrefix, popPrefix } from "./log.ts";
114

125
const FRAMES = ["◒", "◐", "◓", "◑"];
136
const INTERVAL = 80;
@@ -24,38 +17,36 @@ const isInteractive = () => stream.isTTY && !process.env.CI;
2417
// --- Public API ---
2518

2619
/** Print intro bracket: ┌ title — prefixes log output with │ until outro(). */
27-
export function intro(title?: string, options: { tone?: PrefixTone } = {}) {
20+
export function intro(title?: string) {
2821
if (!isHuman()) return;
29-
const tone = options.tone ?? "neutral";
30-
const line = title ? `${formatPrefixSymbol(S_BAR_START, tone)} ${title}` : dim(S_BAR_START);
22+
const line = title ? `${dim(S_BAR_START)} ${title}` : dim(S_BAR_START);
3123
stream.write(`${line}\n`);
32-
pushPrefix(tone);
24+
pushPrefix();
3325
}
3426

3527
/** Print outro bracket: └ message — restores normal log output.
3628
* Pass a string[] to render as next steps after the bracket. */
3729
export function outro(messageOrSteps?: string | readonly string[]) {
3830
if (!isHuman()) return;
39-
const tone = getPrefixTone();
4031
popPrefix();
41-
stream.write(`${formatPrefixSymbol(S_BAR, tone)}\n`);
32+
stream.write(`${dim(S_BAR)}\n`);
4233

4334
if (Array.isArray(messageOrSteps)) {
44-
stream.write(`${formatPrefixSymbol(S_BAR_END, tone)} ${dim("Next steps")}\n`);
35+
stream.write(`${dim(S_BAR_END)} ${dim("Next steps")}\n`);
4536
for (const step of messageOrSteps) {
4637
stream.write(` ${cyan("\u2192")} ${step}\n`);
4738
}
4839
stream.write("\n");
4940
} else {
5041
const label = messageOrSteps ?? "Done";
51-
stream.write(`${formatPrefixSymbol(S_BAR_END, tone)} ${label}\n\n`);
42+
stream.write(`${dim(S_BAR_END)} ${label}\n\n`);
5243
}
5344
}
5445

5546
/** Print a bar separator: │ */
5647
export function bar() {
5748
if (!isHuman()) return;
58-
stream.write(`${formatPrefixSymbol(S_BAR)}\n`);
49+
stream.write(`${dim(S_BAR)}\n`);
5950
}
6051

6152
function createSpinner() {
@@ -114,8 +105,7 @@ export async function withSpinner<T>(
114105
s.stop(doneMessage ?? message.replace(/\.{3}$/, ""));
115106
return result;
116107
} catch (error) {
117-
setPrefixTone("error");
118-
s.error(message.replace(/\.{3}$/, ""));
108+
s.error("Failed");
119109
throw error;
120110
}
121111
}

0 commit comments

Comments
 (0)