From c0567b256cf933d8a5316a47152689e40b01bb3e Mon Sep 17 00:00:00 2001 From: Vivian Vijay Ludrick <116781909+vivianludrick@users.noreply.github.com> Date: Tue, 18 Aug 2026 11:49:53 +0530 Subject: [PATCH 1/2] LOC-7291: download darwin-arm64 binary on Apple Silicon 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 --- lib/LocalBinary.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/LocalBinary.php b/lib/LocalBinary.php index 5c6121a..eacac41 100644 --- a/lib/LocalBinary.php +++ b/lib/LocalBinary.php @@ -53,8 +53,11 @@ private function server_home() { } private function platform_url(){ - 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'; return 'https://s3.amazonaws.com/browserStack/browserstack-local/BrowserStackLocal-darwin-x64'; + } else if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') return 'https://s3.amazonaws.com/browserStack/browserstack-local/BrowserStackLocal.exe'; if ((strtoupper(PHP_OS)) == "LINUX") { From 24f5889b84a32499fd866ba88c75c4acf654ea33 Mon Sep 17 00:00:00 2001 From: Vivian Vijay Ludrick <116781909+vivianludrick@users.noreply.github.com> Date: Thu, 20 Aug 2026 15:56:55 +0530 Subject: [PATCH 2/2] review: check curl result, fall back to x64 when arm64 fetch fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- lib/LocalBinary.php | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/lib/LocalBinary.php b/lib/LocalBinary.php index eacac41..2640edd 100644 --- a/lib/LocalBinary.php +++ b/lib/LocalBinary.php @@ -69,7 +69,12 @@ private function platform_url(){ } public function download_binary($path) { - $url = $this->platform_url(); + $urls = array($this->platform_url()); + // If the arm64 binary is not published to the legacy bucket yet, fall + // back to the x64 binary, which still works on Apple Silicon via + // Rosetta 2 — releasing must not turn a working download into a 404. + if (substr($urls[0], -13) === '-darwin-arm64') + $urls[] = str_replace('-darwin-arm64', '-darwin-x64', $urls[0]); if (!file_exists($path)) mkdir($path, 0777, true); @@ -79,18 +84,29 @@ public function download_binary($path) { $dest_binary_name = $dest_binary_name. ".exe"; } $dest_binary_path = $path. '/'. $dest_binary_name; - $file = fopen($dest_binary_path , "w+"); - $ch = curl_init(""); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_FILE, $file); - $data = curl_exec ($ch); - curl_close ($ch); - - fclose($file); - chmod($dest_binary_path, 0755); - return $dest_binary_path; + foreach ($urls as $url) { + $file = fopen($dest_binary_path , "w+"); + $ch = curl_init(""); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_FILE, $file); + // Fail on HTTP >= 400 instead of writing the error body to disk — + // a saved error body would satisfy the file_exists() check in + // binary_path() and stick until the user deletes it by hand. + curl_setopt($ch, CURLOPT_FAILONERROR, true); + $data = curl_exec ($ch); + curl_close ($ch); + + fclose($file); + if ($data !== false) { + chmod($dest_binary_path, 0755); + return $dest_binary_path; + } + // remove the empty/partial file so the next attempt (or next run) retries + unlink($dest_binary_path); + } + throw new LocalException("Failed to download BrowserStackLocal binary from: " . implode(", ", $urls)); } private function get_available_dirs() {