LOC-7472 Look for BrowserStackLocal.exe on Windows before downloading - #187
pranay-v29 wants to merge 2 commits into
Conversation
Parallel runs on one machine share ~/.browserstack/BrowserStackLocal.exe. While one run is executing it, Windows keeps the file locked for that run's whole lifetime, so a second run that reaches the download path hits EBUSY on the open. 1.5.15 no longer crashes on that, but it waits three seconds and then tries to delete and re-download the file -- which cannot succeed while the first run holds it, so the second run exhausts its retries and never gets a tunnel (SDK-7713). A locked binary that still answers --version is a working binary. Before waiting on or replacing it, probe it and reuse it if it runs; probe again after the wait, since an AV scan or a finishing writer can release it. A file that does not run is still replaced as before. The version check mirrors browserstack-local-python's __verify_binary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited), Workspace UI (inherited) Review profile: ASSERTIVE Plan: Enterprise Run ID: 📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review. 📜 Recent review details🧰 Additional context used🪛 ast-grep (0.45.3)test/local_binary_busy_download.js[warning] 154-154: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use. (detect-non-literal-fs-filename) 🪛 ESLinttest/local_binary_busy_download.js[error] 136-136: 'describe' is not defined. (no-undef) [error] 152-152: 'beforeEach' is not defined. (no-undef) [error] 158-158: 'afterEach' is not defined. (no-undef) [error] 163-163: 'it' is not defined. (no-undef) [error] 167-167: 'it' is not defined. (no-undef) 🔇 Additional comments (2)
📝 WalkthroughWalkthroughLocalBinary now sets its Windows flag during construction. New tests check that synchronous and asynchronous calls to binaryPath() reuse an existing Windows binary without downloading it. ChangesWindows binary detection
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~8 minutes Change: Bug fix Merge Risk: ⚪ Minimal · up to On Windows, an existing BrowserStackLocal.exe is now reused instead of being downloaded again, which avoids the EBUSY failure. Tests cover both the sync and async paths. No merge-blocking risk was found. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
A rabbit checks the Windows trail Comment |
binaryPath() picks the file to look for with this.windows, but that flag was only set inside getBinaryFilename(), which runs on the download path. On a fresh LocalBinary it was still undefined, so on Windows binaryPath() checked for "BrowserStackLocal" without the extension, never found it, and re-downloaded the binary on every start -- writing over BrowserStackLocal.exe each time. That is what the SDK-7713 logs show: a second run started fourteen seconds after the first run's tunnel was up, went straight to the download, and hit EBUSY because the first run was executing the file. It also means every Windows start paid for a full download. Set the flag in the constructor, as Local.js already does. This replaces the --version probe from the previous commit, which only reused the binary after the collision; with the right filename there is no download to collide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@coderabbitai review |
✅ Action performedReview finished.
|
Follow-up to #185 (LOC-7420). Customer ticket: SDK-7713.
Problem
On Windows, this library re-downloads the binary on every start and never reuses the existing one.
binaryPath()chooses the file to look for withthis.windows:but
this.windowswas only set insidegetBinaryFilename(), which runs on the download path.Localcreates a freshLocalBinaryfor each start, so whenbinaryPath()ran the flag was stillundefined. On Windows it looked forBrowserStackLocalwithout the extension, never found it, and downloaded, overwritingBrowserStackLocal.exe.Usually that just costs a full download on every run. When another run is executing the binary, the overwrite fails with
EBUSY.Evidence from the SDK-7713 logs
The customer's
sdk-cli-debug.logshows the second run did not race the first. It started well after the first run's tunnel was up:Run 2 went straight to the download while
BrowserStackLocal.exewas right there. On 1.5.13 that crashed the SDK CLI. On 1.5.15 (#185) it no longer crashes, but run 2 still cannot write the file, so it exhausts its retries and never gets a tunnel.Change
Set
this.windowsin the constructor, the wayLocal.js:18already does.binaryPath()then finds the existing.exeand reuses it, so there is no download and nothing to collide with.Two lines.
getBinaryFilename()still sets the flag too, so nothing that relied on that is affected.This PR previously added a
--versionprobe to reuse a busy binary after the collision. That is removed: with the correct filename the collision doesn't happen, and the probe was also what tripped the Semgrepdetect-child-processrule.Tests
Two tests in
test/local_binary_busy_download.js. Each buildsLocalBinaryas on Windows (stubbingprocess.platform), places a realBrowserStackLocal.exein its directory, and fails if a download is attempted:Both fail against
1e479a6(1.5.15) withshould not re-download, and pass with this change. 15/15 acrosslocal_binary_busy_download.jsandlocal_start_output_handling.js. TheBinary filenamesuite intest/local.js, which setshostOSafter construction, still passes 5/5. ESLint clean.Impact beyond SDK-7713
browserstack-binary) for every SDK language, currently downloads the binary on every run. This removes that.Downloading in syncon each run, and a freshly written.exeon each run is exactly what antivirus scans and locks.Not in scope
~/.browserstack. Two runs starting on a machine with no binary can still both download at once. That case is now much rarer, since it only happens when the binary is genuinely absent.execFileinLocal.startstill throws spawn failures synchronously. The SDK CLI usesstart(), so an unrunnable binary could still crash the CLI there. Wants its own change.Delivery
Once released,
browserstack-binaryneeds to bump from^1.5.15so the SDK CLI picks it up, as SDK-7712 did for 1.5.15.🤖 Generated with Claude Code
Summary by CodeRabbit