2020-04-28 16:46:47 +02:00
"use strict" ;
2021-07-27 17:59:59 +01:00
var _ _createBinding = ( this && this . _ _createBinding ) || ( Object . create ? ( function ( o , m , k , k2 ) {
if ( k2 === undefined ) k2 = k ;
2023-01-18 20:00:33 +00:00
var desc = Object . getOwnPropertyDescriptor ( m , k ) ;
if ( ! desc || ( "get" in desc ? ! m . _ _esModule : desc . writable || desc . configurable ) ) {
desc = { enumerable : true , get : function ( ) { return m [ k ] ; } } ;
}
Object . defineProperty ( o , k2 , desc ) ;
2021-07-27 17:59:59 +01:00
} ) : ( function ( o , m , k , k2 ) {
if ( k2 === undefined ) k2 = k ;
o [ k2 ] = m [ k ] ;
} ) ) ;
var _ _setModuleDefault = ( this && this . _ _setModuleDefault ) || ( Object . create ? ( function ( o , v ) {
Object . defineProperty ( o , "default" , { enumerable : true , value : v } ) ;
} ) : function ( o , v ) {
o [ "default" ] = v ;
} ) ;
2020-04-28 16:46:47 +02:00
var _ _importStar = ( this && this . _ _importStar ) || function ( mod ) {
if ( mod && mod . _ _esModule ) return mod ;
var result = { } ;
2021-07-27 17:59:59 +01:00
if ( mod != null ) for ( var k in mod ) if ( k !== "default" && Object . prototype . hasOwnProperty . call ( mod , k ) ) _ _createBinding ( result , mod , k ) ;
_ _setModuleDefault ( result , mod ) ;
2020-04-28 16:46:47 +02:00
return result ;
} ;
2021-12-08 12:00:54 -08:00
var _ _importDefault = ( this && this . _ _importDefault ) || function ( mod ) {
return ( mod && mod . _ _esModule ) ? mod : { "default" : mod } ;
} ;
2020-04-28 16:46:47 +02:00
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2023-10-02 17:20:50 +01:00
exports . checkDiskUsage = exports . prettyPrintPack = exports . wrapError = exports . fixInvalidNotificationsInFile = exports . fixInvalidNotifications = exports . parseMatrixInput = exports . isHostedRunner = exports . checkForTimeout = exports . withTimeout = exports . tryGetFolderBytes = exports . listFolder = exports . doesDirectoryExist = exports . isInTestMode = exports . supportExpectDiscardedCache = exports . isGoodVersion = exports . delay = exports . bundleDb = exports . codeQlVersionAbove = exports . getCachedCodeQlVersion = exports . cacheCodeQlVersion = exports . isHTTPError = exports . UserError = exports . HTTPError = exports . getRequiredEnvParam = exports . initializeEnvironment = exports . assertNever = exports . apiVersionInRange = exports . DisallowedAPIVersionReason = exports . checkGitHubVersionInRange = exports . GitHubVariant = exports . parseGitHubUrl = exports . getCodeQLDatabasePath = exports . getThreadsFlag = exports . getThreadsFlagValue = exports . getAddSnippetsFlag = exports . getMemoryFlag = exports . getMemoryFlagValue = exports . getMemoryFlagValueForPlatform = exports . withTmpDir = exports . getToolNames = exports . getExtraOptionsEnvParam = exports . DEFAULT _DEBUG _DATABASE _NAME = exports . DEFAULT _DEBUG _ARTIFACT _NAME = exports . GITHUB _DOTCOM _URL = void 0 ;
2020-05-05 11:59:05 +01:00
const fs = _ _importStar ( require ( "fs" ) ) ;
const os = _ _importStar ( require ( "os" ) ) ;
2020-04-28 16:46:47 +02:00
const path = _ _importStar ( require ( "path" ) ) ;
2022-09-23 11:51:06 +01:00
const util _1 = require ( "util" ) ;
2020-11-26 17:54:34 +00:00
const core = _ _importStar ( require ( "@actions/core" ) ) ;
2023-08-07 16:00:32 +01:00
const check _disk _space _1 = _ _importDefault ( require ( "check-disk-space" ) ) ;
2021-12-08 12:00:54 -08:00
const del _1 = _ _importDefault ( require ( "del" ) ) ;
2022-09-23 11:51:06 +01:00
const get _folder _size _1 = _ _importDefault ( require ( "get-folder-size" ) ) ;
2020-11-26 17:54:34 +00:00
const semver = _ _importStar ( require ( "semver" ) ) ;
2020-11-30 16:33:38 +00:00
const apiCompatibility = _ _importStar ( require ( "./api-compatibility.json" ) ) ;
2023-07-06 12:24:38 +01:00
const environment _1 = require ( "./environment" ) ;
2021-12-09 13:43:57 +00:00
/**
* Specifies bundle versions that are known to be broken
* and will not be used if found in the toolcache.
*/
const BROKEN _VERSIONS = [ "0.0.0-20211207" ] ;
2020-07-30 13:00:35 +01:00
/**
2020-08-25 16:19:15 +01:00
* The URL for github.com.
2020-07-30 13:00:35 +01:00
*/
2020-08-25 16:19:15 +01:00
exports . GITHUB _DOTCOM _URL = "https://github.com" ;
2021-10-28 14:15:22 +01:00
/**
2022-01-07 13:11:51 +00:00
* Default name of the debugging artifact.
2021-10-28 14:15:22 +01:00
*/
2022-01-07 13:11:51 +00:00
exports . DEFAULT _DEBUG _ARTIFACT _NAME = "debug-artifacts" ;
/**
* Default name of the database in the debugging artifact.
*/
exports . DEFAULT _DEBUG _DATABASE _NAME = "db" ;
2023-09-05 13:14:47 +02:00
/**
* The default fraction of the total RAM above 8 GB that should be reserved for the system.
*/
const DEFAULT _RESERVED _RAM _SCALING _FACTOR = 0.05 ;
2023-09-18 16:00:59 +01:00
/**
* The minimum amount of memory imposed by a cgroup limit that we will consider. Memory limits below
* this amount are ignored.
*/
const MINIMUM _CGROUP _MEMORY _LIMIT _BYTES = 1024 * 1024 ;
2020-08-10 09:25:14 +02:00
/**
* Get the extra options for the codeql commands.
*/
function getExtraOptionsEnvParam ( ) {
2020-09-14 10:44:43 +01:00
const varName = "CODEQL_ACTION_EXTRA_OPTIONS" ;
2020-08-10 09:25:14 +02:00
const raw = process . env [ varName ] ;
if ( raw === undefined || raw . length === 0 ) {
return { } ;
}
try {
return JSON . parse ( raw ) ;
}
2023-04-06 17:04:21 +01:00
catch ( unwrappedError ) {
const error = wrapError ( unwrappedError ) ;
throw new Error ( ` ${ varName } environment variable is set, but does not contain valid JSON: ${ error . message } ` ) ;
2020-08-10 09:25:14 +02:00
}
}
exports . getExtraOptionsEnvParam = getExtraOptionsEnvParam ;
2020-04-28 11:29:10 -07:00
/**
* Get the array of all the tool names contained in the given sarif contents.
*
* Returns an array of unique string tool names.
*/
2022-01-12 15:26:34 -08:00
function getToolNames ( sarif ) {
2020-04-28 11:29:10 -07:00
const toolNames = { } ;
for ( const run of sarif . runs || [ ] ) {
const tool = run . tool || { } ;
const driver = tool . driver || { } ;
if ( typeof driver . name === "string" && driver . name . length > 0 ) {
toolNames [ driver . name ] = true ;
}
}
return Object . keys ( toolNames ) ;
}
exports . getToolNames = getToolNames ;
2020-05-05 11:59:05 +01:00
// Creates a random temporary directory, runs the given body, and then deletes the directory.
// Mostly intended for use within tests.
async function withTmpDir ( body ) {
2020-09-14 10:44:43 +01:00
const tmpDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "codeql-action-" ) ) ;
2022-06-17 14:07:36 -07:00
const result = await body ( tmpDir ) ;
2021-12-08 12:00:54 -08:00
await ( 0 , del _1 . default ) ( tmpDir , { force : true } ) ;
2020-05-05 17:32:58 +01:00
return result ;
2020-05-05 11:59:05 +01:00
}
exports . withTmpDir = withTmpDir ;
2021-02-16 15:04:38 -08:00
/**
* Gets an OS-specific amount of memory (in MB) to reserve for OS processes
* when the user doesn't explicitly specify a memory setting.
* This is a heuristic to avoid OOM errors (exit code 137 / SIGKILL)
* from committing too much of the available memory to CodeQL.
* @returns number
*/
2023-09-05 14:30:56 +02:00
function getSystemReservedMemoryMegaBytes ( totalMemoryMegaBytes , platform ) {
2021-02-16 15:04:38 -08:00
// Windows needs more memory for OS processes.
2023-07-21 17:35:34 +01:00
const fixedAmount = 1024 * ( platform === "win32" ? 1.5 : 1 ) ;
2023-09-05 14:30:56 +02:00
// Reserve an additional percentage of the amount of memory above 8 GB, since the amount used by
// the kernel for page tables scales with the size of physical memory.
const scaledAmount = getReservedRamScaleFactor ( ) * Math . max ( totalMemoryMegaBytes - 8 * 1024 , 0 ) ;
return fixedAmount + scaledAmount ;
2021-02-16 15:04:38 -08:00
}
2023-09-05 13:14:47 +02:00
function getReservedRamScaleFactor ( ) {
const envVar = Number . parseInt ( process . env [ environment _1 . EnvVar . SCALING _RESERVED _RAM _PERCENTAGE ] || "" , 10 ) ;
if ( envVar < 0 || envVar > 100 || Number . isNaN ( envVar ) ) {
return DEFAULT _RESERVED _RAM _SCALING _FACTOR ;
}
return envVar / 100 ;
}
2020-06-22 17:17:25 +02:00
/**
2021-10-28 15:09:59 -07:00
* Get the value of the codeql `--ram` flag as configured by the `ram` input.
* If no value was specified, the total available memory will be used minus a
* threshold reserved for the OS.
2020-06-22 17:17:25 +02:00
*
2021-10-28 15:09:59 -07:00
* @returns {number} the amount of RAM to use, in megabytes
2020-06-22 17:17:25 +02:00
*/
2023-09-05 14:30:56 +02:00
function getMemoryFlagValueForPlatform ( userInput , totalMemoryBytes , platform ) {
2020-06-22 17:17:25 +02:00
let memoryToUseMegaBytes ;
2020-09-01 14:13:10 +01:00
if ( userInput ) {
memoryToUseMegaBytes = Number ( userInput ) ;
2020-06-22 17:17:25 +02:00
if ( Number . isNaN ( memoryToUseMegaBytes ) || memoryToUseMegaBytes <= 0 ) {
2020-09-14 10:44:43 +01:00
throw new Error ( ` Invalid RAM setting " ${ userInput } ", specified. ` ) ;
2020-06-22 17:17:25 +02:00
}
}
else {
const totalMemoryMegaBytes = totalMemoryBytes / ( 1024 * 1024 ) ;
2023-09-05 14:30:56 +02:00
const reservedMemoryMegaBytes = getSystemReservedMemoryMegaBytes ( totalMemoryMegaBytes , platform ) ;
2021-02-16 15:04:38 -08:00
memoryToUseMegaBytes = totalMemoryMegaBytes - reservedMemoryMegaBytes ;
2020-06-22 17:17:25 +02:00
}
2021-10-28 15:09:59 -07:00
return Math . floor ( memoryToUseMegaBytes ) ;
}
2023-07-21 17:35:34 +01:00
exports . getMemoryFlagValueForPlatform = getMemoryFlagValueForPlatform ;
2023-09-15 18:09:37 +01:00
/**
2023-09-15 18:17:13 +01:00
* Get the total amount of memory available to the Action, taking into account constraints imposed
* by cgroups on Linux.
2023-09-15 18:09:37 +01:00
*/
2023-09-18 16:00:59 +01:00
function getTotalMemoryBytes ( logger ) {
const limits = [ os . totalmem ( ) ] ;
2023-09-15 18:17:13 +01:00
if ( os . platform ( ) === "linux" ) {
2023-09-18 16:00:59 +01:00
limits . push ( ... [
2023-09-15 18:23:25 +01:00
"/sys/fs/cgroup/memory/memory.limit_in_bytes" ,
"/sys/fs/cgroup/memory.max" ,
2023-09-18 16:00:59 +01:00
]
. map ( ( file ) => getCgroupMemoryLimitBytes ( file , logger ) )
. filter ( ( limit ) => limit !== undefined )
. map ( ( limit ) => limit ) ) ;
}
const limit = Math . min ( ... limits ) ;
logger . debug ( ` While resolving RAM, determined that the total memory available to the Action is ${ limit / ( 1024 * 1024 ) } MiB. ` ) ;
return limit ;
}
/**
* Gets the number of bytes of available memory specified by the cgroup limit file at the given path.
*
* May be greater than the total memory reported by the operating system if there is no cgroup limit.
*/
function getCgroupMemoryLimitBytes ( limitFile , logger ) {
if ( ! fs . existsSync ( limitFile ) ) {
logger . debug ( ` While resolving RAM, did not find a cgroup memory limit at ${ limitFile } . ` ) ;
return undefined ;
}
const limit = Number ( fs . readFileSync ( limitFile , "utf8" ) ) ;
if ( ! Number . isInteger ( limit ) ) {
logger . debug ( ` While resolving RAM, ignored the file ${ limitFile } that may contain a cgroup memory limit ` +
"as this file did not contain an integer." ) ;
return undefined ;
}
const displayLimit = ` ${ Math . floor ( limit / ( 1024 * 1024 ) ) } MiB ` ;
2023-09-27 13:56:20 +01:00
if ( limit > os . totalmem ( ) ) {
logger . debug ( ` While resolving RAM, ignored the file ${ limitFile } that may contain a cgroup memory limit as ` +
` its contents ${ displayLimit } were greater than the total amount of system memory. ` ) ;
return undefined ;
}
2023-09-18 16:00:59 +01:00
if ( limit < MINIMUM _CGROUP _MEMORY _LIMIT _BYTES ) {
logger . info ( ` While resolving RAM, ignored a cgroup limit of ${ displayLimit } in ${ limitFile } as it was below ${ MINIMUM _CGROUP _MEMORY _LIMIT _BYTES / ( 1024 * 1024 ) } MiB. ` ) ;
return undefined ;
2023-09-15 18:09:37 +01:00
}
2023-09-18 16:00:59 +01:00
logger . info ( ` While resolving RAM, found a cgroup limit of ${ displayLimit } in ${ limitFile } . ` ) ;
return limit ;
2023-09-15 18:09:37 +01:00
}
2023-07-21 17:35:34 +01:00
/**
* Get the value of the codeql `--ram` flag as configured by the `ram` input.
* If no value was specified, the total available memory will be used minus a
* threshold reserved for the OS.
*
* @returns {number} the amount of RAM to use, in megabytes
*/
2023-09-18 12:43:52 +01:00
function getMemoryFlagValue ( userInput , logger ) {
2023-09-18 16:00:59 +01:00
return getMemoryFlagValueForPlatform ( userInput , getTotalMemoryBytes ( logger ) , process . platform ) ;
2023-07-21 17:35:34 +01:00
}
2021-10-28 15:09:59 -07:00
exports . getMemoryFlagValue = getMemoryFlagValue ;
/**
* Get the codeql `--ram` flag as configured by the `ram` input. If no value was
* specified, the total available memory will be used minus a threshold
* reserved for the OS.
*
* @returns string
*/
2023-09-18 12:43:52 +01:00
function getMemoryFlag ( userInput , logger ) {
const megabytes = getMemoryFlagValue ( userInput , logger ) ;
2023-07-07 12:13:57 +01:00
return ` --ram= ${ megabytes } ` ;
2020-06-22 17:17:25 +02:00
}
exports . getMemoryFlag = getMemoryFlag ;
2020-09-10 17:18:02 +01:00
/**
* Get the codeql flag to specify whether to add code snippets to the sarif file.
*
* @returns string
*/
function getAddSnippetsFlag ( userInput ) {
if ( typeof userInput === "string" ) {
// have to process specifically because any non-empty string is truthy
userInput = userInput . toLowerCase ( ) === "true" ;
}
return userInput ? "--sarif-add-snippets" : "--no-sarif-add-snippets" ;
}
exports . getAddSnippetsFlag = getAddSnippetsFlag ;
2020-06-22 17:17:25 +02:00
/**
2021-10-28 15:09:59 -07:00
* Get the value of the codeql `--threads` flag specified for the `threads`
* input. If no value was specified, all available threads will be used.
2020-08-13 14:25:10 +01:00
*
* The value will be capped to the number of available CPUs.
2020-06-22 17:17:25 +02:00
*
2021-10-28 15:09:59 -07:00
* @returns {number}
2020-06-22 17:17:25 +02:00
*/
2021-10-28 15:09:59 -07:00
function getThreadsFlagValue ( userInput , logger ) {
2020-08-13 14:25:10 +01:00
let numThreads ;
2020-08-17 12:46:55 +01:00
const maxThreads = os . cpus ( ) . length ;
2020-09-01 14:13:10 +01:00
if ( userInput ) {
numThreads = Number ( userInput ) ;
2020-06-22 21:39:09 +02:00
if ( Number . isNaN ( numThreads ) ) {
2020-09-01 14:13:10 +01:00
throw new Error ( ` Invalid threads setting " ${ userInput } ", specified. ` ) ;
2020-06-22 17:17:25 +02:00
}
if ( numThreads > maxThreads ) {
2020-09-01 14:13:10 +01:00
logger . info ( ` Clamping desired number of threads ( ${ numThreads } ) to max available ( ${ maxThreads } ). ` ) ;
2020-06-22 17:17:25 +02:00
numThreads = maxThreads ;
}
2020-06-22 21:39:09 +02:00
const minThreads = - maxThreads ;
if ( numThreads < minThreads ) {
2020-09-01 14:13:10 +01:00
logger . info ( ` Clamping desired number of free threads ( ${ numThreads } ) to max available ( ${ minThreads } ). ` ) ;
2020-06-22 21:39:09 +02:00
numThreads = minThreads ;
}
2020-06-22 17:17:25 +02:00
}
2020-08-13 14:25:10 +01:00
else {
// Default to using all threads
2020-08-17 12:46:55 +01:00
numThreads = maxThreads ;
2020-08-13 14:25:10 +01:00
}
2021-10-28 15:09:59 -07:00
return numThreads ;
}
exports . getThreadsFlagValue = getThreadsFlagValue ;
/**
* Get the codeql `--threads` flag specified for the `threads` input.
* If no value was specified, all available threads will be used.
*
* The value will be capped to the number of available CPUs.
*
* @returns string
*/
function getThreadsFlag ( userInput , logger ) {
return ` --threads= ${ getThreadsFlagValue ( userInput , logger ) } ` ;
2020-06-22 17:17:25 +02:00
}
exports . getThreadsFlag = getThreadsFlag ;
2020-08-24 12:53:09 +01:00
/**
* Get the path where the CodeQL database for the given language lives.
*/
2021-05-17 10:35:09 +01:00
function getCodeQLDatabasePath ( config , language ) {
return path . resolve ( config . dbLocation , language ) ;
2020-08-24 12:53:09 +01:00
}
exports . getCodeQLDatabasePath = getCodeQLDatabasePath ;
2020-09-28 18:28:46 +01:00
/**
* Parses user input of a github.com or GHES URL to a canonical form.
* Removes any API prefix or suffix if one is present.
*/
2021-02-28 01:55:55 -05:00
function parseGitHubUrl ( inputUrl ) {
2020-09-28 18:28:46 +01:00
const originalUrl = inputUrl ;
if ( inputUrl . indexOf ( "://" ) === - 1 ) {
inputUrl = ` https:// ${ inputUrl } ` ;
}
if ( ! inputUrl . startsWith ( "http://" ) && ! inputUrl . startsWith ( "https://" ) ) {
throw new Error ( ` " ${ originalUrl } " is not a http or https URL ` ) ;
}
let url ;
try {
url = new URL ( inputUrl ) ;
}
catch ( e ) {
throw new Error ( ` " ${ originalUrl } " is not a valid URL ` ) ;
}
// If we detect this is trying to be to github.com
// then return with a fixed canonical URL.
if ( url . hostname === "github.com" || url . hostname === "api.github.com" ) {
2020-10-05 16:44:43 +01:00
return exports . GITHUB _DOTCOM _URL ;
2020-09-28 18:28:46 +01:00
}
// Remove the API prefix if it's present
if ( url . pathname . indexOf ( "/api/v3" ) !== - 1 ) {
url . pathname = url . pathname . substring ( 0 , url . pathname . indexOf ( "/api/v3" ) ) ;
}
// Also consider subdomain isolation on GHES
if ( url . hostname . startsWith ( "api." ) ) {
url . hostname = url . hostname . substring ( 4 ) ;
}
// Normalise path to having a trailing slash for consistency
if ( ! url . pathname . endsWith ( "/" ) ) {
url . pathname = ` ${ url . pathname } / ` ;
}
return url . toString ( ) ;
}
2021-02-28 01:55:55 -05:00
exports . parseGitHubUrl = parseGitHubUrl ;
2020-11-26 17:54:34 +00:00
const CODEQL _ACTION _WARNED _ABOUT _VERSION _ENV _VAR = "CODEQL_ACTION_WARNED_ABOUT_VERSION" ;
let hasBeenWarnedAboutVersion = false ;
2021-02-15 09:29:10 +00:00
var GitHubVariant ;
( function ( GitHubVariant ) {
GitHubVariant [ GitHubVariant [ "DOTCOM" ] = 0 ] = "DOTCOM" ;
GitHubVariant [ GitHubVariant [ "GHES" ] = 1 ] = "GHES" ;
GitHubVariant [ GitHubVariant [ "GHAE" ] = 2 ] = "GHAE" ;
2023-03-21 15:09:23 +00:00
GitHubVariant [ GitHubVariant [ "GHE_DOTCOM" ] = 3 ] = "GHE_DOTCOM" ;
2023-07-13 11:17:33 +01:00
} ) ( GitHubVariant || ( exports . GitHubVariant = GitHubVariant = { } ) ) ;
2022-11-14 16:37:48 +00:00
function checkGitHubVersionInRange ( version , logger ) {
2021-02-15 09:29:10 +00:00
if ( hasBeenWarnedAboutVersion || version . type !== GitHubVariant . GHES ) {
2020-11-26 17:54:34 +00:00
return ;
}
const disallowedAPIVersionReason = apiVersionInRange ( version . version , apiCompatibility . minimumVersion , apiCompatibility . maximumVersion ) ;
if ( disallowedAPIVersionReason === DisallowedAPIVersionReason . ACTION _TOO _OLD ) {
2022-11-14 16:37:48 +00:00
logger . warning ( ` The CodeQL Action version you are using is too old to be compatible with GitHub Enterprise ${ version . version } . If you experience issues, please upgrade to a more recent version of the CodeQL Action. ` ) ;
2020-11-26 17:54:34 +00:00
}
if ( disallowedAPIVersionReason === DisallowedAPIVersionReason . ACTION _TOO _NEW ) {
2022-11-14 16:37:48 +00:00
logger . warning ( ` GitHub Enterprise ${ version . version } is too old to be compatible with this version of the CodeQL Action. If you experience issues, please upgrade to a more recent version of GitHub Enterprise or use an older version of the CodeQL Action. ` ) ;
2020-11-26 17:54:34 +00:00
}
hasBeenWarnedAboutVersion = true ;
2022-11-14 16:37:48 +00:00
core . exportVariable ( CODEQL _ACTION _WARNED _ABOUT _VERSION _ENV _VAR , true ) ;
2020-11-26 17:54:34 +00:00
}
2020-11-30 16:33:38 +00:00
exports . checkGitHubVersionInRange = checkGitHubVersionInRange ;
2020-11-26 17:54:34 +00:00
var DisallowedAPIVersionReason ;
( function ( DisallowedAPIVersionReason ) {
DisallowedAPIVersionReason [ DisallowedAPIVersionReason [ "ACTION_TOO_OLD" ] = 0 ] = "ACTION_TOO_OLD" ;
DisallowedAPIVersionReason [ DisallowedAPIVersionReason [ "ACTION_TOO_NEW" ] = 1 ] = "ACTION_TOO_NEW" ;
2023-07-13 11:17:33 +01:00
} ) ( DisallowedAPIVersionReason || ( exports . DisallowedAPIVersionReason = DisallowedAPIVersionReason = { } ) ) ;
2020-11-26 17:54:34 +00:00
function apiVersionInRange ( version , minimumVersion , maximumVersion ) {
if ( ! semver . satisfies ( version , ` >= ${ minimumVersion } ` ) ) {
return DisallowedAPIVersionReason . ACTION _TOO _NEW ;
}
if ( ! semver . satisfies ( version , ` <= ${ maximumVersion } ` ) ) {
return DisallowedAPIVersionReason . ACTION _TOO _OLD ;
}
return undefined ;
}
exports . apiVersionInRange = apiVersionInRange ;
2021-05-13 17:56:27 +00:00
/**
* This error is used to indicate a runtime failure of an exhaustivity check enforced at compile time.
*/
class ExhaustivityCheckingError extends Error {
constructor ( expectedExhaustiveValue ) {
super ( "Internal error: exhaustivity checking failure" ) ;
this . expectedExhaustiveValue = expectedExhaustiveValue ;
}
}
/**
* Used to perform compile-time exhaustivity checking on a value. This function will not be executed at runtime unless
* the type system has been subverted.
*/
function assertNever ( value ) {
throw new ExhaustivityCheckingError ( value ) ;
}
exports . assertNever = assertNever ;
2021-09-15 14:49:20 +01:00
/**
* Set some initial environment variables that we can set even without
* knowing what version of CodeQL we're running.
*/
2022-11-14 16:37:48 +00:00
function initializeEnvironment ( version ) {
2023-07-06 12:24:38 +01:00
core . exportVariable ( String ( environment _1 . EnvVar . FEATURE _MULTI _LANGUAGE ) , "false" ) ;
core . exportVariable ( String ( environment _1 . EnvVar . FEATURE _SANDWICH ) , "false" ) ;
core . exportVariable ( String ( environment _1 . EnvVar . FEATURE _SARIF _COMBINE ) , "true" ) ;
core . exportVariable ( String ( environment _1 . EnvVar . FEATURE _WILL _UPLOAD ) , "true" ) ;
core . exportVariable ( String ( environment _1 . EnvVar . VERSION ) , version ) ;
2021-06-01 14:49:07 -07:00
}
exports . initializeEnvironment = initializeEnvironment ;
/**
* Get an environment parameter, but throw an error if it is not set.
*/
function getRequiredEnvParam ( paramName ) {
const value = process . env [ paramName ] ;
if ( value === undefined || value . length === 0 ) {
throw new Error ( ` ${ paramName } environment variable must be set ` ) ;
}
return value ;
}
exports . getRequiredEnvParam = getRequiredEnvParam ;
2021-06-22 13:05:12 +01:00
class HTTPError extends Error {
constructor ( message , status ) {
super ( message ) ;
this . status = status ;
}
}
exports . HTTPError = HTTPError ;
2022-02-17 11:47:04 -08:00
/**
* An Error class that indicates an error that occurred due to
* a misconfiguration of the action or the CodeQL CLI.
*/
class UserError extends Error {
constructor ( message ) {
super ( message ) ;
}
}
exports . UserError = UserError ;
2021-06-22 13:05:12 +01:00
function isHTTPError ( arg ) {
2023-01-18 20:00:33 +00:00
return arg ? . status !== undefined && Number . isInteger ( arg . status ) ;
2021-06-22 13:05:12 +01:00
}
exports . isHTTPError = isHTTPError ;
2022-03-17 10:07:29 -07:00
let cachedCodeQlVersion = undefined ;
function cacheCodeQlVersion ( version ) {
if ( cachedCodeQlVersion !== undefined ) {
throw new Error ( "cacheCodeQlVersion() should be called only once" ) ;
}
cachedCodeQlVersion = version ;
}
exports . cacheCodeQlVersion = cacheCodeQlVersion ;
function getCachedCodeQlVersion ( ) {
return cachedCodeQlVersion ;
}
exports . getCachedCodeQlVersion = getCachedCodeQlVersion ;
2021-08-12 16:02:19 +01:00
async function codeQlVersionAbove ( codeql , requiredVersion ) {
2023-10-04 11:28:28 +01:00
return semver . gte ( ( await codeql . getVersion ( ) ) . version , requiredVersion ) ;
2021-08-12 16:02:19 +01:00
}
exports . codeQlVersionAbove = codeQlVersionAbove ;
2021-10-28 14:15:22 +01:00
// Create a bundle for the given DB, if it doesn't already exist
2022-01-07 13:11:51 +00:00
async function bundleDb ( config , language , codeql , dbName ) {
2021-10-28 14:15:22 +01:00
const databasePath = getCodeQLDatabasePath ( config , language ) ;
2022-01-07 13:11:51 +00:00
const databaseBundlePath = path . resolve ( config . dbLocation , ` ${ dbName } .zip ` ) ;
2021-12-01 12:25:57 +00:00
// For a tiny bit of added safety, delete the file if it exists.
// The file is probably from an earlier call to this function, either
// as part of this action step or a previous one, but it could also be
// from somewhere else or someone trying to make the action upload a
// non-database file.
if ( fs . existsSync ( databaseBundlePath ) ) {
2021-12-08 15:37:43 -08:00
await ( 0 , del _1 . default ) ( databaseBundlePath , { force : true } ) ;
2021-12-01 12:25:57 +00:00
}
2022-01-07 13:11:51 +00:00
await codeql . databaseBundle ( databasePath , databaseBundlePath , dbName ) ;
2021-10-28 14:15:22 +01:00
return databaseBundlePath ;
}
exports . bundleDb = bundleDb ;
2023-02-13 13:26:01 -08:00
/**
* @param milliseconds time to delay
* @param opts options
* @param opts.allowProcessExit if true, the timer will not prevent the process from exiting
*/
async function delay ( milliseconds , { allowProcessExit } ) {
return new Promise ( ( resolve ) => {
const timer = setTimeout ( resolve , milliseconds ) ;
if ( allowProcessExit ) {
// Immediately `unref` the timer such that it only prevents the process from exiting if the
// surrounding promise is being awaited.
timer . unref ( ) ;
}
} ) ;
2021-10-18 11:43:30 +01:00
}
exports . delay = delay ;
2021-12-09 13:43:57 +00:00
function isGoodVersion ( versionSpec ) {
return ! BROKEN _VERSIONS . includes ( versionSpec ) ;
}
exports . isGoodVersion = isGoodVersion ;
2023-02-15 08:45:13 -08:00
/**
* Checks whether the CodeQL CLI supports the `--expect-discarded-cache` command-line flag.
*/
async function supportExpectDiscardedCache ( codeQL ) {
return codeQlVersionAbove ( codeQL , "2.12.1" ) ;
}
exports . supportExpectDiscardedCache = supportExpectDiscardedCache ;
2022-04-28 19:07:04 +01:00
/*
* Returns whether we are in test mode.
*
* In test mode, we don't upload SARIF results or status reports to the GitHub API.
*/
function isInTestMode ( ) {
2023-07-06 12:24:38 +01:00
return process . env [ environment _1 . EnvVar . TEST _MODE ] === "true" ;
2022-04-28 19:07:04 +01:00
}
exports . isInTestMode = isInTestMode ;
2022-08-01 11:42:55 +02:00
/*
* Returns whether the path in the argument represents an existing directory.
*/
function doesDirectoryExist ( dirPath ) {
try {
const stats = fs . lstatSync ( dirPath ) ;
return stats . isDirectory ( ) ;
}
catch ( e ) {
return false ;
}
}
exports . doesDirectoryExist = doesDirectoryExist ;
2022-08-01 12:52:16 +02:00
/**
2022-08-11 13:58:01 +02:00
* Returns a recursive list of files in a given directory.
2022-08-01 12:52:16 +02:00
*/
function listFolder ( dir ) {
2022-08-10 14:57:57 +02:00
if ( ! doesDirectoryExist ( dir ) ) {
return [ ] ;
}
2022-08-01 12:52:16 +02:00
const entries = fs . readdirSync ( dir , { withFileTypes : true } ) ;
let files = [ ] ;
for ( const entry of entries ) {
if ( entry . isFile ( ) ) {
files . push ( path . resolve ( dir , entry . name ) ) ;
}
else if ( entry . isDirectory ( ) ) {
files = files . concat ( listFolder ( path . resolve ( dir , entry . name ) ) ) ;
}
}
return files ;
}
exports . listFolder = listFolder ;
2022-09-23 11:51:06 +01:00
/**
* Get the size a folder in bytes. This will log any filesystem errors
* as a warning and then return undefined.
*
* @param cacheDir A directory to get the size of.
* @param logger A logger to log any errors to.
* @returns The size in bytes of the folder, or undefined if errors occurred.
*/
async function tryGetFolderBytes ( cacheDir , logger ) {
try {
return await ( 0 , util _1 . promisify ) ( get _folder _size _1 . default ) ( cacheDir ) ;
}
catch ( e ) {
logger . warning ( ` Encountered an error while getting size of folder: ${ e } ` ) ;
return undefined ;
}
}
exports . tryGetFolderBytes = tryGetFolderBytes ;
2022-11-09 17:08:44 +00:00
let hadTimeout = false ;
2022-09-30 10:44:36 +01:00
/**
* Run a promise for a given amount of time, and if it doesn't resolve within
2022-11-09 17:08:44 +00:00
* that time, call the provided callback and then return undefined. Due to the
* limitation outlined below, using this helper function is not recommended
* unless there is no other option for adding a timeout (e.g. the code that
* would need the timeout added is an external library).
2022-09-30 10:44:36 +01:00
*
2022-10-11 22:38:30 +01:00
* Important: This does NOT cancel the original promise, so that promise will
* continue in the background even after the timeout has expired. If the
* original promise hangs, then this will prevent the process terminating.
2022-11-09 17:08:44 +00:00
* If a timeout has occurred then the global hadTimeout variable will get set
* to true, and the caller is responsible for forcing the process to exit
* if this is the case by calling the `checkForTimeout` function at the end
* of execution.
2022-10-11 16:59:48 +01:00
*
2022-09-30 10:44:36 +01:00
* @param timeoutMs The timeout in milliseconds.
* @param promise The promise to run.
* @param onTimeout A callback to call if the promise times out.
* @returns The result of the promise, or undefined if the promise times out.
*/
async function withTimeout ( timeoutMs , promise , onTimeout ) {
2022-10-11 10:04:21 +01:00
let finished = false ;
const mainTask = async ( ) => {
const result = await promise ;
finished = true ;
return result ;
} ;
2023-01-19 20:25:55 +00:00
const timeoutTask = async ( ) => {
2023-02-13 13:26:01 -08:00
await delay ( timeoutMs , { allowProcessExit : true } ) ;
2023-01-19 20:25:55 +00:00
if ( ! finished ) {
// Workaround: While the promise racing below will allow the main code
// to continue, the process won't normally exit until the asynchronous
// task in the background has finished. We set this variable to force
// an exit at the end of our code when `checkForTimeout` is called.
hadTimeout = true ;
onTimeout ( ) ;
}
return undefined ;
} ;
return await Promise . race ( [ mainTask ( ) , timeoutTask ( ) ] ) ;
2022-09-30 10:44:36 +01:00
}
exports . withTimeout = withTimeout ;
2022-11-09 17:08:44 +00:00
/**
* Check if the global hadTimeout variable has been set, and if so then
* exit the process to ensure any background tasks that are still running
* are killed. This should be called at the end of execution if the
* `withTimeout` function has been used.
*/
async function checkForTimeout ( ) {
if ( hadTimeout === true ) {
core . info ( "A timeout occurred, force exiting the process after 30 seconds to prevent hanging." ) ;
2023-02-13 13:26:01 -08:00
await delay ( 30000 , { allowProcessExit : true } ) ;
2022-11-09 17:08:44 +00:00
process . exit ( ) ;
}
}
exports . checkForTimeout = checkForTimeout ;
2022-10-13 14:31:54 +01:00
/**
* This function implements a heuristic to determine whether the
* runner we are on is hosted by GitHub. It does this by checking
* the name of the runner against the list of known GitHub-hosted
* runner names. It also checks for the presence of a toolcache
* directory with the name hostedtoolcache which is present on
* GitHub-hosted runners.
*
* @returns true iff the runner is hosted by GitHub
*/
function isHostedRunner ( ) {
return (
// Name of the runner on hosted Windows runners
2023-01-18 20:00:33 +00:00
process . env [ "RUNNER_NAME" ] ? . includes ( "Hosted Agent" ) ||
2022-10-13 14:31:54 +01:00
// Name of the runner on hosted POSIX runners
2023-01-18 20:00:33 +00:00
process . env [ "RUNNER_NAME" ] ? . includes ( "GitHub Actions" ) ||
2022-10-13 14:31:54 +01:00
// Segment of the path to the tool cache on all hosted runners
2023-01-18 20:00:33 +00:00
process . env [ "RUNNER_TOOL_CACHE" ] ? . includes ( "hostedtoolcache" ) ) ;
2022-10-13 14:31:54 +01:00
}
exports . isHostedRunner = isHostedRunner ;
2022-11-23 13:27:16 +00:00
function parseMatrixInput ( matrixInput ) {
if ( matrixInput === undefined || matrixInput === "null" ) {
return undefined ;
}
return JSON . parse ( matrixInput ) ;
}
exports . parseMatrixInput = parseMatrixInput ;
2023-03-24 20:01:35 +00:00
function removeDuplicateLocations ( locations ) {
const newJsonLocations = new Set ( ) ;
return locations . filter ( ( location ) => {
const jsonLocation = JSON . stringify ( location ) ;
if ( ! newJsonLocations . has ( jsonLocation ) ) {
newJsonLocations . add ( jsonLocation ) ;
return true ;
}
return false ;
} ) ;
}
function fixInvalidNotifications ( sarif , logger ) {
2023-03-27 15:59:29 +01:00
if ( ! Array . isArray ( sarif . runs ) ) {
2023-03-24 20:01:35 +00:00
return sarif ;
}
// Ensure that the array of locations for each SARIF notification contains unique locations.
// This is a workaround for a bug in the CodeQL CLI that causes duplicate locations to be
// emitted in some cases.
let numDuplicateLocationsRemoved = 0 ;
const newSarif = {
... sarif ,
runs : sarif . runs . map ( ( run ) => {
if ( run . tool ? . driver ? . name !== "CodeQL" ||
2023-03-27 15:59:29 +01:00
! Array . isArray ( run . invocations ) ) {
2023-03-24 20:01:35 +00:00
return run ;
}
return {
... run ,
invocations : run . invocations . map ( ( invocation ) => {
2023-03-27 15:59:29 +01:00
if ( ! Array . isArray ( invocation . toolExecutionNotifications ) ) {
2023-03-24 20:01:35 +00:00
return invocation ;
}
return {
... invocation ,
toolExecutionNotifications : invocation . toolExecutionNotifications . map ( ( notification ) => {
2023-03-27 15:59:29 +01:00
if ( ! Array . isArray ( notification . locations ) ) {
2023-03-24 20:01:35 +00:00
return notification ;
}
const newLocations = removeDuplicateLocations ( notification . locations ) ;
numDuplicateLocationsRemoved +=
notification . locations . length - newLocations . length ;
return {
... notification ,
locations : newLocations ,
} ;
} ) ,
} ;
} ) ,
} ;
} ) ,
} ;
if ( numDuplicateLocationsRemoved > 0 ) {
logger . info ( ` Removed ${ numDuplicateLocationsRemoved } duplicate locations from SARIF notification ` +
"objects." ) ;
}
2023-04-04 16:46:45 +01:00
else {
logger . debug ( "No duplicate locations found in SARIF notification objects." ) ;
}
2023-03-24 20:01:35 +00:00
return newSarif ;
}
exports . fixInvalidNotifications = fixInvalidNotifications ;
2023-05-24 12:12:27 +00:00
/**
* Removes duplicates from the sarif file.
*
* When `CODEQL_ACTION_DISABLE_DUPLICATE_LOCATION_FIX` is set to true, this will
* simply rename the input file to the output file. Otherwise, it will parse the
* input file as JSON, remove duplicate locations from the SARIF notification
* objects, and write the result to the output file.
*
* For context, see documentation of:
* `CODEQL_ACTION_DISABLE_DUPLICATE_LOCATION_FIX`. */
2023-03-27 15:44:47 +01:00
function fixInvalidNotificationsInFile ( inputPath , outputPath , logger ) {
2023-07-06 12:24:38 +01:00
if ( process . env [ environment _1 . EnvVar . DISABLE _DUPLICATE _LOCATION _FIX ] === "true" ) {
2023-05-24 11:51:57 +00:00
logger . info ( "SARIF notification object duplicate location fix disabled by the " +
2023-07-06 12:24:38 +01:00
` ${ environment _1 . EnvVar . DISABLE _DUPLICATE _LOCATION _FIX } environment variable. ` ) ;
2023-05-24 11:51:57 +00:00
fs . renameSync ( inputPath , outputPath ) ;
}
else {
let sarif = JSON . parse ( fs . readFileSync ( inputPath , "utf8" ) ) ;
sarif = fixInvalidNotifications ( sarif , logger ) ;
fs . writeFileSync ( outputPath , JSON . stringify ( sarif ) ) ;
}
2023-03-27 15:44:47 +01:00
}
exports . fixInvalidNotificationsInFile = fixInvalidNotificationsInFile ;
2023-04-06 17:04:21 +01:00
function wrapError ( error ) {
return error instanceof Error ? error : new Error ( String ( error ) ) ;
}
exports . wrapError = wrapError ;
2023-07-19 17:37:43 +01:00
function prettyPrintPack ( pack ) {
return ` ${ pack . name } ${ pack . version ? ` @ ${ pack . version } ` : "" } ${ pack . path ? ` : ${ pack . path } ` : "" } ` ;
}
exports . prettyPrintPack = prettyPrintPack ;
2023-08-07 16:00:32 +01:00
async function checkDiskUsage ( logger ) {
const diskUsage = await ( 0 , check _disk _space _1 . default ) ( getRequiredEnvParam ( "GITHUB_WORKSPACE" ) ) ;
const gbInBytes = 1024 * 1024 * 1024 ;
if ( logger && diskUsage . free < 2 * gbInBytes ) {
const message = "The Actions runner is running low on disk space " +
` ( ${ ( diskUsage . free / gbInBytes ) . toPrecision ( 4 ) } GB available). ` ;
if ( process . env [ environment _1 . EnvVar . HAS _WARNED _ABOUT _DISK _SPACE ] !== "true" ) {
logger . warning ( message ) ;
}
else {
logger . debug ( message ) ;
}
core . exportVariable ( environment _1 . EnvVar . HAS _WARNED _ABOUT _DISK _SPACE , "true" ) ;
}
return {
numAvailableBytes : diskUsage . free ,
numTotalBytes : diskUsage . size ,
} ;
}
exports . checkDiskUsage = checkDiskUsage ;
2020-05-13 16:31:24 +01:00
//# sourceMappingURL=util.js.map