From 293dbb83e9873ccb7a9b60c02f5bf4bb671004ad Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 11 Sep 2025 14:59:51 +0200 Subject: [PATCH] fix(resources): match cr to lf change in pr-build-status.mjs Follows: #14732 Build-bot: skip Test-bot: skip --- .github/workflows/pr-build-status.yml | 384 +++++++++--------- .../build/pr-build-status/pr-build-status.mjs | 2 +- 2 files changed, 193 insertions(+), 193 deletions(-) diff --git a/.github/workflows/pr-build-status.yml b/.github/workflows/pr-build-status.yml index 585a0b19b8..20d8007d9b 100644 --- a/.github/workflows/pr-build-status.yml +++ b/.github/workflows/pr-build-status.yml @@ -1,4 +1,4 @@ -# GENERATED FILE - DO NOT EDIT! +# GENERATED FILE - DO NOT EDIT! # # Keyman is copyright (C) SIL Global. MIT License. # @@ -35,194 +35,194 @@ jobs: id: run_pr_build_status_script uses: actions/github-script@v7 with: - script: | - // This code is copied out of resources/build/pr-build-status/pr-build-status.mjs - // where it is tested. It is copied inline here in order to avoid requiring the - // repository to be checked out, which dramatically reduces the run time of the - // check. - // - // Note: we don't currently look at check runs, only statuses - // - // Verify the following statuses: - // 'user_testing' - // 'API Verification' (github-actions[bot]) - // - // At least 1 of the following statuses must be found: - // 'Test*' (keyman-server), e.g. 'Test Build (Keyman)' - // 'Ubuntu Packaging' (github-actions[bot]) - // - // Ignore the following statuses: - // check/web/file-size - // - - function addLog(message) { - console.log(message); - return message + String.fromCharCode(10); - } - - function addStatus(o, type, context, state) { - if(!o[context]) { - o[context] = {type, state}; - return addLog(`Found ${context}: ${state}`); - } else { - return addLog(`Skipping repeated ${context}: ${state}`); - } - } - - function reduceStatuses(statuses) { - let summary = ''; - const filtered_statuses = statuses.reduce((o, status) => { - if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) { - summary += addStatus(o, 'build', status.context, status.state); - } else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') { - summary += addStatus(o, 'user-test', status.context, status.state); - } else if(status.context == 'API Verification') { - summary += addStatus(o, 'check', status.context, status.state); - } else if(status.context == 'Ubuntu Packaging') { - summary += addStatus(o, 'build', status.context, status.state); - } else if(status.context == 'check/web/file-size') { - // Ignore check/web/file-size -- we won't block automerge for this at this point - summary += addLog(`Skipping ${status.context}`); - } else { - // We fail with an 'unknown status' response if we get a new status check - // so we can be sure we are not skipping known status checks - summary += addLog(`Found unknown ${status.context}: ${status.state}`); - o[status.context] = {type: 'unknown', state: status.state}; - } - return o; - - }, {}); - return { filtered_statuses, summary }; - } - - // - // Given the collection of status checks we care about, return - // an aggregate status -- error, failure, pending, or success, - // and a summary description - // - function calculateFinalStatus(filtered_statuses, summary) { - const counts = {}; - let hasBuilds = false; - for(const context of Object.keys(filtered_statuses)) { - const { state, type } = filtered_statuses[context]; - if(type == 'unknown') { - // We special-case for unknown status checks, and never permit them - console.error(`An unknown context ${context} was found, cannot calculate build status.`); - return [ - 'error', `An unknown context ${context} was found, cannot calculate build status.`, summary - ]; - } - if(type == 'build') { - hasBuilds = true; - } - counts[state] = counts[state] ? counts[state] + 1 : 1; - } - - // If we do not have any statuses yet, we wait - if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) { - console.log(`Builds not yet triggered`); - return ['pending', 'Builds have not yet been triggered ⌛', summary]; - } - - const state = - counts.error ? 'error' : - counts.failure ? 'failure' : - counts.pending ? 'pending' : - 'success'; - - let description = ''; - function appendDescription(count, state) { - if(!count) return; - if(description != '') description += '; '; - description += `${count} check${count == 1 ? '' : 's'} ${state}`; - } - appendDescription(counts.error, 'in an error state ❌'); - appendDescription(counts.failure, 'failed ❌'); - appendDescription(counts.pending, 'pending ⌛'); - appendDescription(counts.success, 'completed successfully ✅'); - - console.log(`Finished filtering and reviewing status checks: ${state}: ${description}`); - return [ state, description, summary ]; - } - - async function getCommitStatuses(github, owner, repo, sha) { - console.log(`Getting commit statuses for ${sha}`); - const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', { - owner, - repo, - sha, - headers: { - 'X-GitHub-Api-Version': '2022-11-28' - } - }); - return statuses; - } - - async function getCommitCheckRuns(github, owner, repo, sha) { - console.log(`Getting commit check runs for ${sha}`); - const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', { - owner, - repo, - sha, - headers: { - 'X-GitHub-Api-Version': '2022-11-28' - } - }); - return statuses; - } - - function calculateCheckResult(statuses) { - if(!Array.isArray(statuses)) { - return ['error', 'Failed to retrieve status checks from GitHub ❌', 'statuses is not an array']; - } - - const { filtered_statuses, summary } = reduceStatuses(statuses); - return calculateFinalStatus(filtered_statuses, summary); - } - - async function test(github, owner, repo, sha) { - // Get statuses from sha - const statuses = await getCommitStatuses(github, owner, repo, sha); - return calculateCheckResult(statuses); - } - - async function createCheck(github, owner, repo, sha) { - const check = await github.rest.checks.create({ - owner, - repo, - head_sha: sha, - name: 'Build Outcome', - status: 'in_progress', - }); - return check.data.id; - } - - async function updateCheck(github, owner, repo, checkRunId, status, description, summary) { - const checkStatus = status == 'pending' ? 'in_progress' : 'completed'; - const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure'); - - await github.rest.checks.update({ - owner, - repo, - check_run_id: checkRunId, - status: checkStatus, - conclusion, - output: { - title: description, - summary - } - }); - } - - const { owner, repo } = context.repo; - const sha = - context.payload?.check_suite?.sha || /* check run completed */ - context.payload?.inputs?.commit || /* manual run */ - context.payload?.after || /* push */ - context.payload?.commit?.sha || /* status */ - context.sha; /* probably 'master'! */ - - const checkRunId = await createCheck(github, owner, repo, sha); - const res = await test(github, owner, repo, sha); - await updateCheck(github, owner, repo, checkRunId, res[0], res[1], res[2]); - + script: | + // This code is copied out of resources/build/pr-build-status/pr-build-status.mjs + // where it is tested. It is copied inline here in order to avoid requiring the + // repository to be checked out, which dramatically reduces the run time of the + // check. + // + // Note: we don't currently look at check runs, only statuses + // + // Verify the following statuses: + // 'user_testing' + // 'API Verification' (github-actions[bot]) + // + // At least 1 of the following statuses must be found: + // 'Test*' (keyman-server), e.g. 'Test Build (Keyman)' + // 'Ubuntu Packaging' (github-actions[bot]) + // + // Ignore the following statuses: + // check/web/file-size + // + + function addLog(message) { + console.log(message); + return message + String.fromCharCode(10); + } + + function addStatus(o, type, context, state) { + if(!o[context]) { + o[context] = {type, state}; + return addLog(`Found ${context}: ${state}`); + } else { + return addLog(`Skipping repeated ${context}: ${state}`); + } + } + + function reduceStatuses(statuses) { + let summary = ''; + const filtered_statuses = statuses.reduce((o, status) => { + if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) { + summary += addStatus(o, 'build', status.context, status.state); + } else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') { + summary += addStatus(o, 'user-test', status.context, status.state); + } else if(status.context == 'API Verification') { + summary += addStatus(o, 'check', status.context, status.state); + } else if(status.context == 'Ubuntu Packaging') { + summary += addStatus(o, 'build', status.context, status.state); + } else if(status.context == 'check/web/file-size') { + // Ignore check/web/file-size -- we won't block automerge for this at this point + summary += addLog(`Skipping ${status.context}`); + } else { + // We fail with an 'unknown status' response if we get a new status check + // so we can be sure we are not skipping known status checks + summary += addLog(`Found unknown ${status.context}: ${status.state}`); + o[status.context] = {type: 'unknown', state: status.state}; + } + return o; + + }, {}); + return { filtered_statuses, summary }; + } + + // + // Given the collection of status checks we care about, return + // an aggregate status -- error, failure, pending, or success, + // and a summary description + // + function calculateFinalStatus(filtered_statuses, summary) { + const counts = {}; + let hasBuilds = false; + for(const context of Object.keys(filtered_statuses)) { + const { state, type } = filtered_statuses[context]; + if(type == 'unknown') { + // We special-case for unknown status checks, and never permit them + console.error(`An unknown context ${context} was found, cannot calculate build status.`); + return [ + 'error', `An unknown context ${context} was found, cannot calculate build status.`, summary + ]; + } + if(type == 'build') { + hasBuilds = true; + } + counts[state] = counts[state] ? counts[state] + 1 : 1; + } + + // If we do not have any statuses yet, we wait + if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) { + console.log(`Builds not yet triggered`); + return ['pending', 'Builds have not yet been triggered ⌛', summary]; + } + + const state = + counts.error ? 'error' : + counts.failure ? 'failure' : + counts.pending ? 'pending' : + 'success'; + + let description = ''; + function appendDescription(count, state) { + if(!count) return; + if(description != '') description += '; '; + description += `${count} check${count == 1 ? '' : 's'} ${state}`; + } + appendDescription(counts.error, 'in an error state ❌'); + appendDescription(counts.failure, 'failed ❌'); + appendDescription(counts.pending, 'pending ⌛'); + appendDescription(counts.success, 'completed successfully ✅'); + + console.log(`Finished filtering and reviewing status checks: ${state}: ${description}`); + return [ state, description, summary ]; + } + + async function getCommitStatuses(github, owner, repo, sha) { + console.log(`Getting commit statuses for ${sha}`); + const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', { + owner, + repo, + sha, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }); + return statuses; + } + + async function getCommitCheckRuns(github, owner, repo, sha) { + console.log(`Getting commit check runs for ${sha}`); + const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', { + owner, + repo, + sha, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }); + return statuses; + } + + function calculateCheckResult(statuses) { + if(!Array.isArray(statuses)) { + return ['error', 'Failed to retrieve status checks from GitHub ❌', 'statuses is not an array']; + } + + const { filtered_statuses, summary } = reduceStatuses(statuses); + return calculateFinalStatus(filtered_statuses, summary); + } + + async function test(github, owner, repo, sha) { + // Get statuses from sha + const statuses = await getCommitStatuses(github, owner, repo, sha); + return calculateCheckResult(statuses); + } + + async function createCheck(github, owner, repo, sha) { + const check = await github.rest.checks.create({ + owner, + repo, + head_sha: sha, + name: 'Build Outcome', + status: 'in_progress', + }); + return check.data.id; + } + + async function updateCheck(github, owner, repo, checkRunId, status, description, summary) { + const checkStatus = status == 'pending' ? 'in_progress' : 'completed'; + const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure'); + + await github.rest.checks.update({ + owner, + repo, + check_run_id: checkRunId, + status: checkStatus, + conclusion, + output: { + title: description, + summary + } + }); + } + + const { owner, repo } = context.repo; + const sha = + context.payload?.check_suite?.sha || /* check run completed */ + context.payload?.inputs?.commit || /* manual run */ + context.payload?.after || /* push */ + context.payload?.commit?.sha || /* status */ + context.sha; /* probably 'master'! */ + + const checkRunId = await createCheck(github, owner, repo, sha); + const res = await test(github, owner, repo, sha); + await updateCheck(github, owner, repo, checkRunId, res[0], res[1], res[2]); + diff --git a/resources/build/pr-build-status/pr-build-status.mjs b/resources/build/pr-build-status/pr-build-status.mjs index f8f1b62c1c..137a17cea5 100644 --- a/resources/build/pr-build-status/pr-build-status.mjs +++ b/resources/build/pr-build-status/pr-build-status.mjs @@ -29,7 +29,7 @@ function addLog(message) { console.log(message); - return message + String.fromCharCode(13); + return message + String.fromCharCode(10); } function addStatus(o, type, context, state) {