2023-01-09 14:53:52 +00:00
"use strict" ;
var _ _createBinding = ( this && this . _ _createBinding ) || ( Object . create ? ( function ( o , m , k , k2 ) {
if ( k2 === undefined ) k2 = k ;
Object . defineProperty ( o , k2 , { enumerable : true , get : function ( ) { return m [ k ] ; } } ) ;
} ) : ( 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 ;
} ) ;
var _ _importStar = ( this && this . _ _importStar ) || function ( mod ) {
if ( mod && mod . _ _esModule ) return mod ;
var result = { } ;
if ( mod != null ) for ( var k in mod ) if ( k !== "default" && Object . prototype . hasOwnProperty . call ( mod , k ) ) _ _createBinding ( result , mod , k ) ;
_ _setModuleDefault ( result , mod ) ;
return result ;
} ;
var _ _importDefault = ( this && this . _ _importDefault ) || function ( mod ) {
return ( mod && mod . _ _esModule ) ? mod : { "default" : mod } ;
} ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2023-01-11 15:41:21 +00:00
exports . setupCodeQLBundle = exports . getCodeQLURLVersion = exports . downloadCodeQL = exports . getCodeQLSource = exports . convertToSemVer = exports . getBundleTagNameFromUrl = exports . findCodeQLBundleTagDotcomOnly = exports . getCodeQLActionRepository = exports . CODEQL _DEFAULT _ACTION _REPOSITORY = void 0 ;
2023-01-09 14:53:52 +00:00
const fs = _ _importStar ( require ( "fs" ) ) ;
const path = _ _importStar ( require ( "path" ) ) ;
const toolcache = _ _importStar ( require ( "@actions/tool-cache" ) ) ;
const fast _deep _equal _1 = _ _importDefault ( require ( "fast-deep-equal" ) ) ;
const semver = _ _importStar ( require ( "semver" ) ) ;
const uuid _1 = require ( "uuid" ) ;
const actions _util _1 = require ( "./actions-util" ) ;
const api = _ _importStar ( require ( "./api-client" ) ) ;
const defaults = _ _importStar ( require ( "./defaults.json" ) ) ; // Referenced from codeql-action-sync-tool!
const util = _ _importStar ( require ( "./util" ) ) ;
const util _1 = require ( "./util" ) ;
exports . CODEQL _DEFAULT _ACTION _REPOSITORY = "github/codeql-action" ;
function getCodeQLBundleName ( ) {
let platform ;
if ( process . platform === "win32" ) {
platform = "win64" ;
}
else if ( process . platform === "linux" ) {
platform = "linux64" ;
}
else if ( process . platform === "darwin" ) {
platform = "osx64" ;
}
else {
return "codeql-bundle.tar.gz" ;
}
return ` codeql-bundle- ${ platform } .tar.gz ` ;
}
function getCodeQLActionRepository ( logger ) {
if ( ( 0 , actions _util _1 . isRunningLocalAction ) ( ) ) {
// This handles the case where the Action does not come from an Action repository,
// e.g. our integration tests which use the Action code from the current checkout.
// In these cases, the GITHUB_ACTION_REPOSITORY environment variable is not set.
logger . info ( "The CodeQL Action is checked out locally. Using the default CodeQL Action repository." ) ;
return exports . CODEQL _DEFAULT _ACTION _REPOSITORY ;
}
return util . getRequiredEnvParam ( "GITHUB_ACTION_REPOSITORY" ) ;
}
exports . getCodeQLActionRepository = getCodeQLActionRepository ;
async function findCodeQLBundleTagDotcomOnly ( cliVersion , logger ) {
2023-01-10 13:16:22 +00:00
logger . debug ( ` Trying to find the CodeQL bundle release for CLI version ${ cliVersion } . ` ) ;
2023-01-09 14:53:52 +00:00
const apiClient = api . getApiClient ( ) ;
const codeQLActionRepository = getCodeQLActionRepository ( logger ) ;
const releases = await apiClient . paginate ( apiClient . repos . listReleases , {
owner : codeQLActionRepository . split ( "/" ) [ 0 ] ,
repo : codeQLActionRepository . split ( "/" ) [ 1 ] ,
} ) ;
logger . debug ( ` Found ${ releases . length } releases. ` ) ;
for ( const release of releases ) {
const cliVersionFileVersions = release . assets
. map ( ( asset ) => { var _a ; return ( _a = asset . name . match ( /cli-version-(.*)\.txt/ ) ) === null || _a === void 0 ? void 0 : _a [ 1 ] ; } )
. filter ( ( v ) => v )
. map ( ( v ) => v ) ;
if ( cliVersionFileVersions . length === 0 ) {
logger . debug ( ` Ignoring release ${ release . tag _name } with no CLI version marker file. ` ) ;
continue ;
}
if ( cliVersionFileVersions . length > 1 ) {
logger . warning ( ` Ignoring release ${ release . tag _name } with multiple CLI version marker files. ` ) ;
continue ;
}
if ( cliVersionFileVersions [ 0 ] === cliVersion ) {
return release . tag _name ;
}
}
throw new Error ( ` Failed to find a CodeQL bundle release for CLI version ${ cliVersion } . ` ) ;
}
exports . findCodeQLBundleTagDotcomOnly = findCodeQLBundleTagDotcomOnly ;
2023-01-10 13:16:22 +00:00
async function getCodeQLBundleDownloadURL ( tagName , apiDetails , variant , logger ) {
2023-01-09 14:53:52 +00:00
const codeQLActionRepository = getCodeQLActionRepository ( logger ) ;
const potentialDownloadSources = [
// This GitHub instance, and this Action.
[ apiDetails . url , codeQLActionRepository ] ,
// This GitHub instance, and the canonical Action.
[ apiDetails . url , exports . CODEQL _DEFAULT _ACTION _REPOSITORY ] ,
// GitHub.com, and the canonical Action.
[ util . GITHUB _DOTCOM _URL , exports . CODEQL _DEFAULT _ACTION _REPOSITORY ] ,
] ;
// We now filter out any duplicates.
// Duplicates will happen either because the GitHub instance is GitHub.com, or because the Action is not a fork.
const uniqueDownloadSources = potentialDownloadSources . filter ( ( source , index , self ) => {
return ! self . slice ( 0 , index ) . some ( ( other ) => ( 0 , fast _deep _equal _1 . default ) ( source , other ) ) ;
} ) ;
const codeQLBundleName = getCodeQLBundleName ( ) ;
if ( variant === util . GitHubVariant . GHAE ) {
try {
const release = await api
. getApiClient ( )
. request ( "GET /enterprise/code-scanning/codeql-bundle/find/{tag}" , {
2023-01-10 13:16:22 +00:00
tag : tagName ,
2023-01-09 14:53:52 +00:00
} ) ;
const assetID = release . data . assets [ codeQLBundleName ] ;
if ( assetID !== undefined ) {
const download = await api
. getApiClient ( )
. request ( "GET /enterprise/code-scanning/codeql-bundle/download/{asset_id}" , { asset _id : assetID } ) ;
const downloadURL = download . data . url ;
logger . info ( ` Found CodeQL bundle at GitHub AE endpoint with URL ${ downloadURL } . ` ) ;
return downloadURL ;
}
else {
logger . info ( ` Attempted to fetch bundle from GitHub AE endpoint but the bundle ${ codeQLBundleName } was not found in the assets ${ JSON . stringify ( release . data . assets ) } . ` ) ;
}
}
catch ( e ) {
logger . info ( ` Attempted to fetch bundle from GitHub AE endpoint but got error ${ e } . ` ) ;
}
}
for ( const downloadSource of uniqueDownloadSources ) {
const [ apiURL , repository ] = downloadSource ;
// If we've reached the final case, short-circuit the API check since we know the bundle exists and is public.
if ( apiURL === util . GITHUB _DOTCOM _URL &&
repository === exports . CODEQL _DEFAULT _ACTION _REPOSITORY ) {
break ;
}
const [ repositoryOwner , repositoryName ] = repository . split ( "/" ) ;
try {
const release = await api . getApiClient ( ) . repos . getReleaseByTag ( {
owner : repositoryOwner ,
repo : repositoryName ,
2023-01-10 13:16:22 +00:00
tag : tagName ,
2023-01-09 14:53:52 +00:00
} ) ;
for ( const asset of release . data . assets ) {
if ( asset . name === codeQLBundleName ) {
logger . info ( ` Found CodeQL bundle in ${ downloadSource [ 1 ] } on ${ downloadSource [ 0 ] } with URL ${ asset . url } . ` ) ;
return asset . url ;
}
}
}
catch ( e ) {
logger . info ( ` Looked for CodeQL bundle in ${ downloadSource [ 1 ] } on ${ downloadSource [ 0 ] } but got error ${ e } . ` ) ;
}
}
2023-01-10 13:16:22 +00:00
return ` https://github.com/ ${ exports . CODEQL _DEFAULT _ACTION _REPOSITORY } /releases/download/ ${ tagName } / ${ codeQLBundleName } ` ;
2023-01-09 14:53:52 +00:00
}
2023-01-10 13:16:22 +00:00
function getBundleTagNameFromUrl ( url ) {
const match = url . match ( /\/codeql-bundle-(.*)\// ) ;
if ( match === null || match . length < 2 ) {
throw new Error ( ` Malformed tools url: ${ url } . Tag name could not be inferred ` ) ;
}
return match [ 1 ] ;
}
exports . getBundleTagNameFromUrl = getBundleTagNameFromUrl ;
function convertToSemVer ( version , logger ) {
if ( ! semver . valid ( version ) ) {
logger . debug ( ` Bundle version ${ version } is not in SemVer format. Will treat it as pre-release 0.0.0- ${ version } . ` ) ;
version = ` 0.0.0- ${ version } ` ;
}
const s = semver . clean ( version ) ;
if ( ! s ) {
throw new Error ( ` Bundle version ${ version } is not in SemVer format. ` ) ;
}
return s ;
}
exports . convertToSemVer = convertToSemVer ;
async function getOrFindBundleTagName ( version , logger ) {
if ( version . variant === util . GitHubVariant . DOTCOM ) {
return await findCodeQLBundleTagDotcomOnly ( version . cliVersion , logger ) ;
}
else {
return version . tagName ;
}
}
/**
* Look for a version of the CodeQL tools in the cache which could override the requested CLI version.
*/
async function findOverridingToolsInCache ( requestedCliVersion , logger ) {
const candidates = toolcache
. findAllVersions ( "CodeQL" )
. filter ( util _1 . isGoodVersion )
. map ( ( version ) => ( {
folder : toolcache . find ( "CodeQL" , version ) ,
version ,
} ) )
. filter ( ( { folder } ) => fs . existsSync ( path . join ( folder , "pinned-version" ) ) ) ;
if ( candidates . length === 1 ) {
const candidate = candidates [ 0 ] ;
logger . debug ( ` CodeQL tools version ${ candidate . version } in toolcache overriding version ${ requestedCliVersion } . ` ) ;
return {
codeqlFolder : candidate . folder ,
sourceType : "toolcache" ,
toolsVersion : candidate . version ,
} ;
}
else if ( candidates . length === 0 ) {
logger . debug ( "Did not find any candidate pinned versions of the CodeQL tools in the toolcache." ) ;
}
else {
logger . debug ( "Could not use CodeQL tools from the toolcache since more than one candidate pinned " +
"version was found in the toolcache." ) ;
}
return undefined ;
}
async function getCodeQLSource ( toolsInput , bypassToolcache , defaultCliVersion , apiDetails , variant , logger ) {
2023-01-09 14:53:52 +00:00
if ( toolsInput && toolsInput !== "latest" && ! toolsInput . startsWith ( "http" ) ) {
return {
codeqlTarPath : toolsInput ,
sourceType : "local" ,
toolsVersion : "local" ,
} ;
}
const forceLatestReason =
// We use the special value of 'latest' to prioritize the version in the
// defaults over any pinned cached version.
toolsInput === "latest"
? '"tools: latest" was requested'
: // If the user hasn't requested a particular CodeQL version, then bypass
// the toolcache when the appropriate feature is enabled. This
// allows us to quickly rollback a broken bundle that has made its way
// into the toolcache.
toolsInput === undefined && bypassToolcache
2023-01-10 13:16:22 +00:00
? "a specific version of the CodeQL tools was not requested and the bypass toolcache feature is enabled"
2023-01-09 14:53:52 +00:00
: undefined ;
const forceLatest = forceLatestReason !== undefined ;
if ( forceLatest ) {
logger . debug ( ` Forcing the latest version of the CodeQL tools since ${ forceLatestReason } . ` ) ;
}
2023-01-10 13:16:22 +00:00
/**
* The requested version is:
*
* 1. The one in `defaults.json`, if forceLatest is true.
* 2. The version specified by the tools input URL, if one was provided.
* 3. The default CLI version, otherwise.
* We include a `variant` property to let us verify using the type system that
* `tagName` is only undefined when the variant is Dotcom. This lets us ensure
* that we can always compute `tagName`, either by using the existing tag name
2023-01-12 15:39:26 +00:00
* on enterprise instances, or calling `findCodeQLBundleTagDotcomOnly` on
* Dotcom.
2023-01-10 13:16:22 +00:00
*/
const requestedVersion = forceLatest
? // case 1
{
cliVersion : defaults . cliVersion ,
tagName : defaults . bundleVersion ,
variant ,
}
: toolsInput !== undefined
? // case 2
{
cliVersion : convertToSemVer ( getBundleTagNameFromUrl ( toolsInput ) , logger ) ,
tagName : getBundleTagNameFromUrl ( toolsInput ) ,
url : toolsInput ,
variant ,
}
: // case 3
defaultCliVersion ;
2023-01-09 14:53:52 +00:00
// If we find the specified version, we always use that.
2023-01-10 13:16:22 +00:00
let codeqlFolder = toolcache . find ( "CodeQL" , requestedVersion . cliVersion ) ;
let tagName = requestedVersion [ "tagName" ] ;
2023-01-12 15:39:26 +00:00
if ( ! codeqlFolder ) {
logger . debug ( "Didn't find a version of the CodeQL tools in the toolcache with a version number " +
` exactly matching ${ requestedVersion . cliVersion } . ` ) ;
const allVersions = toolcache . findAllVersions ( "CodeQL" ) ;
logger . debug ( ` Found the following versions of the CodeQL tools in the toolcache: ${ JSON . stringify ( allVersions ) } . ` ) ;
// If there is exactly one version of the CodeQL tools in the toolcache, and that version is
// the form `x.y.z-<tagName>`, then use it.
const candidateVersions = allVersions . filter ( ( version ) => version . startsWith ( ` ${ requestedVersion . cliVersion } - ` ) ) ;
if ( candidateVersions . length === 1 ) {
logger . debug ( "Exactly one candidate version found, using that." ) ;
codeqlFolder = toolcache . find ( "CodeQL" , candidateVersions [ 0 ] ) ;
}
else {
logger . debug ( "Did not find exactly one version of the CodeQL tools starting with the requested version." ) ;
}
}
2023-01-10 13:16:22 +00:00
if ( ! codeqlFolder && ! requestedVersion . cliVersion . startsWith ( "0.0.0" ) ) {
// Fall back to accepting a `0.0.0-<tagName>` version if we didn't find the
// `x.y.z` version. This is to support old versions of the toolcache.
2023-01-12 15:39:26 +00:00
//
// If we are on Dotcom, we will make an HTTP request to the Releases API here
// to find the tag name for the requested version.
2023-01-10 13:16:22 +00:00
tagName =
tagName || ( await getOrFindBundleTagName ( requestedVersion , logger ) ) ;
const fallbackVersion = convertToSemVer ( tagName , logger ) ;
2023-01-12 15:39:26 +00:00
logger . debug ( ` Computed a fallback toolcache version number of ${ fallbackVersion } for CodeQL tools version ` +
` ${ requestedVersion . cliVersion } . ` ) ;
2023-01-10 13:16:22 +00:00
codeqlFolder = toolcache . find ( "CodeQL" , fallbackVersion ) ;
}
2023-01-09 14:53:52 +00:00
if ( codeqlFolder ) {
return {
codeqlFolder ,
sourceType : "toolcache" ,
2023-01-10 13:16:22 +00:00
toolsVersion : requestedVersion . cliVersion ,
2023-01-09 14:53:52 +00:00
} ;
}
2023-01-10 13:16:22 +00:00
logger . debug ( ` Did not find CodeQL tools version ${ requestedVersion . cliVersion } in the toolcache. ` ) ;
// If we don't find the requested version on Enterprise, we may allow a
2023-01-09 14:53:52 +00:00
// different version to save download time if the version hasn't been
// specified explicitly (in which case we always honor it).
2023-01-10 13:16:22 +00:00
if ( variant !== util . GitHubVariant . DOTCOM && ! forceLatest && ! toolsInput ) {
const result = await findOverridingToolsInCache ( requestedVersion . cliVersion , logger ) ;
if ( result !== undefined ) {
return result ;
2023-01-09 14:53:52 +00:00
}
}
return {
2023-01-10 13:16:22 +00:00
codeqlURL : requestedVersion [ "url" ] ||
( await getCodeQLBundleDownloadURL ( tagName || ( await getOrFindBundleTagName ( requestedVersion , logger ) ) , apiDetails , variant , logger ) ) ,
semanticVersion : requestedVersion . cliVersion ,
2023-01-09 14:53:52 +00:00
sourceType : "download" ,
2023-01-10 13:16:22 +00:00
toolsVersion : requestedVersion . cliVersion ,
2023-01-09 14:53:52 +00:00
} ;
}
exports . getCodeQLSource = getCodeQLSource ;
async function downloadCodeQL ( codeqlURL , semanticVersion , apiDetails , tempDir , logger ) {
const parsedCodeQLURL = new URL ( codeqlURL ) ;
const searchParams = new URLSearchParams ( parsedCodeQLURL . search ) ;
const headers = {
accept : "application/octet-stream" ,
} ;
// We only want to provide an authorization header if we are downloading
// from the same GitHub instance the Action is running on.
// This avoids leaking Enterprise tokens to dotcom.
// We also don't want to send an authorization header if there's already a token provided in the URL.
if ( searchParams . has ( "token" ) ) {
logger . debug ( "CodeQL tools URL contains an authorization token." ) ;
}
else if ( codeqlURL . startsWith ( ` ${ apiDetails . url } / ` ) ) {
logger . debug ( "Providing an authorization token to download CodeQL tools." ) ;
headers . authorization = ` token ${ apiDetails . auth } ` ;
}
else {
logger . debug ( "Downloading CodeQL tools without an authorization token." ) ;
}
logger . info ( ` Downloading CodeQL tools from ${ codeqlURL } . This may take a while. ` ) ;
const dest = path . join ( tempDir , ( 0 , uuid _1 . v4 ) ( ) ) ;
const finalHeaders = Object . assign ( { "User-Agent" : "CodeQL Action" } , headers ) ;
const codeqlPath = await toolcache . downloadTool ( codeqlURL , dest , undefined , finalHeaders ) ;
logger . debug ( ` CodeQL bundle download to ${ codeqlPath } complete. ` ) ;
const codeqlExtracted = await toolcache . extractTar ( codeqlPath ) ;
return await toolcache . cacheDir ( codeqlExtracted , "CodeQL" , semanticVersion ) ;
}
exports . downloadCodeQL = downloadCodeQL ;
function getCodeQLURLVersion ( url ) {
const match = url . match ( /\/codeql-bundle-(.*)\// ) ;
if ( match === null || match . length < 2 ) {
throw new Error ( ` Malformed tools url: ${ url } . Version could not be inferred ` ) ;
}
return match [ 1 ] ;
}
exports . getCodeQLURLVersion = getCodeQLURLVersion ;
2023-01-10 13:16:22 +00:00
/**
2023-01-11 15:41:21 +00:00
* Obtains the CodeQL bundle, installs it in the toolcache if appropriate, and extracts it.
2023-01-10 13:16:22 +00:00
*
* @param toolsInput
* @param apiDetails
* @param tempDir
* @param variant
* @param bypassToolcache
* @param defaultCliVersion
* @param logger
* @param checkVersion Whether to check that CodeQL CLI meets the minimum
* version requirement. Must be set to true outside tests.
2023-01-11 15:41:21 +00:00
* @returns the path to the extracted bundle, and the version of the tools
2023-01-10 13:16:22 +00:00
*/
2023-01-11 15:41:21 +00:00
async function setupCodeQLBundle ( toolsInput , apiDetails , tempDir , variant , bypassToolcache , defaultCliVersion , logger ) {
2023-01-10 13:16:22 +00:00
const source = await getCodeQLSource ( toolsInput , bypassToolcache , defaultCliVersion , apiDetails , variant , logger ) ;
let codeqlFolder ;
switch ( source . sourceType ) {
case "local" :
codeqlFolder = await toolcache . extractTar ( source . codeqlTarPath ) ;
break ;
case "toolcache" :
codeqlFolder = source . codeqlFolder ;
logger . debug ( ` CodeQL found in cache ${ codeqlFolder } ` ) ;
break ;
case "download" :
codeqlFolder = await downloadCodeQL ( source . codeqlURL , source . semanticVersion , apiDetails , tempDir , logger ) ;
break ;
default :
util . assertNever ( source ) ;
2023-01-09 14:53:52 +00:00
}
2023-01-10 13:16:22 +00:00
return { codeqlFolder , toolsVersion : source . toolsVersion } ;
2023-01-09 14:53:52 +00:00
}
2023-01-11 15:41:21 +00:00
exports . setupCodeQLBundle = setupCodeQLBundle ;
2023-01-09 14:53:52 +00:00
//# sourceMappingURL=setup-codeql.js.map