| # 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. | |
| name: PR benchmark comment | |
| on: | |
| workflow_run: | |
| workflows: ['PR benchmark'] | |
| types: [completed] | |
| permissions: | |
| actions: read | |
| pull-requests: write | |
| concurrency: | |
| group: benchmark-comment-${{ github.event.workflow_run.head_repository.id }}-${{ github.event.workflow_run.head_branch }} | |
| cancel-in-progress: false | |
| jobs: | |
| comment: | |
| if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion != 'cancelled' }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| steps: | |
| # Never check out PR code, restore its caches, or execute artifact contents. | |
| - name: Download this run's comment input | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: benchmark-comment-${{ github.event.workflow_run.run_attempt }} | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| path: report | |
| - name: Create or update benchmark comment | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const { owner, repo } = context.repo; | |
| const run = context.payload.workflow_run; | |
| if (run.path !== '.github/workflows/benchmark-pr.yml') { | |
| throw new Error('Unexpected source workflow'); | |
| } | |
| // Artifact fields are untrusted: bind the requested PR to GitHub's run data. | |
| const input = JSON.parse(fs.readFileSync('report/benchmark-context.json', 'utf8')); | |
| if (!Number.isSafeInteger(input.number) || input.number <= 0) { | |
| throw new Error('Invalid PR number'); | |
| } | |
| const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: input.number }); | |
| if (pr.state !== 'open' || pr.base.repo.full_name !== `${owner}/${repo}` | |
| || pr.head.repo?.id !== run.head_repository.id | |
| || pr.head.ref !== run.head_branch || pr.head.sha !== run.head_sha | |
| || input.head !== pr.head.sha || input.base !== pr.base.sha) { | |
| core.info('Skipping unrelated or stale benchmark result'); | |
| return; | |
| } | |
| // A delayed delivery from an earlier rerun must not replace the latest result. | |
| const { data: current } = await github.rest.actions.getWorkflowRun({ owner, repo, run_id: run.id }); | |
| if (current.run_attempt !== run.run_attempt) return; | |
| const marker = '<!-- paimon-pr-benchmark -->'; | |
| const path = 'report/results/summary.md'; | |
| const report = fs.existsSync(path) | |
| ? fs.readFileSync(path, 'utf8') | |
| : 'Benchmark did not produce a report. See the workflow logs.'; | |
| if (Buffer.byteLength(report, 'utf8') > 60000) throw new Error('Report too large'); | |
| const body = `${marker}\n[Benchmark run](${run.html_url}) · ${run.conclusion} · attempt ${run.run_attempt}\n\n${report}`; | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, repo, issue_number: pr.number, per_page: 100, | |
| }); | |
| const existing = comments.find(comment => | |
| comment.user.login === 'github-actions[bot]' && comment.body?.startsWith(marker)); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }); | |
| } else { | |
| await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body }); | |
| } |