Skip to content

fix(build): thread caller PATH per request into daemon tool resolution (#1219) - #1236

Merged
zackees merged 1 commit into
mainfrom
fix/1219-caller-path-build
Aug 2, 2026
Merged

fix(build): thread caller PATH per request into daemon tool resolution (#1219)#1236
zackees merged 1 commit into
mainfrom
fix/1219-caller-path-build

Conversation

@zackees

@zackees zackees commented Aug 2, 2026

Copy link
Copy Markdown
Member

Closes #1219

Problem

Tool resolution for bare-name spawns was bound to whichever environment spawned the daemon — for up to 12 h (IDLE_TIMEOUT). Two byte-identical builds could resolve different toolchains (esptool, the Python that runs extra_scripts, objcopy) depending only on which shell started the daemon. This is the mechanism behind the #1217 FastLED CI outage.

Fix

Forward the CLI caller's PATH on every build request and use it at the bare-name spawn sites (issue's "suggested direction 1"):

  • Plumbing (mirrors the existing src_dir/pio_env forwarding): CLI sets caller_path from its own env → BuildRequest (serde-optional; old clients omit it and get today's behavior — HTTP API stays backward compatible) → daemon handler → BuildParams.caller_path.
  • Spawn sites now resolving against the caller's PATH (child PATH is replaced via run_command's env overlay; None ⇒ legacy daemon env):
    • esp32_linker esptool elf2image fallback, and both boot_artifacts spawns (gen_esp32part python + bootloader elf2image)
    • esp8266_linker bare-esptool conversion
    • script_runtime: find_python_with_path probe and the interpreter invocation that runs extra_scripts (so imports/subprocess resolution inside the script see the same environment the probe did)
    • ch32v_linker objcopy (via a new LinkerBase::objcopy_firmware_with_env; AVR/Teensy callers unchanged)
  • Scope: build path only. Deploy-side threading is feat(deploy): thread caller_path into deployers — bare-name deploy-time spawns still resolve against daemon PATH (follow-up to #1219) #1234 (deploy's BuildParams literal carries an explicit None + comment pointing there).

Latent Windows bug fixed along the way

The new contract test exposed that compute_env kept both Path (parent spelling) and PATH (overlay) entries; Command::env's case-insensitive child-env map let whichever applied last silently win — i.e. a PATH overlay could be discarded. compute_env now drops case-variant keys before inserting an overlay var, with a real child-process test asserting the overlay value is exactly what the child sees.

Tests

  • env_overlay_path_reaches_child_over_case_variant (fbuild-core, real child process)
  • test_find_python_with_path_replaces_inherited_path (empty-dir PATH must make the probe fail even though the daemon env has python — the load-bearing regression; on Windows the strict assertion skips when %WINDIR%\py.exe exists, since the OS's system-dir exe fallback for py is Windows resolution, not a PATH leak) + ..._none_matches_find_python
  • build_request_caller_path_defaults_to_none / ..._forwarded (daemon serde compat)
  • test_with_caller_path_stores_value (ch32v builder)

Validation

  • soldr cargo check --workspace --all-targets — clean
  • soldr cargo clippy --workspace --all-targets -- -D warnings — clean
  • soldr cargo fmt --all — applied (includes a formatting-only normalization of daemon_crash_recovery.rs from test(cli): pin daemon crash-recovery with a real-process regression test (#1228) #1232)
  • Touched-crate suites all green: fbuild-build-engine 388 passed, fbuild-core 262, fbuild-daemon 228 (+12 suites), fbuild-build-esp 98, fbuild-build-mcu 63, fbuild-cli 262, fbuild-build 19 suites ok.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@zackees, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7329ea96-1232-4abf-b584-005da38d6cb8

📥 Commits

Reviewing files that changed from the base of the PR and between e460949 and 01d3634.

📒 Files selected for processing (33)
  • crates/fbuild-build-engine/src/lib.rs
  • crates/fbuild-build-engine/src/linker.rs
  • crates/fbuild-build-engine/src/pipeline/context.rs
  • crates/fbuild-build-engine/src/script_runtime.rs
  • crates/fbuild-build-engine/src/script_runtime_tests.rs
  • crates/fbuild-build-esp/src/esp32/esp32_linker.rs
  • crates/fbuild-build-esp/src/esp32/orchestrator/boot_artifacts.rs
  • crates/fbuild-build-esp/src/esp32/orchestrator/build.rs
  • crates/fbuild-build-esp/src/esp8266/esp8266_linker.rs
  • crates/fbuild-build-esp/src/esp8266/orchestrator.rs
  • crates/fbuild-build-mcu/src/ch32v/ch32v_linker.rs
  • crates/fbuild-build-mcu/src/ch32v/orchestrator.rs
  • crates/fbuild-build/src/compile_many.rs
  • crates/fbuild-build/tests/avr_build.rs
  • crates/fbuild-build/tests/clangd_check_parity.rs
  • crates/fbuild-build/tests/eh_frame_strip_esp32.rs
  • crates/fbuild-build/tests/esp32_build.rs
  • crates/fbuild-build/tests/nxplpc_build_flags.rs
  • crates/fbuild-build/tests/nxplpc_core_compile_commands.rs
  • crates/fbuild-build/tests/stm32_acceptance.rs
  • crates/fbuild-build/tests/teensy30_acceptance.rs
  • crates/fbuild-build/tests/teensy_build.rs
  • crates/fbuild-build/tests/teensylc_acceptance.rs
  • crates/fbuild-cli/src/cli/build.rs
  • crates/fbuild-cli/src/cli/clean.rs
  • crates/fbuild-cli/src/daemon_client/types.rs
  • crates/fbuild-cli/src/mcp/tools.rs
  • crates/fbuild-core/src/subprocess.rs
  • crates/fbuild-daemon/src/handlers/emulator/select.rs
  • crates/fbuild-daemon/src/handlers/emulator/tests_process.rs
  • crates/fbuild-daemon/src/handlers/operations/build.rs
  • crates/fbuild-daemon/src/handlers/operations/deploy.rs
  • crates/fbuild-daemon/src/models.rs

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.

#1219)

The daemon captured PATH once at spawn and lived up to 12h, so bare-name
tool spawns (esptool, python, objcopy) resolved against whichever shell
happened to start the daemon. Forward the CLI caller PATH on every build
request (BuildRequest -> BuildParams, like src_dir/pio_env) and apply it
at the bare-name spawn sites: esp32/esp8266 esptool elf2image, the
find_python probe plus extra-script interpreter execution, the
gen_esp32part spawn, and the ch32v objcopy. Caller PATH replaces the
child PATH; an absent field keeps legacy daemon-env behavior, so the
HTTP API stays backward compatible. Deploy-side threading is #1234.

Also fixes a latent Windows env-overlay bug found by the new contract
test: compute_env kept both Path (parent spelling) and PATH
(overlay) entries, and whichever Command::env applied last silently won.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zackees
zackees force-pushed the fix/1219-caller-path-build branch from 624ebd8 to 01d3634 Compare August 2, 2026 02:20
@zackees
zackees merged commit e85fb71 into main Aug 2, 2026
96 of 97 checks passed
@zackees
zackees deleted the fix/1219-caller-path-build branch August 2, 2026 02:31
zackees added a commit that referenced this pull request Aug 2, 2026
…#1240)

Closes #1238

BinArtifactCache gains a serde-defaulted esptool_fingerprint: empty for
a provisioned absolute-path esptool (resolution cannot drift, so
pre-existing cache records stay valid and caching behaves exactly as
before), a short SHA-256 of the caller's PATH for bare-name spawns.
Since #1236 the esptool spawn resolves against the per-request caller
PATH, so two requests with different caller PATHs can resolve different
esptool binaries — they must never share a cached firmware.bin.

The shared BUILD_FINGERPRINT_VERSION constant is left untouched:
bumping it would invalidate every unrelated cache, and the serde
default already yields the correct match semantics.

Also adds the end-to-end subprocess test proving run_command resolves a
bare executable from an overlay PATH (staged uniquely named probe in a
TempDir), that absolute-path spawns work without an overlay, and that
the bare name fails without the overlay.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@fastled-project-sync fastled-project-sync Bot moved this to Triage in FastLED Tracker Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

daemon binds tool resolution to spawn-time PATH for up to 12h — bare-name esptool/python/objcopy spawns resolve nondeterministically

1 participant