diff --git a/.gitea/workflows/changelog_and_release.yml b/.gitea/workflows/changelog_and_release.yml index bb84b14..bad3226 100644 --- a/.gitea/workflows/changelog_and_release.yml +++ b/.gitea/workflows/changelog_and_release.yml @@ -1,13 +1,13 @@ -name: 📦 Tag Release Automation +name: 📦 自动发布 on: push: tags: - "[0-9]*" -# ======================================== -# 📝 全局配置区域 - 在此修改所有配置 -# ======================================== +# ========================================== +# 🔧 配置区域 - 根据你的项目修改 +# ========================================== env: # ===== Token 配置 ===== # Gitea/GitHub 访问令牌 @@ -28,7 +28,12 @@ env: # ===== 工作区配置 ===== # 完整克隆的工作目录 - 自建的runner可以复用工作区 - WORKSPACE_DIR: '/home/workspace' + WORKSPACE_DIR: "/home/workspace" + + # ===== 分支配置 ===== + # 主分支名称(用于推送 CHANGELOG 更新) + # 如果留空,会自动检测(main 或 master) + MAIN_BRANCH: "" # ===== 服务器配置 ===== # Gitea 服务器地址(用于生成头像链接和 API 调用) @@ -45,7 +50,7 @@ env: # ⚠️ 推荐使用 "strip" 模式以支持多个开发版本叠加到同一CHANGELOG区域 CHANGELOG_VERSION_MODE: "strip" - # ===== RELEASE配置 ===== + # ===== RELEASE 配置 ===== # Release 标题模板 # 可用变量: {version} - 将被替换为实际的版本号 RELEASE_TITLE: "{version}" @@ -63,25 +68,35 @@ env: ADDITIONAL_RELEASE_FILES: "" # ===== Git 提交配置 ===== + # Git 用户配置 + GIT_USER_NAME: "github-actions[bot]" + GIT_USER_EMAIL: "github-actions[bot]@users.noreply.github.com" # 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\]| + \[skip ci\]\s*$| + \[ci skip\]\s*$| ^Bump version| ^Release v| - ^chore: update config| + ^chore: update config$| ^style: | ^chore: format| ^chore: lint| - ^Initial commit + ^Initial commit$ # ======================================== # 📌 版本号规范说明 (SemVer) @@ -108,19 +123,22 @@ env: jobs: changelog-and-release: runs-on: ubuntu-22.04 + permissions: + contents: write + steps: - - name: ⚙️ Configuration + - name: ⚙️ 加载配置 id: config run: | echo "======================================" - echo "⚙️ Loading workflow configuration" + echo "⚙️ 加载工作流配置" echo "======================================" # 从环境变量读取配置 VERSION="${{ github.ref_name }}" # 处理 CHANGELOG 版本号 - if [[ "$CHANGELOG_VERSION_MODE" == "strip" ]]; then + if [[ "${{ env.CHANGELOG_VERSION_MODE }}" == "strip" ]]; then # 去除 pre-release 后缀(-beta, -rc1, -alpha 等) CHANGELOG_VERSION=$(echo "$VERSION" | sed -E 's/-(alpha|beta|rc|pre|preview|dev|test)[0-9]*$//') echo "📝 CHANGELOG version mode: strip" @@ -133,13 +151,15 @@ jobs: fi # 处理 Release 标题 - RELEASE_TITLE_PROCESSED="${RELEASE_TITLE//\{version\}/$VERSION}" + RELEASE_TITLE_PROCESSED="${{ env.RELEASE_TITLE }}" + RELEASE_TITLE_PROCESSED="${RELEASE_TITLE_PROCESSED//\{version\}/$VERSION}" # 处理提交消息 - COMMIT_MSG="${COMMIT_MESSAGE//\{version\}/$VERSION}" + COMMIT_MSG="${{ env.COMMIT_MESSAGE }}" + COMMIT_MSG="${COMMIT_MSG//\{version\}/$VERSION}" # 智能判断 pre-release - if [[ "$RELEASE_PRERELEASE_MODE" == "auto" ]]; then + if [[ "${{ env.RELEASE_PRERELEASE_MODE }}" == "auto" ]]; then if [[ "$VERSION" =~ (alpha|beta|rc|pre|preview|dev|test) ]]; then RELEASE_IS_PRERELEASE="true" PRERELEASE_DETECTED="yes (auto-detected: $VERSION contains pre-release keyword)" @@ -148,8 +168,8 @@ jobs: PRERELEASE_DETECTED="no (auto-detected: stable release)" fi else - RELEASE_IS_PRERELEASE="$RELEASE_PRERELEASE_MODE" - PRERELEASE_DETECTED="$RELEASE_PRERELEASE_MODE (manually configured)" + RELEASE_IS_PRERELEASE="${{ env.RELEASE_PRERELEASE_MODE }}" + PRERELEASE_DETECTED="${{ env.RELEASE_PRERELEASE_MODE }} (manually configured)" fi # 导出处理后的配置到 GitHub 环境变量 @@ -159,7 +179,7 @@ jobs: echo "COMMIT_MSG=$COMMIT_MSG" >> $GITHUB_ENV # 处理忽略模式(转换为数组) - IFS='|' read -ra PATTERNS_ARRAY <<< "$IGNORE_PATTERNS" + IFS='|' read -ra PATTERNS_ARRAY <<< "${{ env.IGNORE_PATTERNS }}" IGNORE_JSON="[" for pattern in "${PATTERNS_ARRAY[@]}"; do # 去除首尾空格 @@ -176,322 +196,439 @@ jobs: echo "📋 Configuration Summary:" echo " 🏷️ Tag version: $VERSION" echo " 📝 CHANGELOG version: $CHANGELOG_VERSION" - echo " 🌐 Gitea Server: $GITEA_SERVER" - echo " 📄 CHANGELOG Title: $CHANGELOG_SECTION_TITLE" + 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: $RELEASE_IS_DRAFT" + 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: 📂 Checkout repository + - name: 📥 克隆仓库 + id: clone run: | echo "======================================" - echo "🚀 Starting repository checkout" + echo "🚀 开始准备仓库" echo "======================================" REPO_NAME="${{ github.event.repository.name }}" REPO_DIR="${{ env.WORKSPACE_DIR }}/$REPO_NAME" - echo "📁 Repository: $REPO_NAME" - echo "📍 Target directory: $REPO_DIR" - - if [ -d "$REPO_DIR/.git" ]; then - echo "✓ Repository exists, updating..." - cd "$REPO_DIR" - git clean -fdx - git reset --hard - git fetch --all --tags --force - echo "✓ Repository updated" - else - echo "📥 Cloning repository..." - mkdir -p "${{ env.WORKSPACE_DIR }}" - git clone https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git "$REPO_DIR" - cd "$REPO_DIR" - echo "✓ Repository cloned" - fi - - echo "🏷️ Checking out tag: ${{ github.ref_name }}" - git checkout -f ${{ github.ref_name }} - echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV - - echo "✅ Checkout completed" + echo "📁 仓库名称: $REPO_NAME" + echo "📍 目标目录: $REPO_DIR" + echo "🌐 服务器: ${GITHUB_SERVER_URL}" echo "" - - name: 🔍 Check if bot commit + # 检查仓库状态 + if [ -d "$REPO_DIR" ]; then + echo "📂 目录已存在,检查 Git 仓库状态..." + + if [ -d "$REPO_DIR/.git" ]; then + # 目录存在且是有效的 Git 仓库 + echo "✓ 发现有效的 Git 仓库,执行增量更新..." + cd "$REPO_DIR" + + # 清理工作区 + git clean -fdx + git reset --hard + + # 获取最新代码 + echo "📥 拉取最新代码和标签..." + git fetch --all --tags --force + + echo "✓ 仓库已更新" + else + # 目录存在但不是 Git 仓库(可能之前运行失败) + echo "⚠️ 目录存在但不是有效的 Git 仓库" + echo "🧹 清理损坏的目录..." + rm -rf "$REPO_DIR" + echo "✓ 已清理" + + # 重新克隆 + echo "📥 克隆仓库..." + mkdir -p "${{ env.WORKSPACE_DIR }}" + + git clone \ + https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \ + "$REPO_DIR" + + if [ $? -ne 0 ]; then + echo "❌ 克隆失败" + exit 1 + fi + + cd "$REPO_DIR" + echo "✓ 仓库已克隆" + fi + else + # 目录不存在,首次克隆 + echo "📥 克隆仓库(首次)..." + mkdir -p "${{ env.WORKSPACE_DIR }}" + + git clone \ + https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \ + "$REPO_DIR" + + if [ $? -eq 0 ]; then + cd "$REPO_DIR" + echo "✓ 仓库已克隆" + else + echo "❌ 克隆失败" + exit 1 + fi + fi + + # 检测主分支名称 + echo "" + echo "🔍 检测主分支名称..." + if [ -n "${{ env.MAIN_BRANCH }}" ]; then + MAIN_BRANCH="${{ env.MAIN_BRANCH }}" + echo "✓ 使用配置的主分支: $MAIN_BRANCH" + else + # 自动检测 main 或 master + if git show-ref --verify --quiet refs/remotes/origin/main; then + MAIN_BRANCH="main" + echo "✓ 自动检测到主分支: main" + elif git show-ref --verify --quiet refs/remotes/origin/master; then + MAIN_BRANCH="master" + echo "✓ 自动检测到主分支: master" + else + echo "❌ 错误: 无法检测主分支(main 或 master)" + exit 1 + fi + fi + + # 切换到主分支 + echo "" + echo "🌿 切换到主分支: $MAIN_BRANCH" + git checkout $MAIN_BRANCH + git pull origin $MAIN_BRANCH + echo "✓ 已切换到主分支并更新到最新" + + # 导出到环境变量供后续步骤使用 + echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV + echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV + echo "MAIN_BRANCH=$MAIN_BRANCH" >> $GITHUB_ENV + + echo "" + echo "✅ 仓库准备完成" + echo "======================================" + echo "" + + - name: 🔍 检查 Bot 提交 id: check_bot run: | echo "======================================" - echo "🤖 Checking commit author" + echo "🤖 检查是否为 Bot 提交" echo "======================================" cd ${{ env.REPO_DIR }} - LAST_AUTHOR=$(git log -1 --pretty=format:'%an') - echo "👤 Last commit author: $LAST_AUTHOR" - - if [[ "$LAST_AUTHOR" == "github-actions[bot]" ]]; then - echo "⚠️ Bot commit detected - skipping to prevent loop" - echo "is_bot_commit=true" >> $GITHUB_OUTPUT - exit 0 - fi - - echo "✅ Not a bot commit" - echo "is_bot_commit=false" >> $GITHUB_OUTPUT + COMMIT_MSG=$(git log -1 --pretty=%B ${{ github.ref_name }}) + echo "📝 Tag commit message:" + echo "$COMMIT_MSG" echo "" - - name: 🔍 Check version exists + # 检查是否包含 [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: 🔍 检查版本是否已存在 id: check_version if: steps.check_bot.outputs.is_bot_commit != 'true' run: | echo "======================================" - echo "🔍 Checking if version exists" + echo "🔍 检查 CHANGELOG 版本" echo "======================================" cd ${{ env.REPO_DIR }} if [ ! -f "CHANGELOG.md" ]; then - echo "📝 CHANGELOG.md not found - will create new one" + echo "📝 CHANGELOG.md 不存在,将创建新文件" echo "version_exists=false" >> $GITHUB_OUTPUT - exit 0 + else + CHANGELOG_VERSION="${{ env.CHANGELOG_VERSION }}" + echo "📝 检查版本: $CHANGELOG_VERSION" + + if grep -q "## :bookmark: $CHANGELOG_VERSION" CHANGELOG.md; then + echo "⚠️ 版本 $CHANGELOG_VERSION 已存在于 CHANGELOG.md" + echo "version_exists=true" >> $GITHUB_OUTPUT + else + echo "✅ 版本 $CHANGELOG_VERSION 不存在,可以继续" + echo "version_exists=false" >> $GITHUB_OUTPUT + fi fi - CHANGELOG_VERSION="${{ env.CHANGELOG_VERSION }}" - echo "🔎 Searching for version: $CHANGELOG_VERSION" - - if grep -q "^## :bookmark: $CHANGELOG_VERSION" CHANGELOG.md; then - echo "⚠️ Version $CHANGELOG_VERSION already exists in CHANGELOG" - echo "version_exists=true" >> $GITHUB_OUTPUT - exit 0 - fi - - echo "✅ Version $CHANGELOG_VERSION not found - will proceed" - echo "version_exists=false" >> $GITHUB_OUTPUT + echo "======================================" echo "" - - name: 📝 Generate CHANGELOG + - name: 📝 生成 CHANGELOG id: changelog if: steps.check_bot.outputs.is_bot_commit != 'true' && steps.check_version.outputs.version_exists != 'true' run: | echo "======================================" - echo "📝 Generating CHANGELOG" + echo "📝 生成 CHANGELOG" echo "======================================" cd ${{ env.REPO_DIR }} - # 获取前一个 tag - CURRENT_TAG="${{ github.ref_name }}" - echo "🏷️ Current tag: $CURRENT_TAG" + VERSION="${{ github.ref_name }}" + CHANGELOG_VERSION="${{ env.CHANGELOG_VERSION }}" - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A 1 "^${CURRENT_TAG}$" | tail -n 1) + echo "🏷️ Tag: $VERSION" + echo "📝 CHANGELOG Version: $CHANGELOG_VERSION" + echo "" - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" = "$CURRENT_TAG" ]; then - echo "📌 No previous tag found, using initial commit" - RANGE=$(git rev-list --max-parents=0 HEAD)..HEAD - echo " Range: (initial)..HEAD" + # 查找前一个 tag + PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -v "^${VERSION}$" | head -n 1) + + if [ -z "$PREVIOUS_TAG" ]; then + echo "ℹ️ No previous tag found, using all commits" + COMMIT_RANGE="" else - echo "📌 Previous tag: $PREVIOUS_TAG" - RANGE="${PREVIOUS_TAG}..${CURRENT_TAG}" - echo " Range: $RANGE" + echo "📍 Previous tag: $PREVIOUS_TAG" + COMMIT_RANGE="${PREVIOUS_TAG}..${VERSION}" fi - # 生成 CHANGELOG + # 获取提交记录 + echo "" + echo "📋 获取提交记录..." + if [ -z "$COMMIT_RANGE" ]; then + git log --pretty=format:'%H|%s' > /tmp/commits.txt + else + git log --pretty=format:'%H|%s' "$COMMIT_RANGE" > /tmp/commits.txt + fi + + TOTAL_COMMITS=$(wc -l < /tmp/commits.txt) + echo "✓ 找到 $TOTAL_COMMITS 个提交" + + # Python 脚本生成 CHANGELOG python3 << 'PYSCRIPT' - import subprocess import re import json - import os + import sys from datetime import datetime - # 读取环境变量配置 - gitea_server = os.environ.get('GITEA_SERVER', '') - section_title = os.environ.get('CHANGELOG_SECTION_TITLE', '### :pencil: What\'s Changed') - contributors_title = os.environ.get('CHANGELOG_CONTRIBUTORS_TITLE', '### :busts_in_silhouette: Contributors') - changelog_version = os.environ.get('CHANGELOG_VERSION', '') + # 读取配置 + changelog_version = "${{ env.CHANGELOG_VERSION }}" + gitea_server = "${{ env.GITEA_SERVER }}" + repo = "${{ github.repository }}" + section_title = "${{ env.CHANGELOG_SECTION_TITLE }}" + contributors_title = "${{ env.CHANGELOG_CONTRIBUTORS_TITLE }}" # 读取忽略模式 - ignore_patterns_json = os.environ.get('IGNORE_PATTERNS_JSON', '[]') - ignore_patterns = json.loads(ignore_patterns_json) - - print(f"📋 Configuration:") - print(f" Gitea Server: {gitea_server}") - print(f" Section Title: {section_title}") - print(f" Contributors Title: {contributors_title}") - print(f" CHANGELOG Version: {changelog_version}") - print(f" Ignore Patterns: {len(ignore_patterns)} patterns loaded") + ignore_patterns = json.loads('${{ env.IGNORE_PATTERNS_JSON }}') + print(f"🔍 Loaded {len(ignore_patterns)} ignore patterns:") + for pattern in ignore_patterns: + print(f" - {pattern}") print() - # 获取 commit 范围 - git_range = os.environ.get('RANGE', 'HEAD') - print(f"📊 Git range: {git_range}") - - # 获取 commits - result = subprocess.run( - ['git', 'log', git_range, '--pretty=format:%H|%an|%ae|%s'], - capture_output=True, - text=True - ) + # 读取提交 + with open('/tmp/commits.txt', 'r', encoding='utf-8') as f: + lines = f.readlines() commits = [] - authors_dict = {} - - print(f"📝 Processing commits...") - for line in result.stdout.strip().split('\n'): - if not line: + skipped_commits = [] + for line in lines: + line = line.strip() + if not line or '|' not in line: continue - - parts = line.split('|', 3) - if len(parts) != 4: - continue - - commit_hash, author_name, author_email, message = parts - - # 检查是否应该忽略此提交 + + commit_hash, message = line.split('|', 1) + + # 检查是否应该忽略 should_ignore = False + matched_pattern = None for pattern in ignore_patterns: if re.search(pattern, message): should_ignore = True - print(f" ⏭️ Skipping: {message[:60]}... (matched: {pattern})") + matched_pattern = pattern break - + if should_ignore: - continue + skipped_commits.append((message, matched_pattern)) + else: + commits.append({'hash': commit_hash, 'message': message}) - # 记录作者 - if author_email not in authors_dict: - authors_dict[author_email] = author_name + # 输出统计 + print(f"📊 Commit Statistics:") + print(f" Total: {len(lines)}") + print(f" Valid: {len(commits)}") + print(f" Skipped: {len(skipped_commits)}") + print() - commits.append({ - 'hash': commit_hash[:7], - 'author': author_name, - 'email': author_email, - 'message': message - }) - print(f" ✓ Added: {message[:60]}...") - - print(f"\n✅ Total valid commits: {len(commits)}") - print(f"✅ Total contributors: {len(authors_dict)}") + if skipped_commits: + print("⏭️ Skipped Commits:") + for msg, pattern in skipped_commits: + print(f" ⏭️ Skipping: {msg[:80]}... (matched: {pattern})") + print() if not commits: print("⚠️ No valid commits found") - print("changelog_updated=false", file=open(os.environ['GITHUB_OUTPUT'], 'a')) - exit(0) + with open('/tmp/changelog_updated.txt', 'w') as f: + f.write('false') + sys.exit(0) + + # 收集贡献者 + contributors = set() + for commit in commits: + # 从提交消息中提取可能的用户名(如果使用了 @username 格式) + # 这里简化处理,实际可以通过 git log --format='%an|%ae' 获取 + pass # 生成 CHANGELOG 内容 - print("\n📄 Generating CHANGELOG content...") - - changelog_lines = [ - f"## :bookmark: {changelog_version}", - "", - f"**:calendar: Release Date:** {datetime.now().strftime('%Y-%m-%d')}", - "", - section_title, - "" - ] + changelog_content = f"## :bookmark: {changelog_version}\n\n" + changelog_content += f"*Released on {datetime.now().strftime('%Y-%m-%d')}*\n\n" + changelog_content += f"{section_title}\n\n" for commit in commits: - changelog_lines.append(f"- {commit['message']} ([`{commit['hash']}`](../../commit/{commit['hash']}))") + short_hash = commit['hash'][:7] + message = commit['message'] + changelog_content += f"- {message} ([`{short_hash}`](https://github.com/{repo}/commit/{commit['hash']}))\n" - # 添加贡献者列表 - changelog_lines.extend([ - "", - contributors_title, - "" - ]) + # 如果有贡献者信息,可以在这里添加 + # changelog_content += f"\n{contributors_title}\n\n" - for email, name in sorted(authors_dict.items(), key=lambda x: x[1].lower()): - if gitea_server: - avatar_url = f"{gitea_server}/avatars/{email.replace('@', '%40')}" - changelog_lines.append(f"- [{name}]({avatar_url})") - else: - changelog_lines.append(f"- {name}") - - changelog_content = '\n'.join(changelog_lines) + changelog_content += "\n---\n\n" # 更新或创建 CHANGELOG.md - print("\n📝 Updating CHANGELOG.md...") changelog_file = 'CHANGELOG.md' - - if os.path.exists(changelog_file): + try: with open(changelog_file, 'r', encoding='utf-8') as f: existing_content = f.read() + except FileNotFoundError: + existing_content = "# :memo: CHANGELOG\n\n" - # 在文件开头插入新内容 - if existing_content.strip(): - new_content = f"{changelog_content}\n\n---\n\n{existing_content}" + # 检查版本是否已存在 + if f"## :bookmark: {changelog_version}" in existing_content: + print(f"ℹ️ Version {changelog_version} already exists, appending commits") + # 找到版本区块,在其中追加新的提交 + pattern = rf'(## :bookmark: {re.escape(changelog_version)}.*?\n{re.escape(section_title)}\n\n)(.*?)(\n---|\n## :bookmark: |\Z)' + match = re.search(pattern, existing_content, re.DOTALL) + + if match: + existing_commits = match.group(2).strip() + new_commits = changelog_content.split(f"{section_title}\n\n")[1].split("\n---")[0].strip() + + # 合并提交,去重 + all_commits_text = existing_commits + "\n" + new_commits + # 简单去重(基于完整行) + unique_lines = [] + seen = set() + for line in all_commits_text.split('\n'): + if line.strip() and line not in seen: + unique_lines.append(line) + seen.add(line) + + merged_commits = '\n'.join(unique_lines) + + # 重新组装版本区块 + updated_section = match.group(1) + merged_commits + "\n\n---\n" + new_content = existing_content[:match.start()] + updated_section + existing_content[match.end():] else: - new_content = changelog_content + print("⚠️ Could not find version section, prepending new content") + new_content = existing_content.replace( + "# :memo: CHANGELOG\n\n", + f"# :memo: CHANGELOG\n\n{changelog_content}" + ) else: - print("📝 Creating new CHANGELOG.md") - new_content = f"# Changelog\n\n{changelog_content}" + # 插入新版本 + new_content = existing_content.replace( + "# :memo: CHANGELOG\n\n", + f"# :memo: CHANGELOG\n\n{changelog_content}" + ) with open(changelog_file, 'w', encoding='utf-8') as f: f.write(new_content) - print("✅ CHANGELOG.md updated successfully") - print(f" Lines added: {len(changelog_lines)}") - print(f" Commits: {len(commits)}") - print(f" Contributors: {len(authors_dict)}") - - # 设置输出 - print("changelog_updated=true", file=open(os.environ['GITHUB_OUTPUT'], 'a')) + print(f"✅ CHANGELOG updated successfully") + print(f" Added {len(commits)} commits to version {changelog_version}") + + with open('/tmp/changelog_updated.txt', 'w') as f: + f.write('true') PYSCRIPT - echo "" - echo "✅ CHANGELOG generation completed" + UPDATED=$(cat /tmp/changelog_updated.txt) + echo "changelog_updated=$UPDATED" >> $GITHUB_OUTPUT + + if [ "$UPDATED" = "true" ]; then + echo "" + echo "✅ CHANGELOG 生成完成" + else + echo "" + echo "ℹ️ 没有有效的提交,跳过 CHANGELOG 更新" + fi + + echo "======================================" echo "" - - name: 📤 Commit and push CHANGELOG + - name: 📤 提交 CHANGELOG 到主分支 + id: commit if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" - echo "📤 Committing and pushing CHANGELOG" + echo "📤 提交 CHANGELOG 更新" echo "======================================" cd ${{ env.REPO_DIR }} - # 配置 Git - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" + # 配置 Git 用户信息 + git config user.name "${{ env.GIT_USER_NAME }}" + git config user.email "${{ env.GIT_USER_EMAIL }}" - # 切换到 main 分支 - echo "🔄 Switching to main branch..." - git fetch origin main - git checkout main - git pull origin main - - # 提交更改 - echo "📝 Committing changes..." + # 添加 CHANGELOG.md git add CHANGELOG.md - git commit -m "${{ env.COMMIT_MSG }}" - # 推送更改 - echo "📤 Pushing to remote..." - git push https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git main + # 检查是否有变更 + if git diff --staged --quiet; then + echo "📌 没有变更,跳过提交" + else + echo "📝 提交 CHANGELOG 更新..." + git commit -m "${{ env.COMMIT_MSG }}" + + echo "📤 推送到远程 ${{ env.MAIN_BRANCH }} 分支..." + git push https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git ${{ env.MAIN_BRANCH }} + + if [ $? -eq 0 ]; then + echo "✅ 推送成功" + else + echo "❌ 推送失败" + exit 1 + fi + fi - echo "✅ CHANGELOG pushed successfully" + echo "======================================" echo "" - - name: 📋 Prepare release notes + - name: 📄 提取 Release Notes + id: extract_notes if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" - echo "📋 Preparing release notes" + echo "📄 提取 Release Notes" echo "======================================" cd ${{ env.REPO_DIR }} - echo "📖 Extracting version content from CHANGELOG..." + # 确保使用最新的 CHANGELOG + git pull origin ${{ env.MAIN_BRANCH }} python3 << 'PYSCRIPT' import re - import os - changelog_version = os.environ.get('CHANGELOG_VERSION', '') - print(f"🔍 Looking for version: {changelog_version}") + changelog_version = "${{ env.CHANGELOG_VERSION }}" with open('CHANGELOG.md', 'r', encoding='utf-8') as f: content = f.read() @@ -517,21 +654,25 @@ jobs: print("✅ Release notes generated successfully") PYSCRIPT + + echo "======================================" echo "" - - name: 🚀 Create release + - name: 🚀 创建 Release + id: create_release if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" - echo "🚀 Creating GitHub/Gitea release" + echo "🚀 创建 GitHub/Gitea Release" echo "======================================" cd ${{ env.REPO_DIR }} - echo "📦 Preparing release payload..." - echo "📋 Release title: ${{ env.RELEASE_TITLE_PROCESSED }}" + echo "📦 准备 Release 数据..." + echo "📋 Release 标题: ${{ env.RELEASE_TITLE_PROCESSED }}" echo "🏷️ Pre-release: ${{ env.RELEASE_IS_PRERELEASE }}" echo "📄 Draft: ${{ env.RELEASE_IS_DRAFT }}" + echo "" # 转换 bash 布尔值为 Python 布尔值 if [ "${{ env.RELEASE_IS_DRAFT }}" = "true" ]; then @@ -546,7 +687,8 @@ jobs: PY_PRERELEASE="False" fi - echo "🔄 Converting: draft=${{ env.RELEASE_IS_DRAFT }} → $PY_DRAFT, prerelease=${{ env.RELEASE_IS_PRERELEASE }} → $PY_PRERELEASE" + echo "🔄 转换布尔值: draft=${{ env.RELEASE_IS_DRAFT }} → $PY_DRAFT, prerelease=${{ env.RELEASE_IS_PRERELEASE }} → $PY_PRERELEASE" + echo "" python3 << EOF import json @@ -568,36 +710,40 @@ jobs: print("✓ Payload prepared") EOF - echo "🌐 Sending API request..." + 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 "📡 API Response (HTTP $HTTP_CODE):" + 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 "❌ ERROR: Failed to create release" + echo "❌ 错误: 创建 Release 失败" exit 1 fi - echo "✅ Release created successfully" + echo "✅ Release 创建成功" + echo "======================================" echo "" - - name: 📎 Upload CHANGELOG attachment + - name: 📎 上传附件 + id: upload_assets if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" - echo "📎 Uploading release attachments" + echo "📎 上传 Release 附件" echo "======================================" cd ${{ env.REPO_DIR }} - echo "🔍 Extracting release ID..." + echo "🔍 提取 Release ID..." RELEASE_ID=$(python3 << 'PYSCRIPT' import json import sys @@ -607,108 +753,205 @@ jobs: data = json.load(f) if 'id' not in data: - print("❌ ERROR: No 'id' field in response", file=sys.stderr) + print("❌ 错误: 响应中没有 'id' 字段", file=sys.stderr) sys.exit(1) print(data['id']) except Exception as e: - print(f"❌ ERROR: {e}", file=sys.stderr) + print(f"❌ 错误: {e}", file=sys.stderr) sys.exit(1) PYSCRIPT ) if [ -z "$RELEASE_ID" ]; then - echo "❌ ERROR: Failed to get release ID" + echo "❌ 错误: 无法获取 Release ID" exit 1 fi echo "✓ Release ID: $RELEASE_ID" echo "" + # 上传 CHANGELOG.md + echo "📤 上传 CHANGELOG.md..." + HTTP_CODE=$(curl -s -o /tmp/upload_response_changelog.json -w "%{http_code}" -X POST \ + -H "Authorization: token ${{ env.ACCESS_TOKEN }}" \ + -F "attachment=@CHANGELOG.md" \ + "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=CHANGELOG.md") + + if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "200" ]; then + echo "✅ CHANGELOG.md 上传成功" + else + echo "⚠️ 警告: CHANGELOG.md 上传失败 (HTTP $HTTP_CODE)" + fi + # 上传额外的文件 if [ -n "${{ env.ADDITIONAL_RELEASE_FILES }}" ]; then echo "" - echo "📦 Uploading additional files..." + 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] Uploading $(basename $file)..." + 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) uploaded successfully" + echo "✅ $(basename $file) 上传成功" else - echo "⚠️ WARNING: $(basename $file) upload failed (HTTP $HTTP_CODE)" + echo "⚠️ 警告: $(basename $file) 上传失败 (HTTP $HTTP_CODE)" fi FILE_INDEX=$((FILE_INDEX + 1)) else - echo "⚠️ WARNING: File not found: $file" + echo "⚠️ 警告: 文件不存在: $file" fi done fi echo "" - echo "✅ All attachments processed" + echo "✅ 所有附件处理完成" + echo "======================================" echo "" - - name: 📊 Workflow summary + - name: 📊 生成 Workflow Summary if: always() run: | echo "======================================" - echo "📊 Generating workflow summary" + echo "📊 生成 Workflow Summary" echo "======================================" - echo "## 📦 Workflow Execution Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**🏷️ Tag:** \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY - echo "**📂 Repository:** \`${{ github.repository }}\`" >> $GITHUB_STEP_SUMMARY - echo "**🕒 Time:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### ⚙️ Configuration" >> $GITHUB_STEP_SUMMARY - echo "- **Gitea Server:** \`${{ env.GITEA_SERVER }}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Release Title:** \`${{ env.RELEASE_TITLE_PROCESSED }}\`" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOFSUMMARY' + # 📦 Tag Release 工作流执行报告 - if [ "${{ env.RELEASE_IS_PRERELEASE }}" = "true" ]; then - echo "- **Release Type:** 🏷️ Pre-release" >> $GITHUB_STEP_SUMMARY - else - echo "- **Release Type:** ✅ Latest (Stable)" >> $GITHUB_STEP_SUMMARY - fi + ## ✅ 执行结果 - if [ "${{ env.RELEASE_IS_DRAFT }}" = "true" ]; then - echo "- **Draft:** 📄 Yes" >> $GITHUB_STEP_SUMMARY - fi - - echo "" >> $GITHUB_STEP_SUMMARY + | 项目 | 结果 | + |------|------| + | 🏷️ Tag 版本 | **`${{ github.ref_name }}`** | + | 📝 CHANGELOG 版本 | **`${{ env.CHANGELOG_VERSION }}`** | + | 🌿 主分支 | **`${{ env.MAIN_BRANCH }}`** | + | 🌐 Gitea 服务器 | **`${{ env.GITEA_SERVER }}`** | + EOFSUMMARY if [ "${{ steps.check_bot.outputs.is_bot_commit }}" = "true" ]; then - echo "⏭️ **Status:** Skipped (bot commit detected)" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "The workflow was skipped to prevent infinite loops." >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOFBOT' + | 📋 执行状态 | ⏭️ 已跳过 (Bot 提交) | + + --- + + ⏭️ **工作流已跳过** + + 检测到此提交由 Bot 创建(包含 `[skip ci]` 标记),为防止无限循环,已跳过执行。 + EOFBOT elif [ "${{ steps.check_version.outputs.version_exists }}" = "true" ]; then - echo "⏭️ **Status:** Skipped (version already exists)" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Version \`${{ github.ref_name }}\` already exists in CHANGELOG.md" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOFEXIST' + | 📋 执行状态 | ⏭️ 已跳过 (版本已存在) | + + --- + + ⏭️ **工作流已跳过** + + 版本 `${{ env.CHANGELOG_VERSION }}` 已存在于 CHANGELOG.md 中。 + EOFEXIST elif [ "${{ steps.changelog.outputs.changelog_updated }}" = "true" ]; then - echo "✅ **Status:** Success" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Completed Actions" >> $GITHUB_STEP_SUMMARY - echo "- ✅ CHANGELOG.md updated" >> $GITHUB_STEP_SUMMARY - echo "- ✅ Changes pushed to main branch" >> $GITHUB_STEP_SUMMARY - echo "- ✅ Release created" >> $GITHUB_STEP_SUMMARY - echo "- ✅ CHANGELOG.md attached to release" >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOFSUCCESS' + | 📋 执行状态 | ✅ 执行成功 | + + --- + + ### ✅ 完成的操作 + + - ✅ CHANGELOG.md 已更新 + - ✅ 更改已推送到主分支 (`${{ env.MAIN_BRANCH }}`) + - ✅ Release 已创建 + - ✅ CHANGELOG.md 已作为附件上传 + EOFSUCCESS + if [ -n "${{ env.ADDITIONAL_RELEASE_FILES }}" ]; then - echo "- ✅ Additional files uploaded" >> $GITHUB_STEP_SUMMARY + echo "- ✅ 额外文件已上传" >> $GITHUB_STEP_SUMMARY fi else - echo "ℹ️ **Status:** No updates" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "No valid commits found between tags." >> $GITHUB_STEP_SUMMARY + cat >> $GITHUB_STEP_SUMMARY << 'EOFNOCOMMIT' + | 📋 执行状态 | ℹ️ 无更新 | + + --- + + ℹ️ **无有效提交** + + 在标签之间未找到有效的提交记录。 + 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}/blob/${{ env.MAIN_BRANCH }}/.github/workflows/changelog_and_release.yml) + + --- + +