Skip to content

Commit a4da55c

Browse files
jinwoo-choi-05claude
andcommitted
chore: restructure publish pipeline
- Replace release-helper version with config/sync-version.js - Adopt workspace:~ protocol for inter-package dependencies - Add publish:stable/publish:beta scripts with per-package variants - Add .npmignore for flicking, flicking-plugins, react-flicking, vue3-flicking - Set api-docs-generator to private - Remove stale filters (ngx-flicking-env, packages/**/projects/**) - Add release-helper.json for beforeReleaseScripts - Add dev-guide/PUBLISH_GUIDE.md with version policy and workflows - Add sync-version.test.js with 14 test cases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ad5e457 commit a4da55c

17 files changed

Lines changed: 663 additions & 74 deletions

File tree

AGENTS.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,19 @@ pnpm lint # lint + format 검사
6262
pnpm lint:fix # 자동 수정
6363
pnpm format # 포맷팅만 적용
6464

65-
# 문서
65+
# 퍼블리시 (Publish: npm 패키지 배포, 상세 → dev-guide/PUBLISH_GUIDE.md)
66+
pnpm publish:version # 래퍼 버전 동기화 [patch|minor|major]
67+
pnpm publish:build # 전체 빌드
68+
pnpm publish:stable # 전체 빌드 + 정식 퍼블리시
69+
pnpm publish:stable:{pkg} # 개별 빌드 + 정식 퍼블리시 (flicking|react|vue|plugins)
70+
pnpm publish:beta # 전체 빌드 + 베타 퍼블리시
71+
pnpm publish:beta:{pkg} # 개별 빌드 + 베타 퍼블리시
72+
73+
# 릴리즈 (Release: changelog + commit + tag + GitHub Release)
74+
pnpm release # 전체 릴리즈 파이프라인
75+
pnpm release:changelog # changelog만 생성
76+
77+
# 문서 배포 (Deploy: 문서 사이트)
6678
pnpm docs:build # API 문서 생성 + Docusaurus 빌드
6779
pnpm docs:deploy # 빌드 + gh-pages 배포 (upstream)
6880
pnpm api-docs:generate && pnpm api-docs:docusaurus # API 문서만 재생성
@@ -98,6 +110,7 @@ pnpm api-docs:generate && pnpm api-docs:docusaurus # API 문서만 재생성
98110
| llms.txt | `dev-guide/LLMS_TXT_GUIDE.md` | llms.txt 작성 규칙, 링크 패턴, 체크리스트 | llms.txt 구조·링크 규칙 변경 시 |
99111
| E2E 테스트 | `dev-guide/E2E_TEST_GUIDE.md` | 데모 기반 E2E 테스트 구조, 스펙/테스트 작성법 | E2E 테스트 환경·구조 변경 시 |
100112
| E2E 리뷰 | `dev-guide/E2E_REVIEW_PROCESS.md` | 케이스별 리뷰 절차, 품질 등급, 현황 테이블 | E2E 케이스 리뷰 진행 시 |
113+
| 퍼블리시 | `dev-guide/PUBLISH_GUIDE.md` | 버전 정책, 배포 파이프라인, workspace: 프로토콜 | 배포 환경·버전 정책 변경 시 |
101114
| 하네스 | `dev-guide/HARNESS_GUIDE.md` | Claude Code 스킬, 훅, 서브에이전트 시스템 | 스킬·훅·에이전트 추가/변경 시 |
102115

103116
## 문서 유지보수 원칙
@@ -121,6 +134,7 @@ pnpm api-docs:generate && pnpm api-docs:docusaurus # API 문서만 재생성
121134
| 데모 구조·컴포넌트 변경 | `dev-guide/DEMO_GUIDE.md` |
122135
| E2E 테스트 환경·구조 변경 | `dev-guide/E2E_TEST_GUIDE.md` |
123136
| E2E 케이스 리뷰 진행 | `dev-guide/E2E_REVIEW_PROCESS.md` |
137+
| 배포 환경·버전 정책 변경 | `dev-guide/PUBLISH_GUIDE.md` |
124138
| 스킬·훅·에이전트 변경 | `dev-guide/HARNESS_GUIDE.md` |
125139

126140
### 금지 사항

config/sync-version.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* 코어(@egjs/flicking)의 version을 기준으로
3+
* dependencies에 코어를 가진 래퍼 패키지들의 version을 동기화한다.
4+
*
5+
* 버전 정책:
6+
* - major: 래퍼의 major = 코어의 major (minor/patch 리셋)
7+
* - minor: 래퍼의 minor +1, patch 리셋
8+
* - patch: 래퍼의 patch +1
9+
*
10+
* 정식 배포 전용. 베타 배포 시에는 각 패키지 버전을 수동으로 관리한다.
11+
*
12+
* Usage:
13+
* node config/sync-version.js patch
14+
* node config/sync-version.js minor
15+
* node config/sync-version.js major
16+
*/
17+
const fs = require("fs");
18+
const path = require("path");
19+
20+
function parseVersion(version) {
21+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
22+
if (!match) throw new Error(`Invalid version: ${version}`);
23+
return {
24+
major: Number(match[1]),
25+
minor: Number(match[2]),
26+
patch: Number(match[3]),
27+
prerelease: match[4] || "",
28+
};
29+
}
30+
31+
function formatVersion({ major, minor, patch, prerelease }) {
32+
const base = `${major}.${minor}.${patch}`;
33+
return prerelease ? `${base}-${prerelease}` : base;
34+
}
35+
36+
/**
37+
* 코어 버전과 래퍼의 현재 버전, bump 타입을 받아 래퍼의 새 버전을 계산한다.
38+
* @param {string} coreVersion - 코어 버전 (e.g. "4.17.0")
39+
* @param {string} wrapperVersion - 래퍼의 현재 버전 (e.g. "4.18.0")
40+
* @param {"major"|"minor"|"patch"} bumpType - bump 타입
41+
* @returns {string} 래퍼의 새 버전
42+
*/
43+
function calcNextVersion(coreVersion, wrapperVersion, bumpType) {
44+
const core = parseVersion(coreVersion);
45+
const prev = parseVersion(wrapperVersion);
46+
47+
let next;
48+
49+
if (bumpType === "major") {
50+
next = { major: core.major, minor: 0, patch: 0, prerelease: "" };
51+
} else if (bumpType === "minor") {
52+
const newMinor = Math.max(core.minor, prev.minor + 1);
53+
next = { major: core.major, minor: newMinor, patch: 0, prerelease: "" };
54+
} else {
55+
next = { major: core.major, minor: prev.minor, patch: prev.patch + 1, prerelease: "" };
56+
}
57+
58+
return formatVersion(next);
59+
}
60+
61+
// CLI 실행
62+
if (require.main === module) {
63+
const ROOT = path.resolve(__dirname, "..");
64+
const CORE_PKG = "packages/flicking/package.json";
65+
const WRAPPER_PKGS = [
66+
"packages/react-flicking/package.json",
67+
"packages/vue3-flicking/package.json",
68+
];
69+
70+
const bumpType = process.argv[2];
71+
72+
if (!bumpType || !["major", "minor", "patch"].includes(bumpType)) {
73+
console.error(
74+
"Usage: node config/sync-version.js <major|minor|patch>\n" +
75+
" major 코어 메이저 업데이트 시 (래퍼 메이저 동기화)\n" +
76+
" minor 코어 마이너 업데이트 시 (래퍼 마이너 +1)\n" +
77+
" patch 코어 패치 업데이트 시 (래퍼 패치 +1)"
78+
);
79+
process.exit(1);
80+
}
81+
82+
const coreJson = JSON.parse(fs.readFileSync(path.join(ROOT, CORE_PKG), "utf8"));
83+
console.log(`Core version: ${coreJson.version} (bump type: ${bumpType})\n`);
84+
85+
let changed = 0;
86+
87+
for (const rel of WRAPPER_PKGS) {
88+
const abs = path.join(ROOT, rel);
89+
const json = JSON.parse(fs.readFileSync(abs, "utf8"));
90+
const nextStr = calcNextVersion(coreJson.version, json.version, bumpType);
91+
92+
if (json.version === nextStr) {
93+
console.log(` ${json.name}@${json.version} — already up to date`);
94+
continue;
95+
}
96+
97+
const prev = json.version;
98+
json.version = nextStr;
99+
fs.writeFileSync(abs, JSON.stringify(json, null, 2) + "\n");
100+
console.log(` ${json.name}: ${prev}${nextStr}`);
101+
changed++;
102+
}
103+
104+
console.log(changed > 0 ? `\n${changed} package(s) updated.` : "\nNothing to update.");
105+
}
106+
107+
module.exports = { parseVersion, formatVersion, calcNextVersion };

config/sync-version.test.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { describe, it, expect } from "vitest";
2+
import { parseVersion, formatVersion, calcNextVersion } from "./sync-version.js";
3+
4+
describe("parseVersion", () => {
5+
it("stable 버전을 파싱한다", () => {
6+
expect(parseVersion("4.16.0")).toEqual({ major: 4, minor: 16, patch: 0, prerelease: "" });
7+
});
8+
9+
it("prerelease 버전을 파싱한다", () => {
10+
expect(parseVersion("4.16.0-beta.2")).toEqual({ major: 4, minor: 16, patch: 0, prerelease: "beta.2" });
11+
});
12+
13+
it("잘못된 버전이면 에러를 던진다", () => {
14+
expect(() => parseVersion("invalid")).toThrow("Invalid version");
15+
});
16+
});
17+
18+
describe("formatVersion", () => {
19+
it("stable 버전을 포맷한다", () => {
20+
expect(formatVersion({ major: 4, minor: 16, patch: 0, prerelease: "" })).toBe("4.16.0");
21+
});
22+
23+
it("prerelease 버전을 포맷한다", () => {
24+
expect(formatVersion({ major: 4, minor: 16, patch: 0, prerelease: "beta.2" })).toBe("4.16.0-beta.2");
25+
});
26+
});
27+
28+
// calcNextVersion(코어 버전, 래퍼 현재 버전, bump type) → 래퍼 새 버전
29+
describe("calcNextVersion", () => {
30+
describe("minor", () => {
31+
it("코어 4.17.0, 래퍼 4.15.0 → 래퍼가 뒤처져 있으므로 코어 minor에 맞춰 4.17.0", () => {
32+
expect(calcNextVersion("4.17.0", "4.15.0", "minor")).toBe("4.17.0");
33+
});
34+
35+
it("코어 4.17.0, 래퍼 4.17.0 → 같은 minor이므로 래퍼 minor +1 → 4.18.0", () => {
36+
expect(calcNextVersion("4.17.0", "4.17.0", "minor")).toBe("4.18.0");
37+
});
38+
39+
it("코어 4.17.0, 래퍼 4.18.0 → 래퍼가 앞서있으므로 래퍼 minor +1 → 4.19.0", () => {
40+
expect(calcNextVersion("4.17.0", "4.18.0", "minor")).toBe("4.19.0");
41+
});
42+
43+
it("코어 4.17.0, 래퍼 4.16.3 → minor +1 되면서 patch는 0으로 리셋 → 4.17.0", () => {
44+
expect(calcNextVersion("4.17.0", "4.16.3", "minor")).toBe("4.17.0");
45+
});
46+
});
47+
48+
describe("patch", () => {
49+
it("코어 4.16.1, 래퍼 4.16.0 → 래퍼 patch +1 → 4.16.1", () => {
50+
expect(calcNextVersion("4.16.1", "4.16.0", "patch")).toBe("4.16.1");
51+
});
52+
53+
it("코어 4.16.2, 래퍼 4.16.3 → 래퍼가 앞서있어도 patch +1 → 4.16.4", () => {
54+
expect(calcNextVersion("4.16.2", "4.16.3", "patch")).toBe("4.16.4");
55+
});
56+
57+
it("코어 4.16.1, 래퍼 4.18.0 → 래퍼 minor는 유지하고 patch만 +1 → 4.18.1", () => {
58+
expect(calcNextVersion("4.16.1", "4.18.0", "patch")).toBe("4.18.1");
59+
});
60+
});
61+
62+
describe("major", () => {
63+
it("코어 5.0.0, 래퍼 4.18.3 → 코어 major로 동기화, minor/patch 리셋 → 5.0.0", () => {
64+
expect(calcNextVersion("5.0.0", "4.18.3", "major")).toBe("5.0.0");
65+
});
66+
});
67+
});
68+
69+
describe("CLI", () => {
70+
it("bump type 없이 실행 시 exit code 1", () => {
71+
const { execSync } = require("child_process");
72+
expect(() => {
73+
execSync("node config/sync-version.js", { cwd: process.cwd(), stdio: "pipe" });
74+
}).toThrow();
75+
});
76+
});

0 commit comments

Comments
 (0)