name: Comment on an issue on: workflow_run: workflows: - "Check code formatting" - "Check for private emails used in PRs" - "PR Request Release Note" - "Code lint" types: - completed permissions: contents: read jobs: pr-comment: runs-on: ubuntu-24.04 permissions: pull-requests: write if: > github.event.workflow_run.event == 'pull_request' && ( github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure' ) steps: - name: Fetch Sources uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: sparse-checkout: | .github/workflows/unprivileged-download-artifact/action.yml sparse-checkout-cone-mode: false - name: 'Download artifact' uses: ./.github/workflows/unprivileged-download-artifact id: download-artifact with: run-id: ${{ github.event.workflow_run.id }} artifact-name: workflow-args - name: 'Comment on PR' if: steps.download-artifact.outputs.artifact-id != '' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | var fs = require('fs'); const comments = JSON.parse(fs.readFileSync('./comments')); if (!comments || comments.length == 0) { return; } let runInfo = await github.rest.actions.getWorkflowRun({ owner: context.repo.owner, repo: context.repo.repo, run_id: context.payload.workflow_run.id }); console.log(runInfo); // Query to find the number of the pull request that triggered this job. // The associated pull requests are based off of the branch name, so if // you create a pull request for a branch, close it, and then create // another pull request with the same branch, then this query will return // two associated pull requests. This is why we have to fetch all the // associated pull requests and then iterate through them to find the // one that is open. const gql_query = ` query($repo_owner : String!, $repo_name : String!, $branch: String!) { repository(owner: $repo_owner, name: $repo_name) { ref (qualifiedName: $branch) { associatedPullRequests(first: 100) { nodes { baseRepository { owner { login } } number state } } } } } ` const gql_variables = { repo_owner: runInfo.data.head_repository.owner.login, repo_name: runInfo.data.head_repository.name, branch: runInfo.data.head_branch } const gql_result = await github.graphql(gql_query, gql_variables); console.log(gql_result); // If the branch for the PR was deleted before this job has a chance // to run, then the ref will be null. This can happen if someone: // 1. Rebase the PR, which triggers some workflow. // 2. Immediately merges the PR and deletes the branch. // 3. The workflow finishes and triggers this job. if (!gql_result.repository.ref) { console.log("Ref has been deleted"); return; } console.log(gql_result.repository.ref.associatedPullRequests.nodes); var pr_number = 0; gql_result.repository.ref.associatedPullRequests.nodes.forEach((pr) => { // The largest PR number is the one we care about. The only way // to have more than one associated pull requests is if all the // old pull requests are in the closed state. if (pr.baseRepository.owner.login = context.repo.owner && pr.number > pr_number) { pr_number = pr.number; } }); if (pr_number == 0) { console.log("Error retrieving pull request number"); return; } await comments.forEach(function (comment) { if (comment.id) { // Security check: Ensure that this comment was created by // the github-actions bot, so a malicious input won't overwrite // a user's comment. github.rest.issues.getComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: comment.id }).then((old_comment) => { console.log(old_comment); if (old_comment.data.user.login != "github-actions[bot]") { console.log("Invalid comment id: " + comment.id); return; } github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pr_number, comment_id: comment.id, body: comment.body }); }); } else { github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pr_number, body: comment.body }); } }); - name: Dump comments file if: >- always() && steps.download-artifact.outputs.artifact-id != '' run: cat comments