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
5 changes: 5 additions & 0 deletions src/lib/init/ui/ink-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

import { openSync } from "node:fs";
import { ReadStream } from "node:tty";
import { setTag } from "@sentry/node-core/light";
import { CLI_VERSION } from "../../constants.js";
import { stripAnsi } from "../../formatters/plain-detect.js";
import { formatFeedbackHint, type InitFeedbackOutcome } from "../feedback.js";
Expand Down Expand Up @@ -817,12 +818,16 @@ export class InkUI implements WizardUI {
if (this.cancelRequested) {
// Safety valve: teardown already started but hasn't finished
// (or something is stuck). Force-exit so the user isn't trapped.
setTag("wizard.outcome", "abandoned");
process.exit(130);
}
this.cancelRequested = true;
this.failureMessage = "Setup cancelled.";
this.feedback("cancelled");
this.tearDown();
// Mark as abandoned before exit so the Sentry span carries the
// outcome even though beforeExit never fires for explicit process.exit().
setTag("wizard.outcome", "abandoned");
// Match the SIGINT convention so shells (and CI) see a
// distinguishable exit. The runner's `await using` won't get a
// chance to run after this, but tearDown above already did all
Expand Down
4 changes: 4 additions & 0 deletions src/lib/init/wizard-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ async function preamble(
} catch (err) {
if (err instanceof WizardCancelledError) {
captureException(err);
setTag("wizard.outcome", "bailed");
showCancelledFeedback(ui);
process.exitCode = 0;
return false;
Expand All @@ -409,6 +410,7 @@ async function preamble(
throw err;
}
if (!confirmed) {
setTag("wizard.outcome", "bailed");
showCancelledFeedback(ui);
process.exitCode = 0;
return false;
Expand All @@ -424,6 +426,7 @@ async function preamble(
ui,
});
if (!gitOk) {
setTag("wizard.outcome", "bailed");
showCancelledFeedback(ui);
process.exitCode = 0;
return false;
Expand Down Expand Up @@ -608,6 +611,7 @@ export async function runWizard(initialOptions: WizardOptions): Promise<void> {
: initialOptions;
const context = await resolveInitContext(effectiveOptions, ui);
if (!context) {
setTag("wizard.outcome", "bailed");
return;
}

Expand Down
13 changes: 13 additions & 0 deletions test/lib/init/wizard-runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,19 @@ describe("runWizard", () => {
expect(formatResultSpy).toHaveBeenCalled();
});

test("aborts cleanly when user declines the experimental prompt", async () => {
const { ui, calls, respond } = createMockUI();
respond.select("exit");
useMockUI(ui, calls);

await forceStdinTty(() => runWizard(makeOptions({ yes: false })));

expect(process.exitCode).toBe(0);
expect(lastCancelMessage()).toBe("Setup cancelled.");
expect(lastFeedbackOutcome()).toBe("cancelled");
expect(getWorkflowSpy).not.toHaveBeenCalled();
});

test("stops before workflow creation when preflight returns null", async () => {
resolveInitContextSpy.mockResolvedValue(null);

Expand Down
2 changes: 1 addition & 1 deletion test/lib/resolve-target.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ describe("fetchProjectId", () => {
})
);

expect(fetchProjectId("test-org", "test-project")).rejects.toThrow(
await expect(fetchProjectId("test-org", "test-project")).rejects.toThrow(
ResolutionError
);
});
Expand Down
15 changes: 11 additions & 4 deletions test/lib/sentryclirc-import.property.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,18 @@ describe("property: maskToken", () => {
});

test("output never equals the original input", () => {
// Tokens that are entirely asterisks are already fully masked, so
// maskToken cannot meaningfully change them — exclude that edge case.
fcAssert(
property(string({ minLength: 1, maxLength: 100 }), (token) => {
const masked = maskToken(token);
expect(masked).not.toBe(token);
}),
property(
string({ minLength: 1, maxLength: 100 }).filter(
(t) => !/^\*+$/.test(t)
),
(token) => {
const masked = maskToken(token);
expect(masked).not.toBe(token);
}
),
{ numRuns: DEFAULT_NUM_RUNS }
);
});
Expand Down
Loading