@k2works/claude-code-booster 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +42 -42
  3. package/bin/claude-code-booster +79 -79
  4. package/lib/assets/.claude/README.md +162 -162
  5. package/lib/assets/.claude/SKILLS_TEMPLATE.md +100 -100
  6. package/lib/assets/.claude/scripts/generate-inception-deck.mjs +911 -911
  7. package/lib/assets/.claude/settings.json +11 -11
  8. package/lib/assets/.claude/skills/ai-agent-guidelines/SKILL.md +119 -119
  9. package/lib/assets/.claude/skills/analyzing-architecture/SKILL.md +87 -87
  10. package/lib/assets/.claude/skills/analyzing-business/SKILL.md +117 -117
  11. package/lib/assets/.claude/skills/analyzing-data-model/SKILL.md +80 -80
  12. package/lib/assets/.claude/skills/analyzing-domain-model/SKILL.md +88 -88
  13. package/lib/assets/.claude/skills/analyzing-inception-deck/SKILL.md +137 -137
  14. package/lib/assets/.claude/skills/analyzing-non-functional/SKILL.md +91 -91
  15. package/lib/assets/.claude/skills/analyzing-operation/SKILL.md +91 -91
  16. package/lib/assets/.claude/skills/analyzing-requirements/SKILL.md +89 -87
  17. package/lib/assets/.claude/skills/analyzing-tech-stack/SKILL.md +102 -102
  18. package/lib/assets/.claude/skills/analyzing-test-strategy/SKILL.md +87 -87
  19. package/lib/assets/.claude/skills/analyzing-ui-design/SKILL.md +86 -86
  20. package/lib/assets/.claude/skills/analyzing-usecases/SKILL.md +87 -87
  21. package/lib/assets/.claude/skills/creating-adr/SKILL.md +115 -115
  22. package/lib/assets/.claude/skills/developing-backend/SKILL.md +106 -106
  23. package/lib/assets/.claude/skills/developing-frontend/SKILL.md +96 -96
  24. package/lib/assets/.claude/skills/developing-release/SKILL.md +154 -154
  25. package/lib/assets/.claude/skills/generating-slides/SKILL.md +136 -136
  26. package/lib/assets/.claude/skills/git-commit/SKILL.md +106 -106
  27. package/lib/assets/.claude/skills/killing-processes/SKILL.md +98 -98
  28. package/lib/assets/.claude/skills/managing-docs/SKILL.md +200 -200
  29. package/lib/assets/.claude/skills/managing-operations/DEPLOY.md +77 -77
  30. package/lib/assets/.claude/skills/managing-operations/SETUP_CSHARP.md +80 -80
  31. package/lib/assets/.claude/skills/managing-operations/SETUP_FRONTEND.md +84 -84
  32. package/lib/assets/.claude/skills/managing-operations/SETUP_JAVA.md +75 -75
  33. package/lib/assets/.claude/skills/managing-operations/SKILL.md +156 -156
  34. package/lib/assets/.claude/skills/orchestrating-analysis/SKILL.md +134 -134
  35. package/lib/assets/.claude/skills/orchestrating-development/SKILL.md +243 -243
  36. package/lib/assets/.claude/skills/orchestrating-project/SKILL.md +193 -193
  37. package/lib/assets/.claude/skills/planning-releases/SKILL.md +222 -222
  38. package/lib/assets/.claude/skills/tracking-progress/SKILL.md +164 -164
  39. package/lib/assets/.devcontainer/devcontainer.json +34 -34
  40. package/lib/assets/.env.example +17 -17
  41. package/lib/assets/.gitattributes +4 -4
  42. package/lib/assets/.github/workflows/docker-publish.yml +77 -77
  43. package/lib/assets/.github/workflows/mkdocs.yml +39 -39
  44. package/lib/assets/AGENTS.md +94 -94
  45. package/lib/assets/CLAUDE.md +162 -162
  46. package/lib/assets/README.md +285 -269
  47. package/lib/assets/docker-compose.yml +33 -33
  48. package/lib/assets/docs/assets/css/extra.css +29 -29
  49. package/lib/assets/docs/assets/js/extra.js +44 -44
  50. package/lib/assets/docs/index.md +14 -14
  51. package/lib/assets/docs/reference/CodexCLIMCP/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/343/203/225/343/203/255/343/203/274.md +532 -532
  52. package/lib/assets/docs/reference/CodexCLIMCP/343/202/265/343/203/274/343/203/220/343/203/274/350/250/255/345/256/232/346/211/213/351/240/206.md +341 -341
  53. package/lib/assets/docs/reference/Java/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +578 -578
  54. package/lib/assets/docs/reference/TypeScript/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +465 -465
  55. package/lib/assets/docs/reference/UI/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +448 -448
  56. package/lib/assets/docs/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +242 -242
  57. package/lib/assets/docs/reference//343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +2216 -2216
  58. package/lib/assets/docs/reference//343/202/244/343/203/263/343/203/225/343/203/251/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +1878 -1878
  59. package/lib/assets/docs/reference//343/202/250/343/202/257/343/202/271/343/203/210/343/203/252/343/203/274/343/203/240/343/203/227/343/203/255/343/202/260/343/203/251/343/203/237/343/203/263/343/202/260.md +554 -554
  60. package/lib/assets/docs/reference//343/202/263/343/203/274/343/203/207/343/202/243/343/203/263/343/202/260/343/201/250/343/203/206/343/202/271/343/203/210/343/202/254/343/202/244/343/203/211.md +705 -705
  61. package/lib/assets/docs/reference//343/203/206/343/202/271/343/203/210/346/210/246/347/225/245/343/202/254/343/202/244/343/203/211.md +1313 -1313
  62. package/lib/assets/docs/reference//343/203/207/343/203/274/343/202/277/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +311 -311
  63. package/lib/assets/docs/reference//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +599 -599
  64. package/lib/assets/docs/reference//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +528 -528
  65. package/lib/assets/docs/reference//343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +682 -682
  66. package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +442 -442
  67. package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/203/273/343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273/343/202/254/343/202/244/343/203/211.md +558 -558
  68. package/lib/assets/docs/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +663 -663
  69. package/lib/assets/docs/reference//350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1248 -1248
  70. package/lib/assets/docs/reference//351/201/213/347/224/250/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +392 -392
  71. package/lib/assets/docs/reference//351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +235 -235
  72. package/lib/assets/docs/reference//351/235/236/346/251/237/350/203/275/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1236 -1236
  73. package/lib/assets/docs/template/ADR.md +30 -30
  74. package/lib/assets/docs/template/README.md +50 -50
  75. package/lib/assets/docs/template//343/201/276/343/201/232/343/201/223/343/202/214/343/202/222/350/252/255/343/202/202/343/201/206/343/203/252/343/202/271/343/203/210.md +12 -12
  76. package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +58 -58
  77. package/lib/assets/docs/template//343/202/244/343/203/263/343/202/273/343/203/227/343/202/267/343/203/247/343/203/263/343/203/207/343/203/203/343/202/255.md +13 -13
  78. package/lib/assets/docs/template//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243.md +379 -379
  79. package/lib/assets/docs/template//345/256/214/345/205/250/345/275/242/345/274/217/343/201/256/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271.md +68 -68
  80. package/lib/assets/docs/template//350/246/201/344/273/266/345/256/232/347/276/251.md +669 -669
  81. package/lib/assets/docs/template//350/250/255/350/250/210.md +163 -163
  82. package/lib/assets/gulpfile.js +23 -23
  83. package/lib/assets/mkdocs.yml +65 -65
  84. package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
  85. package/lib/assets/ops/scripts/journal.js +180 -180
  86. package/lib/assets/ops/scripts/mkdocs.js +82 -82
  87. package/lib/assets/ops/scripts/release.js +431 -431
  88. package/lib/assets/ops/scripts/ssh.js +190 -190
  89. package/lib/assets/ops/scripts/vault.js +299 -299
  90. package/lib/assets/package-lock.json +1653 -1653
  91. package/lib/assets/package.json +40 -40
  92. package/lib/gulpfile.js +37 -37
  93. package/package.json +41 -41
@@ -1,431 +1,431 @@
1
- 'use strict';
2
-
3
- import fs from 'fs';
4
- import path from 'path';
5
- import { execSync } from 'child_process';
6
-
7
- // ============================================
8
- // 設定
9
- // ============================================
10
-
11
- /**
12
- * リリース対象のパッケージディレクトリ一覧を返す。
13
- * モノレポの場合は apps/packages 配下を追加する。
14
- * @param {string} rootDir - プロジェクトルート
15
- * @returns {string[]} パッケージディレクトリの絶対パス配列
16
- */
17
- function discoverPackageDirs(rootDir) {
18
- const dirs = [rootDir];
19
- const candidates = ['apps', 'packages'];
20
-
21
- for (const candidate of candidates) {
22
- const dir = path.join(rootDir, candidate);
23
- if (!fs.existsSync(dir)) continue;
24
- const entries = fs.readdirSync(dir, { withFileTypes: true });
25
- for (const entry of entries) {
26
- if (!entry.isDirectory()) continue;
27
- const pkgJson = path.join(dir, entry.name, 'package.json');
28
- if (fs.existsSync(pkgJson)) {
29
- dirs.push(path.join(dir, entry.name));
30
- }
31
- }
32
- }
33
-
34
- return dirs;
35
- }
36
-
37
- // ============================================
38
- // ヘルパー関数
39
- // ============================================
40
-
41
- /**
42
- * root の package.json からバージョンを読み取る
43
- * @param {string} rootDir - プロジェクトルート
44
- * @returns {string} 現在のバージョン (例: "0.1.0")
45
- */
46
- function getCurrentVersion(rootDir) {
47
- const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8'));
48
- return pkg.version;
49
- }
50
-
51
- /**
52
- * ドライラン用の簡易 semver 計算
53
- * @param {string} version - 現在のバージョン (例: "0.1.0")
54
- * @param {string} type - バンプの種類 ("patch" | "minor" | "major")
55
- * @returns {string} 新しいバージョン
56
- */
57
- function semverBump(version, type) {
58
- const [major, minor, patch] = version.split('.').map(Number);
59
- switch (type) {
60
- case 'major':
61
- return `${major + 1}.0.0`;
62
- case 'minor':
63
- return `${major}.${minor + 1}.0`;
64
- case 'patch':
65
- return `${major}.${minor}.${patch + 1}`;
66
- default:
67
- throw new Error(`Unknown bump type: ${type}`);
68
- }
69
- }
70
-
71
- /**
72
- * 対象パッケージのバージョンを同期更新
73
- * @param {string} rootDir - プロジェクトルート
74
- * @param {string} type - バンプの種類 ("patch" | "minor" | "major")
75
- * @returns {string} 新しいバージョン
76
- */
77
- function bumpVersions(rootDir, type) {
78
- const dirs = discoverPackageDirs(rootDir);
79
-
80
- for (const dir of dirs) {
81
- execSync(`npm version ${type} --no-git-tag-version`, {
82
- cwd: dir,
83
- stdio: 'pipe',
84
- });
85
- }
86
-
87
- return getCurrentVersion(rootDir);
88
- }
89
-
90
- /**
91
- * 直近タグから HEAD までの git log を Conventional Commits で分類し Markdown を生成
92
- * @param {string} rootDir - プロジェクトルート
93
- * @param {string} version - リリースバージョン
94
- * @returns {string} CHANGELOG エントリ(Markdown)
95
- */
96
- function generateChangelog(rootDir, version) {
97
- let lastTag;
98
- try {
99
- lastTag = execSync('git describe --tags --abbrev=0', {
100
- cwd: rootDir,
101
- stdio: 'pipe',
102
- encoding: 'utf8',
103
- }).trim();
104
- } catch {
105
- lastTag = null;
106
- }
107
-
108
- const range = lastTag ? `${lastTag}..HEAD` : 'HEAD';
109
- let logOutput;
110
- try {
111
- logOutput = execSync(`git log ${range} --pretty=format:"%h|||%s"`, {
112
- cwd: rootDir,
113
- stdio: 'pipe',
114
- encoding: 'utf8',
115
- }).trim();
116
- } catch {
117
- logOutput = '';
118
- }
119
-
120
- if (!logOutput) {
121
- return buildChangelogEntry(version, {});
122
- }
123
-
124
- const categories = {
125
- feat: { title: 'Features', commits: [] },
126
- fix: { title: 'Bug Fixes', commits: [] },
127
- docs: { title: 'Documentation', commits: [] },
128
- refactor: { title: 'Refactoring', commits: [] },
129
- test: { title: 'Tests', commits: [] },
130
- chore: { title: 'Chores', commits: [] },
131
- perf: { title: 'Performance', commits: [] },
132
- ci: { title: 'CI', commits: [] },
133
- style: { title: 'Styles', commits: [] },
134
- build: { title: 'Build', commits: [] },
135
- other: { title: 'Other', commits: [] },
136
- };
137
-
138
- for (const line of logOutput.split('\n')) {
139
- if (!line.trim()) continue;
140
- const [hash, ...rest] = line.split('|||');
141
- const subject = rest.join('|||');
142
- const match = subject.match(/^(\w+)(?:\(.+?\))?:\s*(.+)$/);
143
- if (match) {
144
- const type = match[1].toLowerCase();
145
- const message = match[2];
146
- if (categories[type]) {
147
- categories[type].commits.push({ hash: hash.trim(), message });
148
- } else {
149
- categories.other.commits.push({ hash: hash.trim(), message: subject });
150
- }
151
- } else {
152
- categories.other.commits.push({ hash: hash.trim(), message: subject });
153
- }
154
- }
155
-
156
- return buildChangelogEntry(version, categories);
157
- }
158
-
159
- /**
160
- * CHANGELOG エントリを Markdown で構築
161
- * @param {string} version
162
- * @param {Object} categories
163
- * @returns {string}
164
- */
165
- function buildChangelogEntry(version, categories) {
166
- const date = new Date().toISOString().slice(0, 10);
167
- const lines = [`## [${version}] - ${date}`, ''];
168
-
169
- let hasContent = false;
170
- for (const [, cat] of Object.entries(categories)) {
171
- if (cat.commits && cat.commits.length > 0) {
172
- hasContent = true;
173
- lines.push(`### ${cat.title}`, '');
174
- for (const c of cat.commits) {
175
- lines.push(`- ${c.message} (${c.hash})`);
176
- }
177
- lines.push('');
178
- }
179
- }
180
-
181
- if (!hasContent) {
182
- lines.push('- No notable changes', '');
183
- }
184
-
185
- return lines.join('\n');
186
- }
187
-
188
- /**
189
- * CHANGELOG.md の先頭にエントリを追加(なければ新規作成)
190
- * @param {string} rootDir - プロジェクトルート
191
- * @param {string} entry - 追加する CHANGELOG エントリ
192
- */
193
- function updateChangelog(rootDir, entry) {
194
- const changelogPath = path.join(rootDir, 'CHANGELOG.md');
195
- const header = '# Changelog\n\n';
196
-
197
- if (fs.existsSync(changelogPath)) {
198
- const existing = fs.readFileSync(changelogPath, 'utf8');
199
- const withoutHeader = existing.replace(/^# Changelog\s*\n*/, '');
200
- fs.writeFileSync(changelogPath, header + entry + '\n' + withoutHeader);
201
- } else {
202
- fs.writeFileSync(changelogPath, header + entry);
203
- }
204
- }
205
-
206
- /**
207
- * 変更ファイルをステージング → commit → tag
208
- * @param {string} rootDir - プロジェクトルート
209
- * @param {string} version - リリースバージョン
210
- */
211
- function createGitCommitAndTag(rootDir, version) {
212
- const filesToStage = collectFilesToStage(rootDir);
213
-
214
- for (const file of filesToStage) {
215
- execSync(`git add "${file}"`, { cwd: rootDir, stdio: 'pipe' });
216
- }
217
-
218
- execSync(`git commit -m "release: v${version}"`, {
219
- cwd: rootDir,
220
- stdio: 'pipe',
221
- env: { ...process.env, USER_APPROVED_COMMIT: '1' },
222
- });
223
-
224
- execSync(`git tag -a "v${version}" -m "v${version}"`, {
225
- cwd: rootDir,
226
- stdio: 'pipe',
227
- });
228
- }
229
-
230
- /**
231
- * ステージング対象のファイルパスを収集
232
- * @param {string} rootDir - プロジェクトルート
233
- * @returns {string[]} 相対パスの配列
234
- */
235
- function collectFilesToStage(rootDir) {
236
- const files = ['package.json', 'CHANGELOG.md'];
237
- const dirs = discoverPackageDirs(rootDir);
238
-
239
- for (const dir of dirs) {
240
- if (dir === rootDir) continue;
241
- const relative = path.relative(rootDir, dir);
242
- files.push(path.join(relative, 'package.json'));
243
- }
244
-
245
- return files;
246
- }
247
-
248
- /**
249
- * リリース結果のサマリーを表示
250
- * @param {string} oldVersion - 旧バージョン
251
- * @param {string} newVersion - 新バージョン
252
- */
253
- function printSummary(oldVersion, newVersion) {
254
- console.log('');
255
- console.log('=== Release Summary ===');
256
- console.log(` Version: ${oldVersion} -> ${newVersion}`);
257
- console.log(` Tag: v${newVersion}`);
258
- console.log(` Commit: release: v${newVersion}`);
259
- console.log('');
260
- console.log('Next steps:');
261
- console.log(' git push && git push --tags');
262
- console.log('');
263
- }
264
-
265
- // ============================================
266
- // コマンド実行ヘルパー
267
- // ============================================
268
-
269
- /**
270
- * シェルコマンドを実行し、失敗時に done コールバックへエラーを渡す
271
- * @param {string} command - 実行するコマンド
272
- * @param {string} rootDir - 実行ディレクトリ
273
- * @param {Function} done - コールバック
274
- */
275
- function runCommandInDir(command, rootDir, done) {
276
- try {
277
- execSync(command, { cwd: rootDir, stdio: 'inherit' });
278
- done();
279
- } catch (error) {
280
- done(error);
281
- }
282
- }
283
-
284
- // ============================================
285
- // タスク登録
286
- // ============================================
287
-
288
- /**
289
- * 指定されたタイプのリリースタスクを生成
290
- * @param {string} rootDir - プロジェクトルート
291
- * @param {string} type - "patch" | "minor" | "major"
292
- * @returns {Function} gulp タスク関数
293
- */
294
- function createReleaseTask(rootDir, type) {
295
- const task = (done) => {
296
- try {
297
- const oldVersion = getCurrentVersion(rootDir);
298
- console.log(`[1/4] バージョンを更新 (${type})...`);
299
- const newVersion = bumpVersions(rootDir, type);
300
-
301
- console.log('[2/4] CHANGELOG を生成...');
302
- const entry = generateChangelog(rootDir, newVersion);
303
- updateChangelog(rootDir, entry);
304
-
305
- console.log('[3/4] git commit + tag...');
306
- createGitCommitAndTag(rootDir, newVersion);
307
-
308
- console.log('[4/4] サマリー表示');
309
- printSummary(oldVersion, newVersion);
310
-
311
- done();
312
- } catch (error) {
313
- done(error);
314
- }
315
- };
316
- task.displayName = `release:${type}:execute`;
317
- return task;
318
- }
319
-
320
- /**
321
- * リリースタスクを gulp に登録する
322
- * @param {import('gulp').Gulp} gulp - Gulp インスタンス
323
- * @param {{ rootDir?: string }} [options] - オプション
324
- * @param {string} [options.rootDir] - プロジェクトルート(省略時は gulpfile の 2 階層上)
325
- */
326
- export default function (gulp, options = {}) {
327
- const rootDir = options.rootDir || path.resolve(process.cwd());
328
-
329
- // ──────────────────────────────────────────────
330
- // preflight: 品質ゲート
331
- // ──────────────────────────────────────────────
332
-
333
- gulp.task('release:preflight:clean', (done) => {
334
- console.log('=== Release Preflight ===');
335
- console.log('[1/5] Working tree がクリーンか確認...');
336
- try {
337
- const status = execSync('git status --porcelain', {
338
- cwd: rootDir,
339
- stdio: 'pipe',
340
- encoding: 'utf8',
341
- }).trim();
342
- if (status) {
343
- done(new Error('Working tree is not clean. Commit or stash your changes first.\n' + status));
344
- return;
345
- }
346
- console.log(' -> Clean');
347
- done();
348
- } catch (error) {
349
- done(error);
350
- }
351
- });
352
-
353
- gulp.task('release:preflight:lint', (done) => {
354
- console.log('[2/5] Lint...');
355
- runCommandInDir('npm run lint', rootDir, done);
356
- });
357
-
358
- gulp.task('release:preflight:test', (done) => {
359
- console.log('[3/5] Test...');
360
- runCommandInDir('npm run test', rootDir, done);
361
- });
362
-
363
- gulp.task('release:preflight:build', (done) => {
364
- console.log('[4/5] Build...');
365
- runCommandInDir('npm run build', rootDir, done);
366
- });
367
-
368
- gulp.task('release:preflight:e2e', (done) => {
369
- console.log('[5/5] E2E Test...');
370
- runCommandInDir('npm run test:e2e', rootDir, done);
371
- });
372
-
373
- gulp.task(
374
- 'release:preflight',
375
- gulp.series(
376
- 'release:preflight:clean',
377
- 'release:preflight:lint',
378
- 'release:preflight:test',
379
- 'release:preflight:build',
380
- 'release:preflight:e2e'
381
- )
382
- );
383
-
384
- // ──────────────────────────────────────────────
385
- // release:patch / minor / major
386
- // ──────────────────────────────────────────────
387
-
388
- gulp.task('release:patch', gulp.series('release:preflight', createReleaseTask(rootDir, 'patch')));
389
- gulp.task('release:minor', gulp.series('release:preflight', createReleaseTask(rootDir, 'minor')));
390
- gulp.task('release:major', gulp.series('release:preflight', createReleaseTask(rootDir, 'major')));
391
-
392
- // ──────────────────────────────────────────────
393
- // release:dry-run
394
- // ──────────────────────────────────────────────
395
-
396
- gulp.task('release:dry-run', (done) => {
397
- try {
398
- const currentVersion = getCurrentVersion(rootDir);
399
- console.log('=== Release Dry Run ===');
400
- console.log('');
401
- console.log(`Current version: ${currentVersion}`);
402
- console.log('');
403
- console.log('Version preview:');
404
- console.log(` patch: ${currentVersion} -> ${semverBump(currentVersion, 'patch')}`);
405
- console.log(` minor: ${currentVersion} -> ${semverBump(currentVersion, 'minor')}`);
406
- console.log(` major: ${currentVersion} -> ${semverBump(currentVersion, 'major')}`);
407
- console.log('');
408
- console.log('CHANGELOG preview:');
409
- console.log('---');
410
- const entry = generateChangelog(rootDir, semverBump(currentVersion, 'patch'));
411
- console.log(entry);
412
- console.log('---');
413
- console.log('');
414
- console.log('To release, run:');
415
- console.log(' npm run release:patch # patch release');
416
- console.log(' npm run release:minor # minor release');
417
- console.log(' npm run release:major # major release');
418
- done();
419
- } catch (error) {
420
- done(error);
421
- }
422
- });
423
-
424
- // ──────────────────────────────────────────────
425
- // release:deploy:* (release + deploy)
426
- // ──────────────────────────────────────────────
427
-
428
- gulp.task('release:deploy:patch', gulp.series('release:patch', 'deploy:prd'));
429
- gulp.task('release:deploy:minor', gulp.series('release:minor', 'deploy:prd'));
430
- gulp.task('release:deploy:major', gulp.series('release:major', 'deploy:prd'));
431
- }
1
+ 'use strict';
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { execSync } from 'child_process';
6
+
7
+ // ============================================
8
+ // 設定
9
+ // ============================================
10
+
11
+ /**
12
+ * リリース対象のパッケージディレクトリ一覧を返す。
13
+ * モノレポの場合は apps/packages 配下を追加する。
14
+ * @param {string} rootDir - プロジェクトルート
15
+ * @returns {string[]} パッケージディレクトリの絶対パス配列
16
+ */
17
+ function discoverPackageDirs(rootDir) {
18
+ const dirs = [rootDir];
19
+ const candidates = ['apps', 'packages'];
20
+
21
+ for (const candidate of candidates) {
22
+ const dir = path.join(rootDir, candidate);
23
+ if (!fs.existsSync(dir)) continue;
24
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
25
+ for (const entry of entries) {
26
+ if (!entry.isDirectory()) continue;
27
+ const pkgJson = path.join(dir, entry.name, 'package.json');
28
+ if (fs.existsSync(pkgJson)) {
29
+ dirs.push(path.join(dir, entry.name));
30
+ }
31
+ }
32
+ }
33
+
34
+ return dirs;
35
+ }
36
+
37
+ // ============================================
38
+ // ヘルパー関数
39
+ // ============================================
40
+
41
+ /**
42
+ * root の package.json からバージョンを読み取る
43
+ * @param {string} rootDir - プロジェクトルート
44
+ * @returns {string} 現在のバージョン (例: "0.1.0")
45
+ */
46
+ function getCurrentVersion(rootDir) {
47
+ const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8'));
48
+ return pkg.version;
49
+ }
50
+
51
+ /**
52
+ * ドライラン用の簡易 semver 計算
53
+ * @param {string} version - 現在のバージョン (例: "0.1.0")
54
+ * @param {string} type - バンプの種類 ("patch" | "minor" | "major")
55
+ * @returns {string} 新しいバージョン
56
+ */
57
+ function semverBump(version, type) {
58
+ const [major, minor, patch] = version.split('.').map(Number);
59
+ switch (type) {
60
+ case 'major':
61
+ return `${major + 1}.0.0`;
62
+ case 'minor':
63
+ return `${major}.${minor + 1}.0`;
64
+ case 'patch':
65
+ return `${major}.${minor}.${patch + 1}`;
66
+ default:
67
+ throw new Error(`Unknown bump type: ${type}`);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * 対象パッケージのバージョンを同期更新
73
+ * @param {string} rootDir - プロジェクトルート
74
+ * @param {string} type - バンプの種類 ("patch" | "minor" | "major")
75
+ * @returns {string} 新しいバージョン
76
+ */
77
+ function bumpVersions(rootDir, type) {
78
+ const dirs = discoverPackageDirs(rootDir);
79
+
80
+ for (const dir of dirs) {
81
+ execSync(`npm version ${type} --no-git-tag-version`, {
82
+ cwd: dir,
83
+ stdio: 'pipe',
84
+ });
85
+ }
86
+
87
+ return getCurrentVersion(rootDir);
88
+ }
89
+
90
+ /**
91
+ * 直近タグから HEAD までの git log を Conventional Commits で分類し Markdown を生成
92
+ * @param {string} rootDir - プロジェクトルート
93
+ * @param {string} version - リリースバージョン
94
+ * @returns {string} CHANGELOG エントリ(Markdown)
95
+ */
96
+ function generateChangelog(rootDir, version) {
97
+ let lastTag;
98
+ try {
99
+ lastTag = execSync('git describe --tags --abbrev=0', {
100
+ cwd: rootDir,
101
+ stdio: 'pipe',
102
+ encoding: 'utf8',
103
+ }).trim();
104
+ } catch {
105
+ lastTag = null;
106
+ }
107
+
108
+ const range = lastTag ? `${lastTag}..HEAD` : 'HEAD';
109
+ let logOutput;
110
+ try {
111
+ logOutput = execSync(`git log ${range} --pretty=format:"%h|||%s"`, {
112
+ cwd: rootDir,
113
+ stdio: 'pipe',
114
+ encoding: 'utf8',
115
+ }).trim();
116
+ } catch {
117
+ logOutput = '';
118
+ }
119
+
120
+ if (!logOutput) {
121
+ return buildChangelogEntry(version, {});
122
+ }
123
+
124
+ const categories = {
125
+ feat: { title: 'Features', commits: [] },
126
+ fix: { title: 'Bug Fixes', commits: [] },
127
+ docs: { title: 'Documentation', commits: [] },
128
+ refactor: { title: 'Refactoring', commits: [] },
129
+ test: { title: 'Tests', commits: [] },
130
+ chore: { title: 'Chores', commits: [] },
131
+ perf: { title: 'Performance', commits: [] },
132
+ ci: { title: 'CI', commits: [] },
133
+ style: { title: 'Styles', commits: [] },
134
+ build: { title: 'Build', commits: [] },
135
+ other: { title: 'Other', commits: [] },
136
+ };
137
+
138
+ for (const line of logOutput.split('\n')) {
139
+ if (!line.trim()) continue;
140
+ const [hash, ...rest] = line.split('|||');
141
+ const subject = rest.join('|||');
142
+ const match = subject.match(/^(\w+)(?:\(.+?\))?:\s*(.+)$/);
143
+ if (match) {
144
+ const type = match[1].toLowerCase();
145
+ const message = match[2];
146
+ if (categories[type]) {
147
+ categories[type].commits.push({ hash: hash.trim(), message });
148
+ } else {
149
+ categories.other.commits.push({ hash: hash.trim(), message: subject });
150
+ }
151
+ } else {
152
+ categories.other.commits.push({ hash: hash.trim(), message: subject });
153
+ }
154
+ }
155
+
156
+ return buildChangelogEntry(version, categories);
157
+ }
158
+
159
+ /**
160
+ * CHANGELOG エントリを Markdown で構築
161
+ * @param {string} version
162
+ * @param {Object} categories
163
+ * @returns {string}
164
+ */
165
+ function buildChangelogEntry(version, categories) {
166
+ const date = new Date().toISOString().slice(0, 10);
167
+ const lines = [`## [${version}] - ${date}`, ''];
168
+
169
+ let hasContent = false;
170
+ for (const [, cat] of Object.entries(categories)) {
171
+ if (cat.commits && cat.commits.length > 0) {
172
+ hasContent = true;
173
+ lines.push(`### ${cat.title}`, '');
174
+ for (const c of cat.commits) {
175
+ lines.push(`- ${c.message} (${c.hash})`);
176
+ }
177
+ lines.push('');
178
+ }
179
+ }
180
+
181
+ if (!hasContent) {
182
+ lines.push('- No notable changes', '');
183
+ }
184
+
185
+ return lines.join('\n');
186
+ }
187
+
188
+ /**
189
+ * CHANGELOG.md の先頭にエントリを追加(なければ新規作成)
190
+ * @param {string} rootDir - プロジェクトルート
191
+ * @param {string} entry - 追加する CHANGELOG エントリ
192
+ */
193
+ function updateChangelog(rootDir, entry) {
194
+ const changelogPath = path.join(rootDir, 'CHANGELOG.md');
195
+ const header = '# Changelog\n\n';
196
+
197
+ if (fs.existsSync(changelogPath)) {
198
+ const existing = fs.readFileSync(changelogPath, 'utf8');
199
+ const withoutHeader = existing.replace(/^# Changelog\s*\n*/, '');
200
+ fs.writeFileSync(changelogPath, header + entry + '\n' + withoutHeader);
201
+ } else {
202
+ fs.writeFileSync(changelogPath, header + entry);
203
+ }
204
+ }
205
+
206
+ /**
207
+ * 変更ファイルをステージング → commit → tag
208
+ * @param {string} rootDir - プロジェクトルート
209
+ * @param {string} version - リリースバージョン
210
+ */
211
+ function createGitCommitAndTag(rootDir, version) {
212
+ const filesToStage = collectFilesToStage(rootDir);
213
+
214
+ for (const file of filesToStage) {
215
+ execSync(`git add "${file}"`, { cwd: rootDir, stdio: 'pipe' });
216
+ }
217
+
218
+ execSync(`git commit -m "release: v${version}"`, {
219
+ cwd: rootDir,
220
+ stdio: 'pipe',
221
+ env: { ...process.env, USER_APPROVED_COMMIT: '1' },
222
+ });
223
+
224
+ execSync(`git tag -a "v${version}" -m "v${version}"`, {
225
+ cwd: rootDir,
226
+ stdio: 'pipe',
227
+ });
228
+ }
229
+
230
+ /**
231
+ * ステージング対象のファイルパスを収集
232
+ * @param {string} rootDir - プロジェクトルート
233
+ * @returns {string[]} 相対パスの配列
234
+ */
235
+ function collectFilesToStage(rootDir) {
236
+ const files = ['package.json', 'CHANGELOG.md'];
237
+ const dirs = discoverPackageDirs(rootDir);
238
+
239
+ for (const dir of dirs) {
240
+ if (dir === rootDir) continue;
241
+ const relative = path.relative(rootDir, dir);
242
+ files.push(path.join(relative, 'package.json'));
243
+ }
244
+
245
+ return files;
246
+ }
247
+
248
+ /**
249
+ * リリース結果のサマリーを表示
250
+ * @param {string} oldVersion - 旧バージョン
251
+ * @param {string} newVersion - 新バージョン
252
+ */
253
+ function printSummary(oldVersion, newVersion) {
254
+ console.log('');
255
+ console.log('=== Release Summary ===');
256
+ console.log(` Version: ${oldVersion} -> ${newVersion}`);
257
+ console.log(` Tag: v${newVersion}`);
258
+ console.log(` Commit: release: v${newVersion}`);
259
+ console.log('');
260
+ console.log('Next steps:');
261
+ console.log(' git push && git push --tags');
262
+ console.log('');
263
+ }
264
+
265
+ // ============================================
266
+ // コマンド実行ヘルパー
267
+ // ============================================
268
+
269
+ /**
270
+ * シェルコマンドを実行し、失敗時に done コールバックへエラーを渡す
271
+ * @param {string} command - 実行するコマンド
272
+ * @param {string} rootDir - 実行ディレクトリ
273
+ * @param {Function} done - コールバック
274
+ */
275
+ function runCommandInDir(command, rootDir, done) {
276
+ try {
277
+ execSync(command, { cwd: rootDir, stdio: 'inherit' });
278
+ done();
279
+ } catch (error) {
280
+ done(error);
281
+ }
282
+ }
283
+
284
+ // ============================================
285
+ // タスク登録
286
+ // ============================================
287
+
288
+ /**
289
+ * 指定されたタイプのリリースタスクを生成
290
+ * @param {string} rootDir - プロジェクトルート
291
+ * @param {string} type - "patch" | "minor" | "major"
292
+ * @returns {Function} gulp タスク関数
293
+ */
294
+ function createReleaseTask(rootDir, type) {
295
+ const task = (done) => {
296
+ try {
297
+ const oldVersion = getCurrentVersion(rootDir);
298
+ console.log(`[1/4] バージョンを更新 (${type})...`);
299
+ const newVersion = bumpVersions(rootDir, type);
300
+
301
+ console.log('[2/4] CHANGELOG を生成...');
302
+ const entry = generateChangelog(rootDir, newVersion);
303
+ updateChangelog(rootDir, entry);
304
+
305
+ console.log('[3/4] git commit + tag...');
306
+ createGitCommitAndTag(rootDir, newVersion);
307
+
308
+ console.log('[4/4] サマリー表示');
309
+ printSummary(oldVersion, newVersion);
310
+
311
+ done();
312
+ } catch (error) {
313
+ done(error);
314
+ }
315
+ };
316
+ task.displayName = `release:${type}:execute`;
317
+ return task;
318
+ }
319
+
320
+ /**
321
+ * リリースタスクを gulp に登録する
322
+ * @param {import('gulp').Gulp} gulp - Gulp インスタンス
323
+ * @param {{ rootDir?: string }} [options] - オプション
324
+ * @param {string} [options.rootDir] - プロジェクトルート(省略時は gulpfile の 2 階層上)
325
+ */
326
+ export default function (gulp, options = {}) {
327
+ const rootDir = options.rootDir || path.resolve(process.cwd());
328
+
329
+ // ──────────────────────────────────────────────
330
+ // preflight: 品質ゲート
331
+ // ──────────────────────────────────────────────
332
+
333
+ gulp.task('release:preflight:clean', (done) => {
334
+ console.log('=== Release Preflight ===');
335
+ console.log('[1/5] Working tree がクリーンか確認...');
336
+ try {
337
+ const status = execSync('git status --porcelain', {
338
+ cwd: rootDir,
339
+ stdio: 'pipe',
340
+ encoding: 'utf8',
341
+ }).trim();
342
+ if (status) {
343
+ done(new Error('Working tree is not clean. Commit or stash your changes first.\n' + status));
344
+ return;
345
+ }
346
+ console.log(' -> Clean');
347
+ done();
348
+ } catch (error) {
349
+ done(error);
350
+ }
351
+ });
352
+
353
+ gulp.task('release:preflight:lint', (done) => {
354
+ console.log('[2/5] Lint...');
355
+ runCommandInDir('npm run lint', rootDir, done);
356
+ });
357
+
358
+ gulp.task('release:preflight:test', (done) => {
359
+ console.log('[3/5] Test...');
360
+ runCommandInDir('npm run test', rootDir, done);
361
+ });
362
+
363
+ gulp.task('release:preflight:build', (done) => {
364
+ console.log('[4/5] Build...');
365
+ runCommandInDir('npm run build', rootDir, done);
366
+ });
367
+
368
+ gulp.task('release:preflight:e2e', (done) => {
369
+ console.log('[5/5] E2E Test...');
370
+ runCommandInDir('npm run test:e2e', rootDir, done);
371
+ });
372
+
373
+ gulp.task(
374
+ 'release:preflight',
375
+ gulp.series(
376
+ 'release:preflight:clean',
377
+ 'release:preflight:lint',
378
+ 'release:preflight:test',
379
+ 'release:preflight:build',
380
+ 'release:preflight:e2e'
381
+ )
382
+ );
383
+
384
+ // ──────────────────────────────────────────────
385
+ // release:patch / minor / major
386
+ // ──────────────────────────────────────────────
387
+
388
+ gulp.task('release:patch', gulp.series('release:preflight', createReleaseTask(rootDir, 'patch')));
389
+ gulp.task('release:minor', gulp.series('release:preflight', createReleaseTask(rootDir, 'minor')));
390
+ gulp.task('release:major', gulp.series('release:preflight', createReleaseTask(rootDir, 'major')));
391
+
392
+ // ──────────────────────────────────────────────
393
+ // release:dry-run
394
+ // ──────────────────────────────────────────────
395
+
396
+ gulp.task('release:dry-run', (done) => {
397
+ try {
398
+ const currentVersion = getCurrentVersion(rootDir);
399
+ console.log('=== Release Dry Run ===');
400
+ console.log('');
401
+ console.log(`Current version: ${currentVersion}`);
402
+ console.log('');
403
+ console.log('Version preview:');
404
+ console.log(` patch: ${currentVersion} -> ${semverBump(currentVersion, 'patch')}`);
405
+ console.log(` minor: ${currentVersion} -> ${semverBump(currentVersion, 'minor')}`);
406
+ console.log(` major: ${currentVersion} -> ${semverBump(currentVersion, 'major')}`);
407
+ console.log('');
408
+ console.log('CHANGELOG preview:');
409
+ console.log('---');
410
+ const entry = generateChangelog(rootDir, semverBump(currentVersion, 'patch'));
411
+ console.log(entry);
412
+ console.log('---');
413
+ console.log('');
414
+ console.log('To release, run:');
415
+ console.log(' npm run release:patch # patch release');
416
+ console.log(' npm run release:minor # minor release');
417
+ console.log(' npm run release:major # major release');
418
+ done();
419
+ } catch (error) {
420
+ done(error);
421
+ }
422
+ });
423
+
424
+ // ──────────────────────────────────────────────
425
+ // release:deploy:* (release + deploy)
426
+ // ──────────────────────────────────────────────
427
+
428
+ gulp.task('release:deploy:patch', gulp.series('release:patch', 'deploy:prd'));
429
+ gulp.task('release:deploy:minor', gulp.series('release:minor', 'deploy:prd'));
430
+ gulp.task('release:deploy:major', gulp.series('release:major', 'deploy:prd'));
431
+ }