From 7953c0b4a4e59e744a5afc690ff40311d40e9cbe Mon Sep 17 00:00:00 2001 From: Gregers Gram Rygg Date: Fri, 4 Oct 2024 13:17:21 +0200 Subject: [PATCH] workflow: Add check-main-status workflow The workflow checks the result of check runs on the latest commit on the main branch. When checks on main are failing the check will fail. If main is pending the check will retry every 5 mintues for an hour. This prevents introducing more potenisal issues when main is already broken. To skip the check when making a fix for main, add the fix-ci label to the PR. Signed-off-by: Gregers Gram Rygg --- .github/workflows/check-main-status.yml | 53 ++++++++++++++++++++++ .github/workflows/dfu_check.yml | 1 + .github/workflows/lib/checkMain.mjs | 59 +++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 .github/workflows/check-main-status.yml create mode 100644 .github/workflows/lib/checkMain.mjs diff --git a/.github/workflows/check-main-status.yml b/.github/workflows/check-main-status.yml new file mode 100644 index 00000000..634ab22a --- /dev/null +++ b/.github/workflows/check-main-status.yml @@ -0,0 +1,53 @@ +name: Check Main Branch Status +on: + pull_request: + merge_group: + workflow_dispatch: + inputs: + pr_number: + description: "Pull Request number" + required: true + +jobs: + check-main-status: + runs-on: ubuntu-24.04 + env: + PR_NUMBER: ${{ github.event.number || github.event.inputs.pr_number }} + steps: + - uses: actions/checkout@v3 + with: + sparse-checkout: | + .github/workflows/lib/ + - run: pwd + - run: ls -lap + - run: ls -lap .github/workflows/lib/ + - uses: actions/github-script@v7 + name: Check if PR has 'fix-ci' label + id: has_fix_ci_label + with: + retries: 3 + script: | + const checkMain = require('./.github/workflows/lib/checkMain.js'); + + const result = checkMain.hasFixCiLabel(github, context.owner, context.repo, process.env.PR_NUMBER); + console.log(`Has 'fix-ci' label: ${result}`); + + return result; + + - uses: actions/github-script@v7 + name: Ensure Main Branch Check Runs Are Successful + if: ${{ steps.has_fix_ci_label.outputs.result == 'false' }} + with: + retries: 3 + script: | + const checkMain = require('./.github/workflows/lib/checkMain.js'); + + function delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + try { + await checkMain.ensureChecksRunsSuccessful(delay, github, context.owner, context.repo); + } catch (error) { + core.setFailed(error.message); + } diff --git a/.github/workflows/dfu_check.yml b/.github/workflows/dfu_check.yml index 18e415fa..864656f7 100644 --- a/.github/workflows/dfu_check.yml +++ b/.github/workflows/dfu_check.yml @@ -2,6 +2,7 @@ name: DFU image compatibility check on: workflow_call: + merge_group: push: branches: - main diff --git a/.github/workflows/lib/checkMain.mjs b/.github/workflows/lib/checkMain.mjs new file mode 100644 index 00000000..1b5ffbb2 --- /dev/null +++ b/.github/workflows/lib/checkMain.mjs @@ -0,0 +1,59 @@ +module.exports = { + async hasFixCiLabel(octokit, owner, repo, prNumber) { + const labels = await octokit.issues.listLabelsOnIssue({ + owner, + repo, + issue_number: prNumber, + }); + + return labels.data.map(label => label.name).includes('fix-ci'); + }, + + async ensureChecksRunsSuccessful(delay, octokit, owner, repo) { + let retryPending = 12; // Retry for up to 1 hour (12 retries with 5 minutes interval) + + while (retryPending > 0) { + const mainRef = await octokit.git.getRef({ + owner, + repo, + ref: 'heads/main', + }); + + const mainSha = mainRef.data.object.sha; + console.log(`Main branch SHA: ${mainSha}`); + + let checkRuns = await octokit.checks.listForRef({ + owner, + repo, + ref: mainSha, + }); + + console.log(`Check runs (${checkRuns.data.total_count}):`); + for (let run of checkRuns.data.check_runs) { + console.log(`- ${run.name}`); + console.log(`\tstatus: ${run.status}`); + console.log(`\tconclusion: ${run.conclusion}`); + } + + const failedChecks = checkRuns.data.check_runs.filter(run => run.conclusion !== null && run.conclusion !== 'success'); + const pendingChecks = checkRuns.data.check_runs.filter(run => run.status !== 'completed'); + + if (failedChecks.length > 0) { + throw new Error('Cannot merge: The main branch has failing checks.'); + } else if (pendingChecks.length === 0) { + console.log('Main branch is healthy.'); + break; + } else { + console.log('Checks are still pending.'); + + retryPending--; + if (retryPending === 0) { + throw new Error('Timeout waiting for pending checks on main branch.'); + } + + console.log('Waiting for 5 minutes before retrying...'); + await delay(5 * 60 * 1000); // Wait for 5 minutes + } + } + }, +};