每次上线前,总有个麻烦事:写 changelog。哪怕有规约、有流程,也难保不漏掉关键改动。有时候只是漏了个小 fix,有时候直接忘了新增了个接口。尤其当你在项目 PR 多、节奏快的团队里,一不留神就踩雷。
这篇文章就聚焦一个实际痛点:如何从 Git 提交记录中自动提取变更内容,生成结构化的 changelog 文档? 我们会用一段 Node.js 脚本来实现,支持提取 commit 中的新增(feat)、修复(fix)、重构(refactor)等内容,并写入 CHANGELOG.md
。此外,也会补充一个 Python 版本,方便习惯不同语言的团队快速上手。
自动生成 changelog 的做法并不新鲜,比如 conventional-changelog
和 semantic-release
已经广泛应用在一些社区项目中。但大多数脚本功能都偏重「CI 集成」,而我们更关注日常开发场景下,如何做得轻量、可控、可定制。
我们从以下三个角度来切入:
想要自动提取变更内容,前提是你的 Git 提交得有「格式」。我们推荐使用 Conventional Commits,格式长这样:
feat: 新增用户登录功能
fix: 修复图片上传失败的 bug
refactor: 重构用户信息接口
这样我们才能靠正则表达式提取关键词,归类成 changelog 项。
我们先用简单的 child_process
执行 git log:
const { execSync } = require('child_process');
function getCommitLogs(fromTag = '') {
const range = fromTag ? `${fromTag}..HEAD` : '';
const log = execSync(`git log ${range} --pretty=format:"%s"`).toString();
return log.split('\n');
}
这个函数支持传入起始 tag(比如 v1.0.0
),只获取从那个版本以来的提交信息。
我们做一个分类 map,把不同类型映射成中文分组标题:
const typeMap = {
feat: '✨ 新功能',
fix: '🐞 修复问题',
refactor: '♻️ 重构优化',
docs: '📚 文档变更',
chore: '🔧 其他修改',
};
function classifyCommits(commits) {
const result = {};
commits.forEach((line) => {
const match = line.match(/^(\w+):\s*(.+)/);
if (!match) return;
const [, type, message] = match;
const title = typeMap[type] || '📦 其他';
if (!result[title]) result[title] = [];
result[title].push(`- ${message}`);
});
return result;
}
最终我们用一个函数拼接 Markdown 字符串:
function generateMarkdown(classifiedCommits) {
const lines = [`## 📝 本次变更 (${new Date().toLocaleDateString()})\n`];
for (const [title, items] of Object.entries(classifiedCommits)) {
lines.push(`### ${title}`);
lines.push(...items);
lines.push('');
}
return lines.join('\n');
}
最后把 markdown 内容追加到 changelog:
const fs = require('fs');
function appendChangelog(content) {
fs.appendFileSync('CHANGELOG.md', `\n${content}`);
}
// changelog-generator.js
const { execSync } = require('child_process');
const fs = require('fs');
const typeMap = {
feat: '✨ 新功能',
fix: '🐞 修复问题',
refactor: '♻️ 重构优化',
docs: '📚 文档变更',
chore: '🔧 其他修改',
};
function getCommits() {
return execSync('git log --pretty=format:"%s"').toString().split('\n');
}
function classify(commits) {
const result = {};
for (let line of commits) {
const match = line.match(/^(\w+):\s*(.+)/);
if (!match) continue;
const [, type, msg] = match;
const title = typeMap[type] || '📦 其他';
if (!result[title]) result[title] = [];
result[title].push(`- ${msg}`);
}
return result;
}
function generateMd(data) {
const now = new Date().toLocaleDateString();
const lines = [`## 📝 更新日志 (${now})\n`];
for (const [title, items] of Object.entries(data)) {
lines.push(`### ${title}`);
lines.push(...items, '');
}
return lines.join('\n');
}
function writeToFile(content) {
fs.appendFileSync('CHANGELOG.md', '\n' + content);
}
const commits = getCommits();
const grouped = classify(commits);
const markdown = generateMd(grouped);
writeToFile(markdown);
console.log('✅ Changelog updated.');
在每次上线前执行:
node changelog-generator.js
团队成员只要保持良好的 commit message,就能自动生成结构化的变更记录。
在 PR 模板中插入生成 changelog 的 commit 类型规范说明,推动团队使用统一提交风格。
可以结合 pre-push
hook 自动触发 changelog 生成,或者和 CI/CD 搭配用脚本生成 changelog 并发布到 Release Notes。
Q1: 一定要用 Conventional Commits 吗?
建议是的。如果提交风格混乱,脚本就无法准确提取变更类型。当然也可以适配项目实际需求调整正则。
Q2: 可以集成到 CI/CD 流程里吗?
完全可以。比如在 GitHub Actions 中增加一步执行 node changelog-generator.js
,并将结果作为 artifact 或 Release Notes 输出。
Q3: 如何避免重复生成同样的 changelog?
可以结合 tag 范围限制,只提取 上一次 tag ~ 当前提交
的日志。
手动写 changelog 麻烦又容易出错,不如让机器来干这活。通过简单的脚本 + 提交规范,我们就能实现 changelog 自动化,不仅提升发布效率,还能提升团队协作的一致性。更重要的是——你再也不用担心「这个功能上线了吗?」的问题了。
后续我们还会分享更多类似的“创意自动化脚本”,比如:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。