@@ -2,63 +2,94 @@ import * as fsPromises from 'fs/promises';
22import * as fs from 'fs' ;
33import * as path from 'path' ;
44import * as tar from 'tar' ;
5- import axios from 'axios' ;
65import * as unzipper from 'unzipper' ;
76import { logger } from "../wrapper/loggerConfig" ;
8- import { finished } from 'stream/promises' ;
7+ import { AstClient } from "../client/AstClient" ;
98
9+ const linuxOS = 'linux' ;
10+ const macOS = 'darwin' ;
11+ const winOS = 'win32' ;
1012type SupportedPlatforms = 'win32' | 'darwin' | 'linux' ;
1113
14+ interface PlatformData {
15+ platform : string ;
16+ extension : string ;
17+ }
18+
1219export class CxInstaller {
13- private readonly platform : string ;
20+ private readonly platform : SupportedPlatforms ;
1421 private cliVersion : string ;
1522 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 ;
2137 }
2238
23- private async getDownloadURL ( ) : Promise < string > {
39+ async getDownloadURL ( ) : Promise < string > {
2440 const cliVersion = await this . readASTCLIVersion ( ) ;
41+ const platformData = CxInstaller . PLATFORMS [ this . platform ] ;
2542
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 ] ;
3543 if ( ! platformData ) {
3644 throw new Error ( 'Unsupported platform or architecture' ) ;
3745 }
3846
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' ;
4065 }
4166
4267 public getExecutablePath ( ) : string {
43- const executableName = this . platform === 'win32' ? 'cx.exe' : 'cx' ;
68+ const executableName = this . platform === winOS ? 'cx.exe' : 'cx' ;
4469 return path . join ( this . resourceDirPath , executableName ) ;
4570 }
4671
4772 public async downloadIfNotInstalledCLI ( ) : Promise < void > {
4873 try {
4974 await fs . promises . mkdir ( this . resourceDirPath , { recursive : true } ) ;
75+ const cliVersion = await this . readASTCLIVersion ( ) ;
5076
5177 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+ }
5483 }
84+
85+ await this . cleanDirectoryContents ( this . resourceDirPath ) ;
5586 const url = await this . getDownloadURL ( ) ;
5687 const zipPath = path . join ( this . resourceDirPath , this . getCompressFolderName ( ) ) ;
5788
58- await this . downloadFile ( url , zipPath ) ;
59- logger . info ( 'Downloaded CLI to:' , zipPath ) ;
89+ await this . client . downloadFile ( url , zipPath ) ;
6090
6191 await this . extractArchive ( zipPath , this . resourceDirPath ) ;
92+ await this . saveVersionFile ( this . resourceDirPath , cliVersion ) ;
6293
6394 fs . unlink ( zipPath , ( err ) => {
6495 if ( err ) {
@@ -75,6 +106,33 @@ export class CxInstaller {
75106 }
76107 }
77108
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+
78136 private async extractArchive ( zipPath : string , extractPath : string ) : Promise < void > {
79137 if ( zipPath . endsWith ( '.zip' ) ) {
80138 await unzipper . Open . file ( zipPath )
@@ -86,24 +144,33 @@ export class CxInstaller {
86144 }
87145 }
88146
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 ) ;
93149 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+ }
96156
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 ;
99163 } 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 ;
103170 }
104171 }
105-
106- private checkExecutableExists ( ) : boolean {
172+
173+ public checkExecutableExists ( ) : boolean {
107174 return fs . existsSync ( this . getExecutablePath ( ) ) ;
108175 }
109176
@@ -122,10 +189,14 @@ export class CxInstaller {
122189 }
123190
124191 private getVersionFilePath ( ) : string {
125- return path . join ( __dirname , '../../../checkmarx-ast-cli.version' ) ;
192+ return path . join ( __dirname , '../../../checkmarx-ast-cli.version' ) ;
126193 }
127194
128195 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 ;
130201 }
131202}
0 commit comments