53f56ce086
- deploy.yml: tag 触发(v*.*.*),四阶段调脚本(compile/test/release/deploy) - manual.yml: workflow_dispatch 支持手动回滚,checkout 指定 tag - scripts/ci/compile.sh: 构建 Go 二进制 + Flutter Web,打包 dist/ - scripts/ci/test.sh: go test + flutter analyze - scripts/ci/release.sh: 解析 CHANGELOG.md → 更新 version.yaml → 创建 Forgejo Release - scripts/ci/deploy.sh: 从 Release 下载产物(自动/手动均可)→ 部署到 EC2 - CHANGELOG.md: Keep a Changelog 格式,初始 v1.0.0 条目 - backend: GetRelease 改读 version.yaml,移除 ENV 变量依赖 - backend/config/version.yaml: 重置为 v1.0.0 - web/download.html: 动态拉取 /api/v1/public/release 展示版本号和更新内容 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
132 lines
3.9 KiB
Bash
Executable File
132 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# deploy.sh <tag> — deploy release to EC2
|
|
# Works in two modes:
|
|
# 1. Automated (after compile.sh): uses dist/ built in the same job
|
|
# 2. Manual/rollback: downloads assets from Forgejo Release by tag
|
|
set -euo pipefail
|
|
|
|
TAG="$1"
|
|
|
|
echo "==> deploy: tag=${TAG}"
|
|
|
|
# Download from Forgejo if dist/ was not built in this job
|
|
if [ ! -f dist/jiu-server ] || [ ! -f dist/web.tar.gz ] || [ ! -f dist/configs.tar.gz ]; then
|
|
echo "==> deploy: dist/ incomplete — downloading assets for ${TAG} from Forgejo"
|
|
mkdir -p dist
|
|
|
|
curl -sf \
|
|
-H "Authorization: token ${FORGEJO_TOKEN}" \
|
|
"${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/tags/${TAG}" \
|
|
-o /tmp/release-info.json
|
|
|
|
python3 - <<'PYEOF'
|
|
import json, urllib.request, os, sys
|
|
|
|
with open('/tmp/release-info.json') as f:
|
|
release = json.load(f)
|
|
|
|
token = os.environ['FORGEJO_TOKEN']
|
|
forgejo_url = os.environ['FORGEJO_URL']
|
|
repo = os.environ['GITEA_REPOSITORY']
|
|
release_id = release['id']
|
|
|
|
for asset in release.get('assets', []):
|
|
url = f"{forgejo_url}/api/v1/repos/{repo}/releases/{release_id}/assets/{asset['id']}"
|
|
req = urllib.request.Request(url, headers={'Authorization': f'token {token}'})
|
|
with urllib.request.urlopen(req) as resp:
|
|
with open(f"dist/{asset['name']}", 'wb') as out:
|
|
out.write(resp.read())
|
|
print(f"Downloaded: {asset['name']}")
|
|
PYEOF
|
|
fi
|
|
|
|
echo "==> deploy: dist/ contents:"
|
|
ls -lh dist/
|
|
|
|
# Extract configs (nginx, version.yaml, etc.)
|
|
rm -rf /tmp/jiu-configs
|
|
mkdir -p /tmp/jiu-configs
|
|
tar -xzf dist/configs.tar.gz -C /tmp/jiu-configs
|
|
|
|
# Extract Flutter web build
|
|
rm -rf /tmp/jiu-web-new
|
|
mkdir -p /tmp/jiu-web-new
|
|
tar -xzf dist/web.tar.gz -C /tmp/jiu-web-new --strip-components=1
|
|
|
|
# Setup SSH
|
|
mkdir -p ~/.ssh
|
|
printf '%s' "${EC2_SSH_KEY}" > ~/.ssh/ec2_deploy.pem
|
|
chmod 600 ~/.ssh/ec2_deploy.pem
|
|
ssh-keyscan -H "${EC2_HOST}" >> ~/.ssh/known_hosts
|
|
|
|
SSH="ssh -i ~/.ssh/ec2_deploy.pem -o StrictHostKeyChecking=no"
|
|
SCP="scp -O -i ~/.ssh/ec2_deploy.pem"
|
|
|
|
echo "==> deploy: uploading files to EC2"
|
|
|
|
# Upload backend binary
|
|
${SCP} dist/jiu-server "${EC2_USER}@${EC2_HOST}:/tmp/jiu-server"
|
|
|
|
# Upload version.yaml
|
|
${SCP} /tmp/jiu-configs/backend/config/version.yaml "${EC2_USER}@${EC2_HOST}:/tmp/version.yaml"
|
|
|
|
# Upload nginx config
|
|
${SCP} /tmp/jiu-configs/deploy/nginx-jiu.conf "${EC2_USER}@${EC2_HOST}:/tmp/nginx-jiu.conf"
|
|
|
|
# Upload Flutter web
|
|
rsync -avz --delete -e "ssh -i ~/.ssh/ec2_deploy.pem -o StrictHostKeyChecking=no" \
|
|
/tmp/jiu-web-new/ "${EC2_USER}@${EC2_HOST}:/tmp/jiu-web-new/"
|
|
|
|
# Upload marketing site (from repo checkout)
|
|
rsync -avz --delete -e "ssh -i ~/.ssh/ec2_deploy.pem -o StrictHostKeyChecking=no" \
|
|
web/ "${EC2_USER}@${EC2_HOST}:/tmp/jiu-marketing-new/"
|
|
|
|
echo "==> deploy: running remote deploy"
|
|
|
|
${SSH} "${EC2_USER}@${EC2_HOST}" << 'ENDSSH'
|
|
set -e
|
|
|
|
# Replace backend binary
|
|
sudo systemctl stop jiu
|
|
cp /tmp/jiu-server /opt/jiu/backend/jiu-server
|
|
chmod +x /opt/jiu/backend/jiu-server
|
|
|
|
# Update version config
|
|
mkdir -p /opt/jiu/backend/config
|
|
cp /tmp/version.yaml /opt/jiu/backend/config/version.yaml
|
|
|
|
# Start and health check
|
|
sudo systemctl start jiu
|
|
echo "Waiting for health check..."
|
|
for i in $(seq 1 30); do
|
|
if curl -sf http://localhost:8080/health > /dev/null 2>&1; then
|
|
echo "Health check passed"
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
curl -sf http://localhost:8080/health > /dev/null || { echo "Health check failed!"; exit 1; }
|
|
|
|
# Swap Flutter web app (atomic)
|
|
rm -rf /opt/jiu/web-old
|
|
mv /opt/jiu/web /opt/jiu/web-old 2>/dev/null || true
|
|
mv /tmp/jiu-web-new /opt/jiu/web
|
|
|
|
# Update marketing site
|
|
mkdir -p /opt/jiu/marketing
|
|
rsync -a --delete /tmp/jiu-marketing-new/ /opt/jiu/marketing/
|
|
rm -rf /tmp/jiu-marketing-new
|
|
|
|
# Update nginx config and reload
|
|
sudo cp /tmp/nginx-jiu.conf /etc/nginx/conf.d/jiu.conf
|
|
sudo nginx -t && sudo nginx -s reload
|
|
|
|
echo "Deploy complete!"
|
|
ENDSSH
|
|
|
|
# Cleanup SSH key
|
|
rm -f ~/.ssh/ec2_deploy.pem
|
|
rm -rf /tmp/jiu-configs /tmp/jiu-web-new
|
|
|
|
echo "==> deploy: done"
|