@@ -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,10 +88,23 @@ 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 = common . parseBool ( process . env . GDN_DEBUG_DROP ) ;
9092
9193 let sarifFile : string = path . join ( process . env . BUILD_STAGINGDIRECTORY , '.gdn' , 'msdo.sarif' ) ;
9294 tl . debug ( `sarifFile = ${ sarifFile } ` ) ;
9395
96+ const gdnTaskLibFolder = path . resolve ( __dirname ) ;
97+ tl . debug ( `gdnTaskLibFolder = ${ gdnTaskLibFolder } ` ) ;
98+
99+ const nodeModulesFolder = path . dirname ( path . dirname ( gdnTaskLibFolder ) ) ;
100+ tl . debug ( `nodeModulesFolder = ${ nodeModulesFolder } ` ) ;
101+
102+ const taskFolder = path . dirname ( nodeModulesFolder ) ;
103+ tl . debug ( `taskFolder = ${ taskFolder } ` ) ;
104+
105+ const debugFolder = path . join ( taskFolder , 'debug' ) ;
106+ tl . debug ( `debugFolder = ${ debugFolder } ` ) ;
107+
94108 try {
95109
96110 if ( successfulExitCodes == null ) {
@@ -140,6 +154,18 @@ export async function run(inputArgs: string[], successfulExitCodes: number[] = n
140154
141155 tool . arg ( '--telemetry-environment' ) ;
142156 tool . arg ( telemetryEnvironment ) ;
157+
158+ // Include the debug drop option on the command line if applicable.
159+ tl . debug ( `GdnDebugDrop = ${ debugDrop } ` ) ;
160+ if ( debugDrop )
161+ {
162+ const dropPathValue = path . join ( taskFolder , 'debug' ) ;
163+ tool . arg ( '--debug-drop' ) . arg ( '--debug-drop-path' ) . arg ( dropPathValue ) ;
164+ const dropPathName = `GDN_DEBUGDROPPATH` ;
165+
166+ tl . debug ( `Debug Drop enabled. ${ dropPathName } : ${ dropPathValue } ` ) ;
167+ process . env [ dropPathName ] = dropPathValue ;
168+ }
143169 } catch ( error ) {
144170 console . error ( 'Exception occurred while initializing MSDO:' ) ;
145171 tl . setResult ( tl . TaskResult . Failed , error ) ;
@@ -154,6 +180,9 @@ export async function run(inputArgs: string[], successfulExitCodes: number[] = n
154180
155181 tl . debug ( 'Running Microsoft Security DevOps...' ) ;
156182
183+ // Ensure debug folder starts clean
184+ cleanupDirectory ( debugFolder ) ;
185+
157186 let exitCode = await tool . exec ( options ) ;
158187
159188 let success = false ;
@@ -164,6 +193,40 @@ export async function run(inputArgs: string[], successfulExitCodes: number[] = n
164193 }
165194 }
166195
196+ // Package up debug drop if applicable.
197+ let debugStagingDir = '' ;
198+ tl . debug ( `GdnDebugDrop = ${ debugDrop } ` ) ;
199+ if ( debugDrop ) {
200+ if ( fs . existsSync ( debugFolder ) ) {
201+ tl . debug ( "Creating debug drop archive..." ) ;
202+ let zippedOutput = getZippedFolder ( debugFolder ) ;
203+
204+ const taskFilePath = path . join ( taskFolder , `task.json` ) ;
205+ tl . debug ( `taskFilePath = ${ taskFilePath } ` ) ;
206+ const taskFile = require ( taskFilePath ) ;
207+ const taskName = taskFile . name . toUpperCase ( ) ;
208+
209+ const instanceDirectory = getInstanceDirectory ( ) ;
210+ debugStagingDir = path . join ( instanceDirectory , '.gdn' , 'debugdrop' ) ;
211+ if ( ! fs . existsSync ( debugStagingDir ) ) {
212+ tl . debug ( `Creating missing folder: ${ debugStagingDir } ` )
213+ fs . mkdirSync ( debugStagingDir )
214+ }
215+
216+ let debugDropArtifact = path . join ( debugStagingDir , `${ taskName } _debug.zip` ) ;
217+ let dupeCount = 0 ;
218+ while ( fs . existsSync ( debugDropArtifact ) ) {
219+ dupeCount += 1 ;
220+ debugDropArtifact = path . join ( debugStagingDir , `${ taskName } _${ dupeCount } _debug.zip` )
221+ }
222+ fs . copyFileSync ( zippedOutput , debugDropArtifact ) ;
223+ tl . debug ( `Finished creating: ${ debugDropArtifact } ` ) ;
224+ tl . debug ( `Cleaning up: ${ path . join ( taskFolder , 'debug' ) } ` ) ;
225+ cleanupDirectory ( path . join ( taskFolder , 'debug' ) ) ;
226+ tl . debug ( `Successfully cleaned up debug dump.` ) ;
227+ }
228+ }
229+
167230 if ( publish && fs . existsSync ( sarifFile ) ) {
168231 if ( common . isNullOrWhiteSpace ( publishArtifactName ) ) {
169232 publishArtifactName = 'CodeAnalysisLogs' ;
@@ -172,10 +235,88 @@ export async function run(inputArgs: string[], successfulExitCodes: number[] = n
172235 console . log ( `##vso[artifact.upload artifactname=${ publishArtifactName } ]${ sarifFile } ` ) ;
173236 }
174237
238+ if ( publish && fs . existsSync ( debugStagingDir ) ) {
239+ console . log ( `##vso[artifact.upload artifactname=DebugDrop]${ debugStagingDir } ` ) ;
240+ }
241+
175242 if ( ! success ) {
176243 throw `MSDO CLI exited with an error exit code: ${ exitCode } ` ;
177244 }
178245 } catch ( error ) {
179246 tl . setResult ( tl . TaskResult . Failed , error ) ;
180247 }
248+ }
249+
250+ function getInstanceDirectory ( ) : string {
251+ let hostType = process . env . SYSTEM_HOSTTYPE ;
252+ if ( hostType ) {
253+ hostType = hostType . toUpperCase ( ) ;
254+ }
255+
256+ if ( hostType == "RELEASE" ) {
257+ return process . env . AGENT_RELEASEDIRECTORY ;
258+ } else { // hostType == "BUILD" or default
259+ return process . env . BUILD_SOURCESDIRECTORY ;
260+ }
261+ }
262+
263+ function getZippedFolder ( dir ) : string {
264+ tl . debug ( `Zipping up folder: ${ dir } ` )
265+ let allPaths = getFilePathsRecursively ( dir ) ;
266+ const zip = new AdmZip ( ) ;
267+ for ( let filePath of allPaths ) {
268+ tl . debug ( `Adding file to archive: ${ filePath } ` ) ;
269+ zip . addLocalFile ( filePath ) ;
270+ }
271+
272+ let destPath = `${ dir } .zip` ;
273+ tl . debug ( `Writing to file: ${ destPath } ` )
274+ zip . writeZip ( destPath ) ;
275+ if ( fs . existsSync ( destPath ) ) {
276+ tl . debug ( `Successfully wrote file: ${ destPath } ` )
277+ } else {
278+ tl . debug ( `Something went wrong! File does not exist: ${ destPath } ` )
279+ }
280+ return destPath ;
281+ }
282+
283+ // Returns a flat array of absolute paths to all files contained in the dir
284+ function getFilePathsRecursively ( dir ) {
285+ tl . debug ( `Searching for files under dir: ${ dir } ` )
286+ var files = [ ] ;
287+ let fileList = fs . readdirSync ( dir ) ;
288+ var remaining = fileList . length ;
289+ if ( ! remaining ) return files ;
290+
291+ for ( let file of fileList ) {
292+ file = path . resolve ( dir , file ) ;
293+ let stat = fs . statSync ( file ) ;
294+ if ( stat && stat . isDirectory ( ) ) {
295+ let f = getFilePathsRecursively ( file ) ;
296+ files = files . concat ( f ) ;
297+ } else {
298+ files . push ( file ) ;
299+ }
300+ if ( ! -- remaining ) {
301+ return files ;
302+ }
303+ }
304+ }
305+
306+ function cleanupDirectory ( dir ) {
307+ if ( ! fs . existsSync ( dir ) ) return ;
308+
309+ let items = fs . readdirSync ( dir ) ;
310+
311+ for ( let item of items ) {
312+ item = path . resolve ( dir , item )
313+ let stat = fs . statSync ( item ) ;
314+ if ( stat && stat . isDirectory ( ) ) {
315+ cleanupDirectory ( item )
316+ } else {
317+ fs . unlinkSync ( item ) ;
318+ }
319+ }
320+
321+ fs . rmdirSync ( dir ) ;
181322}
0 commit comments