action-milestone-sync.yml 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. name: Milestone Sync
  2. on:
  3. pull_request_target:
  4. types:
  5. - opened
  6. - reopened
  7. - ready_for_review
  8. - edited
  9. - milestoned
  10. - closed
  11. branches:
  12. - develop
  13. permissions:
  14. issues: write
  15. pull-requests: write
  16. concurrency:
  17. group: milestone-sync-${{ github.event.pull_request.number }}
  18. jobs:
  19. milestone-sync:
  20. if: github.repository_owner == 'GameServerManagers'
  21. runs-on: ubuntu-latest
  22. steps:
  23. - name: Sync selected milestone to linked issues
  24. uses: actions/github-script@v9
  25. with:
  26. script: |
  27. const { owner, repo } = context.repo;
  28. const pr = context.payload.pull_request;
  29. const action = context.payload.action;
  30. const fixedLabel = 'status: fixed in develop';
  31. if (action === 'closed' && !pr.merged) {
  32. return;
  33. }
  34. const milestone = pr.milestone;
  35. const body = (pr.body || '').replace(/<!--[\s\S]*?-->/g, '');
  36. const issueRef = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?|related to):?\s+(?:([\w.-]+)\/([\w.-]+))?#(\d+)/gi;
  37. const issues = new Map();
  38. for (const m of body.matchAll(issueRef)) {
  39. if (m[2] && (m[2].toLowerCase() !== owner.toLowerCase() || m[3].toLowerCase() !== repo.toLowerCase())) {
  40. continue;
  41. }
  42. const number = Number(m[4]);
  43. const closing = !/^related to$/i.test(m[1]);
  44. issues.set(number, issues.get(number) || closing);
  45. }
  46. for (const [number, closing] of issues) {
  47. let issue;
  48. try {
  49. ({ data: issue } = await github.rest.issues.get({ owner, repo, issue_number: number }));
  50. } catch (error) {
  51. core.warning(`Could not read #${number}: ${error.message}`);
  52. continue;
  53. }
  54. if (issue.pull_request) {
  55. continue;
  56. }
  57. if (milestone && issue.milestone?.number !== milestone.number) {
  58. if (issue.milestone && !pr.merged) {
  59. core.info(`#${number} is in ${issue.milestone.title}; it will move to ${milestone.title} when this PR merges.`);
  60. } else {
  61. await github.rest.issues.update({ owner, repo, issue_number: number, milestone: milestone.number });
  62. core.info(`Set #${number} milestone to ${milestone.title}.`);
  63. }
  64. }
  65. if (pr.merged && closing && issue.state === 'open' && !issue.labels.some((l) => (l.name || l) === fixedLabel)) {
  66. await github.rest.issues.addLabels({ owner, repo, issue_number: number, labels: [fixedLabel] });
  67. core.info(`Labelled #${number} "${fixedLabel}".`);
  68. }
  69. }