FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
codeql-action/src/debug-artifacts.ts at main · github/codeql-action · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
github
/
codeql-action
Public
Notifications
You must be signed in to change notification settings
Fork
487
Star
1.6k
Code
Issues
158
Pull requests
35
Actions
Security and quality
2
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Security and quality
Insights
Expand file tree
Breadcrumbs
codeql-action
/
src
/
debug-artifacts.ts
Copy path
More file actions
More file actions
Latest commit
History
History
History
436 lines (402 loc) · 13.6 KB
Breadcrumbs
codeql-action
/
src
/
debug-artifacts.ts
Copy path
File metadata and controls
436 lines (402 loc) · 13.6 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
import
*
as
fs
from
"fs"
;
import
*
as
path
from
"path"
;
import
*
as
artifact
from
"@actions/artifact"
;
import
*
as
artifactLegacy
from
"@actions/artifact-legacy"
;
import
*
as
core
from
"@actions/core"
;
import
{
ZipArchive
}
from
"archiver"
;
import
{
getOptionalInput
,
getTemporaryDirectory
}
from
"./actions-util"
;
import
{
dbIsFinalized
}
from
"./analyze"
;
import
{
scanArtifactsForTokens
}
from
"./artifact-scanner"
;
import
{
type
CodeQL
}
from
"./codeql"
;
import
{
Config
}
from
"./config-utils"
;
import
{
EnvVar
}
from
"./environment"
;
import
*
as
json
from
"./json"
;
import
{
Language
}
from
"./languages"
;
import
{
Logger
,
withGroup
}
from
"./logging"
;
import
{
isSafeArtifactUpload
,
SafeArtifactUploadVersion
,
}
from
"./tools-features"
;
import
{
bundleDb
,
doesDirectoryExist
,
getCodeQLDatabasePath
,
getErrorMessage
,
GitHubVariant
,
isInTestMode
,
listFolder
,
}
from
"./util"
;
export
function
sanitizeArtifactName
(
name
:
string
)
:
string
{
return
name
.
replace
(
/
[
^
a
-
z
A
-
Z
0
-
9
_
-
]
+
/
g
,
""
)
;
}
/**
* Upload Actions SARIF artifacts for debugging when CODEQL_ACTION_DEBUG_COMBINED_SARIF
* environment variable is set
*/
export
async
function
uploadCombinedSarifArtifacts
(
logger
:
Logger
,
gitHubVariant
:
GitHubVariant
,
codeQlVersion
:
string
|
undefined
,
)
{
const
tempDir
=
getTemporaryDirectory
(
)
;
// Upload Actions SARIF artifacts for debugging when environment variable is set
if
(
process
.
env
[
"CODEQL_ACTION_DEBUG_COMBINED_SARIF"
]
===
"true"
)
{
await
withGroup
(
"Uploading combined SARIF debug artifact"
,
async
(
)
=>
{
logger
.
info
(
"Uploading available combined SARIF files as Actions debugging artifact..."
,
)
;
const
baseTempDir
=
path
.
resolve
(
tempDir
,
"combined-sarif"
)
;
const
toUpload
:
string
[
]
=
[
]
;
if
(
fs
.
existsSync
(
baseTempDir
)
)
{
const
outputDirs
=
fs
.
readdirSync
(
baseTempDir
)
;
for
(
const
outputDir
of
outputDirs
)
{
const
sarifFiles
=
fs
.
readdirSync
(
path
.
resolve
(
baseTempDir
,
outputDir
)
)
.
filter
(
(
f
)
=>
path
.
extname
(
f
)
===
".sarif"
)
;
for
(
const
sarifFile
of
sarifFiles
)
{
toUpload
.
push
(
path
.
resolve
(
baseTempDir
,
outputDir
,
sarifFile
)
)
;
}
}
}
try
{
await
uploadDebugArtifacts
(
logger
,
toUpload
,
baseTempDir
,
"combined-sarif-artifacts"
,
gitHubVariant
,
codeQlVersion
,
)
;
}
catch
(
e
)
{
logger
.
warning
(
`Failed to upload combined SARIF files as Actions debugging artifact. Reason:
${
getErrorMessage
(
e
,
)
}
`
,
)
;
}
}
)
;
}
}
/**
* Try to prepare a SARIF result debug artifact for the given language.
*
*
@return
The path to that debug artifact, or undefined if an error occurs.
*/
function
tryPrepareSarifDebugArtifact
(
config
:
Config
,
language
:
Language
,
logger
:
Logger
,
)
:
string
|
undefined
{
try
{
const
analyzeActionOutputDir
=
process
.
env
[
EnvVar
.
SARIF_RESULTS_OUTPUT_DIR
]
;
if
(
analyzeActionOutputDir
!==
undefined
&&
fs
.
existsSync
(
analyzeActionOutputDir
)
&&
fs
.
lstatSync
(
analyzeActionOutputDir
)
.
isDirectory
(
)
)
{
const
sarifFile
=
path
.
resolve
(
analyzeActionOutputDir
,
`
${
language
}
.sarif`
,
)
;
// Move SARIF to DB location so that they can be uploaded with the same root directory as the other artifacts.
if
(
fs
.
existsSync
(
sarifFile
)
)
{
const
sarifInDbLocation
=
path
.
resolve
(
config
.
dbLocation
,
`
${
language
}
.sarif`
,
)
;
fs
.
copyFileSync
(
sarifFile
,
sarifInDbLocation
)
;
return
sarifInDbLocation
;
}
}
}
catch
(
e
)
{
logger
.
warning
(
`Failed to find SARIF results path for
${
language
}
. Reason:
${
getErrorMessage
(
e
,
)
}
`
,
)
;
}
return
undefined
;
}
/**
* Try to bundle the database for the given language.
*
*
@return
The path to the database bundle, or undefined if an error occurs.
*/
async
function
tryBundleDatabase
(
codeql
:
CodeQL
,
config
:
Config
,
language
:
Language
,
logger
:
Logger
,
)
:
Promise
<
string
|
undefined
>
{
try
{
if
(
dbIsFinalized
(
config
,
language
,
logger
)
)
{
try
{
return
await
createDatabaseBundleCli
(
codeql
,
config
,
language
)
;
}
catch
(
e
)
{
logger
.
warning
(
`Failed to bundle database for
${
language
}
using the CLI. `
+
`Falling back to a partial bundle. Reason:
${
getErrorMessage
(
e
)
}
`
,
)
;
}
}
return
await
createPartialDatabaseBundle
(
config
,
language
)
;
}
catch
(
e
)
{
logger
.
warning
(
`Failed to bundle database for
${
language
}
. Reason:
${
getErrorMessage
(
e
,
)
}
`
,
)
;
return
undefined
;
}
}
/**
* Attempt to upload all available debug artifacts.
*
* Logs and suppresses any errors that occur.
*/
export
async
function
tryUploadAllAvailableDebugArtifacts
(
codeql
:
CodeQL
,
config
:
Config
,
logger
:
Logger
,
codeQlVersion
:
string
|
undefined
,
)
{
const
filesToUpload
:
string
[
]
=
[
]
;
try
{
for
(
const
language
of
config
.
languages
)
{
await
withGroup
(
`Uploading debug artifacts for
${
language
}
`
,
async
(
)
=>
{
logger
.
info
(
"Preparing SARIF result debug artifact..."
)
;
const
sarifResultDebugArtifact
=
tryPrepareSarifDebugArtifact
(
config
,
language
,
logger
,
)
;
if
(
sarifResultDebugArtifact
)
{
filesToUpload
.
push
(
sarifResultDebugArtifact
)
;
logger
.
info
(
"SARIF result debug artifact ready for upload."
)
;
}
logger
.
info
(
"Preparing database logs debug artifact..."
)
;
const
databaseDirectory
=
getCodeQLDatabasePath
(
config
,
language
)
;
const
logsDirectory
=
path
.
resolve
(
databaseDirectory
,
"log"
)
;
if
(
doesDirectoryExist
(
logsDirectory
)
)
{
filesToUpload
.
push
(
...
listFolder
(
logsDirectory
)
)
;
logger
.
info
(
"Database logs debug artifact ready for upload."
)
;
}
// Multilanguage tracing: there are additional logs in the root of the cluster
logger
.
info
(
"Preparing database cluster logs debug artifact..."
)
;
const
multiLanguageTracingLogsDirectory
=
path
.
resolve
(
config
.
dbLocation
,
"log"
,
)
;
if
(
doesDirectoryExist
(
multiLanguageTracingLogsDirectory
)
)
{
filesToUpload
.
push
(
...
listFolder
(
multiLanguageTracingLogsDirectory
)
)
;
logger
.
info
(
"Database cluster logs debug artifact ready for upload."
)
;
}
// Add database bundle
logger
.
info
(
"Preparing database bundle debug artifact..."
)
;
const
databaseBundle
=
await
tryBundleDatabase
(
codeql
,
config
,
language
,
logger
,
)
;
if
(
databaseBundle
)
{
filesToUpload
.
push
(
databaseBundle
)
;
logger
.
info
(
"Database bundle debug artifact ready for upload."
)
;
}
}
)
;
}
}
catch
(
e
)
{
logger
.
warning
(
`Failed to prepare debug artifacts. Reason:
${
getErrorMessage
(
e
)
}
`
,
)
;
return
;
}
try
{
await
withGroup
(
"Uploading debug artifacts"
,
async
(
)
=>
uploadDebugArtifacts
(
logger
,
filesToUpload
,
config
.
dbLocation
,
config
.
debugArtifactName
,
config
.
gitHubVersion
.
type
,
codeQlVersion
,
)
,
)
;
}
catch
(
e
)
{
logger
.
warning
(
`Failed to upload debug artifacts. Reason:
${
getErrorMessage
(
e
)
}
`
,
)
;
}
}
/**
* When a build matrix is used, multiple different jobs arising from the matrix may attempt to upload
* workflow artifacts with the same base name. In that case, only one of the uploads will succeed and
* the others will fail. This function inspects the matrix object to compute a suffix for the artifact
* name that uniquely identifies the matrix values of the current job to avoid name clashes.
*
*
@param
matrix A stringified JSON value, usually the value of the `matrix` input.
*
@returns
A suffix that uniquely identifies the `matrix` value for the current job, or `""` if there
* is no matrix value.
*/
export
function
getArtifactSuffix
(
matrix
:
string
|
undefined
)
:
string
{
let
suffix
=
""
;
if
(
matrix
)
{
try
{
const
matrixObject
=
JSON
.
parse
(
matrix
)
;
if
(
json
.
isObject
(
matrixObject
)
)
{
for
(
const
matrixKey
of
Object
.
keys
(
matrixObject
)
.
sort
(
)
)
suffix
+=
`-
${
matrixObject
[
matrixKey
]
}
`
;
}
else
{
core
.
warning
(
"User-specified `matrix` input is not an object."
)
;
}
}
catch
{
core
.
warning
(
"Could not parse user-specified `matrix` input into JSON. The debug artifact will not be named with the user's `matrix` input."
,
)
;
}
}
return
suffix
;
}
// Enumerates different, possible outcomes for artifact uploads.
export
type
UploadArtifactsResult
=
|
"no-artifacts-to-upload"
|
"upload-successful"
|
"upload-failed"
;
export
async
function
uploadDebugArtifacts
(
logger
:
Logger
,
toUpload
:
string
[
]
,
rootDir
:
string
,
artifactName
:
string
,
ghVariant
:
GitHubVariant
,
codeQlVersion
:
string
|
undefined
,
)
:
Promise
<
UploadArtifactsResult
|
"upload-not-supported"
>
{
const
uploadSupported
=
isSafeArtifactUpload
(
codeQlVersion
)
;
if
(
!
uploadSupported
)
{
core
.
info
(
`Skipping debug artifact upload because the current CLI does not support safe upload. Please upgrade to CLI v
${
SafeArtifactUploadVersion
}
or later.`
,
)
;
return
"upload-not-supported"
;
}
return
uploadArtifacts
(
logger
,
toUpload
,
rootDir
,
artifactName
,
ghVariant
)
;
}
/**
* Uploads the specified files as a single workflow artifact.
*
*
@param
logger The logger to use.
*
@param
toUpload The list of paths to include in the artifact.
*
@param
rootDir The root directory of the paths to include.
*
@param
artifactName The base name for the artifact.
*
@param
ghVariant The GitHub variant.
*
*
@returns
The outcome of the attempt to create and upload the artifact.
*/
export
async
function
uploadArtifacts
(
logger
:
Logger
,
toUpload
:
string
[
]
,
rootDir
:
string
,
artifactName
:
string
,
ghVariant
:
GitHubVariant
,
)
:
Promise
<
UploadArtifactsResult
>
{
if
(
toUpload
.
length
===
0
)
{
return
"no-artifacts-to-upload"
;
}
// When running in test mode, perform a best effort scan of the debug artifacts. The artifact
// scanner is basic and not reliable or fast enough for production use, but it can help catch
// some issues early.
if
(
isInTestMode
(
)
)
{
await
scanArtifactsForTokens
(
toUpload
,
logger
)
;
core
.
exportVariable
(
"CODEQL_ACTION_ARTIFACT_SCAN_FINISHED"
,
"true"
)
;
}
const
suffix
=
getArtifactSuffix
(
getOptionalInput
(
"matrix"
)
)
;
const
artifactUploader
=
await
getArtifactUploaderClient
(
logger
,
ghVariant
)
;
try
{
await
artifactUploader
.
uploadArtifact
(
sanitizeArtifactName
(
`
${
artifactName
}
${
suffix
}
`
)
,
toUpload
.
map
(
(
file
)
=>
path
.
normalize
(
file
)
)
,
path
.
normalize
(
rootDir
)
,
{
// ensure we don't keep the debug artifacts around for too long since they can be large.
retentionDays
:
7
,
}
,
)
;
return
"upload-successful"
;
}
catch
(
e
)
{
// A failure to upload debug artifacts should not fail the entire action.
core
.
warning
(
`Failed to upload debug artifacts:
${
e
}
`
)
;
return
"upload-failed"
;
}
}
// `@actions/artifact@v2` is not yet supported on GHES so the legacy version of the client will be used on GHES
// until it is supported. We also use the legacy version of the client if the feature flag is disabled.
// The feature flag is named `ArtifactV4Upgrade` to reduce customer confusion; customers are primarily affected by
// `actions/download-artifact`, whose upgrade to v4 must be accompanied by the `@actions/artifact@v2` upgrade.
export
async
function
getArtifactUploaderClient
(
logger
:
Logger
,
ghVariant
:
GitHubVariant
,
)
:
Promise
<
artifact
.
ArtifactClient
|
artifactLegacy
.
ArtifactClient
>
{
if
(
ghVariant
===
GitHubVariant
.
GHES
)
{
logger
.
info
(
"Debug artifacts can be consumed with `actions/download-artifact@v3` because the `v4` version is not yet compatible on GHES."
,
)
;
return
artifactLegacy
.
create
(
)
;
}
else
{
logger
.
info
(
"Debug artifacts can be consumed with `actions/download-artifact@v4`."
,
)
;
return
new
artifact
.
DefaultArtifactClient
(
)
;
}
}
/**
* If a database has not been finalized, we cannot run the `codeql database bundle`
* command in the CLI because it will return an error. Instead we directly zip
* all files in the database folder and return the path.
*/
async
function
createPartialDatabaseBundle
(
config
:
Config
,
language
:
Language
,
)
:
Promise
<
string
>
{
const
databasePath
=
getCodeQLDatabasePath
(
config
,
language
)
;
const
databaseBundlePath
=
path
.
resolve
(
config
.
dbLocation
,
`
${
config
.
debugDatabaseName
}
-
${
language
}
-partial.zip`
,
)
;
core
.
info
(
`
${
config
.
debugDatabaseName
}
-
${
language
}
is not finalized. Uploading partial database bundle at
${
databaseBundlePath
}
...`
,
)
;
// See `bundleDb` for explanation behind deleting existing db bundle.
if
(
fs
.
existsSync
(
databaseBundlePath
)
)
{
await
fs
.
promises
.
rm
(
databaseBundlePath
,
{
force
:
true
}
)
;
}
const
output
=
fs
.
createWriteStream
(
databaseBundlePath
)
;
const
zip
=
new
ZipArchive
(
)
;
zip
.
on
(
"error"
,
(
err
)
=>
{
throw
err
;
}
)
;
zip
.
on
(
"warning"
,
(
err
)
=>
{
// Ignore ENOENT warnings. There's nothing anyone can do about it.
if
(
err
.
code
!==
"ENOENT"
)
{
throw
err
;
}
}
)
;
zip
.
pipe
(
output
)
;
zip
.
directory
(
databasePath
,
false
)
;
await
zip
.
finalize
(
)
;
return
databaseBundlePath
;
}
/**
* Runs `codeql database bundle` command and returns the path.
*/
async
function
createDatabaseBundleCli
(
codeql
:
CodeQL
,
config
:
Config
,
language
:
Language
,
)
:
Promise
<
string
>
{
const
databaseBundlePath
=
await
bundleDb
(
config
,
language
,
codeql
,
`
${
config
.
debugDatabaseName
}
-
${
language
}
`
,
{
includeDiagnostics
:
true
}
,
)
;
return
databaseBundlePath
;
}
Back
|
FazBrowse Home
|
New Git URL