@@ -5,6 +5,7 @@ import * as tl from 'azure-pipelines-task-lib/task';
55import { IExecOptions } from "azure-pipelines-task-lib/toolrunner" ;
66import * as common from './msdo-common' ;
77import * as installer from './msdo-installer' ;
8+ import AdmZip = require( 'adm-zip' ) ;
89
910/**
1011 * The default version of Guardian to install if no version is specified.
@@ -87,6 +88,7 @@ async function init() {
8788 */
8889export async function run ( inputArgs : string [ ] , successfulExitCodes : number [ ] = null , publish : boolean = true , publishArtifactName : string = null , telemetryEnvironment : string = 'azdevops' ) : Promise < void > {
8990 let tool = null ;
91+ let debugDrop = process . env . GDN_DEBUG_DROP ?. toLowerCase ( ) ;
9092
9193 let sarifFile : string = path . join ( process . env . BUILD_STAGINGDIRECTORY , '.gdn' , 'msdo.sarif' ) ;
9294 tl . debug ( `sarifFile = ${ sarifFile } ` ) ;
@@ -96,6 +98,15 @@ export async function run(inputArgs: string[], successfulExitCodes: number[] = n
9698 if ( successfulExitCodes == null ) {
9799 successfulExitCodes = [ 0 ] ;
98100 }
101+
102+ const gdnTaskLibFolder = path . resolve ( __dirname ) ;
103+ tl . debug ( `gdnTaskLibFolder = ${ gdnTaskLibFolder } ` ) ;
104+
105+ const nodeModulesFolder = path . dirname ( path . dirname ( gdnTaskLibFolder ) ) ;
106+ tl . debug ( `nodeModulesFolder = ${ nodeModulesFolder } ` ) ;
107+
108+ const taskFolder = path . dirname ( nodeModulesFolder ) ;
109+ tl . debug ( `taskFolder = ${ taskFolder } ` ) ;
99110
100111 await setupEnvironment ( ) ;
101112 await init ( ) ;
@@ -140,6 +151,18 @@ export async function run(inputArgs: string[], successfulExitCodes: number[] = n
140151
141152 tool . arg ( '--telemetry-environment' ) ;
142153 tool . arg ( telemetryEnvironment ) ;
154+
155+ // Include the debug drop option on the command line if applicable.
156+ tl . debug ( `GdnDebugDrop = ${ debugDrop } ` ) ;
157+ if ( debugDrop == 'true' )
158+ {
159+ const dropPathValue = path . join ( taskFolder , 'debug' ) ;
160+ tool . arg ( '--debug-drop' ) . arg ( '--debug-drop-path' ) . arg ( dropPathValue ) ;
161+ const dropPathName = `GDN_DEBUGDROPPATH` ;
162+
163+ tl . debug ( `Debug Drop enabled. ${ dropPathName } : ${ dropPathValue } ` ) ;
164+ process . env [ dropPathName ] = dropPathValue ;
165+ }
143166 } catch ( error ) {
144167 console . error ( 'Exception occurred while initializing MSDO:' ) ;
145168 tl . setResult ( tl . TaskResult . Failed , error ) ;
@@ -154,6 +177,11 @@ export async function run(inputArgs: string[], successfulExitCodes: number[] = n
154177
155178 tl . debug ( 'Running Microsoft Security DevOps...' ) ;
156179
180+ // Ensure debug folder starts clean
181+ const taskFolder = path . dirname ( path . dirname ( path . dirname ( path . resolve ( __dirname ) ) ) ) ;
182+ const debugFolder = path . join ( taskFolder , 'debug' ) ;
183+ cleanupDirectory ( debugFolder ) ;
184+
157185 let exitCode = await tool . exec ( options ) ;
158186
159187 let success = false ;
@@ -164,6 +192,40 @@ export async function run(inputArgs: string[], successfulExitCodes: number[] = n
164192 }
165193 }
166194
195+ // Package up debug drop if applicable.
196+ let debugStagingDir = '' ;
197+ tl . debug ( `GdnDebugDrop = ${ debugDrop } ` ) ;
198+ if ( debugDrop == 'true' ) {
199+ if ( fs . existsSync ( debugFolder ) ) {
200+ tl . debug ( "Creating debug drop archive..." ) ;
201+ let zippedOutput = getZippedFolder ( debugFolder ) ;
202+
203+ const taskFilePath = path . join ( taskFolder , `task.json` ) ;
204+ tl . debug ( `taskFilePath = ${ taskFilePath } ` ) ;
205+ const taskFile = require ( taskFilePath ) ;
206+ const taskName = taskFile . name . toUpperCase ( ) ;
207+
208+ const instanceDirectory = getInstanceDirectory ( ) ;
209+ debugStagingDir = path . join ( instanceDirectory , '.gdn' , 'debugdrop' ) ;
210+ if ( ! fs . existsSync ( debugStagingDir ) ) {
211+ tl . debug ( `Creating missing folder: ${ debugStagingDir } ` )
212+ fs . mkdirSync ( debugStagingDir )
213+ }
214+
215+ let debugDropArtifact = path . join ( debugStagingDir , `${ taskName } _debug.zip` ) ;
216+ let dupeCount = 0 ;
217+ while ( fs . existsSync ( debugDropArtifact ) ) {
218+ dupeCount += 1 ;
219+ debugDropArtifact = path . join ( debugStagingDir , `${ taskName } _${ dupeCount } _debug.zip` )
220+ }
221+ fs . copyFileSync ( zippedOutput , debugDropArtifact ) ;
222+ tl . debug ( `Finished creating: ${ debugDropArtifact } ` ) ;
223+ tl . debug ( `Cleaning up: ${ path . join ( taskFolder , 'debug' ) } ` ) ;
224+ cleanupDirectory ( path . join ( taskFolder , 'debug' ) ) ;
225+ tl . debug ( `Successfully cleaned up debug dump.` ) ;
226+ }
227+ }
228+
167229 if ( publish && fs . existsSync ( sarifFile ) ) {
168230 if ( common . isNullOrWhiteSpace ( publishArtifactName ) ) {
169231 publishArtifactName = 'CodeAnalysisLogs' ;
@@ -172,10 +234,88 @@ export async function run(inputArgs: string[], successfulExitCodes: number[] = n
172234 console . log ( `##vso[artifact.upload artifactname=${ publishArtifactName } ]${ sarifFile } ` ) ;
173235 }
174236
237+ if ( publish && fs . existsSync ( debugStagingDir ) ) {
238+ console . log ( `##vso[artifact.upload artifactname=DebugDrop]${ debugStagingDir } ` ) ;
239+ }
240+
175241 if ( ! success ) {
176242 throw `MSDO CLI exited with an error exit code: ${ exitCode } ` ;
177243 }
178244 } catch ( error ) {
179245 tl . setResult ( tl . TaskResult . Failed , error ) ;
180246 }
247+ }
248+
249+ function getInstanceDirectory ( ) : string {
250+ let hostType = process . env . SYSTEM_HOSTTYPE ;
251+ if ( hostType ) {
252+ hostType = hostType . toUpperCase ( ) ;
253+ }
254+
255+ if ( hostType == "RELEASE" ) {
256+ return process . env . AGENT_RELEASEDIRECTORY ;
257+ } else { // hostType == "BUILD" or default
258+ return process . env . BUILD_SOURCESDIRECTORY ;
259+ }
260+ }
261+
262+ function getZippedFolder ( dir ) : string {
263+ tl . debug ( `Zipping up folder: ${ dir } ` )
264+ let allPaths = getFilePathsRecursively ( dir ) ;
265+ const zip = new AdmZip ( ) ;
266+ for ( let filePath of allPaths ) {
267+ tl . debug ( `Adding file to archive: ${ filePath } ` ) ;
268+ zip . addLocalFile ( filePath ) ;
269+ }
270+
271+ let destPath = `${ dir } .zip` ;
272+ tl . debug ( `Writing to file: ${ destPath } ` )
273+ zip . writeZip ( destPath ) ;
274+ if ( fs . existsSync ( destPath ) ) {
275+ tl . debug ( `Successfully wrote file: ${ destPath } ` )
276+ } else {
277+ tl . debug ( `Something went wrong! File does not exist: ${ destPath } ` )
278+ }
279+ return destPath ;
280+ }
281+
282+ // Returns a flat array of absolute paths to all files contained in the dir
283+ function getFilePathsRecursively ( dir ) {
284+ tl . debug ( `Searching for files under dir: ${ dir } ` )
285+ var files = [ ] ;
286+ let fileList = fs . readdirSync ( dir ) ;
287+ var remaining = fileList . length ;
288+ if ( ! remaining ) return files ;
289+
290+ for ( let file of fileList ) {
291+ file = path . resolve ( dir , file ) ;
292+ let stat = fs . statSync ( file ) ;
293+ if ( stat && stat . isDirectory ( ) ) {
294+ let f = getFilePathsRecursively ( file ) ;
295+ files = files . concat ( f ) ;
296+ } else {
297+ files . push ( file ) ;
298+ }
299+ if ( ! -- remaining ) {
300+ return files ;
301+ }
302+ }
303+ }
304+
305+ function cleanupDirectory ( dir ) {
306+ if ( ! fs . existsSync ( dir ) ) return ;
307+
308+ let items = fs . readdirSync ( dir ) ;
309+
310+ for ( let item of items ) {
311+ item = path . resolve ( dir , item )
312+ let stat = fs . statSync ( item ) ;
313+ if ( stat && stat . isDirectory ( ) ) {
314+ cleanupDirectory ( item )
315+ } else {
316+ fs . unlinkSync ( item ) ;
317+ }
318+ }
319+
320+ fs . rmdirSync ( dir ) ;
181321}
0 commit comments