Skip to content

localenv: reap uv subprocesses on SIGINT/SIGTERM instead of orphaning them - #6107

Open
rugpanov wants to merge 1 commit into
mainfrom
dbconnect/reap-uv-subprocesses
Open

localenv: reap uv subprocesses on SIGINT/SIGTERM instead of orphaning them#6107
rugpanov wants to merge 1 commit into
mainfrom
dbconnect/reap-uv-subprocesses

Conversation

@rugpanov

Copy link
Copy Markdown
Contributor

Changes

Scoped to the (still hidden) environments setup-local command:

  • Install signal.NotifyContext(SIGINT, SIGTERM) in the command's RunE so signals cancel the pipeline context (mirrors experimental/genie). The CLI root does not install a signal handler.
  • Add an opt-in process.WithProcessGroup() option. On Unix it runs the child in its own process group (Setpgid) and, on cancellation, sends SIGTERM to the whole group so grandchildren are reaped, with WaitDelay escalating to SIGKILL if the leader hangs. On non-Unix it sets WaitDelay only (whole-tree kill would need a Job Object; out of scope).
  • Apply the option to the uv spawns in libs/localenv (version probe, python install / sync / pip seed, venv validation, installer pipeline).

Default Background/Forwarded behavior for all other subprocess callers is unchanged.

Why

Previously a SIGTERM to just the CLI PID (a supervisor, CI timeout, or VS Code stopping the child) never cancelled the command context, and even when cancelled exec.CommandContext only SIGKILLs the direct child. uv sync's own subprocesses (Python, build backends) were left running as orphans over a half-written .venv with held locks. DECO-27811.

Tests

  • New unix-guarded test in libs/process spawns a shell that backgrounds a sleep grandchild, cancels the context, and asserts Background returns within the grace period and the grandchild is reaped (kill(pid, 0)ESRCH).
  • go test ./libs/process/... ./libs/localenv/... ./cmd/environments/... and the localenv/help acceptance suites pass; golangci-lint and deadcode clean; builds green for darwin and windows.

This PR was written by Claude Code.

… them

The CLI root never installs a signal handler, so a SIGTERM to just the
CLI PID (supervisor, CI timeout, or VS Code stopping the child) did not
cancel the command context. Even once cancelled, exec.CommandContext only
SIGKILLs the direct child, leaving uv's own subprocesses (Python, build
backends) running as orphans over a half-written .venv with held locks.

Fix, scoped to the hidden `environments setup-local` command:

- Install signal.NotifyContext(SIGINT, SIGTERM) in the command's RunE so
  signals cancel the pipeline context, mirroring experimental/genie.
- Add an opt-in process.WithProcessGroup() execOption. On Unix it puts the
  child in its own process group (Setpgid) and, on cancellation, sends
  SIGTERM to the whole group so grandchildren are reaped, then relies on
  WaitDelay to escalate to SIGKILL if the leader hangs. On non-Unix it sets
  WaitDelay only (whole-tree kill needs a Job Object, out of scope).
- Apply the option to the uv spawns in libs/localenv (version probe, python
  install / sync / pip seed, venv validation, and the installer pipeline).

Default Background/Forwarded behavior for all other callers is unchanged.

DECO-27811

Co-authored-by: Isaac
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approval status: pending

/cmd/environments/ - approved by @rclarey

Files: cmd/environments/sync.go

/libs/localenv/ - approved by @rclarey

Files: libs/localenv/uv.go

/libs/process/ - needs approval

4 files changed
Suggested: @simonfaltum
Also eligible: @renaudhartert-db, @hectorcast-db, @parthban-db, @tanmay-db, @Divyansh-db, @tejaskochar-db, @mihaimitrea-db, @chrisst, @rauchy

Any maintainer (@andrewnester, @anton-107, @denik, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @janniklasrose, @lennartkats-db) can approve all areas.
See OWNERS for ownership rules.

@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 14aa98f

Run: 30562317158

Env 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 4 4 322 1066 5:22
💚​ aws windows 4 4 324 1064 6:54
💚​ azure linux 4 4 322 1065 5:34
💚​ azure windows 4 4 324 1063 4:38
💚​ gcp linux 1 5 321 1067 4:29
💚​ gcp windows 1 5 323 1065 6:20
8 interesting tests: 4 RECOVERED, 4 SKIP
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestFetchRepositoryInfoAPI_FromRepo 💚​R 💚​R 💚​R 💚​R 🙈​S 🙈​S
💚​ TestFetchRepositoryInfoAPI_FromRepo/root 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/subdir 💚​R 💚​R 💚​R 💚​R
Top 3 slowest tests (at least 2 minutes):
duration env testname
5:37 aws windows TestAccept
5:20 gcp windows TestAccept
3:10 azure windows TestAccept

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.

3 participants