Test empty PR #1
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: Check PR template completeness | |
| on: | |
| pull_request_target: | |
| types: [opened, reopened, edited] | |
| schedule: | |
| - cron: '0 8 * * *' # every day at 08:00 UTC | |
| permissions: | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| # ── 1. On every PR open/edit: check that the template was filled in ────────── | |
| check-template: | |
| if: ${{ github.event_name == 'pull_request_target' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Ensure needs-information label exists | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| try { | |
| await github.rest.issues.createLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| name: 'needs-information', | |
| color: 'e4e669', | |
| description: 'More information is needed before this can be reviewed', | |
| }); | |
| } catch (e) { | |
| if (e.status !== 422) throw e; // 422 = already exists, ignore | |
| } | |
| - name: Detect incomplete template | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number, | |
| }); | |
| const body = pr.body || ''; | |
| function isIncomplete(body) { | |
| if (body.trim().length < 30) return true; | |
| // Template HTML comment still present → body was never edited | |
| if (body.includes('<!-- Please describe your change')) return true; | |
| // The placeholder issue reference was not replaced | |
| if (/Fixes\s*#\s*\(issue\)/.test(body)) return true; | |
| // All checklist items still unchecked | |
| const unchecked = (body.match(/- \[ \]/g) || []).length; | |
| const checked = (body.match(/- \[x\]/gi) || []).length; | |
| if (unchecked >= 3 && checked === 0) return true; | |
| return false; | |
| } | |
| const labels = pr.labels.map(l => l.name); | |
| const alreadyFlagged = labels.includes('needs-information'); | |
| if (isIncomplete(body)) { | |
| if (!alreadyFlagged) { | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| labels: ['needs-information'], | |
| }); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| body: [ | |
| `## 📋 Please complete the PR template, @${pr.user.login}`, | |
| '', | |
| 'It looks like the Pull Request description is **empty or hasn\'t been filled in** yet.', | |
| '', | |
| 'To help maintainers review your contribution, please:', | |
| '', | |
| '1. Edit this PR and fill in the description template:', | |
| ' - Describe **what** your change does and **why**', | |
| ' - Link the related issue (e.g. `Fixes #123`)', | |
| ' - Tick the checklist items that apply', | |
| '', | |
| '2. Once the template is complete, the `needs-information` label will be removed automatically.', | |
| '', | |
| '> **Note:** If this PR is not updated within **7 days**, it will be closed automatically. You are welcome to re-open it once the description is complete.', | |
| '', | |
| '_This is an automated message._', | |
| ].join('\n'), | |
| }); | |
| console.log(`PR #${pr.number} flagged as needs-information.`); | |
| } else { | |
| console.log(`PR #${pr.number} still incomplete, already flagged.`); | |
| } | |
| } else { | |
| // Template is now complete — remove the label if it was set | |
| if (alreadyFlagged) { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| name: 'needs-information', | |
| }); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| body: `Thanks for completing the PR template, @${pr.user.login}! The \`needs-information\` label has been removed. A maintainer will review your PR shortly. 🙏`, | |
| }); | |
| console.log(`PR #${pr.number} template now complete, label removed.`); | |
| } else { | |
| console.log(`PR #${pr.number} template looks complete.`); | |
| } | |
| } | |
| # ── 2. Daily: close PRs still labelled needs-information after 7 days ───────── | |
| close-stale-incomplete: | |
| if: ${{ github.event_name == 'schedule' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Close PRs with incomplete template after 7 days of inactivity | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); | |
| // Fetch all open PRs labelled needs-information (paginate if needed) | |
| const prs = await github.paginate(github.rest.pulls.list, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| per_page: 100, | |
| }); | |
| for (const pr of prs) { | |
| const hasLabel = pr.labels.some(l => l.name === 'needs-information'); | |
| if (!hasLabel) continue; | |
| const lastUpdate = new Date(pr.updated_at); | |
| if (lastUpdate >= sevenDaysAgo) { | |
| console.log(`PR #${pr.number} flagged but still recent (${pr.updated_at}), skipping.`); | |
| continue; | |
| } | |
| console.log(`Closing PR #${pr.number} — no activity for 7+ days with incomplete template.`); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| body: [ | |
| '## PR closed — template not completed', | |
| '', | |
| `Hi @${pr.user.login},`, | |
| '', | |
| 'This Pull Request has been **automatically closed** because the description template was not completed within 7 days.', | |
| '', | |
| 'You are welcome to **re-open it** once the template is fully filled in. A complete description helps maintainers understand the context and intent of your change.', | |
| '', | |
| `See our [CONTRIBUTING guide](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${context.payload.repository.default_branch}/CONTRIBUTING.md) for details.`, | |
| '', | |
| '_This is an automated message._', | |
| ].join('\n'), | |
| }); | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number, | |
| state: 'closed', | |
| }); | |
| } |