Skip to content

refactor(fbuild-core): migrate to running-process-core 3.4 (crates.io)#254

Merged
zackees merged 2 commits into
mainfrom
feat/running-process-core-3.4
May 22, 2026
Merged

refactor(fbuild-core): migrate to running-process-core 3.4 (crates.io)#254
zackees merged 2 commits into
mainfrom
feat/running-process-core-3.4

Conversation

@zackees
Copy link
Copy Markdown
Member

@zackees zackees commented May 22, 2026

Summary

Closes the loop on the running-process-core upgrade deferred from #253. Drops the git-pinned ff9d7972 rev (an abandoned feature branch with a pre-publication API) and adopts the published 3.4 release from crates.io. See #32.

Why

The pre-publication API exposed Containment enum, ProcessConfig::containment field, and ContainedProcessGroup::spawn_with_containment(...). The published 3.4 API replaced these with ContainedProcessGroup::spawn(...) returning a custom SpawnedChild (not convertible to std::process::Child), so a clean swap isn't possible without changing fbuild's spawn_contained / spawn_detached return type and rippling into every caller.

What changes

  • subprocess.rs — drop the Containment import and the containment: field from the ProcessConfig literal (the published struct no longer has it).
  • containment.rs Windows path — replace group.spawn_with_containment(command, Containment::Contained)? with command.spawn()? followed by windows_job::assign(child.as_raw_handle())?. Same JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE semantics, reusing the existing windows_job::assign helper already used by the tokio spawn path.
  • containment.rs Windows spawn_detached — drops Job Object assignment entirely (that's what "detached" means) but still injects the originator env var.
  • New helper inject_originator_env mirrors ContainedProcessGroup::inject_originator_env so children still inherit RUNNING_PROCESS_ORIGINATOR=TOOL:PID for cross-process correlation.
  • Unix path is unchanged — it already used a manual pre_exec hook (per-child pgid + PR_SET_PDEATHSIG) and never touched the upstream API.

Test plan

  • uv run soldr cargo check --workspace --all-targets — clean
  • uv run soldr cargo clippy --workspace --all-targets -- -D warnings — clean
  • uv run soldr cargo test --workspace — 534+ tests pass, including:
  • CI passes on Linux/macOS/Windows board builds (containment is exercised by every daemon spawn)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Refactored internal process containment implementation for Windows and Unix environments.
    • Updated subprocess configuration and handling.

Review Change Stack

Drop the git-pinned ff9d7972 rev of `running-process-core` (an
abandoned feature branch) and adopt the published 3.4 release from
crates.io. See #32.

The pre-publication API exposed:
- a `Containment` enum (`Contained` / `Detached`),
- a `ProcessConfig::containment` field, and
- `ContainedProcessGroup::spawn_with_containment(command, mode)`
  returning `ContainedChild { child, .. }`.

The published 3.4 API replaced these with a `ContainedProcessGroup::spawn`
method returning a custom `SpawnedChild` (and `spawn_daemon` for
detached spawns). `SpawnedChild` is not convertible to
`std::process::Child`, so switching wholesale would break fbuild's
`spawn_contained` / `spawn_detached` return type and ripple into every
caller.

Changes:
- `subprocess.rs`: drop the `Containment` import and the `containment:`
  field from `ProcessConfig` literal. The published `ProcessConfig` no
  longer has that field; Windows containment is now applied post-spawn
  by `containment::windows_job::assign` instead.
- `containment.rs`: replace the Windows `spawn_with_containment(_, Contained)`
  call with `command.spawn()` followed by `windows_job::assign(child.as_raw_handle())`
  — the same `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` behaviour, reusing
  the existing `windows_job::assign` helper already used by the tokio
  spawn path.
- Windows `spawn_detached` drops the Job Object assignment entirely
  (that's what "detached" means) but still injects the originator env
  var so cross-process correlation continues to work.
- Add `inject_originator_env` helper that mirrors
  `ContainedProcessGroup::inject_originator_env` so children of the
  std-path spawns still inherit `RUNNING_PROCESS_ORIGINATOR=TOOL:PID`.

Tests: all 534+ workspace tests pass, including the issue #129
EPERM regression test that verifies two sequential `spawn_contained`
calls both succeed after the first child exits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Warning

Review limit reached

@zackees, we couldn't start this review because you've used your available PR reviews for now.

Your plan currently allows 1 review/hour. Refill in 54 minutes and 2 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 72d021f1-93d6-439a-b834-8f71977c185b

📥 Commits

Reviewing files that changed from the base of the PR and between 4bced39 and 2c93e37.

📒 Files selected for processing (1)
  • crates/fbuild-core/src/containment.rs
📝 Walkthrough

Walkthrough

The PR migrates running-process-core from a git-pinned revision to the published crates.io version 3.4 by implementing process containment logic locally: Windows spawns via Job Object assignment, Unix via per-child pre_exec hooks, and detached processes via originator env injection.

Changes

Process Containment Refactor

Layer / File(s) Summary
Dependency upgrade
Cargo.toml
running-process-core dependency switched from git-pinned revision to crates.io version 3.4, with updated comment.
Custom containment implementation
crates/fbuild-core/src/containment.rs
Imports updated to include ContainedProcessGroup and ORIGINATOR_ENV_VAR; spawn_contained refactored to spawn directly then assign to Windows Job Object or install Unix pre_exec hooks; spawn_detached changed to inject originator env and use detached pre_exec; new inject_originator_env helper replaces reliance on spawn_with_containment.
Subprocess module alignment
crates/fbuild-core/src/subprocess.rs
Module documentation updated to describe post-spawn Windows containment assignment; Containment import removed; ProcessConfig construction simplified to remove conditional containment field setting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • FastLED/fbuild#200: Main PR for migrating to published running-process-core API with post-spawn containment model.

Poem

🐰 From git pins to crates.io we hop,
Custom containment logic—no more top-down drop!
Windows Jobs and Unix groups now hand-crafted with care,
The daemon spawns children without API despair! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating from a git-pinned development version to the published crates.io release of running-process-core 3.4.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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/running-process-core-3.4

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 1

Caution

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

⚠️ Outside diff range comments (1)
crates/fbuild-core/src/containment.rs (1)

253-263: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing originator env injection in tokio_spawn::spawn_contained.

The std::process path (spawn_contained at line 117) injects RUNNING_PROCESS_ORIGINATOR via inject_originator_env, but the tokio path does not. Tokio-spawned children (e.g., emulator processes per shared.rs:192) will lack the originator correlation env var, breaking cross-process tracing after crashes.

Add env injection before spawning:

Proposed fix
     pub fn spawn_contained(
         command: &mut tokio::process::Command,
     ) -> std::io::Result<tokio::process::Child> {
-        if !super::is_initialised() {
+        let Some(group) = super::GLOBAL_GROUP.get() else {
             return command.spawn();
-        }
+        };
+        if let Some(value) = group.originator_value() {
+            command.env(super::ORIGINATOR_ENV_VAR, value);
+        }
         configure(command);
         let child = command.spawn()?;
         post_spawn(&child)?;
         Ok(child)
     }
🤖 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 `@crates/fbuild-core/src/containment.rs` around lines 253 - 263, tokio_spawn's
spawn_contained does not inject RUNNING_PROCESS_ORIGINATOR like the std::process
path, so call inject_originator_env(command) inside spawn_contained (e.g., after
configure(command) and before let child = command.spawn()?) and propagate its
Result with ? so any error bubbles up; keep post_spawn(&child)? as-is to match
existing flow and ensure originator env is set for tokio-spawned children
(references: function spawn_contained, configure, inject_originator_env,
post_spawn).
🤖 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 `@crates/fbuild-core/src/containment.rs`:
- Around line 120-127: If windows_job::assign(child.as_raw_handle()) fails after
command.spawn() succeeds, the child is orphaned; update the spawn-and-assign
sequence in the containment logic so that on assign error you terminate the
spawned child (e.g., call child.kill() and wait/cleanup) before returning the
error. Specifically, in the block that uses GLOBAL_GROUP.get(), after
inject_originator_env(command, group) and let child = command.spawn()?, attempt
windows_job::assign(child.as_raw_handle()) and if that returns Err, ensure you
first stop the child process (handle kill()/wait or equivalent on the Child),
ignore any secondary errors appropriately, then propagate the original assign
error to the caller. Ensure no handle leak remains and that errors from
kill/wait are logged/handled but do not mask the job-assignment error.

---

Outside diff comments:
In `@crates/fbuild-core/src/containment.rs`:
- Around line 253-263: tokio_spawn's spawn_contained does not inject
RUNNING_PROCESS_ORIGINATOR like the std::process path, so call
inject_originator_env(command) inside spawn_contained (e.g., after
configure(command) and before let child = command.spawn()?) and propagate its
Result with ? so any error bubbles up; keep post_spawn(&child)? as-is to match
existing flow and ensure originator env is set for tokio-spawned children
(references: function spawn_contained, configure, inject_originator_env,
post_spawn).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 27b01cc6-35d1-4e04-8415-fc20d49320f0

📥 Commits

Reviewing files that changed from the base of the PR and between 60a4ae3 and 4bced39.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml
  • crates/fbuild-core/src/containment.rs
  • crates/fbuild-core/src/subprocess.rs

Comment thread crates/fbuild-core/src/containment.rs
Two issues flagged by CR:

1. **Orphan on Job Object assignment failure** (Major): the upstream
   `spawn_with_containment` was atomic — if assignment failed the child
   was never visible. My split spawn+assign in `spawn_contained`
   (Windows) leaked the child on assign failure. Now kill+wait the
   child before returning the error, preserving the containment
   guarantee.

2. **Missing originator env in tokio path** (Major): the std-path
   `spawn_contained` injects `RUNNING_PROCESS_ORIGINATOR` via the new
   `inject_originator_env` helper, but `tokio_spawn::spawn_contained`
   didn't, so tokio-spawned children (emulator processes via
   `shared.rs`) lacked the correlation env var. Mirror the std-path
   injection in the tokio variant.

Tests: containment regression suite still passes (including the
issue #129 EPERM test). `cargo clippy --workspace -- -D warnings`
clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zackees
Copy link
Copy Markdown
Member Author

zackees commented May 22, 2026

@coderabbitai both addressed in 2c93e37:

  1. Orphan on Job Object assignment failure: now kill+wait the child before returning the error from windows_job::assign, preserving the atomic containment guarantee that the old spawn_with_containment provided.
  2. Missing originator env in tokio path: tokio_spawn::spawn_contained now mirrors the std-path injection of RUNNING_PROCESS_ORIGINATOR so emulator children get the correlation env var.

Containment regression tests still pass; clippy --workspace -- -D warnings clean.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Tip

For best results, initiate chat on the files or code changes.

@zackees — thanks for the thorough follow-up! Both fixes look exactly right:

  1. Kill+wait on Job Object failure — the atomic containment guarantee is fully restored; no orphaned children can escape containment if windows_job::assign fails.
  2. Originator env in tokio pathspawn_contained on the async path now properly propagates RUNNING_PROCESS_ORIGINATOR, keeping correlation consistent between the std and tokio spawn flows.

Great work keeping clippy clean and the containment regression suite green. 🎉

(∩^ω^)⊃━☆゚.*・。゚


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@zackees zackees merged commit 951e4e6 into main May 22, 2026
79 of 81 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.

1 participant