Post build links to pull request #134
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Post build links to pull request | |
on: | |
workflow_run: | |
workflows: ['Create a test build'] | |
types: [completed] | |
permissions: | |
actions: write | |
contents: write | |
pull-requests: write | |
jobs: | |
pr_comment: | |
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/github-script@v7 | |
with: | |
# This snippet is public-domain, combined from | |
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml | |
# https://github.com/AKSW/submission.d2r2.aksw.org/blob/main/.github/workflows/pr-comment.yml | |
script: | | |
// Function Definitions | |
/** | |
* Fetch PR details for a given commit SHA. | |
* @returns {Object} PR details containing prNumber, prRef, prRepoId. | |
* @throws {Error} If no matching PR is found. | |
*/ | |
async function fetchPRDetails() { | |
const iterator = github.paginate.iterator(github.rest.pulls.list, { | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
}); | |
for await (const { data } of iterator) { | |
for (const pull of data) { | |
if (pull.head.sha === '${{github.event.workflow_run.head_sha}}') { | |
return { | |
prNumber: pull.number, | |
prRef: pull.head.ref, | |
prRepoId: pull.head.repo.id | |
}; | |
} | |
} | |
} | |
throw new Error("No matching PR found for the commit SHA"); | |
} | |
/** | |
* Fetch all artifacts for a given workflow run. | |
* @returns {Object} All artifacts data. | |
* @throws {Error} If no artifacts are found. | |
*/ | |
async function fetchAllArtifacts() { | |
const artifactsResponse = await github.rest.actions.listWorkflowRunArtifacts({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
run_id: context.payload.workflow_run.id, | |
}); | |
if (!(artifactsResponse.data && artifactsResponse.data.artifacts && artifactsResponse.data.artifacts.length)) { | |
throw new Error("No artifacts found for the workflow run"); | |
} | |
return artifactsResponse.data.artifacts; | |
} | |
/** | |
* Create or update a comment on the PR. | |
* @param {number} prNumber - The PR number. | |
* @param {string} purpose - The purpose of the comment. | |
* @param {string} body - The comment body. | |
* @throws {Error} If the comment creation or update fails. | |
*/ | |
async function upsertComment(prNumber, purpose, body) { | |
const { data: comments } = await github.rest.issues.listComments({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: prNumber, | |
}); | |
const marker = `<!-- bot: ${purpose} -->`; | |
body = marker + "\n" + body; | |
const existing = comments.filter(c => c.body.includes(marker)); | |
if (existing.length > 0) { | |
const last = existing[existing.length - 1]; | |
core.info(`Updating comment ${last.id}`); | |
await github.rest.issues.updateComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: body, | |
comment_id: last.id, | |
}); | |
} else { | |
core.info(`Creating a comment in PR #${prNumber}`); | |
await github.rest.issues.createComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: body, | |
issue_number: prNumber, | |
}); | |
} | |
} | |
/** | |
* Handle and log errors with detailed context and exit the process. | |
* @param {Error} error - The error object. | |
* @param {string} description - Description of the context where the error occurred. | |
*/ | |
function handleError(error, description, prNumber) { | |
core.error(`Failed to ${description}`); | |
core.error(`Message: ${error.message}`); | |
core.error(`Stack Trace: ${error.stack || 'No stack trace available'}`); | |
if (prNumber) { | |
core.error(`PR Number: ${prNumber}`); | |
} | |
core.error(`PRs: https://api.github.com/repos/${context.repo.owner}/${context.repo.repo}/pulls`); | |
core.error(`SHA: ${{github.event.workflow_run.head_sha}}`); | |
process.exit(1); | |
} | |
// Main Code Execution | |
let prNumber, prRef, prRepoId; | |
// Fetch PR details | |
try { | |
({ prNumber, prRef, prRepoId } = await fetchPRDetails()); | |
core.info(`Found PR: #${prNumber}, Ref: ${prRef}, Repo ID: ${prRepoId}`); | |
} catch (error) { | |
handleError(error, 'fetch PR details', undefined); | |
} | |
// Fetch all artifacts | |
let allArtifacts; | |
try { | |
allArtifacts = await fetchAllArtifacts(); | |
core.info(`Artifacts fetched successfully`); | |
} catch (error) { | |
handleError(error, 'fetch artifacts', prNumber); | |
} | |
// Construct the comment body | |
let body = allArtifacts.reduce((acc, item) => { | |
if (item.name === "assets") return acc; | |
acc += `\n* [${item.name}.zip](https://nightly.link/${context.repo.owner}/${context.repo.repo}/actions/artifacts/${item.id}.zip)`; | |
return acc; | |
}, 'Download the built assets for this pull request:\n'); | |
// Upsert the comment on the PR | |
try { | |
await upsertComment(prNumber, "nightly-link", body); | |
core.info("Comment created/updated successfully"); | |
} catch (error) { | |
handleError(error, 'create/update comment', prNumber); | |
} |