Skip to content

Commit e27ae0f

Browse files
authored
fix: 조직에서 collaborator 기준으로 변경
1 parent 9ce78f7 commit e27ae0f

File tree

1 file changed

+61
-43
lines changed

1 file changed

+61
-43
lines changed

.github/workflows/auto-review-and-merge.yml

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,90 +2,109 @@ name: Auto Assign, Close or Merge PR
22

33
on:
44
pull_request:
5-
branches:
6-
- main
7-
types:
8-
- opened
9-
- reopened
5+
branches: [main]
6+
types: [opened, reopened]
107
pull_request_review:
11-
types:
12-
- submitted
8+
types: [submitted]
139

1410
jobs:
15-
manage-pr:
11+
assign-and-merge:
1612
runs-on: ubuntu-latest
1713
steps:
18-
# 1️⃣ PR 열릴 때 랜덤 리뷰어 2명 지정
19-
- name: Assign 2 random reviewers
20-
id: assign
14+
- name: Assign random collaborators as reviewers & save to PR body
15+
if: github.event_name == 'pull_request'
2116
uses: actions/github-script@v7
2217
with:
18+
github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
2319
script: |
24-
const org = context.repo.owner;
25-
let members = [];
20+
// 현재 리포의 모든 collaborator 가져오기 (작성자 제외)
21+
let collaborators = [];
2622
let page = 1;
27-
28-
// 조직 멤버 가져오기
29-
while(true){
30-
const { data } = await github.rest.orgs.listMembers({
31-
org,
23+
while (true) {
24+
const { data } = await github.rest.repos.listCollaborators({
25+
owner: context.repo.owner,
26+
repo: context.repo.repo,
3227
per_page: 100,
3328
page
3429
});
35-
if(data.length === 0) break;
36-
members = members.concat(data.map(u => u.login));
30+
if (data.length === 0) break;
31+
collaborators = collaborators.concat(data.map(u => u.login));
3732
page++;
3833
}
39-
40-
// PR 작성자 제외
4134
const author = context.payload.pull_request.user.login;
42-
members = members.filter(u => u !== author);
35+
collaborators = collaborators.filter(u => u !== author);
4336
44-
// 랜덤 2명 선택
45-
const shuffled = members.sort(() => 0.5 - Math.random());
46-
const reviewers = shuffled.slice(0, 2);
37+
// 랜덤 2명(또는 남은 만큼) 선정
38+
const reviewers = collaborators.sort(() => 0.5 - Math.random()).slice(0, 2);
4739
4840
// 리뷰어 지정
49-
await github.rest.pulls.requestReviewers({
41+
if (reviewers.length > 0) {
42+
await github.rest.pulls.requestReviewers({
43+
owner: context.repo.owner,
44+
repo: context.repo.repo,
45+
pull_number: context.payload.pull_request.number,
46+
reviewers: reviewers
47+
});
48+
}
49+
50+
// PR 본문에 리뷰어 기록
51+
const body = context.payload.pull_request.body || "";
52+
const reviewersNote = `<!-- reviewers: ${reviewers.join(',')} -->`;
53+
await github.rest.pulls.update({
5054
owner: context.repo.owner,
5155
repo: context.repo.repo,
5256
pull_number: context.payload.pull_request.number,
53-
reviewers: reviewers
57+
body: `${body}\n${reviewersNote}`
5458
});
5559
60+
console.log("Review candidate collaborators:", collaborators);
5661
console.log("Assigned reviewers:", reviewers);
57-
return reviewers.join(',');
58-
github-token: ${{ secrets.ORG_ACCESS_TOKEN }}
5962
60-
# 2️⃣ 리뷰 제출 감지 후 Close / Merge 처리 (리뷰어 2명 모두 승인해야 merge)
61-
- name: Check review and act
63+
- name: Auto merge if reviewers all approved (or none assigned)
64+
if: github.event_name == 'pull_request_review'
6265
uses: actions/github-script@v7
6366
with:
67+
github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
6468
script: |
6569
const pr_number = context.payload.pull_request.number;
70+
const { data: pr } = await github.rest.pulls.get({
71+
owner: context.repo.owner,
72+
repo: context.repo.repo,
73+
pull_number: pr_number
74+
});
75+
const body = pr.body || "";
76+
const reviewersMatch = body.match(/<!-- reviewers: ([^>]*)-->/);
77+
const reviewers = reviewersMatch && reviewersMatch[1].trim()
78+
? reviewersMatch[1].split(',').map(r => r.trim()).filter(r => r)
79+
: [];
6680
67-
// 여러 명이 할당되어 있으므로 배열로 처리
68-
const reviewers = steps.assign.outputs.result.split(',');
81+
// 리뷰어가 없으면 자동 머지
82+
if (reviewers.length === 0) {
83+
await github.rest.pulls.merge({
84+
owner: context.repo.owner,
85+
repo: context.repo.repo,
86+
pull_number: pr_number
87+
});
88+
console.log("No reviewers assigned. PR auto merged.");
89+
return;
90+
}
6991
92+
// 각 리뷰어의 마지막 리뷰 상태 확인
7093
const { data: reviews } = await github.rest.pulls.listReviews({
7194
owner: context.repo.owner,
7295
repo: context.repo.repo,
7396
pull_number: pr_number
7497
});
7598
76-
// 각 리뷰어의 마지막 리뷰 상태 추적
7799
const reviewStates = {};
78100
reviewers.forEach(reviewer => {
79-
// 해당 리뷰어의 마지막 리뷰 찾기
80101
const userReviews = reviews.filter(r => r.user.login === reviewer);
81102
if (userReviews.length > 0) {
82103
reviewStates[reviewer] = userReviews[userReviews.length - 1].state;
83104
}
84105
});
85106
86-
console.log("Review states:", reviewStates);
87-
88-
// CHANGES_REQUESTED가 하나라도 있으면 PR Close
107+
// CHANGES_REQUESTED가 있으면 PR 반려(닫기)
89108
if (Object.values(reviewStates).includes("CHANGES_REQUESTED")) {
90109
await github.rest.issues.createComment({
91110
owner: context.repo.owner,
@@ -103,15 +122,14 @@ jobs:
103122
return;
104123
}
105124
106-
// 2명 모두 APPROVED면 자동 Merge
107-
if (reviewers.every(reviewer => reviewStates[reviewer] === "APPROVED")) {
125+
// 모든 리뷰어 APPROVED면 자동 머지
126+
if (reviewers.length > 0 && reviewers.every(r => reviewStates[r] === "APPROVED")) {
108127
await github.rest.pulls.merge({
109128
owner: context.repo.owner,
110129
repo: context.repo.repo,
111130
pull_number: pr_number
112131
});
113-
console.log("PR auto merged.");
132+
console.log("All reviewers approved. PR auto merged.");
114133
} else {
115134
console.log("Waiting for all reviewers to approve.");
116135
}
117-
github-token: ${{ secrets.ORG_ACCESS_TOKEN }}

0 commit comments

Comments
 (0)