Skip to content

Centralize child-process exit-code propagation (hoist out of individual commands) #1196

@l2ysho

Description

@l2ysho

Follow-up to #1190 / #1180 (fixed in #1195). The fix for apify run swallowing non-zero exit codes was applied locally in RunCommand (src/commands/run.ts):

} catch (err) {
    process.exitCode = (err as ExecaError).exitCode ?? 1;
}

This is correct and contained, but exit-code handling for execWithLog children is inconsistent across the codebase, and the localized fix points at a deeper root cause:

  • exec.ts discards its own work. spawnPromised builds a friendly new Error(message) (with the exit code available on the cause), prints it via error(), then re-throws err.cause (the raw ExecaError) — so the friendly message is thrown away on the propagation path, and any consumer that lets the error bubble to the command framework gets a double-printed, noisy execa message.
  • Three different failure strategies exist for the same event: run.ts sets process.exitCode; create.ts captures git init failures into a bespoke result struct; create.ts/upgrade.ts dependency installs let the error bubble to the framework (which collapses to exit 1, losing the child's real code).
  • The framework catch (apify-command.ts) only does process.exitCode ||= 1, so the exact child code is lost unless each command hand-rolls propagation.

Proposed direction

Make execWithLog/exec.ts throw a single clean, already-logged error carrying the child's exitCode (e.g. an alreadyLogged marker), and have the framework set process.exitCode ||= err.exitCode ?? 1 while skipping the duplicate error() when already logged. Then individual commands drop their bespoke catches (RunCommand keeps only its finally for input cleanup) and exit-code propagation works uniformly for every execWithLog consumer.

Acceptance

Any command spawning a child via execWithLog surfaces the child's exact exit code, with no double-printed error message, and without per-command catch blocks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    t-dxIssues owned by the DX team.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions