Update GitHub Actions for PR Summary Generation #13
Workflow file for this run
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: Generate PR Summary | |
| on: | |
| pull_request: | |
| types: [opened] | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| models: read | |
| jobs: | |
| generate-summary: | |
| # Run when: | |
| # 1. A PR is opened without a description, OR | |
| # 2. Someone comments "/generate-summary" on a PR | |
| if: | | |
| (github.event_name == 'pull_request' && (github.event.pull_request.body == '' || github.event.pull_request.body == null)) || | |
| (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/generate-summary')) | |
| runs-on: ubuntu-latest | |
| outputs: | |
| pr-number: ${{ steps.pr-number.outputs.number }} | |
| steps: | |
| - name: Determine PR number | |
| id: pr-number | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| ISSUE_NUMBER: ${{ github.event.issue.number }} | |
| run: | | |
| if [ "$EVENT_NAME" = "pull_request" ]; then | |
| echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "number=$ISSUE_NUMBER" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: React to comment | |
| if: github.event_name == 'issue_comment' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| await github.rest.reactions.createForIssueComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: context.payload.comment.id, | |
| content: 'rocket', | |
| }); | |
| - name: Get PR diff summary | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_NUMBER: ${{ steps.pr-number.outputs.number }} | |
| run: | | |
| gh pr diff "$PR_NUMBER" --stat > /tmp/diff-stat.txt 2>/dev/null || echo "Unable to retrieve diff" > /tmp/diff-stat.txt | |
| gh pr diff "$PR_NUMBER" --name-only > /tmp/changed-files.txt 2>/dev/null || echo "" > /tmp/changed-files.txt | |
| - name: Read format instructions | |
| run: | | |
| if [ -f .github/copilot-instructions.md ]; then | |
| cat .github/copilot-instructions.md > /tmp/format-instructions.txt | |
| else | |
| echo "No format instructions found" > /tmp/format-instructions.txt | |
| fi | |
| - name: Generate summary with GitHub Models | |
| id: generate | |
| uses: actions/github-script@v7 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const diffStat = fs.readFileSync('/tmp/diff-stat.txt', 'utf8'); | |
| const changedFiles = fs.readFileSync('/tmp/changed-files.txt', 'utf8'); | |
| const formatInstructions = fs.readFileSync('/tmp/format-instructions.txt', 'utf8'); | |
| const prompt = `You are generating a pull request description for an automated sync from an internal development repository to the public GitHub repository. | |
| Here are the format instructions to follow: | |
| --- | |
| ${formatInstructions} | |
| --- | |
| Here are the changed files in this PR: | |
| --- | |
| ${changedFiles} | |
| --- | |
| Here is the diff stat: | |
| --- | |
| ${diffStat} | |
| --- | |
| Generate a PR description that follows the format instructions exactly. Categorize files correctly: | |
| - Files under Samples/ that appear new → New samples | |
| - Files under Samples/ that are modified → Updated samples (use the sample directory name) | |
| - Files under Kits/ → Updated kits | |
| - Everything else (Media/, configs, docs) → General | |
| Also generate a concise PR title (not the H2 heading) that summarizes the changes in | |
| a few words (e.g., "Add TriangleWave sample, update ATGTK helpers"). | |
| Output a JSON object with two fields: | |
| - "title": the PR title string | |
| - "body": the formatted PR description following the instructions | |
| Output ONLY valid JSON, nothing else.`; | |
| const response = await fetch('https://models.github.ai/inference/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`, | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| model: 'openai/gpt-4o-mini', | |
| messages: [ | |
| { role: 'user', content: prompt } | |
| ], | |
| temperature: 0.3, | |
| response_format: { type: 'json_object' }, | |
| }), | |
| }); | |
| if (!response.ok) { | |
| const error = await response.text(); | |
| core.setFailed(`GitHub Models API error: ${response.status} - ${error}`); | |
| return; | |
| } | |
| const result = await response.json(); | |
| let content = result.choices[0].message.content.trim(); | |
| // Strip markdown code fences if present | |
| content = content.replace(/^```(?:json)?\s*\n?/i, '').replace(/\n?```\s*$/i, ''); | |
| let parsed; | |
| try { | |
| parsed = JSON.parse(content); | |
| } catch (e) { | |
| // Fallback: treat entire response as body if JSON parsing fails | |
| parsed = { title: '', body: content }; | |
| } | |
| fs.writeFileSync('/tmp/pr-summary.md', parsed.body); | |
| core.setOutput('summary', parsed.body); | |
| core.setOutput('title', parsed.title); | |
| - name: Update PR title and description | |
| if: steps.generate.outputs.summary != '' | |
| uses: actions/github-script@v7 | |
| env: | |
| PR_TITLE: ${{ steps.generate.outputs.title }} | |
| PR_NUMBER: ${{ steps.pr-number.outputs.number }} | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const summary = fs.readFileSync('/tmp/pr-summary.md', 'utf8'); | |
| const title = process.env.PR_TITLE || ''; | |
| const update = { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: parseInt(process.env.PR_NUMBER, 10), | |
| body: summary, | |
| }; | |
| if (title) { | |
| update.title = title; | |
| } | |
| await github.rest.pulls.update(update); | |
| console.log('PR title and description updated successfully'); | |
| evaluate-summary: | |
| needs: generate-summary | |
| uses: ./.github/workflows/pr-summary-eval-reusable.yml | |
| with: | |
| pr-number: ${{ fromJSON(needs.generate-summary.outputs.pr-number) }} |