Skip to content

Add CLI cancellation propagation#59

Merged
rtbenfield merged 22 commits into
mainfrom
feat/cancelation
Jun 2, 2026
Merged

Add CLI cancellation propagation#59
rtbenfield merged 22 commits into
mainfrom
feat/cancelation

Conversation

@rtbenfield
Copy link
Copy Markdown
Contributor

@rtbenfield rtbenfield commented Jun 1, 2026

Propagates command cancellation through the Prisma CLI so SIGINT/SIGTERM and AbortSignal-driven exits produce the documented COMMAND_CANCELED behavior instead of leaking provider-specific failures or hanging waits.

Changes

  • Runtime and errors: Adds a root AbortSignal in the CLI entrypoint, threads it through CommandContext, and centralizes cancellation mapping in the command runner with COMMAND_CANCELED and exit code 130.
  • App and project workflows: Forwards the command signal through deploy, logs, domain waits, local app processes, GitHub repository setup polling, SDK calls, and Management API calls.
  • Local boundaries: Makes CLI-owned sleeps, local state, token storage, filesystem reads/writes, git subprocesses, and unsupported filesystem/browser boundaries cancellation-aware without adding fake Promise.race wrappers.
  • Auth flow: Makes OAuth callback waits cancellable and preserves aborts through best-effort auth fallback paths.
  • Docs and tests: Documents the cancellation error contract and adds regression coverage across shell, auth, app, project, token storage, local state, build, and process boundaries.

Why

Cancellation is a cross-cutting CLI contract, so the signal is owned at the runtime boundary and passed downward rather than creating command-local controllers. Supported SDK, child-process, and filesystem APIs receive the signal directly; unsupported boundaries use immediate pre/post abort checks so cancellation stays deterministic without pretending external APIs are interruptible.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e50297cc-bca1-4250-9152-f1ca8369efbe

📥 Commits

Reviewing files that changed from the base of the PR and between 01640c4 and 8e6dbeb.

📒 Files selected for processing (2)
  • packages/cli/src/bin.ts
  • packages/cli/src/cli.ts

Summary by CodeRabbit

  • New Features
    • CLI now exposes structured command cancellation and consistently propagates cancellation across runs, builds, deploys auth flows, previews, and filesystem/network operations.
  • Documentation
    • Added COMMAND_CANCELED structured error and guidance to prefer structured error branching; recommends exit code 130 for cancellations.
  • Tests
    • Added/updated tests verifying cancellation behavior across auth, token storage, git, local state, command runner, preview, and build workflows.

Walkthrough

This PR implements comprehensive end-to-end CLI cancellation support by threading an AbortSignal from the process entry point through all CLI subsystems. The entrypoint creates an AbortController wired to SIGINT/SIGTERM, the signal is stored in CliRuntime, and cancellation is detected in the command runner to emit a standardized COMMAND_CANCELED error (exit code 130). Filesystem I/O, authentication, API calls, polling loops, preview/build, provider, and token refresh-lock logic now accept and propagate the signal; tests added/updated cover abort behavior.

Changes

CLI Cancellation Propagation

Layer / File(s) Summary
Documentation & specification
docs/product/error-conventions.md
Adds COMMAND_CANCELED to stable error codes, defines exit code 130 for cancellation, and clarifies structured error codes are the primary branching surface for agents/CI.
Runtime & entrypoint signal infrastructure
packages/cli/src/bin.ts, packages/cli/src/cli.ts, packages/cli/src/shell/runtime.ts, packages/cli/tests/helpers.ts
AbortController created at entry point and wired to SIGINT/SIGTERM; CliRuntime interface gains signal property; fixture/test helpers add AbortController signal to runtime contexts.
Cancellation error conversion & rendering
packages/cli/src/shell/command-runner.ts, packages/cli/src/shell/errors.ts
Command runner recognizes AbortError/CancelledError and runtime.signal.aborted and returns commandCanceledError with exit code 130; new error helper formats standardized cancellation response.
Adapter layer signal threading
packages/cli/src/adapters/git.ts, packages/cli/src/adapters/local-state.ts, packages/cli/src/adapters/mock-api.ts, packages/cli/src/adapters/token-storage.ts
Low-level I/O adapters updated to accept AbortSignal: readGitOriginRemote, LocalStateStore, MockApi.load propagate signal to underlying exec/fs reads/writes; FileTokenStorage adds abort-aware refresh-lock acquisition and sleep helper.
Authentication operations core
packages/cli/src/lib/auth/auth-ops.ts, packages/cli/src/lib/auth/guard.ts
performLogin, readAuthState, performLogout, and requireComputeAuth accept and forward AbortSignal through FileTokenStorage construction and Management API requests (/v1/me, /v1/workspaces).
Authentication login flow with signal
packages/cli/src/lib/auth/login.ts
LoginOptions gains signal field; login() wires abort listener on in-flight auth promise; /auth/callback handler resolves/rejects the shared promise; browser interactions guarded with pre/post abort checks; workspace-name resolution abort-aware.
Auth command controller
packages/cli/src/controllers/auth.ts
runAuthLogin, runAuthLogout, runAuthWhoAmI, requireAuthenticatedAuthState forward context.runtime.signal to auth operations in real and fixture modes.
Project resolution & local binding
packages/cli/src/lib/project/local-pin.ts, packages/cli/src/lib/project/resolution.ts, packages/cli/src/lib/project/setup.ts, packages/cli/src/lib/project/interactive-setup.ts
readLocalResolutionPin, writeLocalResolutionPin, ensureLocalResolutionPinGitignore accept signal; readPackageName and inferTargetName propagate signal through package.json reads; buildProjectSetupSuggestion and projectSetupRequiredError forward signal.
Local development & build framework detection
packages/cli/src/lib/app/bun-project.ts, packages/cli/src/lib/app/local-dev.ts
readBunPackageJson, resolveBunEntrypoint, resolveLocalBuildType, detectLocalBuildType, isNextProject, isBunProject accept and propagate AbortSignal with abort checks around filesystem operations and spawn options.
Preview build strategy & artifact staging
packages/cli/src/lib/app/preview-build.ts
PreviewBuildStrategy stores optional signal; executePreviewBuild and resolvePreviewBuildStrategy thread signal through strategy selection and execution; artifact staging, pnpm hoisting, symlink normalization, and unsupportedFilesystemBoundary implement abort-safe filesystem operations.
SDK/provider interface signal threading
packages/cli/src/lib/app/preview-provider.ts
PreviewAppProvider interface methods updated to accept signal in options for createProject, listApps, removeApp, listDomains, addDomain, showDomain, removeDomain, retryDomain, promoteDeployment, deployApp, updateAppEnv, listAppEnvNames, listDeployments, showDeployment; implementation forwards signal to Management/Compute API and SDK calls.
App environment variables controller
packages/cli/src/controllers/app-env.ts
runEnvAdd, runEnvUpdate, runEnvList, runEnvRemove propagate context.runtime.signal through scope resolution, variable lookups, and Management API operations; branch resolution and pagination loops are signal-aware.
Project controller listing/creation/linking & GitHub integration
packages/cli/src/controllers/project.ts
runProjectList, runProjectCreate, runProjectLink, runGitConnect, runGitDisconnect forward signal through auth, workspace/project listing, local binding, GitHub SCM operations, polling loops, and browser interactions; listRealWorkspaceProjects now accepts signal.
App controller domain/deployment operations
packages/cli/src/controllers/app.ts
runAppBuild, runAppRun, runAppDeploy propagate signal into build/run/deploy helpers; deployment listing/show/promote/logs operations include signal; domain operations (add/show/remove/retry/wait) become signal-aware; polling loops use abortable sleep; git branch reading and framework detection are signal-propagated.
Test coverage for cancellation
packages/cli/tests/*
Tests added/updated across adapters, auth, command-runner, controllers, and token storage to assert abort behavior: aborted reads rethrow original abort reason; signal forwarded into mocked provider/SDK calls; command-runner renders COMMAND_CANCELED JSON/human output; token refresh-lock acquisition abort handling verified.

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.66% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add CLI cancellation propagation' clearly and concisely summarizes the main change: implementing cancellation signal propagation throughout the CLI.
Description check ✅ Passed The description comprehensively explains what changed, why it matters, and how it's implemented, directly relating to the changes shown in the raw summary.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cancelation
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/cancelation

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/cli/src/controllers/project.ts (1)

111-111: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing signal parameter in fixture mode.

The call to readProjectListLocalBinding is missing the required signal parameter. The real-mode branch at line 93 correctly passes context.runtime.signal, but this fixture-mode branch omits it.

🐛 Proposed fix
-  const localBinding = await readProjectListLocalBinding(context.runtime.cwd, workspace, result.projects);
+  const localBinding = await readProjectListLocalBinding(context.runtime.cwd, workspace, result.projects, context.runtime.signal);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/controllers/project.ts` at line 111, The fixture-mode call
to readProjectListLocalBinding is missing the required signal argument; update
the fixture branch where readProjectListLocalBinding(context.runtime.cwd,
workspace, result.projects) is invoked to include the same signal passed in the
real branch (context.runtime.signal) so it calls
readProjectListLocalBinding(context.runtime.cwd, workspace, result.projects,
context.runtime.signal).
packages/cli/src/adapters/git.ts (1)

14-24: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rethrow aborts from readGitOriginRemote.

The blanket catch turns an aborted git config into null, so Ctrl-C/SIGTERM is treated as “no origin remote” and cancellation stops propagating.

Suggested fix
-  } catch {
-    return null;
+  } catch (error) {
+    if (signal?.aborted || (error instanceof Error && error.name === "AbortError")) {
+      throw error;
+    }
+    return null;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/adapters/git.ts` around lines 14 - 24, The catch in
readGitOriginRemote swallows aborts; change it to catch the thrown error as a
variable and rethrow when it's an abort (e.g., err.name === 'AbortError' or
signal?.aborted) so cancellation propagates, otherwise return null for other
failures; update readGitOriginRemote to inspect the caught error and only
suppress non-abort errors.
packages/cli/src/adapters/token-storage.ts (1)

123-151: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clean up the lock file when acquisition fails after open("wx").

Once fs.open(..., "wx") succeeds, this process owns the lock path. If handle.writeFile(lockId, { signal }) aborts or throws, the function exits without returning lockId, but the empty/partial lock file remains. The next caller then sits in the retry loop until the stale-lock timeout expires.

Suggested fix
       try {
         // open does not accept AbortSignal; check before the filesystem boundary.
         const handle = await fs.open(this.lockFilePath, "wx");
+        let wroteLockId = false;
         try {
           await handle.writeFile(lockId, { encoding: "utf8", signal: this.signal });
+          wroteLockId = true;
         } finally {
           await handle.close();
+          if (!wroteLockId) {
+            await fs.unlink(this.lockFilePath).catch(() => {});
+          }
         }
         return lockId;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/adapters/token-storage.ts` around lines 123 - 151, In
acquireRefreshLock, after fs.open(this.lockFilePath, "wx") succeeds the process
owns the lock but an error during handle.writeFile(...) can leave a
partial/empty lock file; change the inner try/finally so that if writeFile
throws you close the handle and remove the lock file (fs.unlink or equivalent on
this.lockFilePath) before rethrowing so other callers won't wait for the
stale-lock timeout; ensure you still call releaseRefreshLock(staleLockId) and
preserve AbortSignal handling and error propagation (referencing
acquireRefreshLock, lockFilePath, handle.writeFile, releaseRefreshLock, and
getStaleRefreshLockId).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/package.json`:
- Around line 44-46: The package versions in packages/cli/package.json
(specifically `@prisma/compute-sdk`@0.20.0 and `@prisma/management-api-sdk`@1.35.0)
don’t appear to accept an AbortSignal, so stop passing options.signal blindly
into SDK calls; inspect the TypeScript signatures of sdk.createProject and
sdk.showService (and any other sdk.* calls where you pass options.signal) and
either (A) remove the signal property from the call site (e.g., stop forwarding
options.signal) or (B) upgrade the dependency to a version that explicitly
supports a signal param and update package.json accordingly; ensure conditional
forwarding if you choose to detect support (check for a declared signal
parameter in the method signature or a runtime option flag) and add a short
comment at each call site (sdk.createProject / sdk.showService) documenting why
signal is not forwarded or which SDK version is required.

In `@packages/cli/src/adapters/local-state.ts`:
- Around line 96-100: The write method in the LocalState adapter currently
passes the root AbortSignal into fs.writeFile which can produce
partially-written JSON on abort; change write (method LocalState.write) to avoid
passing this.signal into writeFile: call this.signal?.throwIfAborted() before
starting and again after the write completes, and invoke writeFile without the {
signal } option, or alternately implement a safe-write by writing to a temporary
file (near this.stateFilePath) and then atomically renaming it to
this.stateFilePath on success so aborts cannot leave a corrupt state.json; keep
the mkdir(path.dirname(this.stateFilePath), { recursive: true }) and ensure
throwIfAborted is used to short-circuit before/after the filesystem mutation.

In `@packages/cli/tests/auth-login.test.ts`:
- Line 77: The mock for handleCallback currently returns undefined (vi.fn()), so
awaiting it resolves immediately; change the mock to return a never-resolving
promise instead (e.g., mock handleCallback to return new Promise(() => {}) or
use vi.fn().mockReturnValue(new Promise(() => {}))) so the test truly waits and
can exercise abort behavior; update the mock for handleCallback in the
auth-login.test.ts setup accordingly.

---

Outside diff comments:
In `@packages/cli/src/adapters/git.ts`:
- Around line 14-24: The catch in readGitOriginRemote swallows aborts; change it
to catch the thrown error as a variable and rethrow when it's an abort (e.g.,
err.name === 'AbortError' or signal?.aborted) so cancellation propagates,
otherwise return null for other failures; update readGitOriginRemote to inspect
the caught error and only suppress non-abort errors.

In `@packages/cli/src/adapters/token-storage.ts`:
- Around line 123-151: In acquireRefreshLock, after fs.open(this.lockFilePath,
"wx") succeeds the process owns the lock but an error during
handle.writeFile(...) can leave a partial/empty lock file; change the inner
try/finally so that if writeFile throws you close the handle and remove the lock
file (fs.unlink or equivalent on this.lockFilePath) before rethrowing so other
callers won't wait for the stale-lock timeout; ensure you still call
releaseRefreshLock(staleLockId) and preserve AbortSignal handling and error
propagation (referencing acquireRefreshLock, lockFilePath, handle.writeFile,
releaseRefreshLock, and getStaleRefreshLockId).

In `@packages/cli/src/controllers/project.ts`:
- Line 111: The fixture-mode call to readProjectListLocalBinding is missing the
required signal argument; update the fixture branch where
readProjectListLocalBinding(context.runtime.cwd, workspace, result.projects) is
invoked to include the same signal passed in the real branch
(context.runtime.signal) so it calls
readProjectListLocalBinding(context.runtime.cwd, workspace, result.projects,
context.runtime.signal).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8547c874-1844-4b49-8dde-822c9bfaac11

📥 Commits

Reviewing files that changed from the base of the PR and between c573c86 and 7ffe411.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (38)
  • docs/product/error-conventions.md
  • packages/cli/package.json
  • packages/cli/src/adapters/git.ts
  • packages/cli/src/adapters/local-state.ts
  • packages/cli/src/adapters/mock-api.ts
  • packages/cli/src/adapters/token-storage.ts
  • packages/cli/src/bin.ts
  • packages/cli/src/cli.ts
  • packages/cli/src/controllers/app-env.ts
  • packages/cli/src/controllers/app.ts
  • packages/cli/src/controllers/auth.ts
  • packages/cli/src/controllers/project.ts
  • packages/cli/src/lib/app/bun-project.ts
  • packages/cli/src/lib/app/local-dev.ts
  • packages/cli/src/lib/app/preview-build.ts
  • packages/cli/src/lib/app/preview-provider.ts
  • packages/cli/src/lib/auth/auth-ops.ts
  • packages/cli/src/lib/auth/guard.ts
  • packages/cli/src/lib/auth/login.ts
  • packages/cli/src/lib/project/interactive-setup.ts
  • packages/cli/src/lib/project/local-pin.ts
  • packages/cli/src/lib/project/resolution.ts
  • packages/cli/src/lib/project/setup.ts
  • packages/cli/src/shell/command-runner.ts
  • packages/cli/src/shell/errors.ts
  • packages/cli/src/shell/runtime.ts
  • packages/cli/tests/app-bun-compat.test.ts
  • packages/cli/tests/app-controller.test.ts
  • packages/cli/tests/app-local-dev.test.ts
  • packages/cli/tests/app-state.test.ts
  • packages/cli/tests/auth-login.test.ts
  • packages/cli/tests/auth-ops.test.ts
  • packages/cli/tests/auth-real-mode.test.ts
  • packages/cli/tests/command-runner-auth.test.ts
  • packages/cli/tests/helpers.ts
  • packages/cli/tests/project-controller.test.ts
  • packages/cli/tests/project-real-mode.test.ts
  • packages/cli/tests/token-storage.test.ts

Comment thread packages/cli/package.json
Comment thread packages/cli/src/adapters/local-state.ts Outdated
Comment thread packages/cli/tests/auth-login.test.ts Outdated
Copy link
Copy Markdown
Member

@luanvdw luanvdw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @rtbenfield this is a great step forward for the overall architecture 🙏

I ran through it with my agent to educate my on the changes and see if it spots anything worth flagging for you to consider (these are mostly because this is a cross-cutting CLI contract)

These are all boundary-contract items rather than objections to the overall architecture:

  1. packages/cli/src/adapters/local-state.ts:96-100

    LocalStateStore.write() now passes the command AbortSignal directly into the persistent state.json write. If cancellation lands mid-write, we can leave partially-written JSON behind and make the next CLI invocation fail for a reason unrelated to the user’s command.

    Suggestion: make persistent state writes atomic, or at least do pre/post throwIfAborted() around an unabortable write. Since this is durable local CLI state, I’d prefer temp-file + rename, similar to the local pin path.

  2. packages/cli/src/adapters/token-storage.ts:123-139

    After fs.open(lockFilePath, "wx") succeeds, this process owns the refresh lock. If handle.writeFile(lockId, { signal }) aborts or throws, we close the handle but leave the lock file behind, and acquireRefreshLock() never returns a lockId, so the normal releaseRefreshLock() cleanup path never runs.

    Suggestion: either avoid aborting that tiny lock-id write and check before/after, or track whether the lock id was written and unlink the lock file on write failure before rethrowing. Cancellation should shorten waits, not create a stale-lock wait for the next command.

  3. packages/cli/src/adapters/git.ts:14-24

    readGitOriginRemote() now passes signal to execFile, but the blanket catch still converts aborts into null. That means cancellation can be misreported as “no origin remote” at this adapter boundary.

    Suggestion: catch the error, rethrow when signal?.aborted or the error is an abort, and only return null for normal git lookup failures.

  4. packages/cli/src/controllers/project.ts

    There are a few type-boundary drifts from threading signal through this file:

    • readProjectListLocalBinding() now requires signal, but the fixture-mode call at line 111 still omits it.
    • The local SourceRepositoryApiClient overloads don’t accept signal, but the new call sites pass it into GET/POST/DELETE options.

    I know tsc --noEmit already has pre-existing repo failures, so I’m not treating global typecheck as newly broken by this PR. But these specific errors are PR-owned and they matter here because local types are the contract documenting which boundaries are cancellation-aware.

Non-blocking maintainability note: this PR now has small abortable sleep / pre-post boundary-check patterns in a few places. That’s okay for this patch, but once these fixes land we should consider a tiny shared helper for “abortable delay” and “unsupported boundary pre/post check” so future cancellation work does not drift by copy/paste.

Again: I think the architecture is spot on. These change are geared towards tightening boundaries.

@rtbenfield
Copy link
Copy Markdown
Contributor Author

Responding to the boundary-contract points here:

  1. Local state writes: fixed in 715d01b by removing the command signal from the durable state.json write and keeping pre/post abort checks around the unabortable write.
  2. Token refresh lock: fixed in 01640c4. The lock-id write is no longer abortable, and if cancellation or any other error happens after open("wx") succeeds but before the lock is returned, the lock file is unlinked before rethrowing.
  3. Git origin lookup: fixed in 01640c4. readGitOriginRemote() now rethrows aborts instead of converting them to null, with a regression test covering an already-aborted signal.
  4. Project controller type drift: fixed in 01640c4. The fixture-mode readProjectListLocalBinding() call now passes the runtime signal, and the local SourceRepositoryApiClient contract includes signal?: AbortSignal on the request option shapes that pass it.

I did not extract the shared abortable delay / boundary-check helper in this PR pass. That cleanup is worth considering, but I kept this focused on tightening the concrete boundaries called out here rather than widening the patch.

Verification run:

  • pnpm --filter @prisma/cli exec vitest run tests/git-adapter.test.ts tests/token-storage.test.ts tests/project-real-mode.test.ts tests/project-controller.test.ts
  • pnpm --filter @prisma/cli build

🤖 written by AI

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/bin.ts`:
- Around line 7-10: The worker branch calls runUpdateDiscoveryWorker() without
handling rejections; update that call site to attach a .catch handler to
runUpdateDiscoveryWorker() (the same call in packages/cli/src/bin.ts) that logs
the error and sets process.exitCode to a non-zero value (e.g., 1) so failures
don't yield unhandled promise rejections and the process exit code is
deterministic; keep the existing .then handler to set process.exitCode = 0 on
success and ensure the .catch handles any thrown error from
runUpdateDiscoveryWorker().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e50297cc-bca1-4250-9152-f1ca8369efbe

📥 Commits

Reviewing files that changed from the base of the PR and between 01640c4 and 8e6dbeb.

📒 Files selected for processing (2)
  • packages/cli/src/bin.ts
  • packages/cli/src/cli.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/bin.ts`:
- Around line 7-10: The worker branch calls runUpdateDiscoveryWorker() without
handling rejections; update that call site to attach a .catch handler to
runUpdateDiscoveryWorker() (the same call in packages/cli/src/bin.ts) that logs
the error and sets process.exitCode to a non-zero value (e.g., 1) so failures
don't yield unhandled promise rejections and the process exit code is
deterministic; keep the existing .then handler to set process.exitCode = 0 on
success and ensure the .catch handles any thrown error from
runUpdateDiscoveryWorker().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e50297cc-bca1-4250-9152-f1ca8369efbe

📥 Commits

Reviewing files that changed from the base of the PR and between 01640c4 and 8e6dbeb.

📒 Files selected for processing (2)
  • packages/cli/src/bin.ts
  • packages/cli/src/cli.ts
🛑 Comments failed to post (1)
packages/cli/src/bin.ts (1)

7-10: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle rejection in the update-check worker path.

runUpdateDiscoveryWorker() has no .catch(). If it rejects, this produces an unhandled promise rejection and process.exitCode is never set, unlike the normal path below which deterministically resolves an exit code. Add a rejection handler so worker failures surface a non-zero exit code instead of an unhandled rejection.

🛡️ Proposed fix
-  runUpdateDiscoveryWorker().then(() => {
-    process.exitCode = 0;
-  });
+  runUpdateDiscoveryWorker().then(() => {
+    process.exitCode = 0;
+  }).catch(() => {
+    process.exitCode = 1;
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/bin.ts` around lines 7 - 10, The worker branch calls
runUpdateDiscoveryWorker() without handling rejections; update that call site to
attach a .catch handler to runUpdateDiscoveryWorker() (the same call in
packages/cli/src/bin.ts) that logs the error and sets process.exitCode to a
non-zero value (e.g., 1) so failures don't yield unhandled promise rejections
and the process exit code is deterministic; keep the existing .then handler to
set process.exitCode = 0 on success and ensure the .catch handles any thrown
error from runUpdateDiscoveryWorker().

Copy link
Copy Markdown
Member

@luanvdw luanvdw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick follow-up! 🙇‍♂️

The remaining CodeRabbit note about adding a .catch() to the update-check worker promise seems like valid hygiene, but I don’t see it as introduced by or blocking this cancellation PR. Fine to handle opportunistically or as a small follow-up.

@rtbenfield rtbenfield merged commit b9a0aab into main Jun 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants