Post CI test comment #6403
This file contains hidden or 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
| # | |
| # Licensed to the Apache Software Foundation (ASF) under one or more | |
| # contributor license agreements. See the NOTICE file distributed with | |
| # this work for additional information regarding copyright ownership. | |
| # The ASF licenses this file to You under the Apache License, Version 2.0 | |
| # (the "License"); you may not use this file except in compliance with | |
| # the License. You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # | |
| # Posts the CI test summary comment on PRs. | |
| # Uses workflow_run trigger so it runs in the base repo context with | |
| # full permissions — this allows posting comments on fork PRs too. | |
| name: Post CI test comment | |
| on: | |
| workflow_run: | |
| workflows: ["Build and test"] | |
| types: | |
| - completed | |
| jobs: | |
| comment: | |
| runs-on: ubuntu-latest | |
| if: > | |
| github.event.workflow_run.event == 'pull_request' || | |
| github.event.workflow_run.event == 'workflow_dispatch' | |
| permissions: | |
| pull-requests: write | |
| actions: read | |
| steps: | |
| - name: Download CI comment artifact | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: ${{ github.event.workflow_run.id }}, | |
| }); | |
| const match = artifacts.data.artifacts.find(a => a.name === 'ci-comment'); | |
| if (!match) { | |
| core.info('No ci-comment artifact found, skipping'); | |
| return; | |
| } | |
| const download = await github.rest.actions.downloadArtifact({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| artifact_id: match.id, | |
| archive_format: 'zip', | |
| }); | |
| const fs = require('fs'); | |
| fs.writeFileSync('${{ github.workspace }}/ci-comment.zip', Buffer.from(download.data)); | |
| - name: Extract artifact | |
| run: | | |
| if [ -f ci-comment.zip ]; then | |
| unzip -o ci-comment.zip -d ci-comment-artifact | |
| fi | |
| - name: Post or update PR comment | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| const prFile = 'ci-comment-artifact/pr-number'; | |
| const commentFile = 'ci-comment-artifact/incremental-test-comment.md'; | |
| if (!fs.existsSync(prFile)) { | |
| core.info('No PR number file found, skipping'); | |
| return; | |
| } | |
| const prNumber = parseInt(fs.readFileSync(prFile, 'utf8').trim(), 10); | |
| if (!prNumber) { | |
| core.warning('Invalid PR number, skipping'); | |
| return; | |
| } | |
| if (!fs.existsSync(commentFile)) { | |
| core.info('No comment file found, skipping'); | |
| return; | |
| } | |
| const body = fs.readFileSync(commentFile, 'utf8').trim(); | |
| if (!body) return; | |
| const marker = '<!-- ci-tested-modules -->'; | |
| try { | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| }); | |
| const existing = comments.find(c => c.body && c.body.includes(marker)); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body: body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body: body, | |
| }); | |
| } | |
| } catch (error) { | |
| core.warning(`Failed to post CI test summary comment: ${error.message}`); | |
| } | |
| - name: Download regen patch artifact | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: ${{ github.event.workflow_run.id }}, | |
| }); | |
| const match = artifacts.data.artifacts.find(a => a.name === 'regen-patch'); | |
| if (!match) { | |
| core.info('No regen-patch artifact found, skipping'); | |
| return; | |
| } | |
| const download = await github.rest.actions.downloadArtifact({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| artifact_id: match.id, | |
| archive_format: 'zip', | |
| }); | |
| const fs = require('fs'); | |
| fs.writeFileSync('${{ github.workspace }}/regen-patch.zip', Buffer.from(download.data)); | |
| - name: Extract regen patch artifact | |
| run: | | |
| if [ -f regen-patch.zip ]; then | |
| unzip -o regen-patch.zip -d regen-patch-artifact | |
| fi | |
| - name: Post or update regen patch comment | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| const regenPatchFile = 'regen-patch-artifact/regen.patch'; | |
| const regenPrFile = 'regen-patch-artifact/pr-number'; | |
| const ciPrFile = 'ci-comment-artifact/pr-number'; | |
| const regenNeeded = fs.existsSync(regenPatchFile); | |
| // The regen artifact carries its own PR number (the ci-comment | |
| // artifact is not produced when the build fails at the regen | |
| // check); on clean runs fall back to the ci-comment artifact so an | |
| // earlier warning comment can be marked resolved. | |
| let prNumber = 0; | |
| if (fs.existsSync(regenPrFile)) { | |
| prNumber = parseInt(fs.readFileSync(regenPrFile, 'utf8').trim(), 10); | |
| } else if (fs.existsSync(ciPrFile)) { | |
| prNumber = parseInt(fs.readFileSync(ciPrFile, 'utf8').trim(), 10); | |
| } | |
| if (!prNumber) { | |
| core.info('No PR number available, skipping regen patch comment'); | |
| return; | |
| } | |
| const marker = '<!-- regen-patch -->'; | |
| const runId = ${{ github.event.workflow_run.id }}; | |
| const runUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${runId}`; | |
| const repoSlug = `${context.repo.owner}/${context.repo.repo}`; | |
| let body; | |
| if (regenNeeded) { | |
| body = [ | |
| marker, | |
| '### :warning: Generated files are out of date', | |
| '', | |
| `The [CI build](${runUrl}) regenerated files that are not committed on this PR.`, | |
| '', | |
| 'If *Allow edits from maintainers* is enabled, a maintainer bot will automatically', | |
| 'push a "Regen" commit to this branch shortly.', | |
| '', | |
| 'Otherwise, you can apply the patch manually — the missing changes are available', | |
| 'as the `regen-patch` artifact of that run:', | |
| '', | |
| '```bash', | |
| `gh pr checkout ${prNumber} --repo ${repoSlug}`, | |
| `gh run download ${runId} --repo ${repoSlug} -n regen-patch`, | |
| 'git apply --index regen.patch && rm regen.patch pr-number', | |
| 'git commit -m "Regen" && git push', | |
| '```', | |
| '', | |
| 'Alternatively, run `./etc/scripts/regen.sh` locally and commit the resulting changes.', | |
| 'If the patch does not apply cleanly, update the branch from the base branch first —', | |
| 'the patch was generated against the merge with the base branch.', | |
| ].join('\n'); | |
| } else { | |
| body = [ | |
| marker, | |
| '### :white_check_mark: Generated files are up to date', | |
| '', | |
| `An earlier CI run reported uncommitted generated changes; the [latest run](${runUrl}) no longer does.`, | |
| ].join('\n'); | |
| } | |
| try { | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| }); | |
| const existing = comments.find(c => c.body && c.body.includes(marker)); | |
| if (existing) { | |
| if (!regenNeeded && existing.body.includes(':white_check_mark:')) { | |
| return; // already marked resolved | |
| } | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body: body, | |
| }); | |
| } else if (regenNeeded) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body: body, | |
| }); | |
| } | |
| } catch (error) { | |
| core.warning(`Failed to post regen patch comment: ${error.message}`); | |
| } |