name: 📦 Tag Release Automation on: push: tags: - "[0-9]*" # ======================================== # 📝 全局配置区域 - 在此修改所有配置 # ======================================== env: # ======================================== # 🔐 认证配置 # ======================================== # Gitea/GitHub 访问令牌 # 用途: # 1. Git 操作:克隆仓库、推送提交 # 2. API 调用:创建 Release、上传附件 # 权限要求: # - repo (仓库完整访问权限) # - write:packages (可选,如需上传包) # 配置位置:仓库 Settings > Secrets > Actions # Secret 名称:WORKFLOW (或其他自定义名称) # # 💡 提示:同一个 token 可用于所有操作 # 如需分离权限,可配置两个不同的 token: # - GIT_TOKEN: 用于 git clone/push # - API_TOKEN: 用于 API 调用 ACCESS_TOKEN: ${{ secrets.WORKFLOW }} # ======================================== # 🌐 服务器配置 # ======================================== # Gitea 服务器地址(用于生成头像链接和 API 调用) GITEA_SERVER: "https://git.mytsl.cn" # ======================================== # 📝 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" # 额外要上传到 Release 的文件(空格分隔) # 例如: "README.md LICENSE docs/guide.pdf" ADDITIONAL_RELEASE_FILES: "" # ======================================== # 💬 Git 提交配置 # ======================================== # Git 提交消息模板 # 可用变量: {version} - 将被替换为实际的版本号 # [skip ci] 标记防止触发无限循环 COMMIT_MESSAGE: ":memo: Auto update CHANGELOG for {version} [skip ci]" # ======================================== # 🚫 提交过滤配置 # ======================================== # 需要忽略的提交模式(支持正则表达式) # 这些提交不会被添加到 CHANGELOG 中 # 使用 | 分隔多个模式 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: changelog-and-release: runs-on: ubuntu-22.04 steps: - name: ⚙️ Configuration id: config run: | echo "======================================" echo "⚙️ Loading workflow configuration" echo "======================================" # 从环境变量读取配置 VERSION="${{ github.ref_name }}" # 处理 CHANGELOG 版本号 if [[ "$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" 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="${RELEASE_TITLE//\{version\}/$VERSION}" # 处理提交消息 COMMIT_MSG="${COMMIT_MESSAGE//\{version\}/$VERSION}" # 智能判断 pre-release if [[ "$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)" else RELEASE_IS_PRERELEASE="false" PRERELEASE_DETECTED="no (auto-detected: stable release)" fi else RELEASE_IS_PRERELEASE="$RELEASE_PRERELEASE_MODE" PRERELEASE_DETECTED="$RELEASE_PRERELEASE_MODE (manually configured)" fi # 导出处理后的配置到 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 # 处理忽略模式(转换为数组) IFS='|' read -ra PATTERNS_ARRAY <<< "$IGNORE_PATTERNS" IGNORE_JSON="[" for pattern in "${PATTERNS_ARRAY[@]}"; do # 去除首尾空格 pattern=$(echo "$pattern" | xargs) if [ -n "$pattern" ]; then IGNORE_JSON="${IGNORE_JSON}\"${pattern}\"," fi done IGNORE_JSON="${IGNORE_JSON%,}]" echo "IGNORE_PATTERNS_JSON=$IGNORE_JSON" >> $GITHUB_ENV # 显示配置摘要 echo "" echo "📋 Configuration Summary:" echo " 🏷️ Tag version: $VERSION" echo " 📝 CHANGELOG version: $CHANGELOG_VERSION" echo " 🌐 Gitea Server: $GITEA_SERVER" echo " 📄 CHANGELOG Title: $CHANGELOG_SECTION_TITLE" echo " 🚀 Release Title: $RELEASE_TITLE_PROCESSED" echo " 🏷️ Pre-release: $PRERELEASE_DETECTED" echo " 📄 Draft: $RELEASE_IS_DRAFT" echo " 💬 Commit Message: $COMMIT_MSG" echo " 📎 Additional Files: ${ADDITIONAL_RELEASE_FILES:-none}" echo "" echo "✅ Configuration loaded successfully" echo "" - name: 📂 Checkout repository run: | echo "======================================" echo "🚀 Starting repository checkout" echo "======================================" REPO_NAME="${{ github.event.repository.name }}" REPO_DIR="/home/workspace/$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 /home/workspace 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 "" - name: 🔍 Check if bot commit id: check_bot run: | echo "======================================" echo "🤖 Checking commit author" 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 echo "" - name: 🔍 Check version exists id: check_version if: steps.check_bot.outputs.is_bot_commit != 'true' run: | echo "======================================" echo "🔍 Checking if version exists" echo "======================================" cd ${{ env.REPO_DIR }} if [ ! -f "CHANGELOG.md" ]; then echo "📝 CHANGELOG.md not found - will create new one" echo "version_exists=false" >> $GITHUB_OUTPUT exit 0 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 "" - name: 📝 Generate 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 "======================================" cd ${{ env.REPO_DIR }} # 获取前一个 tag CURRENT_TAG="${{ github.ref_name }}" echo "🏷️ Current tag: $CURRENT_TAG" PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A 1 "^${CURRENT_TAG}$" | tail -n 1) 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" else echo "📌 Previous tag: $PREVIOUS_TAG" RANGE="${PREVIOUS_TAG}..${CURRENT_TAG}" echo " Range: $RANGE" fi # 生成 CHANGELOG python3 << 'PYSCRIPT' import subprocess import re import json import os 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', '') # 读取忽略模式 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") 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 ) commits = [] authors_dict = {} print(f"📝 Processing commits...") for line in result.stdout.strip().split('\n'): if not line: continue parts = line.split('|', 3) if len(parts) != 4: continue commit_hash, author_name, author_email, message = parts # 检查是否应该忽略此提交 should_ignore = False for pattern in ignore_patterns: if re.search(pattern, message): should_ignore = True print(f" ⏭️ Skipping: {message[:60]}... (matched: {pattern})") break if should_ignore: continue # 记录作者 if author_email not in authors_dict: authors_dict[author_email] = author_name 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 not commits: print("⚠️ No valid commits found") print("changelog_updated=false", file=open(os.environ['GITHUB_OUTPUT'], 'a')) exit(0) # 生成 CHANGELOG 内容 print("\n📄 Generating CHANGELOG content...") changelog_lines = [ f"## :bookmark: {changelog_version}", "", f"**:calendar: Release Date:** {datetime.now().strftime('%Y-%m-%d')}", "", section_title, "" ] for commit in commits: changelog_lines.append(f"- {commit['message']} ([`{commit['hash']}`](../../commit/{commit['hash']}))") # 添加贡献者列表 changelog_lines.extend([ "", contributors_title, "" ]) 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.md print("\n📝 Updating CHANGELOG.md...") changelog_file = 'CHANGELOG.md' if os.path.exists(changelog_file): with open(changelog_file, 'r', encoding='utf-8') as f: existing_content = f.read() # 在文件开头插入新内容 if existing_content.strip(): new_content = f"{changelog_content}\n\n---\n\n{existing_content}" else: new_content = changelog_content else: print("📝 Creating new CHANGELOG.md") new_content = f"# 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')) PYSCRIPT echo "" echo "✅ CHANGELOG generation completed" echo "" - name: 📤 Commit and push CHANGELOG if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" echo "📤 Committing and pushing 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" # 切换到 main 分支 echo "🔄 Switching to main branch..." git fetch origin main git checkout main git pull origin main # 提交更改 echo "📝 Committing changes..." 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 echo "✅ CHANGELOG pushed successfully" echo "" - name: 📋 Prepare release notes if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" echo "📋 Preparing release notes" echo "======================================" cd ${{ env.REPO_DIR }} echo "📖 Extracting version content from CHANGELOG..." python3 << 'PYSCRIPT' import re import os changelog_version = os.environ.get('CHANGELOG_VERSION', '') print(f"🔍 Looking for version: {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 "" - name: 🚀 Create release if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" echo "🚀 Creating GitHub/Gitea release" echo "======================================" cd ${{ env.REPO_DIR }} echo "📦 Preparing release payload..." echo "📋 Release title: ${{ env.RELEASE_TITLE_PROCESSED }}" echo "🏷️ Pre-release: ${{ env.RELEASE_IS_PRERELEASE }}" echo "📄 Draft: ${{ env.RELEASE_IS_DRAFT }}" # 转换 bash 布尔值为 Python 布尔值 if [ "${{ env.RELEASE_IS_DRAFT }}" = "true" ]; then PY_DRAFT="True" else PY_DRAFT="False" fi if [ "${{ env.RELEASE_IS_PRERELEASE }}" = "true" ]; then PY_PRERELEASE="True" else PY_PRERELEASE="False" fi echo "🔄 Converting: draft=${{ env.RELEASE_IS_DRAFT }} → $PY_DRAFT, prerelease=${{ env.RELEASE_IS_PRERELEASE }} → $PY_PRERELEASE" python3 << EOF import json with open('/tmp/release-body.txt', 'r') as f: body = f.read() payload = { "tag_name": "${{ github.ref_name }}", "name": "${{ env.RELEASE_TITLE_PROCESSED }}", "body": body, "draft": $PY_DRAFT, "prerelease": $PY_PRERELEASE } with open('/tmp/payload.json', 'w', encoding='utf-8') as f: json.dump(payload, f, ensure_ascii=False, indent=2) print("✓ Payload prepared") EOF echo "🌐 Sending API request..." 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 "---" cat /tmp/release_response.json | python3 -m json.tool 2>/dev/null || cat /tmp/release_response.json echo "---" if [ "$HTTP_CODE" != "201" ] && [ "$HTTP_CODE" != "200" ]; then echo "❌ ERROR: Failed to create release" exit 1 fi echo "✅ Release created successfully" echo "" - name: 📎 Upload CHANGELOG attachment if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" echo "📎 Uploading release attachments" echo "======================================" cd ${{ env.REPO_DIR }} echo "🔍 Extracting 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("❌ ERROR: No 'id' field in response", file=sys.stderr) sys.exit(1) print(data['id']) except Exception as e: print(f"❌ ERROR: {e}", file=sys.stderr) sys.exit(1) PYSCRIPT ) if [ -z "$RELEASE_ID" ]; then echo "❌ ERROR: Failed to get release ID" exit 1 fi echo "✓ Release ID: $RELEASE_ID" echo "" # 上传额外的文件 if [ -n "${{ env.ADDITIONAL_RELEASE_FILES }}" ]; then echo "" echo "📦 Uploading additional files..." 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)..." 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" else echo "⚠️ WARNING: $(basename $file) upload failed (HTTP $HTTP_CODE)" fi FILE_INDEX=$((FILE_INDEX + 1)) else echo "⚠️ WARNING: File not found: $file" fi done fi echo "" echo "✅ All attachments processed" echo "" - name: 📊 Workflow summary if: always() run: | echo "======================================" echo "📊 Generating 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 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 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 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 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 if [ -n "${{ env.ADDITIONAL_RELEASE_FILES }}" ]; then echo "- ✅ Additional files uploaded" >> $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 fi echo "" echo "✅ Workflow summary generated"