#!/bin/bash set -e # ========================================== # Git 自动发布脚本 v2.0 # ========================================== # 功能: # 1. 检查分支和工作区状态 # 2. 从 release.md 提取版本信息 # 3. 创建并推送 Git 标签 # 4. 通过 API 在 Gitea 创建 Release # 5. 自动处理字符集问题 # # 使用方法: # export GITEA_TOKEN="your_token_here" # ./release.sh # # 要求: # - release.md 文件存在且包含版本号 # - GITEA_TOKEN 环境变量已设置 # - 安装了 jq 命令 # # 更新日志: # v2.0 - 添加字符集自动降级处理 # - 改进错误提示和日志输出 # - 添加 Release 创建验证 # ========================================== # 0. 检查必要工具 if ! command -v jq &> /dev/null; then echo "❌ 错误:未安装 jq 命令" echo "💡 安装方法:" echo " macOS: brew install jq" echo " Ubuntu: sudo apt-get install jq" echo " CentOS: sudo yum install jq" exit 1 fi # 1. 检查分支 branch=$(git rev-parse --abbrev-ref HEAD) if [ "$branch" != "main" ]; then echo "❌ 错误:请在 main 分支运行,当前是 $branch" exit 1 fi echo "✅ 分支: $branch" # 2. 检查工作区是否干净 if [ -n "$(git status --porcelain)" ]; then echo "❌ 错误:工作区有未提交的更改,请先提交或 stash" git status exit 1 fi echo "✅ 工作区干净" # 3. 更新代码 echo "⬇️ 拉取远程代码..." git fetch origin git pull origin main echo "✅ 已同步最新代码" # 4. 从 release.md 提取版本和说明 if [ ! -f release.md ]; then echo "❌ 未找到 release.md" exit 1 fi # 提取最后一个版本号(去掉 ## 和空格) VERSION=$(grep "^## v" release.md | tail -n 1 | sed 's/^## *//') if [ -z "$VERSION" ]; then echo "❌ release.md 中未找到版本号" exit 1 fi # 提取该版本块的内容(从版本标题下一行到下一个版本或文件结尾) TAG_MESSAGE=$(awk " /^## $VERSION\$/ { flag=1; next } /^## v[0-9]/ && flag { exit } flag { print } " release.md) # 提取标题(第一个非空的实质性内容行,通常是 "发布时间:" 后的第一行) # 跳过空行和"发布时间:"行,取第一个 ### 标题 RELEASE_TITLE=$(echo "$TAG_MESSAGE" | grep -m 1 "^###" | sed 's/^### *//' | sed 's/^[🎯✨🔧🐛📦]* *//') # 如果没有找到 ### 标题,尝试找第一个非空行 if [ -z "$RELEASE_TITLE" ]; then RELEASE_TITLE=$(echo "$TAG_MESSAGE" | grep -v "^$" | grep -v "^发布时间:" | head -n 1) fi # 如果还是没有,使用版本号作为标题 if [ -z "$RELEASE_TITLE" ]; then RELEASE_TITLE="Release $VERSION" fi echo "📝 版本号: $VERSION" echo "📌 标题: $RELEASE_TITLE" echo "📄 内容预览:" echo "$TAG_MESSAGE" | head -n 10 echo "..." # 5. 创建 tag(如已存在则删除后重建) if git rev-parse "$VERSION" >/dev/null 2>&1; then echo "⚠️ 标签 $VERSION 已存在,删除旧标签..." git tag -d "$VERSION" git push origin ":refs/tags/$VERSION" fi git tag -a "$VERSION" -m "$TAG_MESSAGE" echo "✅ 已创建 tag $VERSION" # 6. 推送代码和 tag echo "🚀 推送到远程..." git push origin main git push origin "$VERSION" # 7. 创建 Gitea Release echo "" echo "🌐 创建 Gitea Release..." # 自动解析 GITEA_URL 和 REPO remote_url=$(git config --get remote.origin.url) if [[ "$remote_url" == ssh://git@biboer.cn:21174/* ]]; then # 特殊处理 biboer.cn:21174 的情况,Web 界面在 /gitea 路径下 GITEA_URL="https://biboer.cn/gitea" GITEA_REPO=$(echo "$remote_url" | sed 's|ssh://git@biboer\.cn:21174/||' | sed 's|\.git$||') elif [[ "$remote_url" =~ ^ssh://([^/]+)/([^/]+)/(.+)\.git$ ]]; then GITEA_URL="https://${BASH_REMATCH[1]}" GITEA_REPO="${BASH_REMATCH[2]}/${BASH_REMATCH[3]}" elif [[ "$remote_url" =~ ^([^@]+@[^:]+):([^/]+)/(.+)\.git$ ]]; then # git@biboer.cn:21174/gavin/note-to-mp.git hostport=$(echo "${BASH_REMATCH[1]}" | cut -d@ -f2) GITEA_URL="https://${hostport}" GITEA_REPO="${BASH_REMATCH[2]}/${BASH_REMATCH[3]}" else echo "❌ 无法解析远程地址: $remote_url" exit 1 fi if [ -z "$GITEA_TOKEN" ]; then echo "⚠️ 未设置 GITEA_TOKEN,只推送了 tag,没有创建 Release" exit 0 fi # 7.1 检查远程是否已存在该版本的 Release,如果存在则删除 echo "🔍 检查远程 Release 是否已存在..." check_response=$(curl -s -w "\n%{http_code}" \ -X GET "$GITEA_URL/api/v1/repos/$GITEA_REPO/releases/tags/$VERSION" \ -H "Authorization: token $GITEA_TOKEN") check_http_code=$(echo "$check_response" | tail -n 1) check_body=$(echo "$check_response" | sed '$d') if [ "$check_http_code" -eq 200 ]; then # Release 已存在,获取 Release ID 并删除 release_id=$(echo "$check_body" | jq -r '.id') echo "⚠️ 远程已存在 Release $VERSION (ID: $release_id),正在删除..." delete_response=$(curl -s -w "\n%{http_code}" \ -X DELETE "$GITEA_URL/api/v1/repos/$GITEA_REPO/releases/$release_id" \ -H "Authorization: token $GITEA_TOKEN") delete_http_code=$(echo "$delete_response" | tail -n 1) if [ "$delete_http_code" -eq 204 ] || [ "$delete_http_code" -eq 200 ]; then echo "✅ 已删除旧的 Release" else echo "⚠️ 删除 Release 失败 (HTTP $delete_http_code),但将继续创建新的..." fi elif [ "$check_http_code" -eq 404 ]; then echo "✅ 远程不存在该 Release,可以创建" else echo "⚠️ 检查 Release 状态失败 (HTTP $check_http_code),但将继续创建..." fi # 7.2 直接用 shell 拼接 JSON,不用 jq,不用临时文件 JSON_PAYLOAD="{\n \"tag_name\": \"$VERSION\",\n \"name\": \"$VERSION - $RELEASE_TITLE\",\n \"body\": \"$(echo "$TAG_MESSAGE" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/"/\\"/g')\",\n \"draft\": false,\n \"prerelease\": false\n}" echo "🔄 尝试创建 Release (直接拼接 JSON)..." response=$(curl -s -w "\n%{http_code}" \ -X POST "$GITEA_URL/api/v1/repos/$GITEA_REPO/releases" \ -H "Content-Type: application/json; charset=utf-8" \ -H "Authorization: token $GITEA_TOKEN" \ -d "$JSON_PAYLOAD") http_code=$(echo "$response" | tail -n 1) response_body=$(echo "$response" | sed '$d') if [ "$http_code" -eq 201 ]; then echo "✅ Release 创建成功: $VERSION" release_url=$(echo "$response_body" | jq -r '.html_url') echo "🔗 Release 地址: $release_url" elif [[ "$response_body" == *"Conversion from collation"* ]] || [[ "$response_body" == *"utf8"* ]]; then echo "⚠️ 检测到字符集问题,尝试使用英文版本..." echo "🐛 调试信息 - HTTP Code: $http_code" echo "🐛 调试信息 - Response Body: $response_body" # 创建简化的英文版本 ENGLISH_BODY="## Release Notes\n\nThis is release $VERSION: $RELEASE_TITLE\n\nFor detailed Chinese release notes, please see:\n- release.md in the repository\n- Or visit: $GITEA_URL/$GITEA_REPO/src/branch/main/release.md\n\n### Quick Start\n\n\`\`\`bash\ngit pull origin main\ncd web && npm install\nnpm run dev\n\`\`\`" JSON_PAYLOAD_EN=$(jq -n -c \ --arg version "$VERSION" \ --arg name "$VERSION - $RELEASE_TITLE" \ --arg body "$ENGLISH_BODY" \ '{ tag_name: $version, name: $name, body: $body, draft: false, prerelease: false }') response=$(curl -s -w "\n%{http_code}" \ -X POST "$GITEA_URL/api/v1/repos/$GITEA_REPO/releases" \ -H "Content-Type: application/json; charset=utf-8" \ -H "Authorization: token $GITEA_TOKEN" \ -d "$JSON_PAYLOAD_EN") http_code=$(echo "$response" | tail -n 1) response_body=$(echo "$response" | sed '$d') if [ "$http_code" -eq 201 ]; then echo "✅ Release 创建成功 (英文版): $VERSION" release_url=$(echo "$response_body" | jq -r '.html_url') echo "🔗 Release 地址: $release_url" echo "💡 提示: 可以在 Gitea Web 界面手动编辑添加中文说明" else echo "❌ Release 创建失败,HTTP $http_code" echo "📄 错误信息: $response_body" echo "" echo "💡 手动创建步骤:" echo " 1. 访问: $GITEA_URL/$GITEA_REPO/releases" echo " 2. 点击 '新建发布'" echo " 3. 选择标签: $VERSION" echo " 4. 从 release.md 复制说明" fi else echo "❌ Release 创建失败,HTTP $http_code" echo "📄 错误信息: $response_body" echo "" echo "💡 手动创建步骤:" echo " 1. 访问: $GITEA_URL/$GITEA_REPO/releases" echo " 2. 点击 '新建发布'" echo " 3. 选择标签: $VERSION" echo " 4. 从 release.md 复制说明" fi echo "" echo "🎉 发布完成!" echo "📦 版本:$VERSION" echo "" echo "📊 发布总结:" echo " ✅ Git 标签已推送" echo " ✅ 代码已推送到远程" if [ "$http_code" -eq 201 ]; then echo " ✅ Gitea Release 已创建" echo "" echo "🔗 访问地址:" echo " - Release: $release_url" echo " - 标签列表: $GITEA_URL/$GITEA_REPO/tags" echo " - 提交历史: $GITEA_URL/$GITEA_REPO/commits/branch/main" else echo " ⚠️ Gitea Release 创建失败(需手动创建)" echo "" echo "🔗 相关链接:" echo " - 仓库: $GITEA_URL/$GITEA_REPO" echo " - 发布页: $GITEA_URL/$GITEA_REPO/releases" fi echo "" echo "🚀 用户升级指南:" echo " git pull origin main" echo " cd web && npm install" echo " npm run dev"