name: Check pull request template # Runs on pull_request_target so the token can comment, label, and close pull # requests that come from forks. Only the base branch is checked out, and the # script reads nothing but the pull request body, so the head commit never runs. on: pull_request_target: types: [opened, edited, reopened] permissions: pull-requests: write jobs: template: if: >- github.event.pull_request.user.login != 'wsvincent' && github.event.pull_request.user.login != 'jefftriplett' && github.event.pull_request.user.login != 'dependabot[bot]' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.repository.default_branch }} sparse-checkout: .github/scripts - uses: actions/github-script@v7 with: script: | const { checkTemplate } = require("./.github/scripts/check-pr-template.js"); const pr = context.payload.pull_request; const { owner, repo } = context.repo; const issue_number = pr.number; const LABEL = "needs-template"; const MARKER = ""; const { missing, company } = checkTemplate(pr.body); const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number, per_page: 100 }); const existing = comments.find((c) => c.body && c.body.includes(MARKER)); async function say(body) { body = `${MARKER}\n${body}`; if (existing) { await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }); } else { await github.rest.issues.createComment({ owner, repo, issue_number, body }); } } if (missing.length) { const list = missing.map((m) => `- ${m}`).join("\n"); await say( `Thanks for the submission! This pull request does not fill out the ` + `[pull request template](https://github.com/${owner}/${repo}/blob/main/.github/pull_request_template.md), ` + `so it was closed automatically.\n\nMissing:\n${list}\n\n` + `Edit the pull request description to add the missing items. ` + `The check runs again on every edit and reopens the pull request when the template is complete.` ); await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [LABEL] }); if (pr.state === "open") { await github.rest.pulls.update({ owner, repo, pull_number: issue_number, state: "closed" }); } return; } const hadLabel = pr.labels.some((l) => l.name === LABEL); if (hadLabel) { await github.rest.issues.removeLabel({ owner, repo, issue_number, name: LABEL }).catch(() => {}); if (pr.state === "closed" && !pr.merged_at) { await github.rest.pulls.update({ owner, repo, pull_number: issue_number, state: "open" }); } } let note = "The pull request template is complete. Thanks! A maintainer will review it."; if (company) { note += `\n\nThis submission is on behalf of a company. Please read the ` + `[commercial products and services](https://github.com/${owner}/${repo}/blob/main/contributing.md#commercial-products-and-services) ` + `section of the contribution guidelines.`; } if (existing || company) await say(note);