| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- #
- # Copyright (c) 2025, RT-Thread Development Team
- #
- # SPDX-License-Identifier: Apache-2.0
- #
- # Change Logs:
- # Date Author Notes
- # 2025-10-27 GitHub Copilot Post CI results to PR comments
- name: CI Results Comment
- # on:
- # workflow_run:
- # workflows:
- # - "RT-Thread BSP Static Build Check"
- # - "Static code analysis"
- # - "Check File Format and License"
- # - "utest_auto_run"
- # - "ToolsCI"
- # - "pkgs_test"
- # types:
- # - completed
- permissions:
- pull-requests: write
- issues: write
- actions: read
- checks: read
- jobs:
- comment-ci-results:
- runs-on: ubuntu-22.04
- if: github.event.workflow_run.event == 'pull_request' && github.repository_owner == 'RT-Thread'
- steps:
- - name: Get PR number
- id: get-pr
- uses: actions/github-script@v7
- with:
- script: |
- // Get PR number from workflow_run
- const prNumber = context.payload.workflow_run.pull_requests[0]?.number;
- if (!prNumber) {
- console.log('No PR found in workflow_run');
- // Fallback: search for PR by branch
- const pulls = await github.rest.pulls.list({
- owner: context.repo.owner,
- repo: context.repo.repo,
- state: 'open',
- head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`
- });
-
- if (pulls.data.length === 0) {
- console.log('No open PR found for this branch');
- return null;
- }
-
- const pr = pulls.data[0];
- console.log(`Found PR #${pr.number}`);
- return pr.number;
- }
-
- console.log(`Found PR #${prNumber}`);
- return prNumber;
- - name: Get workflow run details
- if: steps.get-pr.outputs.result != 'null'
- id: workflow-details
- uses: actions/github-script@v7
- with:
- script: |
- const prNumber = ${{ steps.get-pr.outputs.result }};
- if (!prNumber) {
- return { success: false, message: 'No PR found' };
- }
- // Get all workflow runs for this PR
- const workflowRuns = await github.rest.actions.listWorkflowRunsForRepo({
- owner: context.repo.owner,
- repo: context.repo.repo,
- event: 'pull_request',
- per_page: 100
- });
- // Filter runs for this specific PR
- const prRuns = workflowRuns.data.workflow_runs.filter(run => {
- return run.pull_requests.some(pr => pr.number === prNumber);
- });
- // Get the latest run for each workflow
- const workflowMap = new Map();
- for (const run of prRuns) {
- const existing = workflowMap.get(run.name);
- if (!existing || new Date(run.created_at) > new Date(existing.created_at)) {
- workflowMap.set(run.name, run);
- }
- }
- // Prepare results summary
- const results = [];
- for (const [name, run] of workflowMap) {
- let status = '🟡';
- let statusText = 'In Progress';
-
- if (run.status === 'completed') {
- if (run.conclusion === 'success') {
- status = '✅';
- statusText = 'Success';
- } else if (run.conclusion === 'failure') {
- status = '❌';
- statusText = 'Failure';
- } else if (run.conclusion === 'cancelled') {
- status = '⏭️';
- statusText = 'Cancelled';
- } else if (run.conclusion === 'skipped') {
- status = '⏭️';
- statusText = 'Skipped';
- }
- } else if (run.status === 'queued') {
- status = '🟠';
- statusText = 'Queued';
- }
-
- results.push({
- name: name,
- status: status,
- statusText: statusText,
- url: run.html_url,
- conclusion: run.conclusion,
- runId: run.id
- });
- }
- return {
- success: true,
- results: results,
- prNumber: prNumber
- };
- - name: Get job details
- if: steps.get-pr.outputs.result != 'null'
- id: job-details
- uses: actions/github-script@v7
- with:
- script: |
- const workflowDetails = ${{ steps.workflow-details.outputs.result }};
- if (!workflowDetails || !workflowDetails.success) {
- return { jobs: [] };
- }
- const allJobs = [];
-
- for (const result of workflowDetails.results) {
- try {
- const jobs = await github.rest.actions.listJobsForWorkflowRun({
- owner: context.repo.owner,
- repo: context.repo.repo,
- run_id: result.runId,
- per_page: 100
- });
- for (const job of jobs.data.jobs) {
- let jobStatus = '⌛';
- if (job.status === 'completed') {
- if (job.conclusion === 'success') {
- jobStatus = '✅';
- } else if (job.conclusion === 'failure') {
- jobStatus = '❌';
- } else if (job.conclusion === 'skipped') {
- jobStatus = '⏭️';
- }
- } else if (job.status === 'in_progress') {
- jobStatus = '🔄';
- } else if (job.status === 'queued') {
- jobStatus = '🟠';
- }
- allJobs.push({
- workflow: result.name,
- name: job.name,
- status: jobStatus,
- conclusion: job.conclusion || job.status,
- url: job.html_url
- });
- }
- } catch (error) {
- console.log(`Error getting jobs for workflow ${result.name}: ${error.message}`);
- }
- }
- return { jobs: allJobs };
- - name: Post or update comment
- if: steps.get-pr.outputs.result != 'null'
- uses: actions/github-script@v7
- with:
- script: |
- const prNumber = ${{ steps.get-pr.outputs.result }};
- const workflowDetails = ${{ steps.workflow-details.outputs.result }};
- const jobDetails = ${{ steps.job-details.outputs.result }};
-
- if (!workflowDetails || !workflowDetails.success) {
- console.log('No workflow details available');
- return;
- }
- // Prepare comment body
- const now = new Date();
- const timestamp = now.toISOString();
- const results = workflowDetails.results;
- const jobs = jobDetails.jobs || [];
- let commentBody = '<!-- CI Results Comment -->\n';
- commentBody += '## 🤖 CI Test Results\n\n';
- commentBody += `**Last Updated:** ${timestamp}\n\n`;
- commentBody += '### Test Spec & Results:\n\n';
- commentBody += '✅ Success | ❌ Failure | 🟠 Queued | 🟡 Progress | ⏭️ Skipped | ⚠️ Quarantine\n\n';
-
- // Group jobs by workflow
- const jobsByWorkflow = new Map();
- for (const job of jobs) {
- if (!jobsByWorkflow.has(job.workflow)) {
- jobsByWorkflow.set(job.workflow, []);
- }
- jobsByWorkflow.get(job.workflow).push(job);
- }
- // Calculate overall statistics
- let totalSuccess = 0;
- let totalFailure = 0;
- let totalQueued = 0;
- let totalProgress = 0;
- let totalSkipped = 0;
- for (const result of results) {
- if (result.conclusion === 'success') totalSuccess++;
- else if (result.conclusion === 'failure') totalFailure++;
- else if (result.statusText === 'Queued') totalQueued++;
- else if (result.statusText === 'In Progress') totalProgress++;
- else if (result.conclusion === 'skipped' || result.conclusion === 'cancelled') totalSkipped++;
- }
- // Summary line
- commentBody += '#### Overall Summary\n\n';
- commentBody += `- ✅ **Success:** ${totalSuccess}\n`;
- commentBody += `- ❌ **Failure:** ${totalFailure}\n`;
- commentBody += `- 🟠 **Queued:** ${totalQueued}\n`;
- commentBody += `- 🟡 **In Progress:** ${totalProgress}\n`;
- commentBody += `- ⏭️ **Skipped:** ${totalSkipped}\n\n`;
- commentBody += '---\n\n';
- commentBody += '### Detailed Results\n\n';
- // Build detailed results
- for (const result of results) {
- commentBody += `<details>\n`;
- commentBody += `<summary>${result.status} <strong>${result.name}</strong> - ${result.statusText}</summary>\n\n`;
- commentBody += `**Workflow:** [${result.name}](${result.url})\n\n`;
-
- // Show jobs for this workflow
- const workflowJobs = jobsByWorkflow.get(result.name) || [];
- if (workflowJobs.length > 0) {
- commentBody += '**Jobs:**\n\n';
- for (const job of workflowJobs) {
- commentBody += `- ${job.status} [${job.name}](${job.url})\n`;
- }
- }
- commentBody += '\n</details>\n\n';
- }
- commentBody += '\n---\n';
- commentBody += '*🤖 This comment is automatically generated and updated by the CI system.*\n';
- // Check if comment already exists
- const comments = await github.rest.issues.listComments({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: prNumber
- });
- const existingComment = comments.data.find(comment =>
- comment.user.login === 'github-actions[bot]' &&
- comment.body.includes('<!-- CI Results Comment -->')
- );
- if (existingComment) {
- // Update existing comment
- await github.rest.issues.updateComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- comment_id: existingComment.id,
- body: commentBody
- });
- console.log(`Updated comment ${existingComment.id} on PR #${prNumber}`);
- } else {
- // Create new comment
- await github.rest.issues.createComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: prNumber,
- body: commentBody
- });
- console.log(`Created new comment on PR #${prNumber}`);
- }
|