55class Platform
66{
77 private const BINARY_NAME = 'volt-test ' ;
8+
9+ private const CURRENT_VERSION = 'v0.0.1-beta ' ;
10+ private const BASE_DOWNLOAD_URL = 'https://github.com/volt-test/binaries/releases/download ' ;
811 private const SUPPORTED_PLATFORMS = [
9- 'linux-amd64 ' => 'linux-amd64/ volt-test ' ,
10- 'darwin-amd64 ' => 'darwin-amd64/ volt-test ' ,
11- 'darwin-arm64 ' => 'darwin-arm64/ volt-test ' ,
12- 'windows-amd64 ' => 'windows-amd64/ volt-test.exe ' ,
13- 'windows-AMD64 ' => 'windows-amd64/ volt-test.exe ' ,
12+ 'linux-amd64 ' => 'volt-test-linux-amd64 ' ,
13+ 'darwin-amd64 ' => 'volt-test-darwin-amd64 ' ,
14+ 'darwin-arm64 ' => 'volt-test-darwin-arm64 ' ,
15+ 'windows-amd64 ' => 'volt-test-windows-amd64 .exe ' ,
16+ 'windows-AMD64 ' => 'volt-test-windows-amd64 .exe ' ,
1417 ];
1518
16- public static function installBinary ( $ testing = false ): void
19+ private static function getVendorDir ( ): string
1720 {
18- $ platform = self ::detectPlatform ($ testing );
19-
20- if (! array_key_exists ($ platform , self ::SUPPORTED_PLATFORMS )) {
21- throw new \RuntimeException ("Platform $ platform is not supported " );
22- }
23-
24- $ sourceFile = __DIR__ . '/../bin/platforms/ ' . self ::SUPPORTED_PLATFORMS [$ platform ];
25- $ targetDir = self ::getBinaryDir ();
26- $ targetFile = $ targetDir . '/ ' . basename (self ::SUPPORTED_PLATFORMS [$ platform ]);
27-
28- if (! file_exists ($ sourceFile )) {
29- throw new \RuntimeException ("Binary not found for platform: $ platform " );
21+ // First try using Composer's environment variable
22+ if (getenv ('COMPOSER_VENDOR_DIR ' )) {
23+ return rtrim (getenv ('COMPOSER_VENDOR_DIR ' ), '/ \\' );
3024 }
3125
32- if (! is_dir ($ targetDir )) {
33- if (! mkdir ($ targetDir , 0755 , true )) {
34- throw new \RuntimeException ("Failed to create directory: $ targetDir " );
26+ // Then try using Composer's home directory
27+ if (getenv ('COMPOSER_HOME ' )) {
28+ $ vendorDir = rtrim (getenv ('COMPOSER_HOME ' ), '/ \\' ) . '/vendor ' ;
29+ if (is_dir ($ vendorDir )) {
30+ return $ vendorDir ;
3531 }
3632 }
3733
38- if (! copy ($ sourceFile , $ targetFile )) {
39- throw new \RuntimeException ("Failed to copy binary to: $ targetFile " );
34+ // Try to find vendor directory relative to current file
35+ $ paths = [
36+ __DIR__ . '/../../../ ' , // From src/VoltTest to vendor
37+ __DIR__ . '/vendor/ ' , // Direct vendor subdirectory
38+ dirname (__DIR__ , 2 ) . '/vendor/ ' , // Two levels up
39+ dirname (__DIR__ , 3 ) . '/vendor/ ' , // Three levels up
40+ dirname (__DIR__ ) . '/vendor/ ' ,
41+ ];
42+
43+ foreach ($ paths as $ path ) {
44+ if (file_exists ($ path . 'autoload.php ' )) {
45+ return rtrim ($ path , '/ \\' );
46+ }
4047 }
4148
42- chmod ($ targetFile , 0755 );
43-
44- // Create symlink in vendor/bin
45- $ vendorBinDir = self ::getVendorBinDir ();
46- if (! is_dir ($ vendorBinDir )) {
47- if (! mkdir ($ vendorBinDir , 0755 , true )) {
48- throw new \RuntimeException ("Failed to create vendor bin directory: $ vendorBinDir " );
49+ // If running as a Composer script, use the working directory
50+ if (getenv ('COMPOSER ' )) {
51+ $ path = getcwd () . '/vendor ' ;
52+ if (is_dir ($ path )) {
53+ return $ path ;
4954 }
5055 }
5156
52- $ symlinkPath = $ vendorBinDir . '/ ' . self ::BINARY_NAME ;
53- if (file_exists ($ symlinkPath )) {
54- unlink ($ symlinkPath );
55- }
57+ throw new \RuntimeException (
58+ 'Could not locate Composer vendor directory. ' .
59+ 'Please ensure you are installing through Composer. '
60+ );
61+ }
5662
57- if (PHP_OS_FAMILY === 'Windows ' ) {
58- // Windows doesn't support symlinks by default, so we'll copy the file
59- if (! copy ($ targetFile , $ symlinkPath )) {
60- throw new \RuntimeException ("Failed to copy binary to vendor/bin: $ symlinkPath " );
61- }
62- } else {
63- if (! symlink ($ targetFile , $ symlinkPath )) {
64- throw new \RuntimeException ("Failed to create symlink in vendor/bin: $ symlinkPath " );
65- }
66- }
63+ private static function getBinaryDir (): string
64+ {
65+ return self ::getVendorDir () . '/bin ' ;
66+ }
6767
68+ private static function getCurrentVersion (): string
69+ {
70+ return self ::CURRENT_VERSION ;
6871 }
6972
7073 private static function detectPlatform ($ testing = false ): string
@@ -82,40 +85,131 @@ private static function detectPlatform($testing = false): string
8285 } elseif (strpos ($ os , 'linux ' ) === 0 ) {
8386 $ os = 'linux ' ;
8487 }
88+
8589 if ($ arch === 'x86_64 ' ) {
8690 $ arch = 'amd64 ' ;
91+ } elseif ($ arch === 'arm64 ' || $ arch === 'aarch64 ' ) {
92+ $ arch = 'arm64 ' ;
8793 }
8894
8995 return "$ os- $ arch " ;
9096 }
9197
92- private static function getBinaryDir ( ): string
98+ public static function installBinary ( $ testing = false ): void
9399 {
94- return self ::getVendorDir () . '/volt-test/bin ' ;
95- }
100+ $ platform = self ::detectPlatform ($ testing );
96101
97- private static function getVendorBinDir (): string
98- {
99- return self ::getVendorDir () . '/bin ' ;
100- }
102+ if (!array_key_exists ($ platform , self ::SUPPORTED_PLATFORMS )) {
103+ throw new \RuntimeException ("Platform $ platform is not supported " );
104+ }
101105
102- private static function getVendorDir (): string
103- {
104- // Traverse up from current directory until we find vendor directory
105- $ dir = __DIR__ ;
106- while ($ dir !== '/ ' && ! is_dir ($ dir . '/vendor ' )) {
107- $ dir = dirname ($ dir );
106+ $ version = self ::getCurrentVersion ();
107+ $ binaryName = self ::SUPPORTED_PLATFORMS [$ platform ];
108+ $ downloadUrl = sprintf ('%s/%s/%s ' , self ::BASE_DOWNLOAD_URL , $ version , $ binaryName );
109+
110+ $ binDir = self ::getBinaryDir ();
111+ $ targetFile = $ binDir . '/ ' . self ::BINARY_NAME ;
112+ if (PHP_OS_FAMILY === 'Windows ' ) {
113+ $ targetFile .= '.exe ' ;
108114 }
109115
110- if (! is_dir ($ dir . '/vendor ' )) {
111- throw new \RuntimeException ('Could not locate vendor directory ' );
116+ if (!is_dir ($ binDir )) {
117+ if (!mkdir ($ binDir , 0755 , true )) {
118+ throw new \RuntimeException ("Failed to create directory: $ binDir " );
119+ }
112120 }
113121
114- return $ dir . '/vendor ' ;
122+ echo "Downloading VoltTest binary $ version for platform: $ platform \n" ;
123+
124+ $ tempFile = tempnam (sys_get_temp_dir (), 'volt-test-download- ' );
125+ if ($ tempFile === false ) {
126+ throw new \RuntimeException ("Failed to create temporary file " );
127+ }
128+
129+ try {
130+ $ ch = curl_init ($ downloadUrl );
131+ if ($ ch === false ) {
132+ throw new \RuntimeException ("Failed to initialize cURL " );
133+ }
134+
135+ $ fp = fopen ($ tempFile , 'w ' );
136+ if ($ fp === false ) {
137+ curl_close ($ ch );
138+ throw new \RuntimeException ("Failed to open temporary file for writing " );
139+ }
140+
141+ curl_setopt_array ($ ch , [
142+ CURLOPT_FILE => $ fp ,
143+ CURLOPT_FOLLOWLOCATION => true ,
144+ CURLOPT_SSL_VERIFYPEER => true ,
145+ CURLOPT_FAILONERROR => true ,
146+ CURLOPT_TIMEOUT => 300 ,
147+ CURLOPT_CONNECTTIMEOUT => 30 ,
148+ CURLOPT_HTTPHEADER => ['User-Agent: volt-test-php-sdk ' ]
149+ ]);
150+
151+ if (curl_exec ($ ch ) === false ) {
152+ throw new \RuntimeException ("Download failed: " . curl_error ($ ch ));
153+ }
154+
155+ $ httpCode = curl_getinfo ($ ch , CURLINFO_HTTP_CODE );
156+ if ($ httpCode !== 200 ) {
157+ throw new \RuntimeException ("HTTP request failed with status $ httpCode " );
158+ }
159+
160+ curl_close ($ ch );
161+ fclose ($ fp );
162+
163+ if (!file_exists ($ tempFile ) || filesize ($ tempFile ) === 0 ) {
164+ throw new \RuntimeException ("Downloaded file is empty or missing " );
165+ }
166+
167+ if (!rename ($ tempFile , $ targetFile )) {
168+ throw new \RuntimeException ("Failed to move downloaded binary to: $ targetFile " );
169+ }
170+
171+ if (!chmod ($ targetFile , 0755 )) {
172+ throw new \RuntimeException ("Failed to set executable permissions on binary " );
173+ }
174+
175+ file_put_contents ($ binDir . '/.volt-test-version ' , $ version );
176+
177+ echo "Successfully installed VoltTest binary $ version to: $ targetFile \n" ;
178+ } catch (\Exception $ e ) {
179+ if (file_exists ($ tempFile )) {
180+ unlink ($ tempFile );
181+ }
182+ throw $ e ;
183+ }
115184 }
116185
117186 public static function getBinaryPath (): string
118187 {
119- return self ::getBinaryDir () . '/ ' . basename (self ::SUPPORTED_PLATFORMS [self ::detectPlatform ()]);
188+ $ binDir = self ::getBinaryDir ();
189+ $ binaryName = self ::BINARY_NAME ;
190+ if (PHP_OS_FAMILY === 'Windows ' ) {
191+ $ binaryName .= '.exe ' ;
192+ }
193+
194+ $ binaryPath = $ binDir . '/ ' . $ binaryName ;
195+ $ versionFile = $ binDir . '/.volt-test-version ' ;
196+
197+ $ needsInstall = true ;
198+
199+ if (file_exists ($ binaryPath ) && file_exists ($ versionFile )) {
200+ try {
201+ $ currentVersion = trim (file_get_contents ($ versionFile ));
202+ $ latestVersion = self ::getCurrentVersion ();
203+ $ needsInstall = $ currentVersion !== $ latestVersion ;
204+ } catch (\Exception $ e ) {
205+ $ needsInstall = true ;
206+ }
207+ }
208+
209+ if ($ needsInstall ) {
210+ self ::installBinary ();
211+ }
212+
213+ return $ binaryPath ;
120214 }
121215}
0 commit comments