1
1
import * as fs from 'fs/promises' ;
2
+ import * as fs1 from "fs"
2
3
import * as path from 'path' ;
3
- import AdmZip from 'adm-zip' ; // For extracting ZIP files
4
4
import * as tar from 'tar' ;
5
5
import axios from 'axios' ;
6
- import { createWriteStream } from "node:fs " ;
6
+ import * as unzipper from "unzipper " ;
7
7
8
8
9
9
export class CxInstaller {
10
10
private readonly platform : string ;
11
11
private cliVersion : string ;
12
+ private readonly resourceDirPath : string ;
12
13
13
14
constructor ( platform : string ) {
14
15
this . platform = platform ;
16
+ this . resourceDirPath = path . join ( __dirname , `../wrapper/resources` ) ;
15
17
}
16
18
17
19
// Method to get the download URL based on OS and architecture
@@ -42,119 +44,73 @@ export class CxInstaller {
42
44
43
45
getExecutablePath ( ) : string {
44
46
let executablePath ;
45
- const dirExecutablePath = path . join ( __dirname , `../wrapper/resources` ) ;
46
47
if ( this . platform === 'win32' ) {
47
- executablePath = path . join ( dirExecutablePath , 'cx.exe' ) ;
48
+ executablePath = path . join ( this . resourceDirPath , 'cx.exe' ) ;
48
49
} else {
49
- executablePath = path . join ( dirExecutablePath , 'cx' ) ;
50
+ executablePath = path . join ( this . resourceDirPath , 'cx' ) ;
50
51
}
51
52
return executablePath ;
52
53
}
53
54
54
- getZipPath ( ) : string {
55
- let executablePath ;
56
- const dirExecutablePath = path . join ( __dirname , `../wrapper/resources/` ) ;
57
- if ( this . platform === 'win32' ) {
58
- executablePath = path . join ( dirExecutablePath , 'cx.zip' ) ;
59
- } else {
60
- executablePath = path . join ( dirExecutablePath , 'cx.tar.gz' ) ;
61
- }
62
- return executablePath ;
63
- }
64
-
65
- async getCLIExecutableName ( ) : Promise < string > {
66
- let platformString : string ;
67
- let archiveExtension : string ;
68
- this . cliVersion = await this . readASTCLIVersion ( ) ;
69
-
70
- switch ( this . platform ) {
71
- case 'win32' :
72
- platformString = 'windows' ;
73
- archiveExtension = 'zip' ;
74
- break ;
75
- case 'darwin' :
76
- archiveExtension = 'tar.gz' ;
77
- platformString = 'darwin' ;
78
- break ;
79
- case 'linux' :
80
- archiveExtension = 'tar.gz' ;
81
- platformString = 'linux' ;
82
- break ;
83
- default :
84
- throw new Error ( 'Unsupported platform or architecture' ) ;
85
- }
86
-
87
- return `ast-cli_${ this . cliVersion } _${ platformString } _x64.${ archiveExtension } ` ;
88
- }
89
-
90
- removeExtension ( fileName : string ) : string {
91
- if ( fileName . endsWith ( '.tar.gz' ) ) {
92
- return fileName . slice ( 0 , - 7 ) ; // Remove '.tar.gz'
55
+ async downloadIfNotInstalledCLI ( ) {
56
+ if ( ! this . checkExecutableExists ( ) ) {
57
+ const url = await this . getDownloadURL ( ) ;
58
+ const zipPath = this . getZipPath ( ) ;
59
+ try {
60
+ await this . downloadFile ( url , zipPath ) ;
61
+ console . log ( 'Downloaded CLI to:' , zipPath ) ;
62
+
63
+ await this . extractArchive ( zipPath , this . resourceDirPath ) ;
64
+ console . log ( 'Extracted CLI to:' , this . resourceDirPath ) ;
65
+ console . log ( 'Done!' ) ;
66
+ } catch ( error ) {
67
+ console . error ( 'Error:' , error ) ;
68
+ }
93
69
}
94
- return fileName . replace ( / \. [ ^ / . ] + $ / , '' ) ; // Remove other extensions like '.zip'
95
70
}
96
71
97
-
98
-
99
- // Method to extract the file (ZIP or tar.gz)
100
- async extractFile ( filePath : string , outputDir : string ) : Promise < void > {
101
- if ( filePath . endsWith ( '.zip' ) ) {
102
- // Extract ZIP file
103
- const zip = new AdmZip ( filePath ) ;
104
- zip . extractAllTo ( outputDir , true ) ; // Extract to outputDir
105
- console . log ( `Extracted ZIP to ${ outputDir } ` ) ;
106
- } else if ( filePath . endsWith ( '.tar.gz' ) ) {
107
- // Extract tar.gz file
108
- await tar . extract ( {
109
- file : filePath ,
110
- cwd : outputDir , // Extract to the outputDir
111
- } ) ;
112
- console . log ( `Extracted tar.gz to ${ outputDir } ` ) ;
72
+ async extractArchive ( zipPath : string , extractPath : string ) : Promise < void > {
73
+ if ( zipPath . endsWith ( '.zip' ) ) {
74
+ console . log ( 'Extracting ZIP file...' ) ;
75
+ // Use unzipper to extract ZIP files
76
+ await unzipper . Open . file ( zipPath )
77
+ . then ( d => d . extract ( { path : extractPath } ) ) ;
78
+ console . log ( 'Extracted ZIP file to:' , extractPath ) ;
79
+ } else if ( zipPath . endsWith ( '.tar.gz' ) ) {
80
+ console . log ( 'Extracting TAR.GZ file...' ) ;
81
+ // Use tar.extract to extract TAR.GZ files
82
+ await tar . extract ( { file : zipPath , cwd : extractPath } ) ;
83
+ console . log ( 'Extracted TAR.GZ file to:' , extractPath ) ;
113
84
} else {
114
- throw new Error ( 'Unsupported archive format ' ) ;
85
+ console . error ( 'Unsupported file type. Only .zip and .tar.gz are supported. ' ) ;
115
86
}
116
87
}
117
88
118
- // Method to execute the installation
119
- async install ( outputPath : string ) : Promise < void > {
120
- const exists = await this . checkExecutableExists ( ) ;
121
- if ( exists ) {
122
- console . log ( 'Executable already exists. Skipping installation.' ) ;
123
- return ;
124
- }
125
-
126
- const url = await this . getDownloadURL ( ) ;
127
- if ( ! url ) {
128
- console . error ( 'No valid download URL available for this platform.' ) ;
129
- return ;
130
- }
131
-
132
- try {
133
- console . log ( `Downloading from: ${ url } ` ) ;
134
- await downloadFile ( url , outputPath ) ;
135
- console . log ( `Downloaded to: ${ outputPath } ` ) ;
136
-
137
- // Now extract the downloaded archive
138
- } catch ( error ) {
139
- console . error ( `Error during installation: ${ error . message } ` ) ;
140
- }
89
+ async downloadFile ( url : string , outputPath : string ) {
90
+ const writer = fs1 . createWriteStream ( outputPath ) ;
91
+ const response = await axios ( { url, responseType : 'stream' } ) ;
92
+ response . data . pipe ( writer ) ;
93
+ return new Promise ( ( resolve , reject ) => {
94
+ writer . on ( 'finish' , resolve ) ;
95
+ writer . on ( 'error' , reject ) ;
96
+ } ) ;
141
97
}
142
98
143
- // Check if the executable exists
144
- async checkExecutableExists ( ) : Promise < boolean > {
99
+ getZipPath ( ) : string {
145
100
let executablePath ;
146
- const dirExecutablePath = path . join ( __dirname , `../../wrapper/resources/` ) ;
147
101
if ( this . platform === 'win32' ) {
148
- executablePath = path . join ( dirExecutablePath , 'cx.exe ' ) ;
102
+ executablePath = path . join ( this . resourceDirPath , 'cx.zip ' ) ;
149
103
} else {
150
- executablePath = path . join ( dirExecutablePath , 'cx' ) ;
104
+ executablePath = path . join ( this . resourceDirPath , 'cx.tar.gz ' ) ;
151
105
}
152
- try {
153
- await fs . access ( executablePath ) ;
154
- console . log ( `Executable exists at: ${ executablePath } ` ) ;
106
+ return executablePath ;
107
+ }
108
+
109
+ checkExecutableExists ( ) : boolean {
110
+ if ( fs1 . existsSync ( this . getExecutablePath ( ) ) ) {
111
+ console . log ( 'Executable exists:' , this . getExecutablePath ( ) ) ;
155
112
return true ;
156
- } catch ( error ) {
157
- console . error ( `Executable does not exist at: ${ executablePath } ` ) ;
113
+ } else {
158
114
return false ;
159
115
}
160
116
}
@@ -175,49 +131,3 @@ export class CxInstaller {
175
131
}
176
132
}
177
133
178
- async function downloadFile ( downloadURLPath : string , filePath : string ) : Promise < void > {
179
- const fileName = "cx" ;
180
- console . log ( `Downloading ${ fileName } from: ${ downloadURLPath } ` ) ;
181
-
182
- try {
183
- // Ensure the directory exists
184
- await fs . mkdir ( path . dirname ( downloadURLPath ) , { recursive : true } ) ;
185
-
186
- // Check if filePath is a directory
187
- try {
188
- const stats = await fs . stat ( filePath ) ;
189
- if ( stats . isDirectory ( ) ) {
190
- // If it's a directory, append the filename from the URL
191
- filePath = path . join ( filePath , path . basename ( downloadURLPath ) ) ;
192
- }
193
- } catch ( error ) {
194
- // If the path doesn't exist, assume it's meant to be a file
195
- // The directory has already been created above
196
- }
197
-
198
- // Perform HTTP GET request
199
- const response = await axios ( {
200
- method : 'GET' ,
201
- url : downloadURLPath ,
202
- responseType : 'stream'
203
- } ) ;
204
- // Create the file stream at the specified filePath
205
- const fileStream = createWriteStream ( process . cwd ( ) + "/src/main/wrapper/resources/cx" ) ;
206
-
207
- // Pipe the response data to the file
208
- response . data . pipe ( fileStream ) ;
209
-
210
- // Wait for the file to finish writing
211
- await new Promise < void > ( ( resolve , reject ) => {
212
- fileStream . on ( 'finish' , resolve ) ;
213
- fileStream . on ( 'error' , reject ) ;
214
- } ) ;
215
-
216
- console . log ( `File downloaded successfully to ${ filePath } ` ) ;
217
-
218
- } catch ( error ) {
219
- console . log ( `Error during file download:` + error . message ) ;
220
- throw new Error ( `Invoking HTTP request to download file failed - ${ error . message } ` ) ;
221
- }
222
- }
223
-
0 commit comments