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
11 changes: 9 additions & 2 deletions .github/scripts/render-distribution-release-notes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ extract_section() {
local ascii_marker="$2"
local body_file="$3"

# 제목은 「# 뒤에 공백」 이 있는 줄뿐이다(마크다운 ATX). 이 구분이 없으면 「#130 에서 …」 처럼
# 이슈 번호로 시작하는 본문 줄이 제목으로 읽혀 섹션이 거기서 끊긴다. 그러면 Work Description
# 이 비어 배포가 「변경 내용이 하나 이상 필요합니다」 로 막힌다(#159).
awk -v marker="$marker" -v ascii_marker="$ascii_marker" '
/^#[#]*[[:space:]]*/ {
/^#+[[:space:]]/ {
heading = $0
if (capturing) {
exit
Expand Down Expand Up @@ -102,7 +105,11 @@ normalized_notes="$(

{
point = $0
sub(/^[[:space:]]*[-*][[:space:]]*/, "", point)
# 목록 표시는 「- 」 나 「* 」 처럼 뒤에 공백이 온다. 공백을 요구하지 않으면
# 「**굵게** 로 시작하는 문단」 의 별표 하나를 목록 표시로 보고 잘라 낸다(#159).
sub(/^[[:space:]]*[-*][[:space:]]+/, "", point)
# 내용 없이 표시만 있는 줄. 이 줄은 비운 것으로 봐야 아래 검사가 걸러 낸다.
sub(/^[[:space:]]*[-*][[:space:]]*$/, "", point)
sub(/^[[:space:]]*[0-9]+\.[[:space:]]*/, "", point)
sub(/^[[:space:]]+/, "", point)
sub(/[[:space:]]+$/, "", point)
Expand Down
39 changes: 39 additions & 0 deletions .github/scripts/render-distribution-release-notes.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,42 @@ test("수동 실행(WIF canary)은 입력값을 ; 로 나눠 릴리스 노트로
assert.match(notes, /^SlowClock 수동 배포 \(WIF canary\)\n/);
assert.match(notes, /포함 이슈\n- #35\n\n변경 내용\n- WIF 인증 업로드 확인\n- 앱이 로그인 화면까지 뜬다\n$/);
});

test("이슈 번호로 시작하는 본문 줄이 섹션을 끊지 않는다", async (t) => {
// 마크다운에서 # 뒤에 공백이 없으면 제목이 아니다. 구분하지 않으면 「#130 에서 …」 처럼
// 이슈 번호로 문장을 시작한 PR 이 그 줄에서 섹션을 끊어 배포가 막힌다(#159 실측).
const body =
"## 📌 Issues\n\nCloses #157\n\n## 📎 Work Description\n\n" +
"#130 에서 들어간 회귀입니다.\n- 회차 식별자를 되풀이하는 일정에만 채운다\n\n" +
"## 📷 Screenshot\n\n없음\n";
const { result, outputPath } = await runRenderer(t, { eventName: "push", body });
const notes = await fs.readFile(outputPath, "utf8");

assert.equal(result.status, 0, result.stderr);
assert.match(notes, /#130 에서 들어간 회귀입니다\./);
assert.match(notes, /회차 식별자를 되풀이하는 일정에만 채운다/);
});

test("섹션은 다음 제목에서 끊긴다", async (t) => {
// 위 완화가 섹션 경계까지 풀어 버리면 Screenshot 문단이 변경 내용으로 섞여 들어간다.
const body =
"## 📌 Issues\n\nCloses #7\n\n## 📎 Work Description\n\n" +
"- 알람이 잠금 화면에서도 뜬다\n\n## 📷 Screenshot\n\n스크린샷 없음\n";
const { result, outputPath } = await runRenderer(t, { eventName: "push", body });
const notes = await fs.readFile(outputPath, "utf8");

assert.equal(result.status, 0, result.stderr);
assert.match(notes, /알람이 잠금 화면에서도 뜬다/);
assert.doesNotMatch(notes, /스크린샷 없음/);
});

test("굵은 글씨로 시작하는 문단의 별표를 잘라 내지 않는다", async (t) => {
const body =
"## 📌 Issues\n\nCloses #8\n\n## 📎 Work Description\n\n" +
"**회차 식별자는 되풀이하는 일정에만 채웁니다.** 나머지는 그대로다.\n";
const { result, outputPath } = await runRenderer(t, { eventName: "push", body });
const notes = await fs.readFile(outputPath, "utf8");

assert.equal(result.status, 0, result.stderr);
assert.match(notes, /\*\*회차 식별자는 되풀이하는 일정에만 채웁니다\.\*\*/);
});