自动化生成与更新 Changelog 文件
在软件开发中,保持 Changelog 文件的更新是一项至关重要的任务。
Changelog 文件记录了项目的每一个重要变更,包括新功能、修复的问题以及任何可能破坏现有功能的变更。对于维护者、贡献者和最终用户来说,这都是一个宝贵的资源。然而,手动维护 Changelog 文件既耗时又容易出错。我们可以利用 Git 和 Bash 脚本来自动化这一过程。
一、为什么需要自动化的 Changelog
- 节省时间:自动化工具可以快速从 Git 提交历史中提取相关信息,并生成格式化的 Changelog。
- 减少错误:手动编辑 Changelog 时容易遗漏或错误地记录某些变更。自动化工具可以确保所有相关提交都被正确记录。
- 保持一致性:自动化的 Changelog 生成工具可以确保每次发布的 Changelog 都遵循相同的格式和约定。
二、如何自动化生成和更新 Changelog
下面是一个基于 Bash 脚本的自动化 Changelog 生成和更新方案的详细步骤:
1. 定义 Changelog 的格式
首先,你需要确定 Changelog 的格式。一个常见的格式包括版本号、发布日期、以及一个或多个包含具体变更的列表。例如:
## [1.2.0] - 2023-04-01
### Added
- 新功能:支持多用户登录。
### Fixed
- 修复了登录时的认证问题。
- 修正了用户资料页面的显示错误。
2. 编写 Bash 脚本
接下来,你可以编写一个 Bash 脚本来自动化地生成和更新 Changelog。这个脚本将执行以下任务:
- 确定当前版本号和下一个版本号的 Git 标签。
- 使用 Git 提交历史来查找两个标签之间的所有相关提交(例如,包含 “feat:” 或 “fix:” 前缀的提交)。
- 将这些提交格式化为 Changelog 条目,并插入到指定的 Changelog 文件中。
以下是一个 Bash 脚本示例:
#!/bin/bash -e
# 脚本所在的顶层目录
toplevel="$(dirname "$(readlink -f "$0")")/.."
cd "$toplevel" || exit 1
# 获取当前版本标签及其对应的提交
CURRENT_TAG_NAME=$(git describe --abbrev=0 --tags)
CURRENT_TAG_COMMIT=$(git rev-parse "$CURRENT_TAG_NAME")
# 尝试找到下一个版本标签的提交(这里简化了逻辑,假设存在且直接获取下一个)
NEXT_TAG_NAMES=$(git tag --sort=-version:refname | grep -Eo 'v[0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?' | grep -vE "^$CURRENT_TAG_NAME$" | head -n 1)
if [ -n "$NEXT_TAG_NAMES" ]; then
NEXT_TAG_COMMIT=$(git rev-parse "$NEXT_TAG_NAMES")
else
echo "No next tag found. Exiting."
exit 1
fi
# 提取版本号
VERSION_NUM="${CURRENT_TAG_NAME#v}"
# 检查指定的 Changelog 文件是否已经包含当前版本号
if grep -q "^## [$VERSION_NUM] " "$1"; then
echo "Version $VERSION_NUM already in Changelog. Exiting."
exit 0
fi
# 临时 Changelog 文件
TMP_CHANGELOG="/tmp/${VERSION_NUM}_changelog.tmp"
# 函数:为给定路径生成 Changelog 片段
generate_changelog_for_path() {
local path="$1"
local sha1=$(git ls-tree "$CURRENT_TAG_COMMIT" "$path" | awk '/blob/ {print $3}')
local sha2=$(git ls-tree "$NEXT_TAG_COMMIT" "$path" | awk '/blob/ {print $3}')
if [ "$sha1" != "$sha2" ]; then
echo "## [$VERSION_NUM] - $path" >> "$TMP_CHANGELOG"
git log --oneline --format=" * %s" "$sha1...$sha2" -- "$path" | grep -E '^(fix:|feat:)' >> "$TMP_CHANGELOG"
echo >> "$TMP_CHANGELOG"
fi
}
# 为主仓库生成 Changelog 片段(空路径表示根仓库)
generate_changelog_for_path ""
# 为每个子模块生成 Changelog 片段
git submodule foreach --quiet 'bash -c "$(declare -f generate_changelog_for_path); path=\"\$sm_path\"; generate_changelog_for_path \"\$path\""'
# 将生成的 Changelog 插入到指定的文件中
if [ -s "$TMP_CHANGELOG" ]; then
# 在 Changelog 文件的指定位置(例如,开头)插入内容
# 这里假设在文件开头插入,你可以根据需要调整 sed 命令
sed -i "1i\\$(cat $TMP_CHANGELOG)" "$1"
# 或者,如果你想要在某个特定的标记(如 "## [Unreleased]")之后插入
# sed -i "/^## [Unreleased]$/r $TMP_CHANGELOG" "$1"
else
echo "No changes found between $CURRENT_TAG_NAME and $NEXT_TAG_NAMES."
fi
# 清理临时文件
rm -f "$TMP_CHANGELOG"
echo "Changelog for $VERSION_NUM has been updated."
3. 集成到 CI/CD 流程
将 Changelog 生成脚本集成到 CI/CD 流程中。这样,每当有新版本发布时,CI/CD 系统都会自动运行该脚本,并更新 Changelog 文件。
4. 注意事项
- 确保 Git 仓库是干净的:在生成 Changelog 之前,确保所有的提交都已经推送到远程仓库,并且没有未提交的更改。
- 测试脚本:在正式使用之前,在测试环境中测试脚本,以确保它按预期工作。
- 处理子模块:如果你的项目包含 Git 子模块,请确保你的脚本能够正确处理这些子模块的 Changelog。