11import * as fs from 'fs/promises' ;
2+ import * as fs1 from "fs"
23import * as path from 'path' ;
3- import AdmZip from 'adm-zip' ; // For extracting ZIP files
44import * as tar from 'tar' ;
55import axios from 'axios' ;
6- import { createWriteStream } from "node:fs " ;
6+ import * as unzipper from "unzipper " ;
77
88
99export class CxInstaller {
1010 private readonly platform : string ;
1111 private cliVersion : string ;
12+ private readonly resourceDirPath : string ;
1213
1314 constructor ( platform : string ) {
1415 this . platform = platform ;
16+ this . resourceDirPath = path . join ( __dirname , `../wrapper/resources` ) ;
1517 }
1618
1719 // Method to get the download URL based on OS and architecture
@@ -42,119 +44,73 @@ export class CxInstaller {
4244
4345 getExecutablePath ( ) : string {
4446 let executablePath ;
45- const dirExecutablePath = path . join ( __dirname , `../wrapper/resources` ) ;
4647 if ( this . platform === 'win32' ) {
47- executablePath = path . join ( dirExecutablePath , 'cx.exe' ) ;
48+ executablePath = path . join ( this . resourceDirPath , 'cx.exe' ) ;
4849 } else {
49- executablePath = path . join ( dirExecutablePath , 'cx' ) ;
50+ executablePath = path . join ( this . resourceDirPath , 'cx' ) ;
5051 }
5152 return executablePath ;
5253 }
5354
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+ }
9369 }
94- return fileName . replace ( / \. [ ^ / . ] + $ / , '' ) ; // Remove other extensions like '.zip'
9570 }
9671
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 ) ;
11384 } else {
114- throw new Error ( 'Unsupported archive format ' ) ;
85+ console . error ( 'Unsupported file type. Only .zip and .tar.gz are supported. ' ) ;
11586 }
11687 }
11788
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+ } ) ;
14197 }
14298
143- // Check if the executable exists
144- async checkExecutableExists ( ) : Promise < boolean > {
99+ getZipPath ( ) : string {
145100 let executablePath ;
146- const dirExecutablePath = path . join ( __dirname , `../../wrapper/resources/` ) ;
147101 if ( this . platform === 'win32' ) {
148- executablePath = path . join ( dirExecutablePath , 'cx.exe ' ) ;
102+ executablePath = path . join ( this . resourceDirPath , 'cx.zip ' ) ;
149103 } else {
150- executablePath = path . join ( dirExecutablePath , 'cx' ) ;
104+ executablePath = path . join ( this . resourceDirPath , 'cx.tar.gz ' ) ;
151105 }
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 ( ) ) ;
155112 return true ;
156- } catch ( error ) {
157- console . error ( `Executable does not exist at: ${ executablePath } ` ) ;
113+ } else {
158114 return false ;
159115 }
160116 }
@@ -175,49 +131,3 @@ export class CxInstaller {
175131 }
176132}
177133
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