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 '
1416 ];
1517
16- public static function installBinary ( $ testing = false ): void
18+ private static function getVendorDir ( ): string
1719 {
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 " );
20+ // First try using Composer's environment variable
21+ if (getenv ('COMPOSER_VENDOR_DIR ' )) {
22+ return rtrim (getenv ('COMPOSER_VENDOR_DIR ' ), '/ \\' );
3023 }
3124
32- if (! is_dir ($ targetDir )) {
33- if (! mkdir ($ targetDir , 0755 , true )) {
34- throw new \RuntimeException ("Failed to create directory: $ targetDir " );
25+ // Then try using Composer's home directory
26+ if (getenv ('COMPOSER_HOME ' )) {
27+ $ vendorDir = rtrim (getenv ('COMPOSER_HOME ' ), '/ \\' ) . '/vendor ' ;
28+ if (is_dir ($ vendorDir )) {
29+ return $ vendorDir ;
3530 }
3631 }
3732
38- if (! copy ($ sourceFile , $ targetFile )) {
39- throw new \RuntimeException ("Failed to copy binary to: $ targetFile " );
33+ // Try to find vendor directory relative to current file
34+ $ paths = [
35+ __DIR__ . '/../../../ ' , // From src/VoltTest to vendor
36+ __DIR__ . '/vendor/ ' , // Direct vendor subdirectory
37+ dirname (__DIR__ , 2 ) . '/vendor/ ' , // Two levels up
38+ dirname (__DIR__ , 3 ) . '/vendor/ ' , // Three levels up
39+ dirname (__DIR__ ) . '/vendor/ ' ,
40+ ];
41+
42+ foreach ($ paths as $ path ) {
43+ if (file_exists ($ path . 'autoload.php ' )) {
44+ return rtrim ($ path , '/ \\' );
45+ }
4046 }
4147
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 " );
48+ // If running as a Composer script, use the working directory
49+ if (getenv ('COMPOSER ' )) {
50+ $ path = getcwd () . '/vendor ' ;
51+ if (is_dir ($ path )) {
52+ return $ path ;
4953 }
5054 }
5155
52- $ symlinkPath = $ vendorBinDir . '/ ' . self ::BINARY_NAME ;
53- if (file_exists ($ symlinkPath )) {
54- unlink ($ symlinkPath );
55- }
56+ throw new \RuntimeException (
57+ 'Could not locate Composer vendor directory. ' .
58+ 'Please ensure you are installing through Composer. '
59+ );
60+ }
5661
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- }
62+ private static function getBinaryDir (): string
63+ {
64+ return self ::getVendorDir () . '/bin ' ;
65+ }
6766
67+ private static function getCurrentVersion (): string
68+ {
69+ return self ::CURRENT_VERSION ;
6870 }
6971
7072 private static function detectPlatform ($ testing = false ): string
7173 {
72- if ($ testing === true ) {
74+ if ($ testing ) {
7375 return 'unsupported-platform ' ;
7476 }
7577 $ os = strtolower (PHP_OS );
@@ -82,40 +84,131 @@ private static function detectPlatform($testing = false): string
8284 } elseif (strpos ($ os , 'linux ' ) === 0 ) {
8385 $ os = 'linux ' ;
8486 }
87+
8588 if ($ arch === 'x86_64 ' ) {
8689 $ arch = 'amd64 ' ;
90+ } elseif ($ arch === 'arm64 ' || $ arch === 'aarch64 ' ) {
91+ $ arch = 'arm64 ' ;
8792 }
8893
8994 return "$ os- $ arch " ;
9095 }
9196
92- private static function getBinaryDir ( ): string
97+ public static function installBinary ( $ testing = false ): void
9398 {
94- return self ::getVendorDir () . '/volt-test/bin ' ;
95- }
99+ $ platform = self ::detectPlatform ($ testing );
96100
97- private static function getVendorBinDir (): string
98- {
99- return self ::getVendorDir () . '/bin ' ;
100- }
101+ if (!array_key_exists ($ platform , self ::SUPPORTED_PLATFORMS )) {
102+ throw new \RuntimeException ("Platform $ platform is not supported " );
103+ }
101104
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 );
105+ $ version = self ::getCurrentVersion ();
106+ $ binaryName = self ::SUPPORTED_PLATFORMS [$ platform ];
107+ $ downloadUrl = sprintf ('%s/%s/%s ' , self ::BASE_DOWNLOAD_URL , $ version , $ binaryName );
108+
109+ $ binDir = self ::getBinaryDir ();
110+ $ targetFile = $ binDir . '/ ' . self ::BINARY_NAME ;
111+ if (PHP_OS_FAMILY === 'Windows ' ) {
112+ $ targetFile .= '.exe ' ;
108113 }
109114
110- if (! is_dir ($ dir . '/vendor ' )) {
111- throw new \RuntimeException ('Could not locate vendor directory ' );
115+ if (!is_dir ($ binDir )) {
116+ if (!mkdir ($ binDir , 0755 , true )) {
117+ throw new \RuntimeException ("Failed to create directory: $ binDir " );
118+ }
112119 }
113120
114- return $ dir . '/vendor ' ;
121+ echo "Downloading VoltTest binary $ version for platform: $ platform \n" ;
122+
123+ $ tempFile = tempnam (sys_get_temp_dir (), 'volt-test-download- ' );
124+ if ($ tempFile === false ) {
125+ throw new \RuntimeException ("Failed to create temporary file " );
126+ }
127+
128+ try {
129+ $ ch = curl_init ($ downloadUrl );
130+ if ($ ch === false ) {
131+ throw new \RuntimeException ("Failed to initialize cURL " );
132+ }
133+
134+ $ fp = fopen ($ tempFile , 'w ' );
135+ if ($ fp === false ) {
136+ curl_close ($ ch );
137+ throw new \RuntimeException ("Failed to open temporary file for writing " );
138+ }
139+
140+ curl_setopt_array ($ ch , [
141+ CURLOPT_FILE => $ fp ,
142+ CURLOPT_FOLLOWLOCATION => true ,
143+ CURLOPT_SSL_VERIFYPEER => true ,
144+ CURLOPT_FAILONERROR => true ,
145+ CURLOPT_TIMEOUT => 300 ,
146+ CURLOPT_CONNECTTIMEOUT => 30 ,
147+ CURLOPT_HTTPHEADER => ['User-Agent: volt-test-php-sdk ' ]
148+ ]);
149+
150+ if (curl_exec ($ ch ) === false ) {
151+ throw new \RuntimeException ("Download failed: " . curl_error ($ ch ));
152+ }
153+
154+ $ httpCode = curl_getinfo ($ ch , CURLINFO_HTTP_CODE );
155+ if ($ httpCode !== 200 ) {
156+ throw new \RuntimeException ("HTTP request failed with status $ httpCode " );
157+ }
158+
159+ curl_close ($ ch );
160+ fclose ($ fp );
161+
162+ if (!file_exists ($ tempFile ) || filesize ($ tempFile ) === 0 ) {
163+ throw new \RuntimeException ("Downloaded file is empty or missing " );
164+ }
165+
166+ if (!rename ($ tempFile , $ targetFile )) {
167+ throw new \RuntimeException ("Failed to move downloaded binary to: $ targetFile " );
168+ }
169+
170+ if (!chmod ($ targetFile , 0755 )) {
171+ throw new \RuntimeException ("Failed to set executable permissions on binary " );
172+ }
173+
174+ file_put_contents ($ binDir . '/.volt-test-version ' , $ version );
175+
176+ echo "Successfully installed VoltTest binary $ version to: $ targetFile \n" ;
177+ } catch (\Exception $ e ) {
178+ if (file_exists ($ tempFile )) {
179+ unlink ($ tempFile );
180+ }
181+ throw $ e ;
182+ }
115183 }
116184
117185 public static function getBinaryPath (): string
118186 {
119- return self ::getBinaryDir () . '/ ' . basename (self ::SUPPORTED_PLATFORMS [self ::detectPlatform ()]);
187+ $ binDir = self ::getBinaryDir ();
188+ $ binaryName = self ::BINARY_NAME ;
189+ if (PHP_OS_FAMILY === 'Windows ' ) {
190+ $ binaryName .= '.exe ' ;
191+ }
192+
193+ $ binaryPath = $ binDir . '/ ' . $ binaryName ;
194+ $ versionFile = $ binDir . '/.volt-test-version ' ;
195+
196+ $ needsInstall = true ;
197+
198+ if (file_exists ($ binaryPath ) && file_exists ($ versionFile )) {
199+ try {
200+ $ currentVersion = trim (file_get_contents ($ versionFile ));
201+ $ latestVersion = self ::getCurrentVersion ();
202+ $ needsInstall = $ currentVersion !== $ latestVersion ;
203+ } catch (\Exception $ e ) {
204+ $ needsInstall = true ;
205+ }
206+ }
207+
208+ if ($ needsInstall ) {
209+ self ::installBinary ();
210+ }
211+
212+ return $ binaryPath ;
120213 }
121- }
214+ }
0 commit comments