Insea 99/week04 #17
Workflow file for this run
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: Auto Assign, Close or Merge PR | |
| on: | |
| pull_request: | |
| branches: [main] | |
| types: [opened, reopened] | |
| pull_request_review: | |
| types: [submitted] | |
| jobs: | |
| assign-and-merge: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Assign random collaborators as reviewers & save to PR body | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} | |
| script: | | |
| // 현재 리포의 모든 collaborator 가져오기 (작성자 제외) | |
| let collaborators = []; | |
| let page = 1; | |
| while (true) { | |
| const { data } = await github.rest.repos.listCollaborators({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| per_page: 100, | |
| page | |
| }); | |
| if (data.length === 0) break; | |
| collaborators = collaborators.concat(data.map(u => u.login)); | |
| page++; | |
| } | |
| const author = context.payload.pull_request.user.login; | |
| collaborators = collaborators.filter(u => u !== author); | |
| // 랜덤 2명(또는 남은 만큼) 선정 | |
| const reviewers = collaborators.sort(() => 0.5 - Math.random()).slice(0, 2); | |
| // 리뷰어 지정 | |
| if (reviewers.length > 0) { | |
| await github.rest.pulls.requestReviewers({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number, | |
| reviewers: reviewers | |
| }); | |
| } | |
| // PR 본문에 리뷰어 기록 | |
| const body = context.payload.pull_request.body || ""; | |
| const reviewersNote = `<!-- reviewers: ${reviewers.join(',')} -->`; | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number, | |
| body: `${body}\n${reviewersNote}` | |
| }); | |
| console.log("Review candidate collaborators:", collaborators); | |
| console.log("Assigned reviewers:", reviewers); | |
| - name: Auto merge if reviewers all approved (or none assigned) | |
| if: github.event_name == 'pull_request_review' | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} | |
| script: | | |
| const pr_number = context.payload.pull_request.number; | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr_number | |
| }); | |
| const body = pr.body || ""; | |
| const reviewersMatch = body.match(/<!-- reviewers: ([^>]*)-->/); | |
| const reviewers = reviewersMatch && reviewersMatch[1].trim() | |
| ? reviewersMatch[1].split(',').map(r => r.trim()).filter(r => r) | |
| : []; | |
| // 리뷰어가 없으면 자동 머지 | |
| if (reviewers.length === 0) { | |
| await github.rest.pulls.merge({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr_number | |
| }); | |
| console.log("No reviewers assigned. PR auto merged."); | |
| return; | |
| } | |
| // 각 리뷰어의 마지막 리뷰 상태 확인 | |
| const { data: reviews } = await github.rest.pulls.listReviews({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr_number | |
| }); | |
| const reviewStates = {}; | |
| reviewers.forEach(reviewer => { | |
| const userReviews = reviews.filter(r => r.user.login === reviewer); | |
| if (userReviews.length > 0) { | |
| reviewStates[reviewer] = userReviews[userReviews.length - 1].state; | |
| } | |
| }); | |
| // CHANGES_REQUESTED가 있으면 PR 반려(닫기) | |
| if (Object.values(reviewStates).includes("CHANGES_REQUESTED")) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr_number, | |
| body: `⚠️ PR 자동 반려: 리뷰어 중 한 명 이상이 변경 요청했습니다.` | |
| }); | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr_number, | |
| state: "closed" | |
| }); | |
| console.log("PR closed due to changes requested."); | |
| return; | |
| } | |
| // 모든 리뷰어 APPROVED면 자동 머지 | |
| if (reviewers.length > 0 && reviewers.every(r => reviewStates[r] === "APPROVED")) { | |
| await github.rest.pulls.merge({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr_number | |
| }); | |
| console.log("All reviewers approved. PR auto merged."); | |
| } else { | |
| console.log("Waiting for all reviewers to approve."); | |
| } |