Abort the CLI immediately on SIGTERM/SIGINT (CI cancellation)#1132
Merged
Conversation
GitHub Actions (and most CI) cancel a job by signaling the process tree. The CLI installed no signal handlers and blocked on its work, so a cancellation couldn't fully complete until slow operations (e.g. the API retry loop or telemetry upload) finished on their own. Race the command against termination signals in main via tokio::select!. On SIGTERM/SIGINT (Unix) or Ctrl-C (Windows) the in-flight work future is dropped, which cancels outstanding requests and the retry loop immediately, and the process exits with the conventional 128 + signal code. If handler installation fails we fall back to the OS default disposition, so behavior is no worse than before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
😎 Merged directly without going through the merge queue, as the queue was empty and the PR was up to date with the target branch - details. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1132 +/- ##
==========================================
+ Coverage 82.48% 82.68% +0.20%
==========================================
Files 69 70 +1
Lines 15482 15505 +23
==========================================
+ Hits 12771 12821 +50
+ Misses 2711 2684 -27 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
|
max-trunk
approved these changes
Jun 30, 2026
acatxnamedvirtue
approved these changes
Jun 30, 2026
trunk-io Bot
pushed a commit
that referenced
this pull request
Jul 1, 2026
The `call_api` retry loop spawns two background tasks that emit "taking longer than expected" progress messages. They were only aborted by explicit `.abort()` calls placed after the retry `.await`, so on the happy path they were cleaned up correctly. But when `call_api`'s future is cancelled mid-flight -- e.g. the losing branch of the `tokio::select!` in the CLI main loop when a SIGINT/SIGTERM wins the race (#1132) -- execution never reaches those `.abort()` calls. Dropping a `JoinHandle` merely *detaches* the task, so the reporters leaked: they kept ticking every 2s, kept emitting "taking longer than expected" after the CLI was interrupted, and kept a clone of the render `Sender` alive, which could wedge the renderer join during shutdown. Bind each spawned handle in an `AbortOnDrop` guard so the tasks are aborted on drop -- covering both normal completion and cancellation. Add a regression test that polls a `call_api` future to spawn the reporters, drops it mid-flight, and asserts the reporters stop emitting. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
GitHub Actions (and most CI) cancel a job by signaling the process tree. The CLI installed no signal handlers and blocked on its work, so a cancellation couldn't fully complete until slow operations (e.g. the API retry loop or telemetry upload) finished on their own — customers reported jobs that wouldn't cancel until the slow upload completed.
Change
mainnow races the command against termination signals viatokio::select!. On SIGTERM/SIGINT (Unix) or Ctrl-C (Windows), the in-flight work future is dropped — cancelling outstanding requests and the retry loop immediately — and the process exits with the conventional128 + signalcode.cli/src/shutdown.rs) to keepmain.rsfocused; exit codes are named constants (EXIT_CODE_SIGINT= 130,EXIT_CODE_SIGTERM= 143) with a comment noting these come from the POSIX128 + signalconvention, which theexitcode/sysexits crate doesn't define.signalfeature to theclicrate's tokio dependency (pullssignal-hook-registryintoCargo.lock).Notes
This is the complementary fix to #1131 (retry deadline): the deadline bounds total retry time but is checked between attempts; this PR provides true immediate mid-request abort when CI actually cancels. The two are independent and can merge in any order.
Testing
cargo build -p trunk-analytics-cliclean on this branch standalone (offmain, without the Add TRUNK_API_CLIENT_RETRY_DEADLINE_SECS to bound API retry time #1131 changes).🤖 Generated with Claude Code