name: 📦 Tag Release Automation on: push: tags: - "[0-9]*" jobs: changelog-and-release: runs-on: ubuntu-22.04 steps: - name: ⚙️ Configuration id: config run: | echo "======================================" echo "⚙️ Loading workflow configuration" echo "======================================" # ======================================== # 📝 自定义配置区域 - 在此修改所有配置 # ======================================== # 📌 语义版本号规范 (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内容是叠加而非覆盖 # Gitea 服务器地址(用于生成头像链接) GITEA_SERVER="https://git.mytsl.cn" # CHANGELOG 配置 CHANGELOG_SECTION_TITLE="### :pencil: What's Changed" 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_TITLE="{version}" # 可用变量: {version} # Pre-release 配置 # - "false": 正式版本(会被标记为 latest) # - "true": 预发布版本(会被标记为 pre-release) # - "auto": 自动判断(根据 tag 名称中是否包含 alpha/beta/rc) RELEASE_PRERELEASE_MODE="auto" # Draft 配置(是否创建为草稿) RELEASE_IS_DRAFT="false" # true 或 false # 需要忽略的提交模式(支持正则表达式) 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" ) # Git 提交消息格式 COMMIT_MESSAGE=":memo: Auto update CHANGELOG for {version} [skip ci]" # 额外要上传到 Release 的文件(空格分隔) ADDITIONAL_RELEASE_FILES="" # 例如: "README.md LICENSE docs/guide.pdf" # ======================================== # 以下为自动处理,无需修改 # ======================================== 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="${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 # 导出配置到环境变量 echo "GITEA_SERVER=$GITEA_SERVER" >> $GITHUB_ENV echo "CHANGELOG_SECTION_TITLE=$CHANGELOG_SECTION_TITLE" >> $GITHUB_ENV echo "CHANGELOG_CONTRIBUTORS_TITLE=$CHANGELOG_CONTRIBUTORS_TITLE" >> $GITHUB_ENV echo "CHANGELOG_VERSION=$CHANGELOG_VERSION" >> $GITHUB_ENV echo "RELEASE_TITLE=$RELEASE_TITLE" >> $GITHUB_ENV echo "RELEASE_IS_PRERELEASE=$RELEASE_IS_PRERELEASE" >> $GITHUB_ENV echo "RELEASE_IS_DRAFT=$RELEASE_IS_DRAFT" >> $GITHUB_ENV echo "COMMIT_MSG=$COMMIT_MSG" >> $GITHUB_ENV echo "ADDITIONAL_RELEASE_FILES=$ADDITIONAL_RELEASE_FILES" >> $GITHUB_ENV # 导出忽略模式(转换为 JSON 数组格式便于后续处理) IGNORE_JSON="[" for pattern in "${IGNORE_PATTERNS[@]}"; do IGNORE_JSON="${IGNORE_JSON}\"${pattern}\"," 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" 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:${{ secrets.WORKFLOW }}@${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 else echo "✓ Non-bot commit - continuing workflow" echo "is_bot_commit=false" >> $GITHUB_OUTPUT fi echo "" - name: 📋 Check if version exists in CHANGELOG if: steps.check_bot.outputs.is_bot_commit == 'false' id: check_version run: | echo "======================================" echo "🔎 Checking CHANGELOG for existing version" echo "======================================" cd ${{ env.REPO_DIR }} CHANGELOG_VERSION="${{ env.CHANGELOG_VERSION }}" echo "🏷️ CHANGELOG version to check: $CHANGELOG_VERSION" git checkout main || git checkout master git pull origin main || git pull origin master || true if [ -f "CHANGELOG.md" ]; then echo "✓ CHANGELOG.md found" if grep -q "## :bookmark: ${CHANGELOG_VERSION}" CHANGELOG.md; then echo "ℹ️ Version ${CHANGELOG_VERSION} already exists in CHANGELOG" echo "📝 Will append new commits to existing entry (content accumulation mode)" echo "version_exists=true" >> $GITHUB_OUTPUT else echo "✓ Version ${CHANGELOG_VERSION} not found - will generate new entry" echo "version_exists=false" >> $GITHUB_OUTPUT fi else echo "ℹ️ CHANGELOG.md does not exist - will create new file" echo "version_exists=false" >> $GITHUB_OUTPUT fi git checkout ${{ github.ref_name }} echo "" - name: 📝 Generate CHANGELOG if: steps.check_bot.outputs.is_bot_commit == 'false' id: changelog run: | echo "======================================" echo "📝 Generating CHANGELOG" echo "======================================" cd ${{ env.REPO_DIR }} VERSION="${{ github.ref_name }}" CHANGELOG_VERSION="${{ env.CHANGELOG_VERSION }}" VERSION_EXISTS="${{ steps.check_version.outputs.version_exists }}" PREVIOUS_TAG=$(git tag --sort=-creatordate | sed -n '2p') echo "🏷️ Tag version: $VERSION" echo "📝 CHANGELOG version: $CHANGELOG_VERSION" echo "🏷️ Previous version: ${PREVIOUS_TAG:-none (first release)}" if [ "$VERSION_EXISTS" = "true" ]; then echo "ℹ️ Updating existing CHANGELOG entry" else echo "ℹ️ Creating new CHANGELOG entry" fi # 从环境变量加载忽略模式 IGNORE_PATTERNS=( "^Merge " "^:memo: Auto update CHANGELOG" "\[skip ci\]" "\[ci skip\]" "^Bump version" "^Release v" "^chore: update config" "^style: " "^chore: format" "^chore: lint" ) echo "🔍 Scanning commits..." TEMP_COMMITS=$(mktemp) if [ -z "$PREVIOUS_TAG" ]; then COMMIT_RANGE="HEAD" else COMMIT_RANGE="${PREVIOUS_TAG}..${VERSION}" fi 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 COMMIT_INDEX=0 CURRENT_HASH="" CURRENT_AUTHOR="" CURRENT_MESSAGE="" CURRENT_BODY="" IN_BODY=false while IFS= read -r line; do if [[ "$line" == "COMMIT_START" ]]; then if [ -n "$CURRENT_HASH" ]; then SHOULD_SKIP=false for pattern in "${IGNORE_PATTERNS[@]}"; do if echo "$CURRENT_MESSAGE" | grep -qiE "$pattern"; then SHOULD_SKIP=true 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" if [ -n "$CURRENT_HASH" ]; then SHOULD_SKIP=false for pattern in "${IGNORE_PATTERNS[@]}"; do if echo "$CURRENT_MESSAGE" | grep -qiE "$pattern"; then SHOULD_SKIP=true 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" if [ $COMMIT_INDEX -eq 0 ]; then echo "⚠️ No valid commits found to add" echo "changelog_updated=false" >> $GITHUB_OUTPUT exit 0 fi echo "✓ Found ${COMMIT_INDEX} valid commits" echo "" echo "📄 Building CHANGELOG entry..." # 使用配置的标题 NEW_ENTRY="## :bookmark: ${CHANGELOG_VERSION}\n\n" NEW_ENTRY="${NEW_ENTRY}${CHANGELOG_SECTION_TITLE}\n\n" REPO_URL="${{ github.server_url }}/${{ github.repository }}" for ((i=0; i<$COMMIT_INDEX; i++)); do hash="${COMMITS_HASH[$i]}" author="${COMMITS_AUTHOR[$i]}" message="${COMMITS_MESSAGE[$i]}" body="${COMMITS_BODY[$i]}" echo " [$((i+1))/$COMMIT_INDEX] ${hash:0:7}: ${message:0:60}..." COMMIT_LINK="([${hash}](${REPO_URL}/commit/${hash}))" if [ -n "$body" ]; then FULL_MESSAGE="${message}\n${body}" LINE_COUNT=$(echo -e "$FULL_MESSAGE" | sed '/^$/d' | wc -l) CURRENT_LINE=1 FORMATTED_MESSAGE="" while IFS= read -r line; do [ -z "$line" ] && continue if [ $CURRENT_LINE -eq $LINE_COUNT ]; then FORMATTED_MESSAGE="${FORMATTED_MESSAGE}${line} ${COMMIT_LINK}" if [ -n "$author" ]; then FORMATTED_MESSAGE="${FORMATTED_MESSAGE} by @${author}" fi else FORMATTED_MESSAGE="${FORMATTED_MESSAGE}${line} \n" fi CURRENT_LINE=$((CURRENT_LINE + 1)) done <<< "$(echo -e "$FULL_MESSAGE" | sed '/^$/d')" NEW_ENTRY="${NEW_ENTRY}- ${FORMATTED_MESSAGE}\n" else if [ -n "$author" ]; then NEW_ENTRY="${NEW_ENTRY}- ${message} ${COMMIT_LINK} by @${author}\n" else NEW_ENTRY="${NEW_ENTRY}- ${message} ${COMMIT_LINK}\n" fi fi done echo "" echo "👥 Collecting contributors..." if [ -z "$PREVIOUS_TAG" ]; then CONTRIBUTORS=$(git log --pretty=format:"%an" --no-merges | grep -v "github-actions\[bot\]" | grep -v "dependabot\[bot\]" | grep -v "renovate\[bot\]" | sort -u) else CONTRIBUTORS=$(git log ${PREVIOUS_TAG}..${VERSION} --pretty=format:"%an" --no-merges | grep -v "github-actions\[bot\]" | grep -v "dependabot\[bot\]" | grep -v "renovate\[bot\]" | sort -u) fi if [ -n "$CONTRIBUTORS" ]; then CONTRIBUTOR_COUNT=$(echo "$CONTRIBUTORS" | wc -l) echo "✓ Found ${CONTRIBUTOR_COUNT} contributors" # 使用配置的标题和服务器地址 NEW_ENTRY="${NEW_ENTRY}\n${CHANGELOG_CONTRIBUTORS_TITLE}\n\n" while IFS= read -r name; do NEW_ENTRY="${NEW_ENTRY}\n" NEW_ENTRY="${NEW_ENTRY} \"${name}\"\n" NEW_ENTRY="${NEW_ENTRY}\n" done <<< "$CONTRIBUTORS" fi NEW_ENTRY="${NEW_ENTRY}\n---\n" echo "" echo "💾 Writing to CHANGELOG.md..." git checkout main || git checkout master if [ -f CHANGELOG.md ]; then if [ "$VERSION_EXISTS" = "true" ]; then # 版本已存在,叠加新的条目(不是替换) echo "🔄 Appending new commits to existing entry for $CHANGELOG_VERSION" # 提取现有版本区域的内容 TEMP_OLD_CONTENT=$(mktemp) TEMP_NEW_FILE=$(mktemp) # 提取旧版本区域的所有内容 IN_VERSION=false while IFS= read -r line; do if [[ "$line" =~ ^##[[:space:]]:bookmark:[[:space:]]${CHANGELOG_VERSION}$ ]]; then IN_VERSION=true elif [[ "$line" =~ ^##[[:space:]]:bookmark: ]] && [ "$IN_VERSION" = true ]; then IN_VERSION=false fi if [ "$IN_VERSION" = true ]; then echo "$line" >> "$TEMP_OLD_CONTENT" fi done < CHANGELOG.md # 提取旧的提交记录(在 What's Changed 和 Contributors 之间的内容) OLD_COMMITS="" if [ -f "$TEMP_OLD_CONTENT" ]; then OLD_COMMITS=$(awk ' /^### :pencil: What'"'"'s Changed/,/^### :busts_in_silhouette: Contributors/ { if ($0 !~ /^### :pencil: What/ && $0 !~ /^### :busts_in_silhouette:/) { if ($0 ~ /^-/) print $0 } } ' "$TEMP_OLD_CONTENT") fi # 提取旧的贡献者(在 Contributors 部分的内容) OLD_CONTRIBUTORS="" if [ -f "$TEMP_OLD_CONTENT" ]; then OLD_CONTRIBUTORS=$(awk ' /^### :busts_in_silhouette: Contributors/,/^---/ { if ($0 ~ //) print $0 } ') if [ -n "$NEW_CONTRIBUTORS" ]; then while IFS= read -r line; do if [[ "$line" =~ href=\"[^\"]+/([^\"]+)\" ]]; then username="${BASH_REMATCH[1]}" if [ -z "${SEEN_CONTRIBUTORS[$username]}" ]; then SEEN_CONTRIBUTORS[$username]=1 MERGED_ENTRY="${MERGED_ENTRY}${line}\n" read -r img_line MERGED_ENTRY="${MERGED_ENTRY}${img_line}\n" read -r close_line MERGED_ENTRY="${MERGED_ENTRY}${close_line}\n" fi fi done <<< "$NEW_CONTRIBUTORS" fi MERGED_ENTRY="${MERGED_ENTRY}\n---\n" # 替换CHANGELOG中的对应版本区域 IN_VERSION=false while IFS= read -r line; do if [[ "$line" =~ ^##[[:space:]]:bookmark:[[:space:]]${CHANGELOG_VERSION}$ ]]; then echo -e "${MERGED_ENTRY}" >> "$TEMP_NEW_FILE" IN_VERSION=true elif [[ "$line" =~ ^##[[:space:]]:bookmark: ]] && [ "$IN_VERSION" = true ]; then IN_VERSION=false echo "$line" >> "$TEMP_NEW_FILE" elif [ "$IN_VERSION" = false ]; then echo "$line" >> "$TEMP_NEW_FILE" fi done < CHANGELOG.md mv "$TEMP_NEW_FILE" CHANGELOG.md rm -f "$TEMP_OLD_CONTENT" echo "✓ Appended new commits to existing CHANGELOG entry" else # 版本不存在,添加新条目 echo "➕ Adding new entry for $CHANGELOG_VERSION" TEMP_FILE=$(mktemp) head -n 1 CHANGELOG.md > "$TEMP_FILE" echo "" >> "$TEMP_FILE" echo -e "${NEW_ENTRY}" >> "$TEMP_FILE" tail -n +3 CHANGELOG.md >> "$TEMP_FILE" mv "$TEMP_FILE" CHANGELOG.md echo "✓ Added new CHANGELOG entry" fi else echo "# CHANGELOG" > CHANGELOG.md echo "" >> CHANGELOG.md echo -e "${NEW_ENTRY}" >> CHANGELOG.md echo "✓ Created new CHANGELOG.md" fi echo "changelog_updated=true" >> $GITHUB_OUTPUT 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 config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" git add CHANGELOG.md if git diff --staged --quiet; then echo "ℹ️ No changes to commit" exit 0 fi echo "📝 Creating commit..." echo "💬 Commit message: $COMMIT_MSG" git commit -m "$COMMIT_MSG" echo "⬆️ Pushing to remote..." git push origin main echo "✅ CHANGELOG pushed to main branch" echo "" - name: 🗑️ Delete old release if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" echo "🔍 Checking for existing release" echo "======================================" OLD_ID=$(curl -s -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \ "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/tags/${{ github.ref_name }}" \ | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('id', ''))" 2>/dev/null || echo "") if [ -n "$OLD_ID" ]; then echo "🗑️ Found existing release (ID: $OLD_ID)" echo "🔄 Deleting old release..." curl -s -X DELETE -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \ "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/$OLD_ID" echo "✓ Old release deleted" sleep 2 else echo "ℹ️ No existing release found" fi echo "" - name: 📋 Generate release notes if: steps.changelog.outputs.changelog_updated == 'true' run: | echo "======================================" echo "📋 Generating release notes" echo "======================================" cd ${{ env.REPO_DIR }} python3 << 'PYSCRIPT' import re import os tag = "${{ github.ref_name }}" changelog_version = "${{ env.CHANGELOG_VERSION }}" print(f"🏷️ Tag version: {tag}") print(f"📝 CHANGELOG version: {changelog_version}") changelog_path = 'CHANGELOG.md' if not os.path.exists(changelog_path): print("❌ ERROR: CHANGELOG.md not found") exit(1) print("📖 Reading CHANGELOG.md...") with open(changelog_path, 'r', encoding='utf-8') as f: changelog = f.read() print(f"🔍 Searching for version entry: {changelog_version}") pattern = rf'##\s+:bookmark:\s+{re.escape(changelog_version)}\s*\n(.*?)(?=^##\s+:bookmark:|\Z)' match = re.search(pattern, changelog, re.DOTALL | re.MULTILINE) 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: $RELEASE_TITLE" echo "🏷️ Pre-release: $RELEASE_IS_PRERELEASE" echo "📄 Draft: $RELEASE_IS_DRAFT" # 转换 bash 布尔值为 Python 布尔值 if [ "$RELEASE_IS_DRAFT" = "true" ]; then PY_DRAFT="True" else PY_DRAFT="False" fi if [ "$RELEASE_IS_PRERELEASE" = "true" ]; then PY_PRERELEASE="True" else PY_PRERELEASE="False" fi echo "🔄 Converting: draft=$RELEASE_IS_DRAFT → $PY_DRAFT, prerelease=$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": "$RELEASE_TITLE", "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 ${{ secrets.GITEA_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 "$ADDITIONAL_RELEASE_FILES" ]; then echo "" echo "📦 Uploading additional files..." FILE_INDEX=2 TOTAL_FILES=$((1 + $(echo "$ADDITIONAL_RELEASE_FILES" | wc -w))) for file in $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 ${{ secrets.GITEA_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:** \`$GITEA_SERVER\`" >> $GITHUB_STEP_SUMMARY echo "- **Release Title:** \`$RELEASE_TITLE\`" >> $GITHUB_STEP_SUMMARY if [ "$RELEASE_IS_PRERELEASE" = "true" ]; then echo "- **Release Type:** 🏷️ Pre-release" >> $GITHUB_STEP_SUMMARY else echo "- **Release Type:** ✅ Latest (Stable)" >> $GITHUB_STEP_SUMMARY fi if [ "$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 "$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"