📦 workflows的文件进行更名,并且调整后缀为`yml`
♻️ `changelog_and_release`代码重构,把配置项全部移动到`env`
This commit is contained in:
parent
8971cbffc3
commit
a21adb9fcc
|
|
@ -1,921 +0,0 @@
|
||||||
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}<a href=\"${GITEA_SERVER}/${name}\">\n"
|
|
||||||
NEW_ENTRY="${NEW_ENTRY} <img src=\"${GITEA_SERVER}/${name}.png\" alt=\"${name}\" width=\"35\" height=\"35\" style=\"border-radius: 50%;\" onerror=\"this.src='${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 "💾 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 ~ /<a href=/) {
|
|
||||||
getline; img=$0
|
|
||||||
getline; close_tag=$0
|
|
||||||
print prev ORS img ORS close_tag
|
|
||||||
prev=""
|
|
||||||
}
|
|
||||||
prev=$0
|
|
||||||
}
|
|
||||||
' "$TEMP_OLD_CONTENT")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 构建新的完整条目(叠加模式)
|
|
||||||
MERGED_ENTRY="## :bookmark: ${CHANGELOG_VERSION}\n\n"
|
|
||||||
MERGED_ENTRY="${MERGED_ENTRY}${CHANGELOG_SECTION_TITLE}\n\n"
|
|
||||||
|
|
||||||
# 先添加旧的提交(如果有)
|
|
||||||
if [ -n "$OLD_COMMITS" ]; then
|
|
||||||
MERGED_ENTRY="${MERGED_ENTRY}${OLD_COMMITS}\n"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 再添加新的提交(从NEW_ENTRY中提取)
|
|
||||||
NEW_COMMITS=$(echo -e "$NEW_ENTRY" | awk '
|
|
||||||
/^### :pencil: What/,/^### :busts_in_silhouette:/ {
|
|
||||||
if ($0 !~ /^### :pencil: What/ && $0 !~ /^### :busts_in_silhouette:/) {
|
|
||||||
if ($0 ~ /^-/) print $0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
')
|
|
||||||
MERGED_ENTRY="${MERGED_ENTRY}${NEW_COMMITS}\n"
|
|
||||||
|
|
||||||
# 合并贡献者(去重)
|
|
||||||
MERGED_ENTRY="${MERGED_ENTRY}\n${CHANGELOG_CONTRIBUTORS_TITLE}\n\n"
|
|
||||||
|
|
||||||
# 使用数组来去重贡献者
|
|
||||||
declare -A SEEN_CONTRIBUTORS
|
|
||||||
|
|
||||||
# 处理旧的贡献者
|
|
||||||
if [ -n "$OLD_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 <<< "$OLD_CONTRIBUTORS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 处理新的贡献者
|
|
||||||
NEW_CONTRIBUTORS=$(echo -e "$NEW_ENTRY" | awk '
|
|
||||||
/^### :busts_in_silhouette: Contributors/,/^---/ {
|
|
||||||
if ($0 ~ /<a href=/) print $0
|
|
||||||
else if ($0 ~ /<img src=/) print $0
|
|
||||||
else if ($0 ~ /<\/a>/) 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"
|
|
||||||
|
|
@ -0,0 +1,735 @@
|
||||||
|
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"
|
||||||
Loading…
Reference in New Issue