mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 14:50:02 +00:00
fix(cli): keep post-render exit reset root-owned
This commit is contained in:
@@ -110,6 +110,7 @@ import {
|
|||||||
CliRuntimeError,
|
CliRuntimeError,
|
||||||
CliUsageError,
|
CliUsageError,
|
||||||
consumeCommandResult,
|
consumeCommandResult,
|
||||||
|
registerRootExitCodeSanitizer,
|
||||||
registerRootExitRequester,
|
registerRootExitRequester,
|
||||||
type CommandResult,
|
type CommandResult,
|
||||||
} from "./utils/commandResult.js";
|
} from "./utils/commandResult.js";
|
||||||
@@ -303,6 +304,12 @@ registerRootExitRequester((exitCode) => {
|
|||||||
}).finally(() => process.exit(exitCode));
|
}).finally(() => process.exit(exitCode));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerRootExitCodeSanitizer(() => {
|
||||||
|
if (process.exitCode !== undefined && process.exitCode !== 0) {
|
||||||
|
process.exitCode = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Sync-only: exit handlers cannot await promises or drain microtasks.
|
// Sync-only: exit handlers cannot await promises or drain microtasks.
|
||||||
// _trackCommandResult / _trackCliError are captured references resolved
|
// _trackCommandResult / _trackCliError are captured references resolved
|
||||||
// at init time, so they're callable synchronously here.
|
// at init time, so they're callable synchronously here.
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface CommandResult {
|
|||||||
const SUCCESS_RESULT: CommandResult = { exitCode: 0, kind: "success" };
|
const SUCCESS_RESULT: CommandResult = { exitCode: 0, kind: "success" };
|
||||||
let pendingResult: CommandResult = SUCCESS_RESULT;
|
let pendingResult: CommandResult = SUCCESS_RESULT;
|
||||||
let rootExitRequester: ((exitCode: number) => void) | undefined;
|
let rootExitRequester: ((exitCode: number) => void) | undefined;
|
||||||
|
let rootExitCodeSanitizer: (() => void) | undefined;
|
||||||
|
|
||||||
export class CliUsageError extends Error {
|
export class CliUsageError extends Error {
|
||||||
readonly result: CommandResult;
|
readonly result: CommandResult;
|
||||||
@@ -97,6 +98,16 @@ export function registerRootExitRequester(requester: (exitCode: number) => void)
|
|||||||
rootExitRequester = requester;
|
rootExitRequester = requester;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Called only by cli.ts to retain ownership of process exit-code mutation. */
|
||||||
|
export function registerRootExitCodeSanitizer(sanitizer: () => void): void {
|
||||||
|
rootExitCodeSanitizer = sanitizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ask cli.ts to clear stray process exit state after a successful render. */
|
||||||
|
export function sanitizeSuccessfulExitCode(): void {
|
||||||
|
rootExitCodeSanitizer?.();
|
||||||
|
}
|
||||||
|
|
||||||
/** Ask cli.ts to finalize telemetry/output and then terminate the process. */
|
/** Ask cli.ts to finalize telemetry/output and then terminate the process. */
|
||||||
export function requestCliExit(exitCode = 0): void {
|
export function requestCliExit(exitCode = 0): void {
|
||||||
if (!rootExitRequester) {
|
if (!rootExitRequester) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { registerRootExitCodeSanitizer } from "./commandResult.js";
|
||||||
import {
|
import {
|
||||||
_resetRenderSuccessForTests,
|
_resetRenderSuccessForTests,
|
||||||
isRenderSucceeded,
|
isRenderSucceeded,
|
||||||
@@ -38,6 +39,12 @@ describe("render-success-state flag", () => {
|
|||||||
describe("runPostRenderStep", () => {
|
describe("runPostRenderStep", () => {
|
||||||
const originalExitCode = process.exitCode;
|
const originalExitCode = process.exitCode;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
registerRootExitCodeSanitizer(() => {
|
||||||
|
process.exitCode = 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
process.exitCode = originalExitCode;
|
process.exitCode = originalExitCode;
|
||||||
_resetRenderSuccessForTests();
|
_resetRenderSuccessForTests();
|
||||||
@@ -118,6 +125,12 @@ describe("runPostRenderStep", () => {
|
|||||||
describe("runPostRenderStepAsync", () => {
|
describe("runPostRenderStepAsync", () => {
|
||||||
const originalExitCode = process.exitCode;
|
const originalExitCode = process.exitCode;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
registerRootExitCodeSanitizer(() => {
|
||||||
|
process.exitCode = 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
process.exitCode = originalExitCode;
|
process.exitCode = originalExitCode;
|
||||||
_resetRenderSuccessForTests();
|
_resetRenderSuccessForTests();
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
* still exited 1 after the terminal "artifact validated" checkpoint.
|
* still exited 1 after the terminal "artifact validated" checkpoint.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { sanitizeSuccessfulExitCode } from "./commandResult.js";
|
||||||
|
|
||||||
let renderSucceeded = false;
|
let renderSucceeded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,8 +59,8 @@ const defaultErrorSink: PostRenderErrorSink = (message) => {
|
|||||||
* Run a post-artifact-validated cleanup step so a throw cannot flip the CLI
|
* Run a post-artifact-validated cleanup step so a throw cannot flip the CLI
|
||||||
* exit code. `markRenderSucceeded()` MUST have been called first — this
|
* exit code. `markRenderSucceeded()` MUST have been called first — this
|
||||||
* helper is only safe on the success path where the artifact is already
|
* helper is only safe on the success path where the artifact is already
|
||||||
* committed to disk. Logs a compact warning to stderr, resets a stray
|
* committed to disk. Logs a compact warning to stderr, asks the root CLI to
|
||||||
* `process.exitCode` back to 0, and swallows the error.
|
* clear a stray exit code, and swallows the error.
|
||||||
*/
|
*/
|
||||||
export function runPostRenderStep(
|
export function runPostRenderStep(
|
||||||
label: string,
|
label: string,
|
||||||
@@ -87,9 +89,7 @@ export async function runPostRenderStepAsync(
|
|||||||
function reportPostRenderStepFailure(label: string, err: unknown, sink: PostRenderErrorSink): void {
|
function reportPostRenderStepFailure(label: string, err: unknown, sink: PostRenderErrorSink): void {
|
||||||
const message = err instanceof Error ? err.message : String(err);
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
sink(` [hyperframes] Post-render step '${label}' failed (render already succeeded): ${message}`);
|
sink(` [hyperframes] Post-render step '${label}' failed (render already succeeded): ${message}`);
|
||||||
// Guard against the failing step (or something it triggered) setting a
|
// The failing step (or something it triggered) may have set a non-zero
|
||||||
// non-zero exitCode. The render succeeded → the CLI must exit 0.
|
// exitCode. The render succeeded, so ask the root CLI owner to clear it.
|
||||||
if (process.exitCode !== undefined && process.exitCode !== 0) {
|
sanitizeSuccessfulExitCode();
|
||||||
process.exitCode = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user