Skip to content

Commit c873ed2

Browse files
authored
fix(sim-cli): report an embedded SimApiError with its own exit code (#8462)
* fix(sim-cli): report an embedded SimApiError with its own exit code The embedded renderer returned 1 for every SimApiError, so an operation wait that timed out (exit 4) or needs configuration (exit 3) read as a generic failure in-process while the installed CLI reported the real status. Return error.exitCode, as the terminal renderer does. * test(sim-cli): make the embedded wait-timeout test independent of runner speed The test gave the wait 10 ms of real time, so a slow runner could reach the deadline before the first status check and exit 4 without printing the receipt. The clock now advances only when the stubbed status is served, so the wait times out only after it has a receipt to print.
1 parent e0c90b3 commit c873ed2

2 files changed

Lines changed: 32 additions & 3 deletions

File tree

‎packages/sim-cli/src/embed.test.ts‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,35 @@ describe('runEmbeddedCli', () => {
9797
expect(JSON.parse(a.stdout).data[0].id).toBe(wsA)
9898
expect(JSON.parse(b.stdout).data[0].id).toBe(wsB)
9999
})
100+
101+
it('reports a thrown error with its own exit code, as the installed CLI does', async () => {
102+
const receipt = {
103+
operationId: 'op-1',
104+
requestId: 'req-1',
105+
workspaceId: IDENTITY.workspaceId,
106+
kind: 'workflow_import',
107+
applied: true,
108+
status: 'processing',
109+
issues: [],
110+
}
111+
// The clock moves only when a status is served, so the wait can time out
112+
// only after it has a receipt to print, however slow the runner is.
113+
let now = Date.now()
114+
vi.spyOn(Date, 'now').mockImplementation(() => now)
115+
const result = await runEmbeddedCli(
116+
['--output', 'json', 'workspaces', 'operations', 'wait', 'op-1', '--wait-timeout', '60'],
117+
{
118+
...IDENTITY,
119+
transport: async () => {
120+
now += 61_000
121+
return jsonResponse({ data: receipt })
122+
},
123+
}
124+
)
125+
expect(result.exitCode).toBe(4)
126+
expect(JSON.parse(result.stdout)).toMatchObject({ operationId: 'op-1', status: 'processing' })
127+
expect(result.stderr).toContain('OPERATION_WAIT_TIMEOUT')
128+
})
100129
})
101130

102131
describe('embedded artifact destinations', () => {

‎packages/sim-cli/src/embed.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ export function createEmbeddedClient(identity: EmbeddedCliIdentity): SimClient {
7070
/**
7171
* Runs one CLI invocation in-process. `argv` is the token list exactly as the
7272
* terminal would receive it (no leading node/binary tokens). Errors the
73-
* installed CLI would print-and-exit-1 on come back the same way: rendered to
74-
* stderr, exitCode 1 — never thrown.
73+
* installed CLI would print-and-exit on come back the same way: rendered to
74+
* stderr with the same exit code — never thrown.
7575
*/
7676
export async function runEmbeddedCli(
7777
argv: string[],
@@ -191,7 +191,7 @@ function renderEmbeddedError(ctx: EmbedContext, error: unknown): number {
191191
if (error.details !== undefined) {
192192
for (const line of formatApiErrorDetails(error.details)) ctx.stderr.diagnostic(sanitize(line))
193193
}
194-
return 1
194+
return error.exitCode
195195
}
196196
ctx.stderr.diagnostic(
197197
// utils-lint-allow: this published standalone CLI cannot import the private @sim/utils package.

0 commit comments

Comments
 (0)