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
4 changes: 3 additions & 1 deletion .github/actions/release-notes/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ runs:
using: 'composite'
steps:
- name: Node 설정
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: 24
# 의존성 설치 없이 Node 만 쓴다. packageManager 가 npm 으로 바뀌어도 캐시를 찾지 않게 끈다
package-manager-cache: false

# 노트 형식은 spec 이 있는 스크립트 하나에만 둔다
- name: 노트 만들기
Expand Down
4 changes: 3 additions & 1 deletion .github/actions/version-label/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ runs:
using: 'composite'
steps:
- name: Node 설정
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: 24
# 의존성 설치 없이 Node 만 쓴다. packageManager 가 npm 으로 바뀌어도 캐시를 찾지 않게 끈다
package-manager-cache: false

# 판단 규칙은 spec 이 있는 스크립트 하나에만 둔다
- name: 버전 레이블 읽기
Expand Down
85 changes: 85 additions & 0 deletions .github/scripts/buildReleaseNotes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
extractWorkSection,
formatHeadings,
getSection,
removeExcludedHeadings,
} from './buildReleaseNotes';

/**
Expand Down Expand Up @@ -74,6 +75,43 @@ describe('extractWorkSection 은 작업 내용 절만 꺼낸다', () => {
});
});

describe('removeExcludedHeadings 는 리뷰어용 소제목 블록을 뺀다', () => {
it('검증, 참고 블록을 다음 소제목이나 절 끝까지 뺀다', () => {
const section = [
'### 1. 기능',
'- 바뀐 동작',
'### 검증',
'- 테스트 통과',
'### 2. 다른 기능',
'- 또 바뀐 동작',
'### 참고',
'- 배포 직후 새로고침',
].join('\n');

expect(removeExcludedHeadings(section)).toBe(
'### 1. 기능\n- 바뀐 동작\n### 2. 다른 기능\n- 또 바뀐 동작',
);
});

it.each(['### 검증', '### 3. 테스트', '### 참고 '])(
'"%s" 처럼 번호나 공백이 붙어도 뺀다',
(heading) => {
expect(removeExcludedHeadings(`### 기능\n- a\n${heading}\n- 빠질 내용`)).toBe(
'### 기능\n- a',
);
},
);

it('제목에 검증이라는 말이 들어 있을 뿐인 소제목은 남긴다', () => {
const section = '### 웹훅 검증을 추가\n- 시크릿 확인';
expect(removeExcludedHeadings(section)).toBe(section);
});

it('전부 빠지면 빈 문자열이다', () => {
expect(removeExcludedHeadings('### 검증\n- 테스트')).toBe('');
});
});

describe('formatHeadings 는 소제목을 노트 형식으로 바꾼다', () => {
it('번호를 떼고 끝에 PR 번호를 붙인다', () => {
expect(formatHeadings('### 1. 옛 파일 정리\n\n- 내용', 105)).toBe(
Expand Down Expand Up @@ -127,6 +165,53 @@ describe('buildReleaseNotes 는 v3.0.0, v3.0.1 과 같은 형식의 노트를
expect(notes).toContain('## Etc\n\n### 푸터 버전 표시 (#105)');
});

/**
* v3.0.2 노트에 `### 검증 (#112)`, `### 참고 (#112)` 가 섞였던 본문 구조다.
* 작업 내용 절 안에 검증과 참고를 `###` 로 두었다.
*/
it('#112 처럼 작업 내용 안에 검증과 참고를 둔 본문도 기능 소제목만 싣는다', () => {
const body = `## 작업 내용

### 1. 서버 응답에서 테마를 뺐다

문단

### 2. 테마를 사용자별로 localStorage 에 둔다

문단

### 검증

- 단위 테스트

### 참고

- 배포 직후 새로고침
`;
const { notes, fallback } = buildReleaseNotes({
...base,
prNumber: 112,
title: 'fix: 테마',
body,
});

expect(fallback).toBe(false);
expect(notes).toContain('### 서버 응답에서 테마를 뺐다 (#112)');
expect(notes).toContain('### 테마를 사용자별로 localStorage 에 둔다 (#112)');
expect(notes).not.toContain('검증');
expect(notes).not.toContain('참고');
});

it('작업 내용이 검증뿐이면 PR 제목으로 대신한다', () => {
const { notes, fallback } = buildReleaseNotes({
...base,
title: 'fix: 제목 요약',
body: '## 작업 내용\n\n### 검증\n\n- 테스트',
});
expect(fallback).toBe(true);
expect(notes).toContain('### 제목 요약 (#105)');
});

it('직전 태그가 없으면 Full Changelog 를 쓰지 않는다', () => {
const { notes } = buildReleaseNotes({
...base,
Expand Down
22 changes: 21 additions & 1 deletion .github/scripts/buildReleaseNotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ export function extractWorkSection(body: string): string | null {
return section || null;
}

/** 작업 내용 안에 있어도 노트에 싣지 않는 소제목. 방문자가 아니라 리뷰어가 읽는 내용이다 (#115) */
export const EXCLUDED_HEADINGS = ['검증', '참고', '테스트'];

/**
* @description 작업 내용에서 `EXCLUDED_HEADINGS` 소제목 블록을 뺀다. 블록은 다음 `###` 소제목이나 절 끝까지다
* @param section 작업 내용 절
* @returns 뺀 절. 남은 내용이 없으면 빈 문자열
* @example
* removeExcludedHeadings('### 기능\n- a\n### 검증\n- 테스트 통과'); // '### 기능\n- a'
*/
export function removeExcludedHeadings(section: string): string {
let skipping = false;
const kept = section.split('\n').filter((line) => {
const heading = /^###[ \t]+(?:\d+\.[ \t]*)?(.+?)[ \t]*$/.exec(line)?.[1];
if (heading !== undefined) skipping = EXCLUDED_HEADINGS.includes(heading);
return !skipping;
});
return kept.join('\n').trim();
}

/**
* @description 작업 내용의 소제목을 노트 형식으로 바꾼다. `### 1. 제목` 의 번호를 떼고 끝에 `(#PR)` 을 붙인다
* @param section 작업 내용 절
Expand All @@ -77,7 +97,7 @@ export function formatHeadings(section: string, prNumber: number): string {
*/
export function buildReleaseNotes(input: ReleaseNotesInput): { notes: string; fallback: boolean } {
const { title, body, prNumber, tag, prevTag, repo } = input;
const section = extractWorkSection(body);
const section = removeExcludedHeadings(extractWorkSection(body) ?? '') || null;
const summary = title.replace(/^[a-z]+!?:\s*/, '').trim();

const content = section ? formatHeadings(section, prNumber) : `### ${summary} (#${prNumber})`;
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 머지 커밋 가져오기
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
fetch-depth: 0
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/sync-release-prs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
prs: ${{ steps.list.outputs.prs }}
steps:
- name: 설정 파일 가져오기
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
ref: ${{ github.base_ref }}
sparse-checkout: .github
Expand Down Expand Up @@ -63,7 +63,7 @@ jobs:
steps:
# PR 브랜치가 아니라 base 를 checkout 한다. 액션이 생기기 전에 딴 PR 에는 .github/actions 가 없다 (sync-base 참고)
- name: base 브랜치 가져오기
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
ref: ${{ github.base_ref }}
token: ${{ secrets.RELEASE_BOT_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/version-bump.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: PR 브랜치 가져오기
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.ref }}
token: ${{ secrets.RELEASE_BOT_TOKEN }}
Expand Down
Loading