diff --git a/.github/actions/label/build.js b/.github/actions/label/build.js new file mode 100644 index 00000000000..1a05c417fa0 --- /dev/null +++ b/.github/actions/label/build.js @@ -0,0 +1,175 @@ +/** + * Javascript module for the label action. + */ + +const [owner, repo] = ["gear-tech", "gear"]; +const { LABEL, REF, HEAD_SHA } = process.env; +const linux = LABEL === "A0-pleasereview"; +const checks = linux ? ["linux", "win-cross"] : ["x86"]; +const workflow_id = linux + ? ".github/workflows/build.yml" + : ".github/workflows/build-macos.yml"; + +/** + * Sleep for ms milliseconds. + **/ +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); + +/** + * If skipping this action. + **/ +const skip = async ({ github }) => { + const { + data: { check_runs }, + } = await github.rest.checks.listForRef({ + owner, + repo, + ref: REF, + }); + + const runs = linux + ? check_runs.filter((run) => run.name === "build" || run.name === "build / linux") + : check_runs.filter((run) => run.name === "build / macox-x86"); + + if (runs.length === 0) return false; + for (run of runs) { + if (run.conclusion !== "skipped") return true; + } + + return false; +}; + +/** + * Create build checks. + **/ +const createChecks = async ({ core, github }) => { + let status = {}; + for (check of checks) { + const { data: res } = await github.rest.checks.create({ + owner, + repo, + name: `build / ${check}`, + head_sha: HEAD_SHA, + }); + + core.info(`Created check ${check}`); + status[check] = res; + } + + return status; +}; + +/** + * Dispatch the target workflow. + */ +const dispatchWorkflow = async ({ core, github }) => { + await github.rest.actions.createWorkflowDispatch({ + owner, + repo, + workflow_id, + ref: REF, + }); + + await sleep(10000); + + // Get the target workflow run + const { + data: { workflow_runs }, + } = await github.rest.actions.listWorkflowRuns({ + owner, + repo, + workflow_id, + head_sha: HEAD_SHA, + }); + + if (workflow_runs.length != 1) { + core.setFailed(`Incorrect workflow runs`); + return; + } + + return workflow_runs[0]; +}; + +/// List jobs of workflow run. +const listJobs = async ({ github, core, run_id }) => { + const { + data: { jobs }, + } = await github.rest.actions.listJobsForWorkflowRun({ + owner, + repo, + run_id, + }); + + if (jobs.length === 0) { + core.setFailed(`Empty jobs from dispatched workflow`); + return; + } + + const requiredJobs = jobs.filter((job) => checks.includes(job.name)); + if (requiredJobs.length !== checks.length) { + core.setFailed(`Incorrect count for disptached jobs`); + return; + } + + return requiredJobs; +}; + +/** + * The main function. + **/ +module.exports = async ({ github, core }) => { + if (await skip({ github })) { + core.info("Build has already been processed."); + return; + } + + const run = await dispatchWorkflow({ core, github }); + let labelChecks = await createChecks({ core, github }); + + while (true) { + const jobs = await listJobs({ github, core, run_id: run.id }); + completed = jobs.filter((job) => job.status === "completed").length; + + for (job of jobs) { + let checkJob = labelChecks[job.name]; + if ( + checkJob.status !== job.status || + checkJob.conclusion !== job.conclusion + ) { + core.info( + `Updating check ${job.name}, status: ${job.status}, conclusion: ${job.conclusion}` + ); + + let { status, conclusion } = job; + + let data = { + owner, + repo, + check_run_id: checkJob.id, + status, + output: { + title: `Build ${job.name}`, + summary: `ref ${job.html_url}`, + }, + }; + + labelChecks[job.name].status = status; + if (conclusion) { + data.conclusion = conclusion; + labelChecks[job.name].conclusion = conclusion; + } + + await github.rest.checks.update(data); + } else { + continue; + } + } + + if (completed === checks.length) { + core.info("All jobs completed."); + break; + } else { + await sleep(10000); + } + } +}; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 954c8137883..0aaf814d993 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -247,7 +247,7 @@ jobs: env: CARGO_BUILD_TARGET: x86_64-pc-windows-msvc - macos-x86: + macos: needs: linux if: ${{ always() && inputs.macos }} uses: ./.github/workflows/build-macos.yml diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index d6f0ff658b4..840adc07d1e 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -1,3 +1,4 @@ +# Prefix A for adjusting the new created checks into this check suite. name: Label on: @@ -26,100 +27,5 @@ jobs: REF: ${{ github.head_ref || github.ref_name }} with: script: | - const { LABEL, REF, HEAD_SHA } = process.env; - const [owner, repo] = ["gear-tech", "gear"]; - const linux = (LABEL === "A0-pleasereview"); - - // List the latest check runs - const { - data: { check_runs } - } = await github.rest.checks.listForRef({ - owner, - repo, - ref: REF, - }); - - // Filter the traget skipped workflow run. - const runs = linux - ?check_runs.filter((run) => run.name === "build") - :check_runs.filter((run) => run.name === "build / macox-x86") - - if (runs.length === 0) return; - if (runs[0].conclusion !== "skipped") return; - - /** - * The main logic starts from here. - **/ - - // Dispatch target workflow. - const workflow_id = linux - ?".github/workflows/build.yml" - :".github/workflows/build-macos.yml"; - - const sleep = ms => new Promise(r => setTimeout(r, ms)); - await github.rest.actions.createWorkflowDispatch({ - owner, - repo, - workflow_id, - ref: REF, - }); - - await sleep(10000); - - // Create check and update status - const { data: { workflow_runs } } = await github.rest.actions.listWorkflowRuns({ - owner, - repo, - workflow_id, - head_sha: HEAD_SHA, - }); - - console.log(workflow_runs); - - if (workflow_runs.length != 1) { - core.setFailed(`Empty workflow runs`); - return; - } - - const { data: { jobs } } = await github.rest.actions.listJobsForWorkflowRun({ - owner, - repo, - run_id: workflow_runs[0].id - }); - - console.log(jobs); - - if (jobs.length === 0) { - core.setFailed(`Empty jobs from dispatched workflow`); - return; - } - - const checks = linux - ?["linux", "win-cross"] - :["macos-x86"]; - - for (check of checks) { - let runs = jobs.filter((run) => run.name === check); - if (runs.length !== 1) { - core.setFailed(`Could not find action ${check} from ${workflow_runs}`); - return; - } - - const { - status, - conclusion, - id: external_id, - started_at, - url: details_url, - } = runs[0]; - - const { data: res } = await github.rest.checks.create({ - owner, - repo, - name: `build / ${check}`, - head_sha: HEAD_SHA, - details_url, - // external_id, - }); - console.log(res); - } + const script = require('./.github/actions/label/build.js'); + await script({ github, core });