This repository was archived by the owner on Jul 4, 2026. It is now read-only.
Post Test Results #89
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
| name: Post Test Results | |
| on: | |
| workflow_run: | |
| workflows: ["CI/CD Pipeline"] | |
| types: [completed] | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| actions: read | |
| jobs: | |
| comment-on-pr: | |
| # Run regardless of success or failure | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - name: Download artifacts from triggering workflow | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: ./artifacts | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| continue-on-error: true | |
| - name: Get job results from workflow run | |
| id: get-jobs | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| // Get all jobs from the triggering workflow run | |
| const { data: jobs } = await github.rest.actions.listJobsForWorkflowRun({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: ${{ github.event.workflow_run.id }} | |
| }); | |
| // Extract job results for each node version | |
| const results = jobs.jobs | |
| .filter(job => job.name.includes('build-and-test')) | |
| .map(job => { | |
| const match = job.name.match(/\((\d+\.x)\)/); | |
| const nodeVersion = match ? match[1] : 'unknown'; | |
| let status; | |
| if (job.conclusion === 'success') { | |
| status = '✅ Passed'; | |
| } else if (job.conclusion === 'failure') { | |
| status = '❌ Failed'; | |
| } else if (job.conclusion === 'cancelled') { | |
| status = '⚠️ Cancelled'; | |
| } else if (job.conclusion === 'skipped') { | |
| status = '⏭️ Skipped'; | |
| } else { | |
| status = '❓ Unknown'; | |
| } | |
| return { | |
| nodeVersion, | |
| status, | |
| conclusion: job.conclusion, | |
| htmlUrl: job.html_url | |
| }; | |
| }); | |
| // Save results to file | |
| fs.mkdirSync('./job-results', { recursive: true }); | |
| fs.writeFileSync( | |
| './job-results/results.json', | |
| JSON.stringify(results, null, 2) | |
| ); | |
| console.log('Job results:', JSON.stringify(results, null, 2)); | |
| return results; | |
| - name: Parse test logs (supplementary) | |
| id: parse-logs | |
| run: node .github/scripts/parse-logs.js | |
| continue-on-error: true | |
| - name: Post comment on Pull Request | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| // 1) Try payload PR (works when GH provides it) | |
| let prNumber = context.payload.workflow_run?.pull_requests?.[0]?.number; | |
| // 2) Resolve from workflow run's head repo + head branch (works for forks) | |
| if (!prNumber) { | |
| const runId = context.payload.workflow_run.id; | |
| const { data: run } = await github.rest.actions.getWorkflowRun({ | |
| owner, repo, run_id: runId | |
| }); | |
| const headBranch = run.head_branch; | |
| const headRepoFull = run.head_repository?.full_name; | |
| if (headRepoFull && headBranch) { | |
| const headSpecifier = `${headRepoFull.split('/')[0]}:${headBranch}`; | |
| // List PRs where this fork branch is the head | |
| const { data: prs } = await github.rest.pulls.list({ | |
| owner, repo, state: 'open', head: headSpecifier | |
| }); | |
| if (prs.length) { | |
| prNumber = prs[0].number; | |
| core.info(`Resolved PR #${prNumber} via head=${headSpecifier}`); | |
| } else { | |
| core.info(`No open PR found with head=${headSpecifier}`); | |
| } | |
| } else { | |
| core.info('head_repository or head_branch missing on workflow run.'); | |
| } | |
| } | |
| if (!prNumber) { | |
| core.info('No pull request associated with this run.'); | |
| return; | |
| } | |
| // Read job results (if generated by the earlier step) | |
| const resultsPath = './job-results/results.json'; | |
| let results = []; | |
| if (fs.existsSync(resultsPath)) { | |
| results = JSON.parse(fs.readFileSync(resultsPath, 'utf8')); | |
| } else { | |
| core.info('No job results file found; proceeding with overall status only.'); | |
| } | |
| // Build comment body | |
| const overallStatus = context.payload.workflow_run.conclusion; | |
| const statusEmoji = overallStatus === 'success' ? '✅' | |
| : overallStatus === 'failure' ? '❌' | |
| : 'ℹ️'; | |
| let body = `### ${statusEmoji} CI/CD Test Results\n\n`; | |
| body += `**Overall Status**: ${overallStatus}\n\n`; | |
| if (results.length) { | |
| body += `#### Node.js Version Results:\n\n`; | |
| for (const r of results) { | |
| body += `- **Node ${r.nodeVersion}**: ${r.status}${r.htmlUrl ? ` ([View logs](${r.htmlUrl}))` : ''}\n`; | |
| } | |
| body += `\n`; | |
| } | |
| body += `---\n[View full workflow run](${context.payload.workflow_run.html_url})`; | |
| // Upsert comment | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner, repo, issue_number: prNumber | |
| }); | |
| const botComment = comments.find(c => | |
| c.user?.type === 'Bot' && c.body?.includes('CI/CD Test Results') | |
| ); | |
| if (botComment) { | |
| await github.rest.issues.updateComment({ | |
| owner, repo, comment_id: botComment.id, body | |
| }); | |
| core.info(`Updated existing comment on PR #${prNumber}`); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner, repo, issue_number: prNumber, body | |
| }); | |
| core.info(`Created new comment on PR #${prNumber}`); | |
| } |