diff --git a/.github/workflows/frontend-js-size-comment.yml b/.github/workflows/frontend-js-size-comment.yml index 3f75abf6f9..4d40aa8946 100644 --- a/.github/workflows/frontend-js-size-comment.yml +++ b/.github/workflows/frontend-js-size-comment.yml @@ -6,6 +6,27 @@ on: - Frontend JS size types: - completed + pull_request_target: + types: + - opened + - synchronize + - reopened + - ready_for_review + paths: + - packages/frontend/** + - packages/frontend-shared/** + - packages/frontend-builder/** + - packages/i18n/** + - packages/icons-subsetter/** + - packages/misskey-js/** + - packages/misskey-reversi/** + - packages/misskey-bubble-game/** + - package.json + - pnpm-lock.yaml + - pnpm-workspace.yaml + - .node-version + - .github/workflows/frontend-js-size.yml + - .github/workflows/frontend-js-size-comment.yml permissions: actions: read @@ -16,50 +37,148 @@ permissions: jobs: comment: name: Comment frontend JS size - if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' + if: github.event_name == 'pull_request_target' || (github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success') runs-on: ubuntu-latest + concurrency: + group: frontend-js-size-comment-${{ github.event.pull_request.number || github.event.workflow_run.id }} + cancel-in-progress: true steps: - - name: Download size report + - name: Find size report run + if: github.event_name == 'pull_request_target' + id: find-report-run + uses: actions/github-script@v9 + with: + script: | + const workflow_id = 'frontend-js-size.yml'; + const artifactName = 'frontend-js-size-report'; + const headSha = context.payload.pull_request.head.sha; + const prNumber = context.payload.pull_request.number; + const pollIntervalMs = 30_000; + const timeoutMs = 90 * 60_000; + const startedAt = Date.now(); + const { owner, repo } = context.repo; + + async function listSizeWorkflowRuns() { + const runsForHead = await github.paginate(github.rest.actions.listWorkflowRuns, { + owner, + repo, + workflow_id, + event: 'pull_request', + head_sha: headSha, + per_page: 100, + }); + + if (runsForHead.length > 0) { + return runsForHead; + } + + const recentRuns = await github.paginate(github.rest.actions.listWorkflowRuns, { + owner, + repo, + workflow_id, + event: 'pull_request', + per_page: 100, + }); + return recentRuns.filter((run) => + run.pull_requests?.some((pullRequest) => pullRequest.number === prNumber)); + } + + async function findReportRun() { + const runs = (await listSizeWorkflowRuns()) + .sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at)); + + for (const run of runs) { + if (run.status !== 'completed') continue; + if (run.conclusion !== 'success') { + core.warning(`Frontend JS size run ${run.id} completed with conclusion: ${run.conclusion}`); + return { done: true, run: null }; + } + + const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { + owner, + repo, + run_id: run.id, + per_page: 100, + }); + const report = artifacts.find((artifact) => artifact.name === artifactName && !artifact.expired); + if (report) return { done: true, run }; + } + + return { done: false, run: null }; + } + + while (Date.now() - startedAt < timeoutMs) { + const { done, run } = await findReportRun(); + if (run) { + core.info(`Found frontend JS size report on workflow run ${run.id}.`); + core.setOutput('run-id', String(run.id)); + return; + } + if (done) { + return; + } + + core.info('Waiting for frontend JS size report artifact...'); + await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)); + } + + core.warning(`Timed out waiting for ${artifactName} from ${workflow_id} for ${headSha}.`); + + - name: Download size report from workflow_run + if: github.event_name == 'workflow_run' uses: actions/download-artifact@v8 with: name: frontend-js-size-report - path: frontend-js-size-report + path: ${{ runner.temp }}/frontend-js-size-report github-token: ${{ github.token }} repository: ${{ github.repository }} run-id: ${{ github.event.workflow_run.id }} + - name: Download size report from pull_request_target + if: github.event_name == 'pull_request_target' && steps.find-report-run.outputs.run-id != '' + uses: actions/download-artifact@v8 + with: + name: frontend-js-size-report + path: ${{ runner.temp }}/frontend-js-size-report + github-token: ${{ github.token }} + repository: ${{ github.repository }} + run-id: ${{ steps.find-report-run.outputs.run-id }} + - name: Comment on pull request + if: github.event_name == 'workflow_run' || steps.find-report-run.outputs.run-id != '' uses: actions/github-script@v9 with: script: | const fs = require('node:fs'); + const path = require('node:path'); const marker = ''; - const body = fs.readFileSync('frontend-js-size-report/frontend-js-size-report.md', 'utf8'); + const reportDir = path.join(process.env.RUNNER_TEMP, 'frontend-js-size-report'); + const reportPath = [ + path.join(reportDir, 'report.md'), + path.join(reportDir, 'frontend-js-size-report.md'), + ].find((file) => fs.existsSync(file)); + if (reportPath == null) { + core.setFailed('The frontend JS size report artifact does not contain a report markdown file.'); + return; + } + + const body = fs.readFileSync(reportPath, 'utf8'); + const prNumberPath = path.join(reportDir, 'pr-number.txt'); + let issue_number = fs.existsSync(prNumberPath) + ? Number(fs.readFileSync(prNumberPath, 'utf8').trim()) + : context.payload.pull_request?.number; if (!body.includes(marker)) { core.setFailed('The frontend JS size report is missing the expected marker.'); return; } - - const { owner, repo } = context.repo; - const workflowRun = context.payload.workflow_run; - let issue_number = workflowRun.pull_requests?.[0]?.number; - - if (issue_number == null) { - const { data: pullRequests } = await github.rest.repos.listPullRequestsAssociatedWithCommit({ - owner, - repo, - commit_sha: workflowRun.head_sha, - }); - issue_number = pullRequests.find((pr) => pr.head.sha === workflowRun.head_sha)?.number - ?? pullRequests[0]?.number; - } - - if (issue_number == null) { - core.info(`No pull request found for workflow run ${workflowRun.id}.`); + if (!Number.isInteger(issue_number)) { + core.setFailed('The frontend JS size report is missing a valid pull request number.'); return; } + const { owner, repo } = context.repo; + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, diff --git a/.github/workflows/frontend-js-size.yml b/.github/workflows/frontend-js-size.yml index a511560a02..7d3641355b 100644 --- a/.github/workflows/frontend-js-size.yml +++ b/.github/workflows/frontend-js-size.yml @@ -330,14 +330,18 @@ jobs: env: BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | + mkdir -p frontend-js-size-report node .github/tmp/frontend-js-size-report.mjs before after frontend-js-size-report.md - cat frontend-js-size-report.md >> "$GITHUB_STEP_SUMMARY" + mv frontend-js-size-report.md frontend-js-size-report/report.md + printf '%s\n' "$PR_NUMBER" > frontend-js-size-report/pr-number.txt + cat frontend-js-size-report/report.md >> "$GITHUB_STEP_SUMMARY" - name: Upload size report uses: actions/upload-artifact@v7 with: name: frontend-js-size-report - path: frontend-js-size-report.md + path: frontend-js-size-report/ if-no-files-found: error retention-days: 1