pr-template.yml 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. name: Check pull request template
  2. # Runs on pull_request_target so the token can comment, label, and close pull
  3. # requests that come from forks. Only the base branch is checked out, and the
  4. # script reads nothing but the pull request body, so the head commit never runs.
  5. on:
  6. pull_request_target:
  7. types: [opened, edited, reopened]
  8. permissions:
  9. pull-requests: write
  10. jobs:
  11. template:
  12. if: >-
  13. github.event.pull_request.user.login != 'wsvincent' &&
  14. github.event.pull_request.user.login != 'jefftriplett' &&
  15. github.event.pull_request.user.login != 'dependabot[bot]'
  16. runs-on: ubuntu-latest
  17. steps:
  18. - uses: actions/checkout@v4
  19. with:
  20. ref: ${{ github.event.repository.default_branch }}
  21. sparse-checkout: .github/scripts
  22. - uses: actions/github-script@v7
  23. with:
  24. script: |
  25. const { checkTemplate } = require("./.github/scripts/check-pr-template.js");
  26. const pr = context.payload.pull_request;
  27. const { owner, repo } = context.repo;
  28. const issue_number = pr.number;
  29. const LABEL = "needs-template";
  30. const MARKER = "<!-- pr-template-check -->";
  31. const { missing, company } = checkTemplate(pr.body);
  32. const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number, per_page: 100 });
  33. const existing = comments.find((c) => c.body && c.body.includes(MARKER));
  34. async function say(body) {
  35. body = `${MARKER}\n${body}`;
  36. if (existing) {
  37. await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
  38. } else {
  39. await github.rest.issues.createComment({ owner, repo, issue_number, body });
  40. }
  41. }
  42. if (missing.length) {
  43. const list = missing.map((m) => `- ${m}`).join("\n");
  44. await say(
  45. `Thanks for the submission! This pull request does not fill out the ` +
  46. `[pull request template](https://github.com/${owner}/${repo}/blob/main/.github/pull_request_template.md), ` +
  47. `so it was closed automatically.\n\nMissing:\n${list}\n\n` +
  48. `Edit the pull request description to add the missing items. ` +
  49. `The check runs again on every edit and reopens the pull request when the template is complete.`
  50. );
  51. await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [LABEL] });
  52. if (pr.state === "open") {
  53. await github.rest.pulls.update({ owner, repo, pull_number: issue_number, state: "closed" });
  54. }
  55. return;
  56. }
  57. const hadLabel = pr.labels.some((l) => l.name === LABEL);
  58. if (hadLabel) {
  59. await github.rest.issues.removeLabel({ owner, repo, issue_number, name: LABEL }).catch(() => {});
  60. if (pr.state === "closed" && !pr.merged_at) {
  61. await github.rest.pulls.update({ owner, repo, pull_number: issue_number, state: "open" });
  62. }
  63. }
  64. let note = "The pull request template is complete. Thanks! A maintainer will review it.";
  65. if (company) {
  66. note +=
  67. `\n\nThis submission is on behalf of a company. Please read the ` +
  68. `[commercial products and services](https://github.com/${owner}/${repo}/blob/main/contributing.md#commercial-products-and-services) ` +
  69. `section of the contribution guidelines.`;
  70. }
  71. if (existing || company) await say(note);