2022-11-22 16:47:27 +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 ;
} ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2022-11-23 12:43:32 +00:00
exports . getCategoryInputOrThrow = exports . getStepsCallingAction = exports . getWorkflowRunID = exports . getWorkflowPath = exports . getWorkflow = exports . formatWorkflowCause = exports . formatWorkflowErrors = exports . validateWorkflow = exports . getWorkflowErrors = exports . WorkflowErrors = exports . patternIsSuperset = void 0 ;
2022-11-22 16:47:27 +00:00
const fs = _ _importStar ( require ( "fs" ) ) ;
const path = _ _importStar ( require ( "path" ) ) ;
const core = _ _importStar ( require ( "@actions/core" ) ) ;
const yaml = _ _importStar ( require ( "js-yaml" ) ) ;
const api = _ _importStar ( require ( "./api-client" ) ) ;
const util _1 = require ( "./util" ) ;
function isObject ( o ) {
return o !== null && typeof o === "object" ;
}
const GLOB _PATTERN = new RegExp ( "(\\*\\*?)" ) ;
function escapeRegExp ( string ) {
return string . replace ( /[.*+?^${}()|[\]\\]/g , "\\$&" ) ; // $& means the whole matched string
}
function patternToRegExp ( value ) {
return new RegExp ( ` ^ ${ value
. toString ( )
. split ( GLOB _PATTERN )
. reduce ( function ( arr , cur ) {
if ( cur === "**" ) {
arr . push ( ".*?" ) ;
}
else if (cur === "*") {
arr.push("[^/]*?");
}
else if (cur) {
arr.push(escapeRegExp(cur));
}
return arr;
}, [])
.join("")} $ ` ) ;
}
// this function should return true if patternA is a superset of patternB
// e.g: * is a superset of main-* but main-* is not a superset of *.
function patternIsSuperset ( patternA , patternB ) {
return patternToRegExp ( patternA ) . test ( patternB ) ;
}
exports . patternIsSuperset = patternIsSuperset ;
function branchesToArray ( branches ) {
if ( typeof branches === "string" ) {
return [ branches ] ;
}
if ( Array . isArray ( branches ) ) {
if ( branches . length === 0 ) {
return "**" ;
}
return branches ;
}
return "**" ;
}
function toCodedErrors ( errors ) {
2022-11-22 16:57:52 +00:00
return Object . entries ( errors ) . reduce ( ( acc , [ code , message ] ) => {
acc [ code ] = { message , code } ;
2022-11-22 16:47:27 +00:00
return acc ;
} , { } ) ;
}
// code to send back via status report
// message to add as a warning annotation to the run
exports . WorkflowErrors = toCodedErrors ( {
MismatchedBranches : ` Please make sure that every branch in on.pull_request is also in on.push so that Code Scanning can compare pull requests against the state of the base branch. ` ,
MissingPushHook : ` Please specify an on.push hook so that Code Scanning can compare pull requests against the state of the base branch. ` ,
PathsSpecified : ` Using on.push.paths can prevent Code Scanning annotating new alerts in your pull requests. ` ,
PathsIgnoreSpecified : ` Using on.push.paths-ignore can prevent Code Scanning annotating new alerts in your pull requests. ` ,
CheckoutWrongHead : ` git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results. ` ,
} ) ;
function getWorkflowErrors ( doc ) {
var _a , _b , _c , _d , _e ;
const errors = [ ] ;
const jobName = process . env . GITHUB _JOB ;
if ( jobName ) {
const job = ( _a = doc === null || doc === void 0 ? void 0 : doc . jobs ) === null || _a === void 0 ? void 0 : _a [ jobName ] ;
const steps = job === null || job === void 0 ? void 0 : job . steps ;
if ( Array . isArray ( steps ) ) {
for ( const step of steps ) {
// this was advice that we used to give in the README
// we actually want to run the analysis on the merge commit
// to produce results that are more inline with expectations
// (i.e: this is what will happen if you merge this PR)
// and avoid some race conditions
if ( ( step === null || step === void 0 ? void 0 : step . run ) === "git checkout HEAD^2" ) {
errors . push ( exports . WorkflowErrors . CheckoutWrongHead ) ;
break ;
}
}
}
}
let missingPush = false ;
if ( doc . on === undefined ) {
// this is not a valid config
}
else if ( typeof doc . on === "string" ) {
if ( doc . on === "pull_request" ) {
missingPush = true ;
}
}
else if ( Array . isArray ( doc . on ) ) {
const hasPush = doc . on . includes ( "push" ) ;
const hasPullRequest = doc . on . includes ( "pull_request" ) ;
if ( hasPullRequest && ! hasPush ) {
missingPush = true ;
}
}
else if ( isObject ( doc . on ) ) {
const hasPush = Object . prototype . hasOwnProperty . call ( doc . on , "push" ) ;
const hasPullRequest = Object . prototype . hasOwnProperty . call ( doc . on , "pull_request" ) ;
if ( ! hasPush && hasPullRequest ) {
missingPush = true ;
}
if ( hasPush && hasPullRequest ) {
const paths = ( _b = doc . on . push ) === null || _b === void 0 ? void 0 : _b . paths ;
// if you specify paths or paths-ignore you can end up with commits that have no baseline
// if they didn't change any files
// currently we cannot go back through the history and find the most recent baseline
if ( Array . isArray ( paths ) && paths . length > 0 ) {
errors . push ( exports . WorkflowErrors . PathsSpecified ) ;
}
const pathsIgnore = ( _c = doc . on . push ) === null || _c === void 0 ? void 0 : _c [ "paths-ignore" ] ;
if ( Array . isArray ( pathsIgnore ) && pathsIgnore . length > 0 ) {
errors . push ( exports . WorkflowErrors . PathsIgnoreSpecified ) ;
}
}
// if doc.on.pull_request is null that means 'all branches'
// if doc.on.pull_request is undefined that means 'off'
// we only want to check for mismatched branches if pull_request is on.
if ( doc . on . pull _request !== undefined ) {
const push = branchesToArray ( ( _d = doc . on . push ) === null || _d === void 0 ? void 0 : _d . branches ) ;
if ( push !== "**" ) {
const pull _request = branchesToArray ( ( _e = doc . on . pull _request ) === null || _e === void 0 ? void 0 : _e . branches ) ;
if ( pull _request !== "**" ) {
const difference = pull _request . filter ( ( value ) => ! push . some ( ( o ) => patternIsSuperset ( o , value ) ) ) ;
if ( difference . length > 0 ) {
// there are branches in pull_request that may not have a baseline
// because we are not building them on push
errors . push ( exports . WorkflowErrors . MismatchedBranches ) ;
}
}
else if ( push . length > 0 ) {
// push is set up to run on a subset of branches
// and you could open a PR against a branch with no baseline
errors . push ( exports . WorkflowErrors . MismatchedBranches ) ;
}
}
}
}
if ( missingPush ) {
errors . push ( exports . WorkflowErrors . MissingPushHook ) ;
}
return errors ;
}
exports . getWorkflowErrors = getWorkflowErrors ;
async function validateWorkflow ( ) {
let workflow ;
try {
workflow = await getWorkflow ( ) ;
}
catch ( e ) {
return ` error: getWorkflow() failed: ${ String ( e ) } ` ;
}
let workflowErrors ;
try {
workflowErrors = getWorkflowErrors ( workflow ) ;
}
catch ( e ) {
return ` error: getWorkflowErrors() failed: ${ String ( e ) } ` ;
}
if ( workflowErrors . length > 0 ) {
let message ;
try {
message = formatWorkflowErrors ( workflowErrors ) ;
}
catch ( e ) {
return ` error: formatWorkflowErrors() failed: ${ String ( e ) } ` ;
}
core . warning ( message ) ;
}
return formatWorkflowCause ( workflowErrors ) ;
}
exports . validateWorkflow = validateWorkflow ;
function formatWorkflowErrors ( errors ) {
const issuesWere = errors . length === 1 ? "issue was" : "issues were" ;
const errorsList = errors . map ( ( e ) => e . message ) . join ( " " ) ;
return ` ${ errors . length } ${ issuesWere } detected with this workflow: ${ errorsList } ` ;
}
exports . formatWorkflowErrors = formatWorkflowErrors ;
function formatWorkflowCause ( errors ) {
if ( errors . length === 0 ) {
return undefined ;
}
return errors . map ( ( e ) => e . code ) . join ( "," ) ;
}
exports . formatWorkflowCause = formatWorkflowCause ;
async function getWorkflow ( ) {
const relativePath = await getWorkflowPath ( ) ;
const absolutePath = path . join ( ( 0 , util _1 . getRequiredEnvParam ) ( "GITHUB_WORKSPACE" ) , relativePath ) ;
return yaml . load ( fs . readFileSync ( absolutePath , "utf-8" ) ) ;
}
exports . getWorkflow = getWorkflow ;
/**
* Get the path of the currently executing workflow.
*/
async function getWorkflowPath ( ) {
const repo _nwo = ( 0 , util _1 . getRequiredEnvParam ) ( "GITHUB_REPOSITORY" ) . split ( "/" ) ;
const owner = repo _nwo [ 0 ] ;
const repo = repo _nwo [ 1 ] ;
const run _id = Number ( ( 0 , util _1 . getRequiredEnvParam ) ( "GITHUB_RUN_ID" ) ) ;
const apiClient = api . getApiClient ( ) ;
const runsResponse = await apiClient . request ( "GET /repos/:owner/:repo/actions/runs/:run_id?exclude_pull_requests=true" , {
owner ,
repo ,
run _id ,
} ) ;
const workflowUrl = runsResponse . data . workflow _url ;
const workflowResponse = await apiClient . request ( ` GET ${ workflowUrl } ` ) ;
return workflowResponse . data . path ;
}
exports . getWorkflowPath = getWorkflowPath ;
/**
* Get the workflow run ID.
*/
function getWorkflowRunID ( ) {
const workflowRunID = parseInt ( ( 0 , util _1 . getRequiredEnvParam ) ( "GITHUB_RUN_ID" ) , 10 ) ;
if ( Number . isNaN ( workflowRunID ) ) {
throw new Error ( "GITHUB_RUN_ID must define a non NaN workflow run ID" ) ;
}
return workflowRunID ;
}
exports . getWorkflowRunID = getWorkflowRunID ;
2022-11-23 12:43:32 +00:00
function getStepsCallingAction ( job , actionName ) {
2022-11-22 18:15:28 +00:00
const steps = job . steps ;
if ( ! Array . isArray ( steps ) ) {
2022-11-23 12:43:32 +00:00
throw new Error ( ` Could not get steps calling ${ actionName } since job.steps was not an array. ` ) ;
2022-11-22 18:15:28 +00:00
}
2022-11-23 12:43:32 +00:00
return steps . filter ( ( step ) => { var _a ; return ( _a = step . uses ) === null || _a === void 0 ? void 0 : _a . includes ( actionName ) ; } ) ;
2022-11-22 18:15:28 +00:00
}
2022-11-23 12:43:32 +00:00
exports . getStepsCallingAction = getStepsCallingAction ;
2022-11-22 18:26:14 +00:00
/**
2022-11-23 12:43:32 +00:00
* Makes a best effort attempt to retrieve the value of a particular input with which
* an Action in the workflow would be invoked.
2022-11-22 18:26:14 +00:00
*
2022-11-23 12:43:32 +00:00
* @returns the value of the input, or undefined if no such input is passed to the Action
* @throws an error if the value of the input could not be determined, or we could not
* determine that no such input is passed to the Action.
2022-11-22 18:26:14 +00:00
*/
2022-11-23 12:43:32 +00:00
function getInputOrThrow ( workflow , actionName , inputName , matrixVars ) {
2022-11-22 18:15:28 +00:00
if ( ! workflow . jobs ) {
2022-11-23 12:43:32 +00:00
throw new Error ( ` Could not get ${ inputName } input to ${ actionName } since the workflow has no jobs. ` ) ;
2022-11-22 18:15:28 +00:00
}
2022-11-23 12:43:32 +00:00
const inputs = Object . values ( workflow . jobs )
. map ( ( job ) => getStepsCallingAction ( job , actionName ) . map ( ( step ) => { var _a ; return ( _a = step . with ) === null || _a === void 0 ? void 0 : _a [ inputName ] ; } ) )
2022-11-22 18:15:28 +00:00
. flat ( )
2022-11-23 12:43:32 +00:00
. filter ( ( input ) => input !== undefined )
. map ( ( input ) => input ) ;
if ( inputs . length === 0 ) {
2022-11-22 18:15:28 +00:00
return undefined ;
}
2022-11-23 12:43:32 +00:00
if ( ! inputs . every ( ( input ) => input === inputs [ 0 ] ) ) {
throw new Error ( ` Could not get ${ inputName } input to ${ actionName } since there were multiple steps calling ` +
` ${ actionName } with different values for ${ inputName } . ` ) ;
2022-11-22 18:15:28 +00:00
}
2022-11-22 18:26:14 +00:00
// Make a basic attempt to substitute matrix variables
// First normalize by removing whitespace
2022-11-23 12:43:32 +00:00
let input = inputs [ 0 ] . replace ( /\${{\s+/ , "${{" ) . replace ( /\s+}}/ , "}}" ) ;
2022-11-22 18:26:14 +00:00
for ( const [ key , value ] of Object . entries ( matrixVars ) ) {
2022-11-23 12:43:32 +00:00
input = input . replace ( ` \$ {{matrix. ${ key } }} ` , value ) ;
2022-11-22 18:26:14 +00:00
}
2022-11-23 12:43:32 +00:00
if ( input . includes ( "${{" ) ) {
throw new Error ( ` Could not get ${ inputName } input to ${ actionName } since it contained an unrecognized dynamic value. ` ) ;
2022-11-22 18:15:28 +00:00
}
2022-11-23 12:43:32 +00:00
return input ;
}
/**
* Makes a best effort attempt to retrieve the category input for the particular job,
* given a set of matrix variables.
*
* @returns the category input, or undefined if the category input is not defined
* @throws an error if the category input could not be determined
*/
function getCategoryInputOrThrow ( workflow , matrixVars ) {
return getInputOrThrow ( workflow , "github/codeql-action/analyze" , "category" , matrixVars ) ;
2022-11-22 18:15:28 +00:00
}
2022-11-23 12:43:32 +00:00
exports . getCategoryInputOrThrow = getCategoryInputOrThrow ;
2022-11-22 16:47:27 +00:00
//# sourceMappingURL=workflow.js.map