Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 6 additions & 13 deletions .github/scripts/reconcile-issue-metadata.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,12 @@ export const ASSIGNEE_BY_MODULE = Object.freeze({
platform: "1hyok",
});

// #1910: 담당 이관은 모듈마다 소급 범위가 다르다. 설정은 이미 열려 있는 이슈까지 곧바로 옮기고,
// 온보딩은 이 결정 전에 열린 이슈를 옮기지 않는다.
//
// 소급 여부가 왜 선택지인가. issue-metadata-guard 의 스케줄 실행은 열린 이슈 전체를 훑으므로,
// 지도만 바꾸면 진행 중인 이슈까지 전부 새 담당자에게 넘어간다. 그러면 그 이슈로 열어 둔 PR 이
// validate-pr-issue-link 에서 "작성자가 대표 이슈의 담당자가 아니다" 로 빨개지고, 옮겨 받은
// 사람은 남이 절반쯤 짜 둔 브랜치를 떠안는다. 그래서 소급은 기본값이 아니라 결정 사항이다.
//
// fromIssue 미만의 이슈는 before 가 계속 맡는다. 경계는 결정을 적은 이슈 번호이고, 그보다 큰
// 번호는 전부 결정 이후에 열린 이슈다.
export const HANDOVER_BY_MODULE = Object.freeze({
onboarding: Object.freeze({ before: "1hyok", fromIssue: 1910 }),
});
// #1910: 담당 이관은 모듈마다 소급 범위를 고를 수 있다. 경계(fromIssue)를 두면 그 번호 미만의
// 열린 이슈는 before 가 계속 맡고, 경계가 없는 모듈은 스케줄 리컨사일이 열린 이슈 전체를 새
// 담당자에게 옮긴다. 온보딩은 #1910 에서 경계를 두었다가 온보딩 전체를 준혁이 맡기로 하면서
// 경계를 걷어 소급했다(0911). 그 결과 이관 전 이슈로 열어 둔 PR 은 작성자와 담당자가 달라
// validate-pr-issue-link 가 빨개지고, 리뷰어가 풀어야 한다.
export const HANDOVER_BY_MODULE = Object.freeze({});

/** 이관 경계를 반영한 담당자. 경계가 없는 모듈은 지도 그대로다. */
export function assigneeForIssue(moduleKey, issueNumber) {
Expand Down
7 changes: 4 additions & 3 deletions .github/scripts/reconcile-issue-metadata.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,10 @@ test("a module without a handover entry moves its open issues right away", () =>

test("an unknown issue number fails the handover judgement instead of guessing", () => {
// 경계 판정을 못 하면 어느 쪽으로도 접지 않는다. 접으면 옛 이슈가 조용히 새 담당자에게 간다.
const module = Object.keys(HANDOVER_BY_MODULE)[0];
assert.throws(() => assigneeForIssue(module, undefined), /이슈 번호/);
assert.throws(() => assigneeForIssue(module, "1910번"), /이슈 번호/);
for (const module of Object.keys(HANDOVER_BY_MODULE)) {
assert.throws(() => assigneeForIssue(module, undefined), /이슈 번호/);
assert.throws(() => assigneeForIssue(module, "1910번"), /이슈 번호/);
}
});

test("maps every work type to exactly one classification label", () => {
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/pr-assign-author.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# PR 작성자 자동 어사인
#
# 어사인이 비어 있는 PR 은 보드·필터에서 담당자가 사라진다. 이슈는
# issue-metadata-guard 가 어사인을 강제하는데 PR 은 손에 맡겨져 있었다.
# PR 이 열리면 작성자를 어사인하고, 누가 떼어 내도 다시 건다.
# review-request-all 과 같은 조건(봇·외부 기여자 제외)으로 돈다.
name: pr-assign-author

on:
pull_request:
types: [opened, reopened, ready_for_review, unassigned]

permissions:
pull-requests: write

jobs:
assign:
if: github.event.pull_request.state == 'open'
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- name: 작성자를 어사인
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
AUTHOR: ${{ github.event.pull_request.user.login }}
ASSOCIATION: ${{ github.event.pull_request.author_association }}
run: |
set -euo pipefail

case "$AUTHOR" in
*'[bot]'|dependabot|github-actions) echo "봇 PR — 건너뜀"; exit 0 ;;
esac

# 외부 기여자 PR 은 토큰이 read-only 라 어사인할 수 없다.
case "$ASSOCIATION" in
OWNER|MEMBER|COLLABORATOR) ;;
*) echo "외부 기여자($ASSOCIATION) — 건너뜀"; exit 0 ;;
esac

current=$(gh api "repos/$REPO/pulls/$PR_NUMBER" \
--jq '[.assignees[].login] | join(" ")')
case " $current " in
*" $AUTHOR "*) echo "이미 어사인됨"; exit 0 ;;
esac

gh api -X POST "repos/$REPO/issues/$PR_NUMBER/assignees" \
-f "assignees[]=$AUTHOR" >/dev/null
echo "어사인: $AUTHOR"
Loading