LOC-7291: download darwin-arm64 binary on Apple Silicon - #32
LOC-7291: download darwin-arm64 binary on Apple Silicon#32vivianludrick wants to merge 2 commits into
Conversation
On macOS the binding now selects BrowserStackLocal-darwin-arm64 when the runtime reports arm64/aarch64; x64 runtimes (including x64-under-Rosetta) keep BrowserStackLocal-darwin-x64, preserving current behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
07souravkunda
left a comment
There was a problem hiding this comment.
Detection logic is correct and idiomatic — php_uname('m') against arm64/aarch64 matches how the other bindings in this set do it, and x64-under-Rosetta correctly keeps the x64 binary.
No code objection. The one thing to resolve before release is the legacy-bucket gap you flagged yourself: unlike the bindings that resolve their URL from Rails, this one hardcodes s3.amazonaws.com/browserStack/browserstack-local/, so it regresses on release day rather than degrading later — and the resulting 404 is sticky because the error body gets written to the binary path and short-circuits every later run. Details inline.
| if (PHP_OS == "Darwin") | ||
| if (PHP_OS == "Darwin") { | ||
| if (in_array(php_uname('m'), array('arm64', 'aarch64'))) | ||
| return 'https://s3.amazonaws.com/browserStack/browserstack-local/BrowserStackLocal-darwin-arm64'; |
There was a problem hiding this comment.
This URL has no object behind it and no publisher, so releasing this turns a working download into a hard 404 on Apple Silicon.
You've documented it in the PR body, but it's worth separating this binding from the others: nodejs/python/ruby/java/csharp resolve their source URL from Rails / bstack-local-prod, so they only degrade once Rosetta is gone. php and perl hardcode the legacy s3.amazonaws.com/browserStack/browserstack-local/ host, so they break immediately on release — an arm64 Mac that works today via the Rosetta x64 binary starts failing at download.
And the failure is sticky. download_binary() never checks the curl result, so S3's 404 body gets written to ~/.browserstack/BrowserStackLocal and chmod 0755'd. binary_path() then short-circuits on file_exists($binary_path) on every subsequent run, so the user stays broken until they delete the file by hand — no amount of retrying recovers it.
Fix — either:
- Gate the release on
BrowserStackLocal-darwin-arm64being published to that bucket, and added to whatever publishes the other legacy objects; or - Fall back to the x64 URL when the arm64 download fails, so the regression can't fire even if the release ordering slips.
Given the sticky-file behaviour, option 2 looks worth doing regardless of ordering.
There was a problem hiding this comment.
Went with option 2 (plus the sticky-file fix) — done in the latest commit:
CURLOPT_FAILONERRORstops HTTP error bodies from ever reaching disk.- A failed
-darwin-arm64download falls back to the-darwin-x64URL (still works on Apple Silicon via Rosetta 2), so release ordering can't turn a working download into a 404. - If every attempt fails, the partial/empty file is
unlink()ed and aLocalExceptionis thrown —binary_path()can no longer short-circuit on a poisoned file.
Option 1 (publishing the arm64 object to the legacy bucket + wiring it into whatever publishes the others) is still worth doing so arm64 users get the native binary here too — tracking that with the release-ordering work, but it's no longer a correctness gate for this PR.
Per review on PR #32: this binding hardcodes the legacy S3 host, where the arm64 object may not exist yet — and curl's result was ignored, so an HTTP error body would be saved as the binary and satisfy binary_path()'s file_exists() check forever. Now: CURLOPT_FAILONERROR stops error bodies from reaching disk; a failed -darwin-arm64 download falls back to the x64 binary (works on Apple Silicon via Rosetta 2); if everything fails, the partial file is removed and LocalException is thrown instead of returning a poisoned path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| return $dest_binary_path; | ||
| } | ||
| // remove the empty/partial file so the next attempt (or next run) retries | ||
| unlink($dest_binary_path); |
What
On macOS, the binding now downloads
BrowserStackLocal-darwin-arm64when the runtime reports arm64; x64 runtimes (including x64-under-Rosetta) keepBrowserStackLocal-darwin-x64, preserving current behavior.Detection: php_uname('m') in arm64/aarch64.
Why
The Local binary now ships a native Apple Silicon build (browserstack/browserStackTunnel#980). macOS 27 (fall 2026) removes Rosetta 2, so arm64 Macs need the native binary.
Testing
php -l clean; platform_url() returns the arm64 URL on an Apple Silicon Mac.
⚠ Release ordering
Do not release this before:
binaries/master/BrowserStackLocal-darwin-arm64.zip(public ACL line is already in the Jenkins job) — otherwise arm64 Macs get download failures.Related Jira
LOC-7291 / epic LOC-7287 (LOC-7285 = binary PR)
🤖 Generated with Claude Code