Files
actions-template/.gitea/workflows/changelog_and_release.yml
T
csh 207588ae36 🐛 fix(ci): 修复 mawk 不支持区间量词导致 CHANGELOG 去重失效
去重用的 awk 正则含 [0-9a-f]{7} 区间量词,ubuntu:22.04 默认的
mawk 不支持区间表达式,会把 {7} 当字面量,导致 EXISTING_HASHES
恒为空、去重完全失效,rc2 把 rc1 的提交整批重复插入。
改用不依赖区间的等价写法,并清理 0.0.5 段已产生的重复内容。
2026-07-20 17:32:13 +08:00

1244 lines
47 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: 📦 自动发布
on:
workflow_run:
workflows: ["🧰 准备环境"]
types:
- completed
concurrency:
group: release-${{ github.repository }}
cancel-in-progress: false
# ==========================================
# 🔧 配置区域 - 根据你的项目修改
# ==========================================
env:
# ===== Token 配置 =====
# Gitea/GitHub 访问令牌
# 用途:
# 1. Git 操作:推送提交(仓库读取由 Prepare 完成)
# 2. API 调用:创建 Release、上传附件
# 权限要求:
# - repo (仓库完整访问权限)
# - write:packages (可选,如需上传包)
# 配置位置:仓库 Settings > Secrets > Actions
# Secret 名称:WORKFLOW (或其他自定义名称)
#
# 💡 提示:同一个 token 可用于所有操作
# 如需分离权限,可配置两个不同的 token:
# - GIT_TOKEN: 用于 git push
# - API_TOKEN: 用于 API 调用
ACCESS_TOKEN: ${{ secrets.WORKFLOW }}
# ===== 工作区配置 =====
WORKSPACE_ROOT: "/data/workspace"
WORKSPACE_SLOT: "release"
# ===== 分支配置 =====
# 主分支名称(用于推送 CHANGELOG 更新)
# 必须明确配置主分支(例如 main 或 master
MAIN_BRANCH: "main"
# ===== 服务器配置 =====
# Gitea 服务器地址(用于生成头像链接和 API 调用)
GITEA_SERVER: "${{ github.server_url }}"
# ===== CHANGELOG 配置 =====
# CHANGELOG 变更列表标题
CHANGELOG_SECTION_TITLE: "### :pencil: What's Changed"
# CHANGELOG 贡献者列表标题
CHANGELOG_CONTRIBUTORS_TITLE: "### :busts_in_silhouette: Contributors"
# CHANGELOG 版本号处理方式
# - "full": 使用完整 tag 名称(2025.10.31-1-beta → ## :bookmark: 2025.10.31-1-beta
# - "strip": 去除 pre-release 后缀(1.0.0-beta1 → ## :bookmark: 1.0.0
# ⚠️ 推荐使用 "strip" 模式以支持多个开发版本叠加到同一CHANGELOG区域
CHANGELOG_VERSION_MODE: "strip"
# ===== RELEASE 配置 =====
# Release 标题模板
# 可用变量: {version} - 将被替换为实际的版本号
RELEASE_TITLE: "{version}"
# Pre-release 配置
# - "false": 正式版本(会被标记为 latest)
# - "true": 预发布版本(会被标记为 pre-release
# - "auto": 自动判断(根据 tag 名称中是否包含 alpha/beta/rc
RELEASE_PRERELEASE_MODE: "auto"
# Draft 配置(是否创建为草稿)
# - "true": 创建为草稿,不会立即发布
# - "false": 立即发布 Release
RELEASE_IS_DRAFT: "false"
# 统一管理 pre-release 关键词
PRERELEASE_KEYWORDS: "alpha|beta|rc|pre|preview|dev|test"
# 额外要上传到 Release 的文件(空格分隔)
# 例如: "README.md LICENSE docs/guide.pdf"
ADDITIONAL_RELEASE_FILES: ""
# ===== Git 提交配置 =====
# Git 用户配置
GIT_USER_NAME: "ci[bot]"
GIT_USER_EMAIL: "ci[bot]@tinysoft.com.cn"
# Git 提交消息模板
# 可用变量: {version} - 将被替换为实际的版本号
# [skip ci] 标记防止触发无限循环
COMMIT_MESSAGE: ":memo: Auto update CHANGELOG for {version} [skip ci]"
# ===== 提交过滤配置 =====
# 需要忽略的提交模式(支持正则表达式)
# 这些提交不会被添加到 CHANGELOG 中
# 使用 | 分隔多个模式
#
# ⚠️ 重要说明:
# - 使用 ^ 表示行首匹配,$ 表示行尾匹配
# - [skip ci] 和 [ci skip] 仅匹配行尾,避免误过滤其他提交
# - 如果要完全匹配某个字符串,使用 ^...$
IGNORE_PATTERNS: >-
^Merge |
^:memo: Auto update CHANGELOG|
\\[skip ci\\]|
\\[ci skip\\]|
^Bump version|
^Release v|
^chore: update config$|
^style: |
^chore: format|
^chore: lint|
^Initial commit$
# ========================================
# 📌 版本号规范说明 (SemVer)
# ========================================
#
# 格式: MAJOR.MINOR.PATCH[-prerelease]
#
# 开发版本示例:
# - 1.0.0-beta1, 1.0.0-beta2, 1.0.0-beta10 等
# - 1.2.0-alpha1, 1.2.0-rc1 等
#
# 正式版本示例:
# - 1.0.0 (首个稳定版,PATCH必须从0开始)
# - 1.1.0 (新增功能,向后兼容)
# - 1.1.1 (Bug修复)
# - 2.0.0 (破坏性变更,不向后兼容)
#
# ⚠️ 重要规则:
# 1. 开发版本都以 .0-beta[N] 或 .0-alpha[N] 结尾
# 2. 正式发布必须从 .0 开始(如 1.0.0, 1.1.0, 2.0.0
# 3. 多个开发版本(如1.0.0-beta1, 1.0.0-beta2)会累加到同一个CHANGELOG区域(1.0.0)
# 4. CHANGELOG内容是叠加而非覆盖
jobs:
route-release:
if: ${{ github.event.workflow_run.conclusion == 'success' && contains(github.event.workflow_run.path, '@refs/tags/') }}
runs-on: standard-ubuntu-22
permissions:
contents: read
outputs:
should_release: ${{ steps.route.outputs.should_release }}
version: ${{ steps.route.outputs.version }}
tag_ref: ${{ steps.route.outputs.tag_ref }}
source_sha: ${{ steps.route.outputs.source_sha }}
env:
UPSTREAM_PATH: ${{ github.event.workflow_run.path }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
steps:
- name: 🧭 恢复并验证 Tag
id: route
shell: bash
run: |
set -euo pipefail
if ! command -v git >/dev/null 2>&1; then
echo "Prepare 成功后仍缺少发布路由所需命令: git" >&2
exit 1
fi
echo "should_release=false" >> "$GITHUB_OUTPUT"
case "$HEAD_SHA" in
''|*[!0-9a-fA-F]*)
echo "无效的上游提交 SHA: $HEAD_SHA" >&2
exit 1
;;
esac
FULL_REF="${UPSTREAM_PATH##*@}"
case "$FULL_REF" in
refs/tags/*) ;;
*)
echo "workflow_run.path 未包含有效 Tag ref: $UPSTREAM_PATH" >&2
exit 1
;;
esac
VERSION="${FULL_REF#refs/tags/}"
case "$VERSION" in
[0-9]*) ;;
*)
echo "跳过非版本 Tag: $VERSION"
exit 0
;;
esac
if ! git check-ref-format "$FULL_REF"; then
echo "数字开头的 Tag ref 格式无效: $FULL_REF" >&2
exit 1
fi
REPO_NAME="${{ github.event.repository.name }}"
REPOSITORY_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/repository.git"
if [ ! -d "$REPOSITORY_DIR" ] || \
[ "$(git --git-dir="$REPOSITORY_DIR" rev-parse --is-bare-repository 2>/dev/null)" != "true" ]; then
echo "Prepare 未创建有效共享 bare 仓库: $REPOSITORY_DIR" >&2
exit 1
fi
if ! git --git-dir="$REPOSITORY_DIR" cat-file -e "${HEAD_SHA}^{commit}"; then
echo "共享仓库中不存在上游提交: $HEAD_SHA" >&2
exit 1
fi
if ! tag_sha=$(git --git-dir="$REPOSITORY_DIR" rev-parse "${FULL_REF}^{commit}" 2>/dev/null); then
echo "共享仓库中不存在可解析的 Tag: $FULL_REF" >&2
exit 1
fi
if [ "$tag_sha" != "$HEAD_SHA" ]; then
echo "Tag/SHA 不匹配: $FULL_REF -> $tag_sha,上游为 $HEAD_SHA" >&2
exit 1
fi
echo "should_release=true" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag_ref=$FULL_REF" >> "$GITHUB_OUTPUT"
echo "source_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT"
echo "发布路由通过: $VERSION @ $HEAD_SHA"
changelog-and-release:
needs: route-release
if: ${{ needs.route-release.outputs.should_release == 'true' }}
runs-on: standard-ubuntu-22
permissions:
contents: write
env:
VERSION: ${{ needs.route-release.outputs.version }}
TAG_REF: ${{ needs.route-release.outputs.tag_ref }}
SOURCE_SHA: ${{ needs.route-release.outputs.source_sha }}
steps:
- name: ⚙️ 加载配置
id: config
run: |
echo "======================================"
echo "⚙️ 加载工作流配置"
echo "======================================"
# 从环境变量读取配置
VERSION="${{ env.VERSION }}"
KEYWORDS="${{ env.PRERELEASE_KEYWORDS }}"
STRIP_REGEX="-($KEYWORDS)[0-9]*$" # 用于 sed 删除后缀
MATCH_REGEX="($KEYWORDS)" # 用于 bash =~ 匹配
# 处理 CHANGELOG 版本号
if [[ "${{ env.CHANGELOG_VERSION_MODE }}" == "strip" ]]; then
# 去除 pre-release 后缀(-beta, -rc1, -alpha 等)
CHANGELOG_VERSION=$(echo "$VERSION" | sed -E "s/${STRIP_REGEX}//")
echo "📝 CHANGELOG version mode: strip"
echo " Tag: $VERSION → CHANGELOG: $CHANGELOG_VERSION"
else
# 使用完整 tag 名称
CHANGELOG_VERSION="$VERSION"
echo "📝 CHANGELOG version mode: full"
echo " Tag: $VERSION → CHANGELOG: $CHANGELOG_VERSION"
fi
# 处理 Release 标题
RELEASE_TITLE_PROCESSED="${{ env.RELEASE_TITLE }}"
RELEASE_TITLE_PROCESSED="${RELEASE_TITLE_PROCESSED//\{version\}/$VERSION}"
# 处理提交消息
COMMIT_MSG="${{ env.COMMIT_MESSAGE }}"
COMMIT_MSG="${COMMIT_MSG//\{version\}/$VERSION}"
# 智能判断 pre-release
if [[ "${{ env.RELEASE_PRERELEASE_MODE }}" == "auto" ]]; then
if [[ "$VERSION" =~ $MATCH_REGEX ]]; then
RELEASE_IS_PRERELEASE="true"
PRERELEASE_DETECTED="yes (auto-detected: $VERSION contains pre-release keyword)"
else
RELEASE_IS_PRERELEASE="false"
PRERELEASE_DETECTED="no (auto-detected: stable release)"
fi
else
RELEASE_IS_PRERELEASE="${{ env.RELEASE_PRERELEASE_MODE }}"
PRERELEASE_DETECTED="${{ env.RELEASE_PRERELEASE_MODE }} (manually configured)"
fi
# 处理忽略模式(去除空格,保持管道分隔格式)
echo "🔧 处理忽略模式..."
PROCESSED_PATTERNS=""
IFS='|' read -ra PATTERNS_ARRAY <<< "${{ env.IGNORE_PATTERNS }}"
for pattern in "${PATTERNS_ARRAY[@]}"; do
# 使用 sed 去除首尾空格,保留转义字符
pattern=$(echo "$pattern" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$pattern" ]; then
if [ -z "$PROCESSED_PATTERNS" ]; then
PROCESSED_PATTERNS="$pattern"
else
PROCESSED_PATTERNS="$PROCESSED_PATTERNS|$pattern"
fi
fi
done
echo "✅ 已处理 $(echo "$PROCESSED_PATTERNS" | grep -o '|' | wc -l | awk '{print $1+1}') 个忽略模式"
# 导出处理后的配置到 GitHub 环境变量
echo "CHANGELOG_VERSION=$CHANGELOG_VERSION" >> $GITHUB_ENV
echo "RELEASE_TITLE_PROCESSED=$RELEASE_TITLE_PROCESSED" >> $GITHUB_ENV
echo "RELEASE_IS_PRERELEASE=$RELEASE_IS_PRERELEASE" >> $GITHUB_ENV
echo "COMMIT_MSG=$COMMIT_MSG" >> $GITHUB_ENV
echo "PRERELEASE_STRIP_REGEX=$STRIP_REGEX" >> $GITHUB_ENV
echo "PRERELEASE_MATCH_REGEX=$MATCH_REGEX" >> $GITHUB_ENV
echo "IGNORE_PATTERNS_PROCESSED=$PROCESSED_PATTERNS" >> $GITHUB_ENV
# 显示配置摘要
echo ""
echo "📋 Configuration Summary:"
echo " 🏷️ Tag version: $VERSION"
echo " 📝 CHANGELOG version: $CHANGELOG_VERSION"
echo " 🌐 Gitea Server: ${{ env.GITEA_SERVER }}"
echo " 📄 CHANGELOG Title: ${{ env.CHANGELOG_SECTION_TITLE }}"
echo " 🚀 Release Title: $RELEASE_TITLE_PROCESSED"
echo " 🏷️ Pre-release: $PRERELEASE_DETECTED"
echo " 📄 Draft: ${{ env.RELEASE_IS_DRAFT }}"
echo " 💬 Commit Message: $COMMIT_MSG"
echo " 📎 Additional Files: ${ADDITIONAL_RELEASE_FILES:-none}"
echo ""
echo "✅ Configuration loaded successfully"
echo "======================================"
echo ""
- name: 📥 对齐发布 Worktree
id: prepare_worktree
shell: bash
run: |
set -euo pipefail
for command in git python3 curl flock realpath; do
if ! command -v "$command" >/dev/null 2>&1; then
echo "Prepare 成功后仍缺少发布所需命令: $command" >&2
exit 1
fi
done
REPO_NAME="${{ github.event.repository.name }}"
HEAD_SHA="${{ env.SOURCE_SHA }}"
MAIN_BRANCH="${{ env.MAIN_BRANCH }}"
REPOSITORY_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/repository.git"
WORKTREE_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/worktrees/release"
WORKTREE_LOCK="${WORKSPACE_ROOT}/${REPO_NAME}/worktree-admin.lock"
MAIN_REMOTE_REF="refs/remotes/origin/${MAIN_BRANCH}"
case "$HEAD_SHA" in
''|*[!0-9a-fA-F]*)
echo "无效的路由提交 SHA: $HEAD_SHA" >&2
exit 1
;;
esac
if [ -z "$MAIN_BRANCH" ]; then
echo "未配置 MAIN_BRANCH" >&2
exit 1
fi
if [ ! -d "$REPOSITORY_DIR" ] || \
[ "$(git --git-dir="$REPOSITORY_DIR" rev-parse --is-bare-repository 2>/dev/null)" != "true" ]; then
echo "Prepare 未创建有效共享 bare 仓库: $REPOSITORY_DIR" >&2
exit 1
fi
if ! git --git-dir="$REPOSITORY_DIR" cat-file -e "${HEAD_SHA}^{commit}"; then
echo "共享仓库中不存在发布提交: $HEAD_SHA" >&2
exit 1
fi
if ! git --git-dir="$REPOSITORY_DIR" show-ref --verify --quiet "$MAIN_REMOTE_REF"; then
echo "Prepare 未 fetch 配置的主分支: $MAIN_REMOTE_REF" >&2
exit 1
fi
if [ ! -f "$WORKTREE_DIR/.git" ]; then
exec 9>"$WORKTREE_LOCK"
flock 9
if [ ! -f "$WORKTREE_DIR/.git" ]; then
if [ -e "$WORKTREE_DIR" ]; then
echo "发布 Worktree 路径已存在但不是 linked worktree: $WORKTREE_DIR" >&2
exit 1
fi
mkdir -p "$(dirname "$WORKTREE_DIR")"
git --git-dir="$REPOSITORY_DIR" worktree add --detach "$WORKTREE_DIR" "$HEAD_SHA"
fi
flock -u 9
fi
common_dir=$(git -C "$WORKTREE_DIR" rev-parse --path-format=absolute --git-common-dir)
if [ "$common_dir" != "$(realpath "$REPOSITORY_DIR")" ]; then
echo "发布 Worktree 不属于共享仓库: $WORKTREE_DIR" >&2
exit 1
fi
git -C "$WORKTREE_DIR" checkout --detach --force "$HEAD_SHA"
git -C "$WORKTREE_DIR" reset --hard "$HEAD_SHA"
git -C "$WORKTREE_DIR" clean -ffdx
actual_sha=$(git -C "$WORKTREE_DIR" rev-parse HEAD)
if [ "$actual_sha" != "$HEAD_SHA" ]; then
echo "发布 Worktree 提交不匹配: 期望 $HEAD_SHA,实际 $actual_sha" >&2
exit 1
fi
git -C "$WORKTREE_DIR" config user.name "${{ env.GIT_USER_NAME }}"
git -C "$WORKTREE_DIR" config user.email "${{ env.GIT_USER_EMAIL }}"
echo "REPOSITORY_DIR=$REPOSITORY_DIR" >> "$GITHUB_ENV"
echo "REPO_DIR=$WORKTREE_DIR" >> "$GITHUB_ENV"
echo "REPO_NAME=$REPO_NAME" >> "$GITHUB_ENV"
echo "MAIN_REMOTE_REF=$MAIN_REMOTE_REF" >> "$GITHUB_ENV"
echo "发布 Worktree: $WORKTREE_DIR @ $actual_sha"
- name: 🤖 检查 Bot 提交
id: check_bot
run: |
echo "======================================"
echo "🤖 检查是否为 Bot 提交"
echo "======================================"
cd ${{ env.REPO_DIR }}
COMMIT_MSG=$(git log -1 --pretty=%B "${{ env.SOURCE_SHA }}")
echo "📝 Tag commit message:"
echo "$COMMIT_MSG"
echo ""
# 检查是否包含 [skip ci] 或由 bot 创建
if echo "$COMMIT_MSG" | grep -qE '\[skip ci\]|\[ci skip\]'; then
echo "⏭️ Bot commit detected (contains [skip ci])"
echo "is_bot_commit=true" >> $GITHUB_OUTPUT
else
echo "✅ Not a bot commit"
echo "is_bot_commit=false" >> $GITHUB_OUTPUT
fi
echo "======================================"
echo ""
- name: 📝 生成 CHANGELOG
id: changelog
if: steps.check_bot.outputs.is_bot_commit != 'true'
run: |
echo "======================================"
echo "📝 生成 CHANGELOG"
echo "======================================"
cd ${{ env.REPO_DIR }}
VERSION="${{ env.VERSION }}"
CHANGELOG_VERSION="${{ env.CHANGELOG_VERSION }}"
echo "🏷️ Tag: $VERSION"
echo "📝 CHANGELOG Version: $CHANGELOG_VERSION"
echo ""
# 查找前一个 tag
# 排除所有属于当前 CHANGELOG 版本的 tag(例如 0.0.2-rc1, 0.0.2-rc2 都属于 0.0.2
echo "🔍 查找前一个版本的 tag..."
# 获取所有 tag
ALL_TAGS=$(git tag --sort=-version:refname)
# 遍历查找第一个不属于当前 CHANGELOG 版本的 tag
PREVIOUS_TAG=""
while IFS= read -r tag; do
# 跳过空行
[ -z "$tag" ] && continue
# 对每个 tag 执行 strip 操作(去除 pre-release 后缀)
if [[ "${{ env.CHANGELOG_VERSION_MODE }}" == "strip" ]]; then
tag_stripped=$(echo "$tag" | sed -E "s/${{ env.PRERELEASE_STRIP_REGEX }}//")
else
tag_stripped="$tag"
fi
# 如果这个 tag 的 CHANGELOG 版本不等于当前版本,就是我们要找的
if [ "$tag_stripped" != "$CHANGELOG_VERSION" ]; then
PREVIOUS_TAG="$tag"
break
fi
done <<< "$ALL_TAGS"
if [ -z "$PREVIOUS_TAG" ]; then
echo "ℹ️ 未找到前一个tag,将使用所有提交"
COMMIT_RANGE="HEAD"
else
echo "📍 前一个tag: $PREVIOUS_TAG"
# 对 previous tag 也执行 strip,显示对应的 CHANGELOG 版本
if [[ "${{ env.CHANGELOG_VERSION_MODE }}" == "strip" ]]; then
PREV_CHANGELOG_VERSION=$(echo "$PREVIOUS_TAG" | sed -E "s/${{ env.PRERELEASE_STRIP_REGEX }}//")
echo " (对应 CHANGELOG 版本: $PREV_CHANGELOG_VERSION)"
fi
COMMIT_RANGE="${PREVIOUS_TAG}..${VERSION}"
fi
echo ""
echo "🔍 扫描提交记录..."
TEMP_COMMITS=$(mktemp)
git log $COMMIT_RANGE --no-merges --reverse --format="COMMIT_START%n%H%n%an%n%s%nBODY_START%n%b%nBODY_END" > "$TEMP_COMMITS"
declare -a COMMITS_HASH
declare -a COMMITS_AUTHOR
declare -a COMMITS_MESSAGE
declare -a COMMITS_BODY
# 从环境变量加载忽略模式 (转换为 bash 数组)
IFS='|' read -ra IGNORE_PATTERNS <<< "${{ env.IGNORE_PATTERNS_PROCESSED }}"
echo "📋 已加载 ${#IGNORE_PATTERNS[@]} 个忽略模式"
COMMIT_INDEX=0
CURRENT_HASH=""
CURRENT_AUTHOR=""
CURRENT_MESSAGE=""
CURRENT_BODY=""
IN_BODY=false
SKIPPED_COUNT=0
while IFS= read -r line; do
if [[ "$line" == "COMMIT_START" ]]; then
# 处理前一个 commit
if [ -n "$CURRENT_HASH" ]; then
SHOULD_SKIP=false
for pattern in "${IGNORE_PATTERNS[@]}"; do
[ -z "$pattern" ] && continue
if echo "$CURRENT_MESSAGE" | grep -qiE "$pattern"; then
SHOULD_SKIP=true
echo " ⏭️ 跳过: ${CURRENT_MESSAGE:0:70}... (匹配: ${pattern})"
SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
break
fi
done
if [ "$SHOULD_SKIP" = false ]; then
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))
fi
fi
# 重置状态
CURRENT_HASH=""
CURRENT_AUTHOR=""
CURRENT_MESSAGE=""
CURRENT_BODY=""
IN_BODY=false
STATE="HASH"
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"
# 处理最后一个 commit
if [ -n "$CURRENT_HASH" ]; then
SHOULD_SKIP=false
for pattern in "${IGNORE_PATTERNS[@]}"; do
[ -z "$pattern" ] && continue
if echo "$CURRENT_MESSAGE" | grep -qiE "$pattern"; then
SHOULD_SKIP=true
echo " ⏭️ 跳过: ${CURRENT_MESSAGE:0:70}... (匹配: ${pattern})"
SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
break
fi
done
if [ "$SHOULD_SKIP" = false ]; then
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))
fi
fi
rm -f "$TEMP_COMMITS"
VALID_COUNT=$COMMIT_INDEX
echo ""
echo "📊 提交统计:"
echo " ✅ 有效提交: $VALID_COUNT"
echo " ⏭️ 已跳过: $SKIPPED_COUNT"
echo " 📝 总计: $((VALID_COUNT + SKIPPED_COUNT))"
echo ""
if [ $VALID_COUNT -eq 0 ]; then
echo "⚠️ 未找到有效的提交"
echo "changelog_updated=false" >> $GITHUB_OUTPUT
echo "content_changed=false" >> $GITHUB_OUTPUT
exit 0
fi
# ============================================
# 构建 CHANGELOG 条目
# ============================================
echo "📄 构建 CHANGELOG 条目..."
REPO_URL="${{ github.server_url }}/${{ github.repository }}"
# 初始化条目内容
NEW_ENTRY="## :bookmark: ${CHANGELOG_VERSION}\n\n"
NEW_ENTRY="${NEW_ENTRY}${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]}"
echo " [$((i+1))/$VALID_COUNT] ${hash:0:7}: ${message:0:60}..."
# 构建提交链接 - 使用短 hash 格式
short_hash="${hash:0:7}"
COMMIT_LINK="([${short_hash}](${REPO_URL}/commit/${hash}))"
# 构建作者信息
AUTHOR_INFO=""
if [ -n "$author" ]; then
AUTHOR_INFO=" by @${author}"
fi
# 处理 body (如果存在)
if [ -n "$body" ]; then
# 移除首尾空行
cleaned_body=$(echo "$body" | sed -e :a -e '/^\s*$/d;')
if [ -n "$cleaned_body" ]; then
# 有 body: 主题在第一行,body 内容缩进显示,链接和作者放在一起
NEW_ENTRY="${NEW_ENTRY}- ${message} \n"
# 将 body 按行添加,每行缩进两个空格,使用 Markdown 换行
while IFS= read -r line; do
[ -z "$line" ] && continue
NEW_ENTRY="${NEW_ENTRY} ${line} \n"
done <<< "$cleaned_body"
# 链接和作者放在缩进行
NEW_ENTRY="${NEW_ENTRY} ${COMMIT_LINK}${AUTHOR_INFO}\n"
else
# body 为空 (只有空白): 单行格式
NEW_ENTRY="${NEW_ENTRY}- ${message} ${COMMIT_LINK}${AUTHOR_INFO}\n"
fi
else
# 没有 body: 单行格式
NEW_ENTRY="${NEW_ENTRY}- ${message} ${COMMIT_LINK}${AUTHOR_INFO}\n"
fi
done
echo ""
echo "👥 收集贡献者..."
# 收集贡献者 (去除 bot)
# 动态过滤配置的 bot 用户名
if [ "$COMMIT_RANGE" = "HEAD" ]; then
CONTRIBUTORS=$(git log --pretty=format:"%an" --no-merges | \
grep -v "github-actions\[bot\]" | \
grep -v "dependabot\[bot\]" | \
grep -v "renovate\[bot\]" | \
grep -vxF "${{ env.GIT_USER_NAME }}" | \
sort -u)
else
CONTRIBUTORS=$(git log $COMMIT_RANGE --pretty=format:"%an" --no-merges | \
grep -v "github-actions\[bot\]" | \
grep -v "dependabot\[bot\]" | \
grep -v "renovate\[bot\]" | \
grep -vxF "${{ env.GIT_USER_NAME }}" | \
sort -u)
fi
if [ -n "$CONTRIBUTORS" ]; then
CONTRIBUTOR_COUNT=$(echo "$CONTRIBUTORS" | wc -l)
echo "✓ 找到 ${CONTRIBUTOR_COUNT} 个贡献者"
NEW_ENTRY="${NEW_ENTRY}\n${{ env.CHANGELOG_CONTRIBUTORS_TITLE }}\n\n"
while IFS= read -r name; do
[ -z "$name" ] && continue
NEW_ENTRY="${NEW_ENTRY}<a href=\"${{ env.GITEA_SERVER }}/${name}\">\n"
NEW_ENTRY="${NEW_ENTRY} <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"
NEW_ENTRY="${NEW_ENTRY}</a>\n"
done <<< "$CONTRIBUTORS"
fi
NEW_ENTRY="${NEW_ENTRY}\n---\n"
echo ""
echo "💾 写入 CHANGELOG.md..."
# 从 Prepare 已 fetch 的远端引用准备主分支,再写入 CHANGELOG
MAIN_BRANCH="${{ env.MAIN_BRANCH }}"
MAIN_REMOTE_REF="${{ env.MAIN_REMOTE_REF }}"
git -C "${{ env.REPO_DIR }}" checkout --force -B "$MAIN_BRANCH" "$MAIN_REMOTE_REF"
git -C "${{ env.REPO_DIR }}" reset --hard "$MAIN_REMOTE_REF"
git -C "${{ env.REPO_DIR }}" clean -ffdx
# ============================================
# 更新或创建 CHANGELOG.md
# ============================================
if [ -f CHANGELOG.md ]; then
# CHANGELOG 文件已存在
if grep -q "^## :bookmark: ${CHANGELOG_VERSION}$" CHANGELOG.md; then
# 版本已存在 - 内容叠加模式
echo "🔄 版本 ${CHANGELOG_VERSION} 已存在,执行内容叠加..."
# 提取现有版本区域中的 commit hashes (用于去重)
EXISTING_HASHES=$(awk -v version="${CHANGELOG_VERSION}" '
BEGIN { in_version=0 }
/^## :bookmark: / && $0 ~ version"$" { in_version=1; next }
/^## :bookmark: / && in_version { in_version=0 }
in_version && /\(\[[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\]/ {
if (match($0, /\[[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\]/)) {
hash = substr($0, RSTART+1, RLENGTH-2)
print hash
}
}
' CHANGELOG.md | sort -u)
# 找出真正的新 commits (基于 hash 去重)
NEW_COMMIT_ENTRIES=""
NEW_COMMIT_COUNT=0
for ((i=0; i<$VALID_COUNT; i++)); do
hash="${COMMITS_HASH[$i]}"
short_hash="${hash:0:7}"
# 检查这个 commit 是否已存在
if echo "$EXISTING_HASHES" | grep -q "^${short_hash}$"; then
echo " ️ 跳过已存在: ${short_hash} ${COMMITS_MESSAGE[$i]:0:50}..."
continue
fi
# 这是一个新的 commit,添加它
author="${COMMITS_AUTHOR[$i]}"
message="${COMMITS_MESSAGE[$i]}"
body="${COMMITS_BODY[$i]}"
COMMIT_LINK="([${short_hash}](${REPO_URL}/commit/${hash}))"
# 构建作者信息
AUTHOR_INFO=""
if [ -n "$author" ]; then
AUTHOR_INFO=" by @${author}"
fi
if [ -n "$body" ]; then
cleaned_body=$(echo "$body" | sed -e :a -e '/^\s*$/d;')
if [ -n "$cleaned_body" ]; then
NEW_COMMIT_ENTRIES="${NEW_COMMIT_ENTRIES}- ${message} \n"
while IFS= read -r line; do
[ -z "$line" ] && continue
NEW_COMMIT_ENTRIES="${NEW_COMMIT_ENTRIES} ${line} \n"
done <<< "$cleaned_body"
NEW_COMMIT_ENTRIES="${NEW_COMMIT_ENTRIES} ${COMMIT_LINK}${AUTHOR_INFO}\n"
else
NEW_COMMIT_ENTRIES="${NEW_COMMIT_ENTRIES}- ${message} ${COMMIT_LINK}${AUTHOR_INFO}\n"
fi
else
NEW_COMMIT_ENTRIES="${NEW_COMMIT_ENTRIES}- ${message} ${COMMIT_LINK}${AUTHOR_INFO}\n"
fi
NEW_COMMIT_COUNT=$((NEW_COMMIT_COUNT + 1))
done
if [ $NEW_COMMIT_COUNT -gt 0 ]; then
echo "✓ 发现 ${NEW_COMMIT_COUNT} 个新的commits需要添加"
# 在 Contributors 部分之前插入新的 commits
TEMP_FILE=$(mktemp)
IN_VERSION=false
ADDED=false
while IFS= read -r line; do
# 检测版本标题
if [[ "$line" =~ ^##[[:space:]]:bookmark:[[:space:]]${CHANGELOG_VERSION}$ ]]; then
echo "$line" >> "$TEMP_FILE"
IN_VERSION=true
# 检测到 Contributors 标题,在它之前插入新 commits
elif [[ "$line" =~ ^###[[:space:]]:busts_in_silhouette: ]] && [ "$IN_VERSION" = true ] && [ "$ADDED" = false ]; then
echo "" >> "$TEMP_FILE"
echo -e "${NEW_COMMIT_ENTRIES}" >> "$TEMP_FILE"
echo "$line" >> "$TEMP_FILE"
ADDED=true
# 遇到下一个版本标题
elif [[ "$line" =~ ^##[[:space:]]:bookmark: ]] && [ "$IN_VERSION" = true ]; then
# 如果还没添加(可能没有 Contributors 部分)
if [ "$ADDED" = false ]; then
echo "" >> "$TEMP_FILE"
echo -e "${NEW_COMMIT_ENTRIES}" >> "$TEMP_FILE"
ADDED=true
fi
echo "$line" >> "$TEMP_FILE"
IN_VERSION=false
else
echo "$line" >> "$TEMP_FILE"
fi
done < CHANGELOG.md
# 如果到文件末尾还没添加(版本在最后且没有 Contributors
if [ "$IN_VERSION" = true ] && [ "$ADDED" = false ]; then
echo "" >> "$TEMP_FILE"
echo -e "${NEW_COMMIT_ENTRIES}" >> "$TEMP_FILE"
fi
mv "$TEMP_FILE" CHANGELOG.md
echo "✅ 已追加 ${NEW_COMMIT_COUNT} 个新commits到现有版本"
echo "changelog_updated=true" >> $GITHUB_OUTPUT
echo "content_changed=true" >> $GITHUB_OUTPUT
else
echo "️ 所有commits都已存在于CHANGELOG中"
echo " (通常发生在重建已删除的 tag)"
echo " 将从现有内容创建 Release"
echo "changelog_updated=true" >> $GITHUB_OUTPUT
echo "content_changed=false" >> $GITHUB_OUTPUT
fi
else
# 版本不存在 - 添加新版本条目
echo " 添加新版本条目: ${CHANGELOG_VERSION}"
TEMP_FILE=$(mktemp)
# 读取第一行 (CHANGELOG 标题)
head -n 1 CHANGELOG.md > "$TEMP_FILE"
echo "" >> "$TEMP_FILE"
# 插入新条目
echo -e "${NEW_ENTRY}" >> "$TEMP_FILE"
# 追加剩余内容 (从第3行开始,跳过标题后的空行)
tail -n +3 CHANGELOG.md >> "$TEMP_FILE"
mv "$TEMP_FILE" CHANGELOG.md
echo "✅ 已添加新的CHANGELOG条目"
echo "changelog_updated=true" >> $GITHUB_OUTPUT
echo "content_changed=true" >> $GITHUB_OUTPUT
fi
else
# CHANGELOG 文件不存在 - 创建新文件
echo "📄 创建新的 CHANGELOG.md"
echo "# :memo: CHANGELOG" > CHANGELOG.md
echo "" >> CHANGELOG.md
echo -e "${NEW_ENTRY}" >> CHANGELOG.md
echo "✅ 已创建新的CHANGELOG.md"
echo "changelog_updated=true" >> $GITHUB_OUTPUT
echo "content_changed=true" >> $GITHUB_OUTPUT
fi
echo ""
echo "✅ CHANGELOG 生成完成"
echo "======================================"
echo ""
- name: 📤 提交 CHANGELOG 到主分支
id: commit
if: steps.changelog.outputs.content_changed == 'true'
run: |
echo "======================================"
echo "📤 提交 CHANGELOG 更新"
echo "======================================"
cd ${{ env.REPO_DIR }}
# 配置 Git 用户信息
git config user.name "${{ env.GIT_USER_NAME }}"
git config user.email "${{ env.GIT_USER_EMAIL }}"
# 添加 CHANGELOG.md
git add CHANGELOG.md
# 检查是否有变更
if git diff --staged --quiet; then
echo "📌 没有变更,跳过提交"
else
echo "📝 提交 CHANGELOG 更新..."
git commit -m "${{ env.COMMIT_MSG }}"
echo "📤 推送到远程 ${{ env.MAIN_BRANCH }} 分支..."
if git -c credential.helper= \
-c 'credential.helper=!f() { test "$1" = get || exit 0; printf "%s\n" username=oauth2; printf "%s\n" "password=${ACCESS_TOKEN}"; }; f' \
-c credential.interactive=never \
-C "${{ env.REPO_DIR }}" push origin "${{ env.MAIN_BRANCH }}"; then
echo "✅ 推送成功"
else
echo "❌ 推送失败"
exit 1
fi
fi
echo "======================================"
echo ""
- name: 📄 提取 Release Notes
id: extract_notes
if: steps.changelog.outputs.changelog_updated == 'true'
run: |
echo "======================================"
echo "📄 提取 Release Notes"
echo "======================================"
cd ${{ env.REPO_DIR }}
python3 << 'PYSCRIPT'
import re
changelog_version = "${{ env.CHANGELOG_VERSION }}"
with open('CHANGELOG.md', 'r', encoding='utf-8') as f:
content = f.read()
# 匹配版本区块
pattern = rf'## :bookmark: {re.escape(changelog_version)}\s*\n(.*?)(?=\n---\n|\n## :bookmark: |\Z)'
match = re.search(pattern, content, re.DOTALL)
if match:
version_content = match.group(1).strip()
# 清理多余的空行
version_content = re.sub(r'\n\s*\n\s*\n+', '\n\n', version_content).strip()
# 移除可能的分隔线
version_content = re.sub(r'^-{3,}\s*\n', '', version_content)
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:
f.write(version_content)
print("✅ Release notes generated successfully")
PYSCRIPT
echo "======================================"
echo ""
- name: 🚀 创建 Release
id: create_release
if: steps.changelog.outputs.changelog_updated == 'true'
run: |
echo "======================================"
echo "🚀 创建 GitHub/Gitea Release"
echo "======================================"
cd ${{ env.REPO_DIR }}
echo "📦 准备 Release 数据..."
echo "📋 Release 标题: ${{ env.RELEASE_TITLE_PROCESSED }}"
echo "🏷️ Pre-release: ${{ env.RELEASE_IS_PRERELEASE }}"
echo "📄 Draft: ${{ env.RELEASE_IS_DRAFT }}"
echo ""
python3 << 'PYSCRIPT'
import json
import os
with open('/tmp/release-body.txt', 'r', encoding='utf-8') as release_body_file:
body = release_body_file.read()
payload = {
"tag_name": os.environ["VERSION"],
"name": os.environ["RELEASE_TITLE_PROCESSED"],
"body": body,
"draft": os.environ["RELEASE_IS_DRAFT"] == "true",
"prerelease": os.environ["RELEASE_IS_PRERELEASE"] == "true",
}
with open('/tmp/payload.json', 'w', encoding='utf-8') as payload_file:
json.dump(payload, payload_file, ensure_ascii=False, indent=2)
print("✓ Payload prepared")
PYSCRIPT
echo "🌐 发送 API 请求..."
HTTP_CODE=$(curl -s -o /tmp/release_response.json -w "%{http_code}" -X POST \
-H "Authorization: token ${{ env.ACCESS_TOKEN }}" \
-H "Content-Type: application/json" \
-d @/tmp/payload.json \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases")
echo ""
echo "📡 API 响应 (HTTP $HTTP_CODE):"
echo "---"
cat /tmp/release_response.json | python3 -m json.tool 2>/dev/null || cat /tmp/release_response.json
echo "---"
echo ""
if [ "$HTTP_CODE" != "201" ] && [ "$HTTP_CODE" != "200" ]; then
echo "❌ 错误: 创建 Release 失败"
exit 1
fi
echo "✅ Release 创建成功"
echo "======================================"
echo ""
- name: 📎 上传附件
id: upload_assets
if: steps.changelog.outputs.changelog_updated == 'true'
run: |
cd ${{ env.REPO_DIR }}
echo "🔍 提取 Release ID..."
RELEASE_ID=$(python3 << 'PYSCRIPT'
import json
import sys
try:
with open('/tmp/release_response.json', 'r') as f:
data = json.load(f)
if 'id' not in data:
print("❌ 错误: 响应中没有 'id' 字段", file=sys.stderr)
sys.exit(1)
print(data['id'])
except Exception as e:
print(f"❌ 错误: {e}", file=sys.stderr)
sys.exit(1)
PYSCRIPT
)
if [ -z "$RELEASE_ID" ]; then
echo "❌ 错误: 无法获取 Release ID"
exit 1
fi
echo "✓ Release ID: $RELEASE_ID"
echo ""
# 上传额外的文件
if [ -n "${{ env.ADDITIONAL_RELEASE_FILES }}" ]; then
echo ""
echo "📦 上传额外文件..."
FILE_INDEX=2
TOTAL_FILES=$((1 + $(echo "${{ env.ADDITIONAL_RELEASE_FILES }}" | wc -w)))
for file in ${{ env.ADDITIONAL_RELEASE_FILES }}; do
if [ -f "$file" ]; then
echo "📤 [$FILE_INDEX/$TOTAL_FILES] 上传 $(basename $file)..."
HTTP_CODE=$(curl -s -o /tmp/upload_response_${FILE_INDEX}.json -w "%{http_code}" -X POST \
-H "Authorization: token ${{ env.ACCESS_TOKEN }}" \
-F "attachment=@${file}" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=$(basename $file)")
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "200" ]; then
echo "✅ $(basename $file) 上传成功"
else
echo "⚠️ 警告: $(basename $file) 上传失败 (HTTP $HTTP_CODE)"
fi
FILE_INDEX=$((FILE_INDEX + 1))
else
echo "⚠️ 警告: 文件不存在: $file"
fi
done
fi
echo ""
echo "✅ 所有附件处理完成"
echo "======================================"
echo ""
- name: 📋 生成 Workflow Summary
if: always()
run: |
echo "======================================"
echo "📋 生成 Workflow Summary"
echo "======================================"
cat >> $GITHUB_STEP_SUMMARY << 'EOFSUMMARY'
# 📦 Tag Release 工作流执行报告
## ✅ 执行结果
| 项目 | 结果 |
|------|------|
| 🏷️ Tag 版本 | **`${{ env.VERSION }}`** |
| 📝 CHANGELOG 版本 | **`${{ env.CHANGELOG_VERSION }}`** |
| 🌿 主分支 | **`${{ env.MAIN_BRANCH }}`** |
| 🌐 Gitea 服务器 | **`${{ env.GITEA_SERVER }}`** |
EOFSUMMARY
if [ "${{ steps.check_bot.outputs.is_bot_commit }}" = "true" ]; then
cat >> $GITHUB_STEP_SUMMARY << 'EOFBOT'
| 📋 执行状态 | ⏭️ 已跳过 (Bot 提交) |
---
⏭️ **工作流已跳过**
检测到此提交由 Bot 创建(包含 `[skip ci]` 标记),为防止无限循环,已跳过执行。
EOFBOT
elif [ "${{ steps.changelog.outputs.changelog_updated }}" = "true" ]; then
if [ "${{ steps.changelog.outputs.content_changed }}" = "true" ]; then
cat >> $GITHUB_STEP_SUMMARY << 'EOFSUCCESS'
| 📋 执行状态 | ✅ 执行成功 |
---
### ✅ 完成的操作
- ✅ CHANGELOG.md 已更新
- ✅ 更改已推送到主分支 (`${{ env.MAIN_BRANCH }}`)
- ✅ Release 已创建
EOFSUCCESS
else
cat >> $GITHUB_STEP_SUMMARY << 'EOFREBUILD'
| 📋 执行状态 | ✅ 执行成功 (重建 Tag) |
---
### ✅ 完成的操作
- ️ CHANGELOG.md 无需更新(内容已存在)
- ✅ Release 已创建
💡 **说明**: 检测到这是重建已删除的 tag,CHANGELOG 中已包含所有提交内容,因此直接从现有内容创建 Release。
EOFREBUILD
fi
if [ -n "${{ env.ADDITIONAL_RELEASE_FILES }}" ]; then
echo "- ✅ 额外文件已上传" >> $GITHUB_STEP_SUMMARY
fi
else
cat >> $GITHUB_STEP_SUMMARY << 'EOFNOCOMMIT'
| 📋 执行状态 | ℹ️ 无更新 |
---
ℹ️ **无新内容需要更新**
可能的原因:
- 在标签之间未找到有效的提交记录
- 或者版本 \`${{ env.CHANGELOG_VERSION }}\` 已包含所有相关提交(常见于重新创建已删除的 tag)
💡 如果你删除了 tag 后重新创建,CHANGELOG 中的内容已经存在,无需重复添加。
EOFNOCOMMIT
fi
cat >> $GITHUB_STEP_SUMMARY << 'EOFEND'
---
## 🔗 快速链接
- 📝 [查看 CHANGELOG](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.MAIN_BRANCH }}/CHANGELOG.md)
- 🚀 [查看 Releases](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases)
- 🔧 [查看 Workflow 配置](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.MAIN_BRANCH }}/.gitea/workflows/changelog_and_release.yml)
---
<div align="center">
*📊 由 [GitHub Actions](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions) 自动生成和更新*
EOFEND
echo "*🤖 生成时间: $(date -u '+%Y-%m-%d %H:%M:%S UTC')*" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "</div>" >> $GITHUB_STEP_SUMMARY
echo ""
echo "✅ Workflow summary 生成完成"
echo "======================================"
echo ""
- name: 💡 输出使用说明
if: success()
run: |
echo "======================================"
echo "✅ Tag Release 工作流执行完成!"
echo "======================================"
echo ""
if [ "${{ steps.check_bot.outputs.is_bot_commit }}" = "true" ]; then
echo "⏭️ 已跳过: Bot 提交检测"
echo " 原因: 检测到 [skip ci] 标记,防止无限循环"
elif [ "${{ steps.changelog.outputs.changelog_updated }}" = "true" ]; then
echo "📊 执行结果:"
echo " - Tag: ${{ env.VERSION }}"
echo " - CHANGELOG 版本: ${{ env.CHANGELOG_VERSION }}"
echo " - 主分支: ${{ env.MAIN_BRANCH }}"
echo " - Release 标题: ${{ env.RELEASE_TITLE_PROCESSED }}"
echo " - Pre-release: ${{ env.RELEASE_IS_PRERELEASE }}"
echo ""
if [ "${{ steps.changelog.outputs.content_changed }}" = "true" ]; then
echo "✅ 已完成的操作:"
echo " - CHANGELOG.md 已更新并推送到 ${{ env.MAIN_BRANCH }} 分支"
echo " - Release 已创建"
else
echo "✅ 已完成的操作:"
echo " - Release 已创建(从现有 CHANGELOG 内容)"
echo ""
echo "💡 说明: 检测到这是重建已删除的 tag,CHANGELOG 中已包含"
echo " 所有提交内容,因此直接从现有内容创建 Release。"
fi
else
echo "️ 无新内容需要更新"
echo ""
echo "可能的原因:"
echo " - 在标签之间未找到有效的提交记录"
echo " - 或者版本 ${{ env.CHANGELOG_VERSION }} 已包含所有相关提交"
echo ""
echo "💡 提示: 如果你删除了 tag 后重新创建,CHANGELOG 中的"
echo " 内容已经存在,这是正常现象,无需重复添加。"
fi
echo ""
echo "🔗 快速链接:"
echo " CHANGELOG: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.MAIN_BRANCH }}/CHANGELOG.md"
echo " Releases: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases"
echo ""
echo "======================================"
- name: 🧹 清理工作区
if: always()
run: |
echo "🧹 清理临时文件..."
rm -rf /tmp/commits.txt /tmp/changelog_updated.txt /tmp/content_changed.txt /tmp/release-body.txt /tmp/payload.json /tmp/release_response.json /tmp/upload_response_*.json
echo "✅ 清理完成"