diff --git a/lib/LocalBinary.php b/lib/LocalBinary.php index 5c6121a..2640edd 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") { @@ -66,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); @@ -76,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() {