@@ -2,63 +2,94 @@ import * as fsPromises from 'fs/promises';
2
2
import * as fs from 'fs' ;
3
3
import * as path from 'path' ;
4
4
import * as tar from 'tar' ;
5
- import axios from 'axios' ;
6
5
import * as unzipper from 'unzipper' ;
7
6
import { logger } from "../wrapper/loggerConfig" ;
8
- import { finished } from 'stream/promises' ;
7
+ import { AstClient } from "../client/AstClient" ;
9
8
9
+ const linuxOS = 'linux' ;
10
+ const macOS = 'darwin' ;
11
+ const winOS = 'win32' ;
10
12
type SupportedPlatforms = 'win32' | 'darwin' | 'linux' ;
11
13
14
+ interface PlatformData {
15
+ platform : string ;
16
+ extension : string ;
17
+ }
18
+
12
19
export class CxInstaller {
13
- private readonly platform : string ;
20
+ private readonly platform : SupportedPlatforms ;
14
21
private cliVersion : string ;
15
22
private readonly resourceDirPath : string ;
16
- private readonly cliDefaultVersion = '2.2.5' ; // This will be used if the version file is not found. Should be updated with the latest version.
17
-
18
- constructor ( platform : string ) {
19
- this . platform = platform ;
20
- this . resourceDirPath = path . join ( __dirname , `../wrapper/resources` ) ;
23
+ private readonly installedCLIVersionFileName = 'cli-version' ;
24
+ private readonly cliDefaultVersion = '2.2.5' ; // Update this with the latest version.
25
+ private readonly client : AstClient ;
26
+
27
+ private static readonly PLATFORMS : Record < SupportedPlatforms , PlatformData > = {
28
+ win32 : { platform : 'windows' , extension : 'zip' } ,
29
+ darwin : { platform : macOS , extension : 'tar.gz' } ,
30
+ linux : { platform : linuxOS , extension : 'tar.gz' }
31
+ } ;
32
+
33
+ constructor ( platform : string , client : AstClient ) {
34
+ this . platform = platform as SupportedPlatforms ;
35
+ this . resourceDirPath = path . join ( __dirname , '../wrapper/resources' ) ;
36
+ this . client = client ;
21
37
}
22
38
23
- private async getDownloadURL ( ) : Promise < string > {
39
+ async getDownloadURL ( ) : Promise < string > {
24
40
const cliVersion = await this . readASTCLIVersion ( ) ;
41
+ const platformData = CxInstaller . PLATFORMS [ this . platform ] ;
25
42
26
- const platforms : Record < SupportedPlatforms , { platform : string ; extension : string } > = {
27
- win32 : { platform : 'windows' , extension : 'zip' } ,
28
- darwin : { platform : 'darwin' , extension : 'tar.gz' } ,
29
- linux : { platform : 'linux' , extension : 'tar.gz' }
30
- } ;
31
-
32
- const platformKey = this . platform as SupportedPlatforms ;
33
-
34
- const platformData = platforms [ platformKey ] ;
35
43
if ( ! platformData ) {
36
44
throw new Error ( 'Unsupported platform or architecture' ) ;
37
45
}
38
46
39
- return `https://download.checkmarx.com/CxOne/CLI/${ cliVersion } /ast-cli_${ cliVersion } _${ platformData . platform } _x64.${ platformData . extension } ` ;
47
+ const architecture = this . getArchitecture ( ) ;
48
+
49
+ return `https://download.checkmarx.com/CxOne/CLI/${ cliVersion } /ast-cli_${ cliVersion } _${ platformData . platform } _${ architecture } .${ platformData . extension } ` ;
50
+ }
51
+
52
+ private getArchitecture ( ) : string {
53
+ // For non-linux platforms we default to x64.
54
+ if ( this . platform !== linuxOS ) {
55
+ return 'x64' ;
56
+ }
57
+
58
+ const archMap : Record < string , string > = {
59
+ 'arm64' : 'arm64' ,
60
+ 'arm' : 'armv6'
61
+ } ;
62
+
63
+ // Default to 'x64' if the current architecture is not found in the map.
64
+ return archMap [ process . arch ] || 'x64' ;
40
65
}
41
66
42
67
public getExecutablePath ( ) : string {
43
- const executableName = this . platform === 'win32' ? 'cx.exe' : 'cx' ;
68
+ const executableName = this . platform === winOS ? 'cx.exe' : 'cx' ;
44
69
return path . join ( this . resourceDirPath , executableName ) ;
45
70
}
46
71
47
72
public async downloadIfNotInstalledCLI ( ) : Promise < void > {
48
73
try {
49
74
await fs . promises . mkdir ( this . resourceDirPath , { recursive : true } ) ;
75
+ const cliVersion = await this . readASTCLIVersion ( ) ;
50
76
51
77
if ( this . checkExecutableExists ( ) ) {
52
- logger . info ( 'Executable already installed.' ) ;
53
- return ;
78
+ const installedVersion = await this . readInstalledVersionFile ( this . resourceDirPath ) ;
79
+ if ( installedVersion === cliVersion ) {
80
+ logger . info ( 'Executable already installed.' ) ;
81
+ return ;
82
+ }
54
83
}
84
+
85
+ await this . cleanDirectoryContents ( this . resourceDirPath ) ;
55
86
const url = await this . getDownloadURL ( ) ;
56
87
const zipPath = path . join ( this . resourceDirPath , this . getCompressFolderName ( ) ) ;
57
88
58
- await this . downloadFile ( url , zipPath ) ;
59
- logger . info ( 'Downloaded CLI to:' , zipPath ) ;
89
+ await this . client . downloadFile ( url , zipPath ) ;
60
90
61
91
await this . extractArchive ( zipPath , this . resourceDirPath ) ;
92
+ await this . saveVersionFile ( this . resourceDirPath , cliVersion ) ;
62
93
63
94
fs . unlink ( zipPath , ( err ) => {
64
95
if ( err ) {
@@ -75,6 +106,33 @@ export class CxInstaller {
75
106
}
76
107
}
77
108
109
+ private async cleanDirectoryContents ( directoryPath : string ) : Promise < void > {
110
+ try {
111
+ const files = await fsPromises . readdir ( directoryPath ) ;
112
+
113
+ await Promise . all ( files . map ( async ( file ) => {
114
+ const filePath = path . join ( directoryPath , file ) ;
115
+ const fileStat = await fsPromises . stat ( filePath ) ;
116
+
117
+ if ( fileStat . isDirectory ( ) ) {
118
+ await fsPromises . rm ( filePath , { recursive : true , force : true } ) ;
119
+ logger . info ( `Directory ${ filePath } deleted.` ) ;
120
+ } else {
121
+ await fsPromises . unlink ( filePath ) ;
122
+ logger . info ( `File ${ filePath } deleted.` ) ;
123
+ }
124
+ } ) ) ;
125
+
126
+ logger . info ( `All contents in ${ directoryPath } have been cleaned.` ) ;
127
+ } catch ( error ) {
128
+ if ( error . code === 'ENOENT' ) {
129
+ logger . info ( `Directory at ${ directoryPath } does not exist.` ) ;
130
+ } else {
131
+ logger . error ( `Failed to clean directory contents: ${ error . message } ` ) ;
132
+ }
133
+ }
134
+ }
135
+
78
136
private async extractArchive ( zipPath : string , extractPath : string ) : Promise < void > {
79
137
if ( zipPath . endsWith ( '.zip' ) ) {
80
138
await unzipper . Open . file ( zipPath )
@@ -86,24 +144,33 @@ export class CxInstaller {
86
144
}
87
145
}
88
146
89
- private async downloadFile ( url : string , outputPath : string ) {
90
- logger . info ( 'Downloading file from:' , url ) ;
91
- const writer = fs . createWriteStream ( outputPath ) ;
92
-
147
+ private async saveVersionFile ( resourcePath : string , version : string ) : Promise < void > {
148
+ const versionFilePath = path . join ( resourcePath , this . installedCLIVersionFileName ) ;
93
149
try {
94
- const response = await axios ( { url, responseType : 'stream' } ) ;
95
- response . data . pipe ( writer ) ;
150
+ await fsPromises . writeFile ( versionFilePath , `${ version } ` , 'utf8' ) ;
151
+ logger . info ( `Version file created at ${ versionFilePath } with version ${ version } ` ) ;
152
+ } catch ( error ) {
153
+ logger . error ( `Failed to create version file: ${ error . message } ` ) ;
154
+ }
155
+ }
96
156
97
- await finished ( writer ) ; // Use stream promises to await the writer
98
- logger . info ( 'Download finished' ) ;
157
+ private async readInstalledVersionFile ( resourcePath : string ) : Promise < string | null > {
158
+ const versionFilePath = path . join ( resourcePath , this . installedCLIVersionFileName ) ;
159
+ try {
160
+ const content = await fsPromises . readFile ( versionFilePath , 'utf8' ) ;
161
+ logger . info ( `Version file content: ${ content } ` ) ;
162
+ return content ;
99
163
} catch ( error ) {
100
- logger . error ( 'Error downloading file:' , error . message || error ) ;
101
- } finally {
102
- writer . close ( ) ;
164
+ if ( error . code === 'ENOENT' ) {
165
+ logger . warn ( `Version file not found at ${ versionFilePath } .` ) ;
166
+ } else {
167
+ logger . error ( `Failed to read version file: ${ error . message } ` ) ;
168
+ }
169
+ return null ;
103
170
}
104
171
}
105
-
106
- private checkExecutableExists ( ) : boolean {
172
+
173
+ public checkExecutableExists ( ) : boolean {
107
174
return fs . existsSync ( this . getExecutablePath ( ) ) ;
108
175
}
109
176
@@ -122,10 +189,14 @@ export class CxInstaller {
122
189
}
123
190
124
191
private getVersionFilePath ( ) : string {
125
- return path . join ( __dirname , '../../../checkmarx-ast-cli.version' ) ;
192
+ return path . join ( __dirname , '../../../checkmarx-ast-cli.version' ) ;
126
193
}
127
194
128
195
private getCompressFolderName ( ) : string {
129
- return `ast-cli.${ this . platform === 'win32' ? 'zip' : 'tar.gz' } ` ;
196
+ return `ast-cli.${ this . platform === winOS ? 'zip' : 'tar.gz' } ` ;
197
+ }
198
+
199
+ public getPlatform ( ) : SupportedPlatforms {
200
+ return this . platform ;
130
201
}
131
202
}
0 commit comments