index.js (169 lines of code) (raw):

// load modules const core = require('@actions/core') const github = require('@actions/github') // inputs const token = core.getInput('gh_token') // oddly this is not in github.context :/ only the // name is, and the name doesn't even have to be unique. const workflowRef = core.getInput('workflow_ref') const mainBranch = core.getInput('main_branch') const allowOverrideAll = core.getInput('allow_override_all') // contexts const octokit = github.getOctokit(token) const context = github.context // static constants const overrideFlag = 'override_main_branch_checks' const uniqueIdentifier = '<!-- unique_identifier: action_comment_marker -->' // stuff from contextx const workflowName = context.workflow const { owner, repo } = context.repo const prNumber = context.payload.pull_request.number // other consts const commentHeader = "**Workflow Status Tracker**" const commentIntro = ( `The following workflows are failing on ${mainBranch}. ` + "You can make specific workflows not fail by adding " + `\`[ci ${overrideFlag} \$workflow]\` to your PR description ` + `or bypass _all_ by adding \`[ci ${overrideFlag}]\`.` ) const commentTailer = ( "This comment created by the main-branch-check action. " + "It will be removed when no workflows are red on the " + `${mainBranch} branch.` ) async function updateStatusComment(overridden, workflowStatus, workflowUrl) { const level = overridden ? 'warning' : 'failure' const { data: comments } = await octokit.rest.issues.listComments({ owner, repo, issue_number: prNumber, }) let statusComment = comments.find(comment => comment.body.includes(uniqueIdentifier)) if (!statusComment && workflowStatus === "success") { console.log("No existing comment found, and job was successful, nothing to do") return } let commentBody = "" let lines = [] if (statusComment) { [commentBody, _] = statusComment.body.split(uniqueIdentifier) lines = commentBody.trim().split(/\n+/) } else { lines = [commentHeader, commentIntro] } const workflowNameLink = `[${workflowName}](${workflowUrl})` const workflowMarkerPattern = `- ${workflowNameLink}: ` const workflowLine = `${workflowMarkerPattern}${level}` const workflowMatch = `[${workflowName}]` const workflowIndex = lines.findIndex(line => line.includes(workflowMatch)) if (workflowStatus === "failure") { if (workflowIndex !== -1) { // update existing workflow line with new level console.log("Updating workflow in comment") lines[workflowIndex] = workflowLine } else { console.log("Adding new workflow to comment") lines.push(workflowLine) } } else { // If workflow succeeded remove from list if (workflowIndex !== -1) { console.log("Removing successful workflow from comment") lines.splice(workflowIndex, 1) } } // construct final comment // Header should have additional newline lines[0] += "\n" // Intro too lines[1] += "\n" commentBody = lines.join('\n').trim() + `\n\n${uniqueIdentifier}\n${commentTailer}\n` // Update or remove the comment as necessary if (!statusComment) { console.log("Creating comment") await octokit.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: commentBody, }) } else if (!commentBody.match(/^- .+/gm)) { console.log("Removing comment, last workflow passing or no workflows listed") await octokit.rest.issues.deleteComment({ owner, repo, comment_id: statusComment.id, }) } else { console.log("Updating comment with modified workflow list") // Update the comment with the new body await octokit.rest.issues.updateComment({ owner, repo, comment_id: statusComment.id, body: commentBody, }) } } async function getOverrideFlags() { console.log("Gathering PR description...") // Fetch PR description const { data: pr } = await octokit.rest.pulls.get({ owner, repo, pull_number: prNumber, }) let flags = [] const prDesc = pr.body if (prDesc !== null) { const matcher = `\\[ci ${overrideFlag} ?(.*)\\]` const re = new RegExp(matcher, 'gi') const results = [...prDesc.matchAll(re)] for (const result of results) { if (result[1] === "") { if (allowOverrideAll) { flags = flags.concat(["all"]) } else { console.log("Override flag was set for all workflows, but action configured to not allow that. Please specify workflows to override.") } } else { flags = flags.concat([result[1]]) } } } return flags } async function isWorkflowOverridden(flags) { return (flags.includes("all") || flags.includes(workflowName)) } async function run() { console.log("Running action") try { console.log("Checking this is a pull request") // Ensure this action is triggered within a PR context if (!context.payload.pull_request) { core.setFailed('This action must be triggered by a pull request') return } console.log(`owner: ${owner}, repo: ${repo}, pr: ${prNumber}`) console.log("Gathering workflow data...") // Get the current workflow run's ID and use it to fetch its details const start = workflowRef.indexOf("workflows/") + "workflows/".length const end = workflowRef.indexOf("@") const workflowPath = workflowRef.substring(start, end) console.log(`Gathering workflow runs for ${workflowPath}...`) const { data: runs } = await octokit.rest.actions.listWorkflowRuns({ owner, repo, workflow_id: workflowPath, branch: mainBranch, status: 'completed', }) const sortedRuns = runs.workflow_runs.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)) const latestRun = sortedRuns[0] const flags = await getOverrideFlags() const overridden = await isWorkflowOverridden(flags) if (!latestRun) { console.log(`No run of this workflow found on ${mainBranch}, must be new.`) return } console.log("Creating, updating, or removing comment, as necessary") await updateStatusComment(overridden, latestRun.conclusion, latestRun?.html_url) if (latestRun.conclusion !== 'success') { let msg = `Latest run of workflow on ${mainBranch} branch is failing: ${latestRun?.html_url}` if (overridden) { console.log(msg) console.log("Override flag found, not failing the run.") } else { core.setFailed(msg) } } else { console.log(`Latest run of workflow on ${mainBranch} branch is successful: ${latestRun?.html_url}`) } } catch (error) { core.setFailed(`Action failed with error: ${error}`) } } run()