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
23 changes: 13 additions & 10 deletions .github/scripts/render-release-scope.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,25 @@ function sectionHasContent(lines, section) {
}

/**
* `## 포함 이슈` 는 항상 산출값으로 덮어쓰고, `## QA 포인트` 는 비어 있을 때만 초안을 채운다.
* 사람이 다시 쓴 QA 문구와 나머지 섹션은 건드리지 않는다.
* 산출된 목록이 있으면 `## 포함 이슈` 를 덮어쓰고, 비었으면 이미 적힌 목록을 남긴다 —
* 이미 배포된 릴리스 PR 을 다시 산출할 때 누적 PR 이 0건이라, 덮어쓰면 사람이 확인한
* 릴리스 노트 입력을 지우게 된다.
* `## QA 포인트` 는 항상 비어 있을 때만 초안을 채워 사람이 쓴 문장을 덮지 않는다.
* 섹션 자체가 없으면 둘 다 자리를 만든다 — 비어 있으면 배포가 중단되기 때문이다.
*/
export function applyReleaseScopeToBody(body, { issueSection, qaSection }) {
export function applyReleaseScopeToBody(body, { issueSection, qaSection, overwriteIssues = true }) {
let lines = String(body ?? "").split(/\r?\n/);

const replaceSection = (heading, content) => {
const writeSection = (heading, content, { onlyWhenEmpty }) => {
const section = findSection(lines, heading);
if (!section) {
const tail = lines.length > 0 && lines.at(-1).trim() === "" ? [] : [""];
lines = [...lines, ...tail, `## ${heading}`, "", ...content.split("\n"), ""];
return;
}
if (onlyWhenEmpty && sectionHasContent(lines, section)) {
return;
}
lines = [
...lines.slice(0, section.start + 1),
"",
Expand All @@ -122,12 +128,8 @@ export function applyReleaseScopeToBody(body, { issueSection, qaSection }) {
];
};

replaceSection(ISSUE_HEADING, issueSection);

const qa = findSection(lines, QA_HEADING);
if (!qa || !sectionHasContent(lines, qa)) {
replaceSection(QA_HEADING, qaSection);
}
writeSection(ISSUE_HEADING, issueSection, { onlyWhenEmpty: !overwriteIssues });
writeSection(QA_HEADING, qaSection, { onlyWhenEmpty: true });

return lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
}
Expand All @@ -144,6 +146,7 @@ async function main() {
body: applyReleaseScopeToBody(context.releasePullRequest?.body, {
issueSection: renderIssueSection(scope.includedIssues),
qaSection: renderQaDraftSection(scope),
overwriteIssues: scope.includedIssues.length > 0,
}),
};
await fs.mkdir(path.dirname(outputPath), { recursive: true });
Expand Down
25 changes: 25 additions & 0 deletions .github/scripts/render-release-scope.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,28 @@ test("leaves no heading inside the rendered sections", () => {
[],
);
});

test("keeps an issue list already in the body when nothing new was collected", () => {
const body = ["## 포함 이슈", "", "- #506", "- #618", "", "## QA 포인트", "", "- 사람이 쓴 문장"].join("\n");

const updated = applyReleaseScopeToBody(body, {
issueSection: renderIssueSection([]),
qaSection: "- 자동 초안",
overwriteIssues: false,
});

assert.match(updated, /- #506\n- #618/);
assert.doesNotMatch(updated, /연결된 이슈 없음/);
});

test("still fills an empty issue section when nothing was collected", () => {
const body = ["## 포함 이슈", "", "## QA 포인트", "", "- 사람이 쓴 문장"].join("\n");

const updated = applyReleaseScopeToBody(body, {
issueSection: renderIssueSection([]),
qaSection: "- 자동 초안",
overwriteIssues: false,
});

assert.match(updated, /## 포함 이슈\n\n- 연결된 이슈 없음/);
});
Loading