306 lines
8.5 KiB
Bash
Executable File
306 lines
8.5 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# Memory App 发布脚本
|
||
# 功能:自动递增版本号、编译 APK、上传到 R2、更新服务器版本信息
|
||
|
||
set -e
|
||
|
||
# ============ 配置区域 ============
|
||
# R2 配置
|
||
R2_ACCOUNT_ID="ebf33b5ee4eb26f32af0c6e06102e000"
|
||
R2_ACCESS_KEY_ID="8acbc8a9386d60d0e8dac6bd8165c618"
|
||
R2_ACCESS_KEY_SECRET="72935e23b5b4be8fda99008e75285e8ac778f8926656c42780b25785bb149443"
|
||
R2_BUCKET_NAME="memory"
|
||
R2_PUBLIC_URL="https://cdn.amos.us.kg"
|
||
|
||
# API 配置
|
||
API_BASE_URL="https://x.amos.us.kg/api"
|
||
|
||
# 服务器配置
|
||
SERVER_HOST="95.181.190.226"
|
||
SERVER_PORT="33"
|
||
SERVER_USER="root"
|
||
SERVER_PASSWORD="xiaobiao."
|
||
SERVER_PROJECT_PATH="/amos/memory"
|
||
|
||
# 签名配置
|
||
KEYSTORE_FILE="release.keystore"
|
||
KEYSTORE_PASSWORD="memory123"
|
||
KEY_ALIAS="memory"
|
||
KEY_PASSWORD="memory123"
|
||
|
||
# ============ 函数定义 ============
|
||
|
||
# 获取当前版本号
|
||
get_current_version() {
|
||
grep "versionName" android/app/build.gradle.kts | head -1 | sed 's/.*"\(.*\)".*/\1/'
|
||
}
|
||
|
||
# 获取当前版本代码
|
||
get_current_version_code() {
|
||
grep "versionCode" android/app/build.gradle.kts | head -1 | sed 's/[^0-9]*//g'
|
||
}
|
||
|
||
# 递增版本号 (1.1.9 -> 1.2.0, 1.1.0 -> 1.1.1)
|
||
increment_version() {
|
||
local version=$1
|
||
local major=$(echo $version | cut -d. -f1)
|
||
local minor=$(echo $version | cut -d. -f2)
|
||
local patch=$(echo $version | cut -d. -f3)
|
||
|
||
patch=$((patch + 1))
|
||
if [ $patch -ge 10 ]; then
|
||
patch=0
|
||
minor=$((minor + 1))
|
||
if [ $minor -ge 10 ]; then
|
||
minor=0
|
||
major=$((major + 1))
|
||
fi
|
||
fi
|
||
|
||
echo "${major}.${minor}.${patch}"
|
||
}
|
||
|
||
# 更新 build.gradle.kts 中的版本号
|
||
update_version_in_gradle() {
|
||
local new_version=$1
|
||
local new_code=$2
|
||
|
||
sed -i '' "s/versionCode = [0-9]*/versionCode = ${new_code}/" android/app/build.gradle.kts
|
||
sed -i '' "s/versionName = \"[^\"]*\"/versionName = \"${new_version}\"/" android/app/build.gradle.kts
|
||
sed -i '' "s/VERSION_CODE\", \"[0-9]*\"/VERSION_CODE\", \"${new_code}\"/" android/app/build.gradle.kts
|
||
}
|
||
|
||
# 编译 Docker 镜像并导出
|
||
build_docker() {
|
||
echo "🐳 编译 Docker 镜像..."
|
||
cd server
|
||
docker build --platform linux/amd64 -t memory-server:latest .
|
||
docker save memory-server:latest -o memory-server.tar
|
||
cd ..
|
||
echo "✅ Docker 镜像已导出到 server/memory-server.tar"
|
||
}
|
||
|
||
# 部署 Docker 镜像到服务器
|
||
deploy_docker() {
|
||
echo "🚀 部署 Docker 镜像到服务器..."
|
||
|
||
# 上传镜像到服务器
|
||
echo "📤 上传镜像文件..."
|
||
sshpass -p "${SERVER_PASSWORD}" scp -P ${SERVER_PORT} -o StrictHostKeyChecking=no server/memory-server.tar ${SERVER_USER}@${SERVER_HOST}:${SERVER_PROJECT_PATH}/
|
||
|
||
# 在服务器上加载镜像并重启
|
||
echo "🔄 加载镜像并重启服务..."
|
||
sshpass -p "${SERVER_PASSWORD}" ssh -p ${SERVER_PORT} -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_HOST} << EOF
|
||
cd ${SERVER_PROJECT_PATH}
|
||
docker load -i memory-server.tar
|
||
docker compose down
|
||
docker compose up -d
|
||
# 清理旧镜像
|
||
echo "🧹 清理旧镜像..."
|
||
docker image prune -f
|
||
# 删除镜像文件
|
||
rm -f memory-server.tar
|
||
echo "✅ 服务已重启"
|
||
EOF
|
||
|
||
echo "✅ 部署完成!"
|
||
}
|
||
|
||
# 编译 APK
|
||
build_apk() {
|
||
echo "📦 编译 APK..."
|
||
cd android
|
||
KEYSTORE_FILE=$KEYSTORE_FILE \
|
||
KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD \
|
||
KEY_ALIAS=$KEY_ALIAS \
|
||
KEY_PASSWORD=$KEY_PASSWORD \
|
||
gradle assembleRelease --quiet
|
||
cd ..
|
||
}
|
||
|
||
# 上传到 R2
|
||
upload_to_r2() {
|
||
local version=$1
|
||
local apk_path="android/app/build/outputs/apk/release/app-release.apk"
|
||
local remote_path="releases/memory-${version}.apk"
|
||
|
||
echo "☁️ 上传到 R2..."
|
||
|
||
# 使用 AWS CLI 上传 (需要配置 AWS CLI)
|
||
AWS_ACCESS_KEY_ID=$R2_ACCESS_KEY_ID \
|
||
AWS_SECRET_ACCESS_KEY=$R2_ACCESS_KEY_SECRET \
|
||
aws s3 cp "$apk_path" "s3://${R2_BUCKET_NAME}/${remote_path}" \
|
||
--endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" \
|
||
--content-type "application/vnd.android.package-archive"
|
||
|
||
echo "${R2_PUBLIC_URL}/${remote_path}"
|
||
}
|
||
|
||
# 清理旧版本 APK,只保留最新的 3 个
|
||
cleanup_old_apks() {
|
||
echo "🧹 清理旧版本 APK..."
|
||
|
||
# 列出所有 APK 文件,按时间倒序排列
|
||
local apk_list=$(AWS_ACCESS_KEY_ID=$R2_ACCESS_KEY_ID \
|
||
AWS_SECRET_ACCESS_KEY=$R2_ACCESS_KEY_SECRET \
|
||
aws s3 ls "s3://${R2_BUCKET_NAME}/releases/" \
|
||
--endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" \
|
||
| grep "memory-.*\.apk" \
|
||
| sort -r \
|
||
| awk '{print $4}')
|
||
|
||
# 跳过前 3 个(最新的),删除其余的
|
||
local count=0
|
||
for apk in $apk_list; do
|
||
count=$((count + 1))
|
||
if [ $count -gt 3 ]; then
|
||
echo " 删除: $apk"
|
||
AWS_ACCESS_KEY_ID=$R2_ACCESS_KEY_ID \
|
||
AWS_SECRET_ACCESS_KEY=$R2_ACCESS_KEY_SECRET \
|
||
aws s3 rm "s3://${R2_BUCKET_NAME}/releases/${apk}" \
|
||
--endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"
|
||
fi
|
||
done
|
||
|
||
if [ $count -le 3 ]; then
|
||
echo " 无需清理,当前共 ${count} 个版本"
|
||
else
|
||
echo "✅ 已清理 $((count - 3)) 个旧版本"
|
||
fi
|
||
}
|
||
|
||
# 更新服务器版本信息,返回 0 成功,1 失败
|
||
update_server_version() {
|
||
local version_code=$1
|
||
local version_name=$2
|
||
local download_url=$3
|
||
local update_log=$4
|
||
|
||
echo "🔄 更新服务器版本信息..."
|
||
echo " version_code: ${version_code}"
|
||
echo " version_name: ${version_name}"
|
||
echo " download_url: ${download_url}"
|
||
|
||
# 使用 printf 构建正确的 JSON
|
||
local json_data=$(printf '{"version_code":%d,"version_name":"%s","download_url":"%s","update_log":"%s","force_update":false}' \
|
||
"$version_code" "$version_name" "$download_url" "$update_log")
|
||
|
||
local response=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE_URL}/version" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$json_data")
|
||
|
||
local http_code=$(echo "$response" | tail -n1)
|
||
local body=$(echo "$response" | sed '$d')
|
||
|
||
if [ "$http_code" = "200" ]; then
|
||
echo "✅ 服务器版本信息已更新"
|
||
return 0
|
||
else
|
||
echo "❌ 更新服务器版本失败 (HTTP $http_code)"
|
||
echo " 响应: $body"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# ============ 主流程 ============
|
||
|
||
echo "🚀 Memory App 发布脚本"
|
||
echo "========================"
|
||
|
||
# 选择发布内容
|
||
echo ""
|
||
echo "请选择要发布的内容:"
|
||
echo " 1) 只发布 APK"
|
||
echo " 2) 只部署后端"
|
||
echo " 3) 发布 APK + 部署后端"
|
||
echo ""
|
||
read -p "请选择 (1/2/3): " choice
|
||
|
||
BUILD_APK=false
|
||
DEPLOY_BACKEND=false
|
||
|
||
case $choice in
|
||
1)
|
||
BUILD_APK=true
|
||
;;
|
||
2)
|
||
DEPLOY_BACKEND=true
|
||
;;
|
||
3)
|
||
BUILD_APK=true
|
||
DEPLOY_BACKEND=true
|
||
;;
|
||
*)
|
||
echo "❌ 无效选择"
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
# APK 发布流程
|
||
if [ "$BUILD_APK" = true ]; then
|
||
# 获取当前版本
|
||
current_version=$(get_current_version)
|
||
current_code=$(get_current_version_code)
|
||
echo ""
|
||
echo "📌 当前版本: v${current_version} (code: ${current_code})"
|
||
|
||
# 计算新版本
|
||
new_version=$(increment_version $current_version)
|
||
new_code=$((current_code + 1))
|
||
echo "📌 新版本: v${new_version} (code: ${new_code})"
|
||
|
||
# 获取更新日志
|
||
echo ""
|
||
read -p "📝 请输入更新日志: " update_log
|
||
|
||
if [ -z "$update_log" ]; then
|
||
update_log="Bug 修复和性能优化"
|
||
fi
|
||
|
||
echo ""
|
||
echo "📋 发布信息:"
|
||
echo " 版本: v${new_version} (code: ${new_code})"
|
||
echo " 更新日志: ${update_log}"
|
||
echo ""
|
||
|
||
# 更新版本号
|
||
echo "📝 更新版本号..."
|
||
update_version_in_gradle $new_version $new_code
|
||
|
||
# 编译
|
||
build_apk
|
||
|
||
# 先更新服务器版本信息,成功后再上传 APK
|
||
download_url="${R2_PUBLIC_URL}/releases/memory-${new_version}.apk"
|
||
if update_server_version $new_code "$new_version" "$download_url" "$update_log"; then
|
||
# 上传 APK
|
||
upload_to_r2 $new_version
|
||
echo "✅ 上传完成: $download_url"
|
||
|
||
# 清理旧版本
|
||
cleanup_old_apks
|
||
|
||
echo ""
|
||
echo "🎉 APK 发布完成!"
|
||
echo " 版本: v${new_version}"
|
||
echo " 下载: ${download_url}"
|
||
else
|
||
echo ""
|
||
echo "❌ 服务器版本更新失败,APK 未上传"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
# 后端部署流程
|
||
if [ "$DEPLOY_BACKEND" = true ]; then
|
||
echo ""
|
||
build_docker
|
||
deploy_docker
|
||
echo ""
|
||
echo "🎉 后端部署完成!"
|
||
fi
|
||
|
||
echo ""
|
||
echo "✨ 全部完成!"
|