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
27 changes: 26 additions & 1 deletion .github/scripts/review-debt-policy.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,32 @@ test("changes requested becomes debt only after explicit rerequest and a fix", (
assert.match(guard, /sort_by\(\.t\) \| last/);
assert.doesNotMatch(guard, /group_by\(\.u/);
assert.match(guard, /\(\.parents \| length\) < 2/);
assert.match(guard, /select\(\.commit\.committer\.date > \\"\$blocked_at\\"\)/);
// 반영 판정의 기준 시각은 여전히 최신 변경요청 시각이다. 집계가 jq 에서 awk 로
// 옮겨 갔을 뿐, 그 이전 커밋을 반영으로 세지 않는다.
assert.match(guard, /-v cutoff="\$blocked_at"/);
assert.match(guard, /\$4 > cutoff/);
});

test("a reviewer's own commit is never counted as the author's fix", () => {
// 누가 올렸는지를 안 보면 리뷰어가 미는 CI 재트리거 커밋이 «작성자가 반영했다» 가
// 된다. 8/28 에 리뷰어가 건 +0/-0 커밋 하나로 koongmai PR 3건(#1379·#1365·#882)이
// 전부 가짜 빚이 됐고, 재트리거한 리뷰어가 그 대가로 자기 PR 을 못 열었다 (#1459).
assert.match(guard, /\(\.author\.login \/\/ ""\)/);
assert.match(guard, /login == target/);
// 계정이 연결되지 않은 커밋은 login 이 비어 가릴 수 없다. 같은 PR 에서 작성자
// 것으로 확인된 커밋의 이메일을 폴백 신원으로 쓴다.
assert.match(guard, /\(\.commit\.author\.email \/\/ ""\)/);
assert.match(guard, /email in own/);
});

test("an empty commit is not a fix", () => {
// pulls/{n}/commits 응답에는 파일 정보가 없다. 작성자 커밋으로 좁힌 후보에 한해
// commits/{sha} 를 조회해 바뀐 파일이 0건이면 버린다.
assert.match(guard, /repos\/\$REPO\/commits\/\$sha/);
assert.match(guard, /\(\.files \/\/ \[\]\) \| length/);
assert.match(guard, /\[ "\$changed" -gt 0 \] \|\| continue/);
// 조회 실패를 «빈 커밋» 으로 접으면 조회 장애가 빚을 통째로 지운다. 반영으로 센다.
assert.match(guard, /\'\'\|\*\[!0-9\]\*\) changed=1/);
});

test("a fix delivered by a merge commit still counts as a response", () => {
Expand Down
48 changes: 44 additions & 4 deletions .github/workflows/review-debt-guard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,50 @@ jobs:
#
# 페이지마다 집계하면 100건이 넘는 PR 에서 숫자가 여러 줄로 나와 비교가
# 깨진다. 건별로 뽑아 한 번에 센다.
fixed=$( { gh api "repos/$REPO/pulls/$pn/commits?per_page=100" --paginate \
--jq ".[] | select((.parents | length) < 2)
| select(.commit.committer.date > \"$blocked_at\") | .sha" 2>/dev/null || true; } \
| wc -l | tr -d ' ')
#
# 세는 것은 «작성자가 올린 실질 변경» 뿐이다. 누가 올렸는지·무엇이 바뀌었는지를
# 안 보면 리뷰어가 미는 CI 재트리거 빈 커밋까지 «작성자가 반영했다» 로 세어,
# 재트리거한 리뷰어가 그 대가로 자기 PR 을 못 열게 된다 — 8/28 에 리뷰어가 건
# +0/-0 커밋 하나로 koongmai PR 3건(#1379·#1365·#882)이 전부 가짜 빚이 됐다(#1459).
commits_file=$(mktemp)
{ gh api "repos/$REPO/pulls/$pn/commits?per_page=100" --paginate \
--jq '.[] | select((.parents | length) < 2)
| [.sha, (.author.login // ""), (.commit.author.email // ""), .commit.committer.date]
| @tsv' 2>/dev/null || true; } > "$commits_file"

# 계정이 연결되지 않은 커밋은 .author 가 비어 login 으로 가릴 수 없다. 같은 PR 에서
# 작성자 것으로 확인된 커밋의 이메일을 모아 폴백 신원으로 쓴다.
author_emails=$(awk -F'\t' -v author="$pr_author" '
BEGIN { target = tolower(author) }
tolower($2) == target && $3 != "" { print tolower($3) }' "$commits_file" | sort -u)

author_commits=$(awk -F'\t' -v author="$pr_author" -v cutoff="$blocked_at" -v emails="$author_emails" '
BEGIN {
target = tolower(author)
count = split(emails, list, "\n")
for (i = 1; i <= count; i++) if (list[i] != "") own[list[i]] = 1
}
$4 > cutoff {
login = tolower($2)
email = tolower($3)
if (login != "") { if (login == target) print $1 }
else if (email != "" && (email in own)) print $1
}' "$commits_file")

# 빈 커밋은 반영이 아니다. pulls/{n}/commits 응답에는 파일 정보가 없어 후보에
# 한해 commits/{sha} 를 더 조회한다 — 후보는 대개 한 자릿수라 비용이 작다.
# 조회가 실패하면 반영으로 센다. 빚을 씌우는 오탐보다 놓치는 쪽이 안전하고,
# 놓친 빚은 다음 PR 에서 다시 잡힌다.
fixed=0
while IFS= read -r sha; do
[ -n "$sha" ] || continue
changed=$( { gh api "repos/$REPO/commits/$sha" --jq '(.files // []) | length' 2>/dev/null || true; } | tail -1)
case "$changed" in
''|*[!0-9]*) changed=1 ;;
esac
[ "$changed" -gt 0 ] || continue
fixed=$((fixed + 1))
done <<< "$author_commits"

said=$( { gh api "repos/$REPO/issues/$pn/comments?per_page=100" --paginate \
--jq ".[] | select(.user.login == \"$pr_author\")
Expand Down
Loading