Skip to content

Commit 08a632d

Browse files
committed
chore: 添加版本分析工具并发布 v0.1.22
新增功能: - 创建 version-analyze.mjs 脚本,用于分析 git 提交历史 - 支持三种模式:analyze(输出 JSON)、generate(预览)、update(自动更新) - 自动按类型分类提交:feat/fix/perf/refactor/test/docs/chore/style/ci - 自动生成语义化版本号和对应的变更日志 更新日志: - 更新版本号从 0.1.21 → 0.1.22 - 在 CHANGELOG.md 中添加 v0.1.22 条目
1 parent 0d36be2 commit 08a632d

3 files changed

Lines changed: 216 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# 更新日志
22

3+
4+
## v0.1.22
5+
6+
### 新增
7+
8+
- 完善 OAuth 认证流程与模型导入系统
9+
310
## v0.1.21
411

512
### 修复

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nextcowork",
3-
"version": "0.1.21",
3+
"version": "0.1.22",
44
"description": "NextCoWork — 编码型 Agent 桌面端",
55
"main": "./out/main/index.js",
66
"author": "NextCoWork",

scripts/version-analyze.mjs

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#!/usr/bin/env node
2+
3+
import { execSync } from 'child_process';
4+
import fs from 'fs';
5+
import path from 'path';
6+
import { fileURLToPath } from 'url';
7+
8+
const __filename = fileURLToPath(import.meta.url);
9+
const __dirname = path.dirname(__filename);
10+
const projectRoot = path.resolve(__dirname, '..');
11+
const pkgPath = path.join(projectRoot, 'package.json');
12+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
13+
14+
const commitTypes = {
15+
'feat': '新增',
16+
'fix': '修复',
17+
'perf': '性能',
18+
'refactor': '重构',
19+
'test': '测试',
20+
'docs': '文档',
21+
'chore': '杂项',
22+
'style': '风格',
23+
'ci': 'CI'
24+
};
25+
26+
function parseCommits() {
27+
try {
28+
// 获取上一个版本的 tag
29+
const tags = execSync('git tag -l --sort=-creatordate', { encoding: 'utf-8' })
30+
.trim()
31+
.split('\n')
32+
.filter(tag => tag.startsWith('v'));
33+
34+
const lastTag = tags[0] || 'HEAD~10';
35+
36+
// 获取提交历史
37+
const range = tags.length > 0 ? `${lastTag}..HEAD` : 'HEAD~10..HEAD';
38+
const commits = execSync(`git log ${range} --format=%B%n---COMMIT_END---`, {
39+
encoding: 'utf-8'
40+
}).split('---COMMIT_END---').filter(Boolean);
41+
42+
const grouped = {};
43+
44+
commits.forEach(msg => {
45+
const lines = msg.trim().split('\n');
46+
const firstLine = lines[0];
47+
48+
// 解析 conventional commit 格式
49+
const match = firstLine.match(/^(feat|fix|perf|refactor|test|docs|chore|style|ci)(\(.+\))?!?:\s*(.+)$/);
50+
51+
if (match) {
52+
const type = match[1];
53+
const scope = match[2] ? match[2].slice(1, -1) : '';
54+
const subject = match[3];
55+
const typeLabel = commitTypes[type] || type;
56+
57+
if (!grouped[typeLabel]) {
58+
grouped[typeLabel] = [];
59+
}
60+
61+
grouped[typeLabel].push({
62+
scope,
63+
subject,
64+
fullMessage: firstLine
65+
});
66+
}
67+
});
68+
69+
return {
70+
lastTag,
71+
commits: grouped,
72+
commitCount: commits.length
73+
};
74+
} catch (error) {
75+
console.error('错误:', error.message);
76+
return { commits: {}, commitCount: 0, lastTag: null };
77+
}
78+
}
79+
80+
function getNextVersion(currentVersion) {
81+
const parts = currentVersion.split('.');
82+
const patch = parseInt(parts[2]) + 1;
83+
return `${parts[0]}.${parts[1]}.${patch}`;
84+
}
85+
86+
function generateChangelogEntry(version, analysis) {
87+
let entry = `## ${version}\n\n`;
88+
89+
if (Object.keys(analysis.commits).length === 0) {
90+
entry += '无重大变化\n\n';
91+
return entry;
92+
}
93+
94+
// 按顺序输出:新增、改动、修复、其他
95+
const order = ['新增', '改动', '改进', '重构', '性能', '修复', '测试', '文档', '杂项'];
96+
97+
for (const type of order) {
98+
if (analysis.commits[type]) {
99+
entry += `### ${type}\n\n`;
100+
analysis.commits[type].forEach(commit => {
101+
const prefix = commit.scope ? `[${commit.scope}] ` : '';
102+
entry += `- ${prefix}${commit.subject}\n`;
103+
});
104+
entry += '\n';
105+
}
106+
}
107+
108+
return entry;
109+
}
110+
111+
function updateChangelog(newVersion, analysis) {
112+
const changelogPath = path.join(projectRoot, 'CHANGELOG.md');
113+
let content = fs.readFileSync(changelogPath, 'utf-8');
114+
115+
const entry = generateChangelogEntry(newVersion, analysis);
116+
117+
// 在第一个 ## 标题之前插入新版本
118+
const lines = content.split('\n');
119+
let insertIndex = -1;
120+
121+
for (let i = 0; i < lines.length; i++) {
122+
if (lines[i].startsWith('##') && lines[i].includes('v')) {
123+
insertIndex = i;
124+
break;
125+
}
126+
}
127+
128+
if (insertIndex > 0) {
129+
lines.splice(insertIndex, 0, '', entry.trim());
130+
fs.writeFileSync(changelogPath, lines.join('\n'));
131+
return true;
132+
}
133+
134+
return false;
135+
}
136+
137+
function updatePackageJson(newVersion) {
138+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
139+
pkg.version = newVersion;
140+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
141+
}
142+
143+
export function analyzeVersion() {
144+
const analysis = parseCommits();
145+
const nextVersion = getNextVersion(pkg.version);
146+
147+
return {
148+
currentVersion: pkg.version,
149+
nextVersion,
150+
analysis
151+
};
152+
}
153+
154+
export function generateRelease() {
155+
const { currentVersion, nextVersion, analysis } = analyzeVersion();
156+
157+
console.log(`\n当前版本: ${currentVersion}`);
158+
console.log(`下一个版本: ${nextVersion}`);
159+
console.log(`自 ${analysis.lastTag || '开始'} 以来的提交: ${analysis.commitCount}\n`);
160+
161+
console.log('提交类型分布:');
162+
Object.entries(analysis.commits).forEach(([type, commits]) => {
163+
console.log(` ${type}: ${commits.length}`);
164+
});
165+
166+
const entry = generateChangelogEntry(nextVersion, analysis);
167+
console.log('\n生成的变更日志:\n');
168+
console.log(entry);
169+
170+
return { currentVersion, nextVersion, analysis };
171+
}
172+
173+
if (process.argv[1] === __filename) {
174+
const command = process.argv[2];
175+
176+
if (command === 'analyze') {
177+
const result = analyzeVersion();
178+
console.log(JSON.stringify(result, null, 2));
179+
} else if (command === 'generate') {
180+
generateRelease();
181+
} else if (command === 'update') {
182+
const { nextVersion, analysis } = analyzeVersion();
183+
184+
console.log(`更新版本号到 ${nextVersion}...`);
185+
updatePackageJson(nextVersion);
186+
187+
console.log(`更新 CHANGELOG.md...`);
188+
if (updateChangelog(nextVersion, analysis)) {
189+
console.log('✓ 版本日志已更新');
190+
console.log(`\n请运行以下命令提交更改:`);
191+
console.log(` git add package.json CHANGELOG.md`);
192+
console.log(` git commit -m "chore: release v${nextVersion}"`);
193+
console.log(` git tag v${nextVersion}`);
194+
} else {
195+
console.error('✗ 更新失败');
196+
process.exit(1);
197+
}
198+
} else {
199+
console.log(`
200+
版本分析工具
201+
202+
用法:
203+
node scripts/version-analyze.mjs analyze # 分析提交并输出 JSON
204+
node scripts/version-analyze.mjs generate # 生成变更日志预览
205+
node scripts/version-analyze.mjs update # 更新版本号和变更日志
206+
`);
207+
}
208+
}

0 commit comments

Comments
 (0)