@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.
- package/LICENSE +21 -21
- package/README.md +42 -42
- package/bin/claude-code-booster +79 -79
- package/lib/assets/.claude/README.md +162 -162
- package/lib/assets/.claude/SKILLS_TEMPLATE.md +100 -100
- package/lib/assets/.claude/scripts/generate-inception-deck.mjs +911 -911
- package/lib/assets/.claude/settings.json +11 -11
- package/lib/assets/.claude/skills/ai-agent-guidelines/SKILL.md +119 -119
- package/lib/assets/.claude/skills/analyzing-architecture/SKILL.md +87 -87
- package/lib/assets/.claude/skills/analyzing-business/SKILL.md +117 -117
- package/lib/assets/.claude/skills/analyzing-data-model/SKILL.md +80 -80
- package/lib/assets/.claude/skills/analyzing-domain-model/SKILL.md +88 -88
- package/lib/assets/.claude/skills/analyzing-inception-deck/SKILL.md +137 -137
- package/lib/assets/.claude/skills/analyzing-non-functional/SKILL.md +91 -91
- package/lib/assets/.claude/skills/analyzing-operation/SKILL.md +91 -91
- package/lib/assets/.claude/skills/analyzing-requirements/SKILL.md +89 -87
- package/lib/assets/.claude/skills/analyzing-tech-stack/SKILL.md +102 -102
- package/lib/assets/.claude/skills/analyzing-test-strategy/SKILL.md +87 -87
- package/lib/assets/.claude/skills/analyzing-ui-design/SKILL.md +86 -86
- package/lib/assets/.claude/skills/analyzing-usecases/SKILL.md +87 -87
- package/lib/assets/.claude/skills/creating-adr/SKILL.md +115 -115
- package/lib/assets/.claude/skills/developing-backend/SKILL.md +106 -106
- package/lib/assets/.claude/skills/developing-frontend/SKILL.md +96 -96
- package/lib/assets/.claude/skills/developing-release/SKILL.md +154 -154
- package/lib/assets/.claude/skills/generating-slides/SKILL.md +136 -136
- package/lib/assets/.claude/skills/git-commit/SKILL.md +106 -106
- package/lib/assets/.claude/skills/killing-processes/SKILL.md +98 -98
- package/lib/assets/.claude/skills/managing-docs/SKILL.md +200 -200
- package/lib/assets/.claude/skills/managing-operations/DEPLOY.md +77 -77
- package/lib/assets/.claude/skills/managing-operations/SETUP_CSHARP.md +80 -80
- package/lib/assets/.claude/skills/managing-operations/SETUP_FRONTEND.md +84 -84
- package/lib/assets/.claude/skills/managing-operations/SETUP_JAVA.md +75 -75
- package/lib/assets/.claude/skills/managing-operations/SKILL.md +156 -156
- package/lib/assets/.claude/skills/orchestrating-analysis/SKILL.md +134 -134
- package/lib/assets/.claude/skills/orchestrating-development/SKILL.md +243 -243
- package/lib/assets/.claude/skills/orchestrating-project/SKILL.md +193 -193
- package/lib/assets/.claude/skills/planning-releases/SKILL.md +222 -222
- package/lib/assets/.claude/skills/tracking-progress/SKILL.md +164 -164
- package/lib/assets/.devcontainer/devcontainer.json +34 -34
- package/lib/assets/.env.example +17 -17
- package/lib/assets/.gitattributes +4 -4
- package/lib/assets/.github/workflows/docker-publish.yml +77 -77
- package/lib/assets/.github/workflows/mkdocs.yml +39 -39
- package/lib/assets/AGENTS.md +94 -94
- package/lib/assets/CLAUDE.md +162 -162
- package/lib/assets/README.md +285 -269
- package/lib/assets/docker-compose.yml +33 -33
- package/lib/assets/docs/assets/css/extra.css +29 -29
- package/lib/assets/docs/assets/js/extra.js +44 -44
- package/lib/assets/docs/index.md +14 -14
- 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
- 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
- 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
- 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
- package/lib/assets/docs/reference/UI/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +448 -448
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/lib/assets/docs/reference//351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +235 -235
- 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
- package/lib/assets/docs/template/ADR.md +30 -30
- package/lib/assets/docs/template/README.md +50 -50
- 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
- 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
- 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
- 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
- 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
- package/lib/assets/docs/template//350/246/201/344/273/266/345/256/232/347/276/251.md +669 -669
- package/lib/assets/docs/template//350/250/255/350/250/210.md +163 -163
- package/lib/assets/gulpfile.js +23 -23
- package/lib/assets/mkdocs.yml +65 -65
- package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
- package/lib/assets/ops/scripts/journal.js +180 -180
- package/lib/assets/ops/scripts/mkdocs.js +82 -82
- package/lib/assets/ops/scripts/release.js +431 -431
- package/lib/assets/ops/scripts/ssh.js +190 -190
- package/lib/assets/ops/scripts/vault.js +299 -299
- package/lib/assets/package-lock.json +1653 -1653
- package/lib/assets/package.json +40 -40
- package/lib/gulpfile.js +37 -37
- 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
|
+
}
|