Skip to content

Test empty PR

Test empty PR #1

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',
});
}