feat(ci): Release Notes 改为增量展示,正式版覆盖整个版本周期

CHANGELOG.md 仍保持整个版本累积(strip 模式),但 Release Notes 改为:
- 预发布(rc):只展示相对紧邻上一个 tag 的增量(如 rc1→rc2 新提交),
  避免同版本各 rc 之间大量重复。
- 正式版:跳过同版本 rc,基准落到上一个不同版本 tag,
  覆盖整个版本开发周期的全量提交。
基准 tag 按 RELEASE_IS_PRERELEASE 区分,Release Notes 独立用 git log 重新生成。
This commit is contained in:
csh
2026-07-20 18:13:58 +08:00
parent c3a6e6dcb6
commit 3054338cf7
+155 -24
View File
@@ -490,6 +490,38 @@ jobs:
COMMIT_RANGE="${PREVIOUS_TAG}..${VERSION}" COMMIT_RANGE="${PREVIOUS_TAG}..${VERSION}"
fi fi
# 计算 Release Notes 的增量基准 tag,规则按当前发布是否为预发布(rc)区分:
# - 预发布(rc):基准 = 紧邻的上一个 tag(不跳过同版本 rc)
# 例如 0.0.5-rc2 的基准是 0.0.5-rc1Release Notes 只展示 rc1→rc2 的新提交,
# 避免同一版本各 rc 之间大量重复。
# - 正式版:基准 = 上一个"不同 CHANGELOG 版本"的 tag(即上面的 PREVIOUS_TAG
# 例如 0.0.5 正式版会跳过 0.0.5-rc1/rc2,基准落到 0.0.4-rc2
# Release Notes 覆盖整个 0.0.5 开发周期的全量提交。
# 注意:CHANGELOG.md 始终是整个版本的累积(strip 模式),与此处无关。
if [[ "${RELEASE_IS_PRERELEASE}" == "true" ]]; then
NOTES_BASE_TAG=""
while IFS= read -r tag; do
[ -z "$tag" ] && continue
if [ "$tag" != "$VERSION" ]; then
NOTES_BASE_TAG="$tag"
break
fi
done <<< "$ALL_TAGS"
echo "🏷️ 预发布:Release Notes 基准 = 紧邻上一个 tag"
else
NOTES_BASE_TAG="$PREVIOUS_TAG"
echo "🏷️ 正式版:Release Notes 基准 = 上一个不同版本 tag(覆盖整个版本周期)"
fi
if [ -z "$NOTES_BASE_TAG" ]; then
echo "️ 未找到基准 tagRelease Notes 将包含全部提交"
RELEASE_NOTES_RANGE="HEAD"
else
echo "📍 Release Notes 增量基准: $NOTES_BASE_TAG"
RELEASE_NOTES_RANGE="${NOTES_BASE_TAG}..${VERSION}"
fi
echo "RELEASE_NOTES_RANGE=$RELEASE_NOTES_RANGE" >> $GITHUB_ENV
echo "" echo ""
echo "🔍 扫描提交记录..." echo "🔍 扫描提交记录..."
@@ -918,40 +950,139 @@ jobs:
if: steps.changelog.outputs.changelog_updated == 'true' if: steps.changelog.outputs.changelog_updated == 'true'
run: | run: |
echo "======================================" echo "======================================"
echo "📄 提取 Release Notes" echo "📄 生成增量 Release Notes"
echo "======================================" echo "======================================"
cd ${{ env.REPO_DIR }} cd ${{ env.REPO_DIR }}
python3 << 'PYSCRIPT' # Release Notes 只展示本次 rc 相对上一个 tag 的增量提交
import re # CHANGELOG.md 仍是整个版本的累积,二者用途不同)
RELEASE_NOTES_RANGE="${{ env.RELEASE_NOTES_RANGE }}"
REPO_URL="${{ github.server_url }}/${{ github.repository }}"
echo "📊 增量范围: $RELEASE_NOTES_RANGE"
changelog_version = "${{ env.CHANGELOG_VERSION }}" TEMP_COMMITS=$(mktemp)
git log $RELEASE_NOTES_RANGE --no-merges --reverse \
--format="COMMIT_START%n%H%n%an%n%s%nBODY_START%n%b%nBODY_END" > "$TEMP_COMMITS"
with open('CHANGELOG.md', 'r', encoding='utf-8') as f: declare -a COMMITS_HASH
content = f.read() declare -a COMMITS_AUTHOR
declare -a COMMITS_MESSAGE
declare -a COMMITS_BODY
# 匹配版本区块 IFS='|' read -ra IGNORE_PATTERNS <<< "${{ env.IGNORE_PATTERNS_PROCESSED }}"
pattern = rf'## :bookmark: {re.escape(changelog_version)}\s*\n(.*?)(?=\n---\n|\n## :bookmark: |\Z)'
match = re.search(pattern, content, re.DOTALL)
if match: COMMIT_INDEX=0
version_content = match.group(1).strip() CURRENT_HASH=""
# 清理多余的空行 CURRENT_AUTHOR=""
version_content = re.sub(r'\n\s*\n\s*\n+', '\n\n', version_content).strip() CURRENT_MESSAGE=""
# 移除可能的分隔线 CURRENT_BODY=""
version_content = re.sub(r'^-{3,}\s*\n', '', version_content) IN_BODY=false
print(f"✓ Found changelog for version {changelog_version}")
print(f"📊 Content length: {len(version_content)} characters")
else:
version_content = "⚠️ No changelog found for this version"
print(f"⚠️ WARNING: Version {changelog_version} not found in CHANGELOG")
with open('/tmp/release-body.txt', 'w', encoding='utf-8') as f: flush_commit() {
f.write(version_content) [ -z "$CURRENT_HASH" ] && return
for pattern in "${IGNORE_PATTERNS[@]}"; do
[ -z "$pattern" ] && continue
if echo "$CURRENT_MESSAGE" | grep -qiE "$pattern"; then
return
fi
done
COMMITS_HASH[$COMMIT_INDEX]="$CURRENT_HASH"
COMMITS_AUTHOR[$COMMIT_INDEX]="$CURRENT_AUTHOR"
COMMITS_MESSAGE[$COMMIT_INDEX]="$CURRENT_MESSAGE"
COMMITS_BODY[$COMMIT_INDEX]="$CURRENT_BODY"
COMMIT_INDEX=$((COMMIT_INDEX + 1))
}
print("✅ Release notes generated successfully") while IFS= read -r line; do
PYSCRIPT if [[ "$line" == "COMMIT_START" ]]; then
flush_commit
CURRENT_HASH=""
CURRENT_AUTHOR=""
CURRENT_MESSAGE=""
CURRENT_BODY=""
IN_BODY=false
elif [[ "$line" == "BODY_START" ]]; then
IN_BODY=true
CURRENT_BODY=""
elif [[ "$line" == "BODY_END" ]]; then
IN_BODY=false
elif [ "$IN_BODY" = true ]; then
if [ -n "$CURRENT_BODY" ]; then
CURRENT_BODY="${CURRENT_BODY}"$'\n'"${line}"
else
CURRENT_BODY="$line"
fi
else
if [ -z "$CURRENT_HASH" ]; then
CURRENT_HASH="$line"
elif [ -z "$CURRENT_AUTHOR" ]; then
CURRENT_AUTHOR="$line"
elif [ -z "$CURRENT_MESSAGE" ]; then
CURRENT_MESSAGE="$line"
fi
fi
done < "$TEMP_COMMITS"
flush_commit
rm -f "$TEMP_COMMITS"
VALID_COUNT=$COMMIT_INDEX
echo "✅ 增量有效提交: $VALID_COUNT"
NOTES="${CHANGELOG_SECTION_TITLE}\n\n"
for ((i=0; i<$VALID_COUNT; i++)); do
hash="${COMMITS_HASH[$i]}"
author="${COMMITS_AUTHOR[$i]}"
message="${COMMITS_MESSAGE[$i]}"
body="${COMMITS_BODY[$i]}"
short_hash="${hash:0:7}"
COMMIT_LINK="([${short_hash}](${REPO_URL}/commit/${hash}))"
AUTHOR_INFO=""
[ -n "$author" ] && AUTHOR_INFO=" by @${author}"
if [ -n "$body" ]; then
cleaned_body=$(echo "$body" | sed -e :a -e '/^\s*$/d;')
else
cleaned_body=""
fi
if [ -n "$cleaned_body" ]; then
NOTES="${NOTES}- ${message} \n"
while IFS= read -r bline; do
[ -z "$bline" ] && continue
NOTES="${NOTES} ${bline} \n"
done <<< "$cleaned_body"
NOTES="${NOTES} ${COMMIT_LINK}${AUTHOR_INFO}\n"
else
NOTES="${NOTES}- ${message} ${COMMIT_LINK}${AUTHOR_INFO}\n"
fi
done
# 增量贡献者(同样过滤 bot)
if [ "$RELEASE_NOTES_RANGE" = "HEAD" ]; then
CONTRIBUTORS=$(git log --pretty=format:"%an" --no-merges)
else
CONTRIBUTORS=$(git log $RELEASE_NOTES_RANGE --pretty=format:"%an" --no-merges)
fi
CONTRIBUTORS=$(echo "$CONTRIBUTORS" | \
grep -v "github-actions\[bot\]" | \
grep -v "dependabot\[bot\]" | \
grep -v "renovate\[bot\]" | \
grep -vxF "${{ env.GIT_USER_NAME }}" | \
sort -u)
if [ -n "$CONTRIBUTORS" ]; then
NOTES="${NOTES}\n${{ env.CHANGELOG_CONTRIBUTORS_TITLE }}\n\n"
while IFS= read -r name; do
[ -z "$name" ] && continue
NOTES="${NOTES}<a href=\"${{ env.GITEA_SERVER }}/${name}\">\n"
NOTES="${NOTES} <img src=\"${{ env.GITEA_SERVER }}/${name}.png\" alt=\"${name}\" width=\"35\" height=\"35\" style=\"border-radius: 50%;\" onerror=\"this.src='${{ env.GITEA_SERVER }}/assets/img/avatar_default.png'\" />\n"
NOTES="${NOTES}</a>\n"
done <<< "$CONTRIBUTORS"
fi
echo -e "$NOTES" > /tmp/release-body.txt
echo "✅ Release notes 生成完成 ($(wc -c < /tmp/release-body.txt) 字节)"
echo "======================================" echo "======================================"
echo "" echo ""