480ce836bb
Deploy / build-linux-web (push) Successful in 53s
Deploy / build-windows (push) Successful in 1m48s
Deploy / build-macos (push) Successful in 1m17s
Deploy / build-android (push) Successful in 4m13s
Deploy / build-ios (push) Successful in 9s
Deploy / release-deploy (push) Successful in 1m37s
移动端响应式适配(抽屉导航/列表卡片/弹窗自适应)、Android 正式签名与 APK 发布、 iOS(TestFlight) 工程与 CI、多平台构建流水线、相关文档同步。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
158 lines
5.6 KiB
Bash
Executable File
158 lines
5.6 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 ] || [ ! -f dist/marketing.tar.gz ]; then
|
||
echo "==> deploy: dist/ incomplete — downloading assets for ${TAG} from Forgejo"
|
||
mkdir -p dist
|
||
|
||
curl -kf \
|
||
-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, ssl, os, sys
|
||
|
||
ctx = ssl.create_default_context()
|
||
ctx.check_hostname = False
|
||
ctx.verify_mode = ssl.CERT_NONE
|
||
|
||
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, context=ctx) 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
|
||
|
||
# Extract marketing site
|
||
rm -rf /tmp/jiu-marketing-new
|
||
mkdir -p /tmp/jiu-marketing-new
|
||
tar -xzf dist/marketing.tar.gz -C /tmp/jiu-marketing-new
|
||
|
||
# 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 (built by compile.sh)
|
||
rsync -avz --delete -e "ssh -i ~/.ssh/ec2_deploy.pem -o StrictHostKeyChecking=no" \
|
||
/tmp/jiu-marketing-new/ "${EC2_USER}@${EC2_HOST}:/tmp/jiu-marketing-new/"
|
||
|
||
# Upload desktop client installers (served from /downloads/ by nginx).
|
||
# Guarded: may be absent in a partial manual deploy.
|
||
[ -f dist/jiu-windows-x64-setup.exe ] && ${SCP} dist/jiu-windows-x64-setup.exe "${EC2_USER}@${EC2_HOST}:/tmp/jiu-windows-x64-setup.exe" || true
|
||
[ -f dist/jiu-macos-x64.zip ] && ${SCP} dist/jiu-macos-x64.zip "${EC2_USER}@${EC2_HOST}:/tmp/jiu-macos-x64.zip" || true
|
||
[ -f dist/jiu-android.apk ] && ${SCP} dist/jiu-android.apk "${EC2_USER}@${EC2_HOST}:/tmp/jiu-android.apk" || true
|
||
|
||
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(WorkingDirectory=/opt/jiu,读 config/version.yaml)
|
||
mkdir -p /opt/jiu/config
|
||
cp /tmp/version.yaml /opt/jiu/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 -f http://localhost:8080/health > /dev/null 2>&1; then
|
||
echo "Health check passed"
|
||
break
|
||
fi
|
||
sleep 2
|
||
done
|
||
curl -f 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
|
||
|
||
# Publish desktop client installers to the nginx-served downloads dir.
|
||
# Keep only the latest version: clear old installers, then drop in the new ones.
|
||
mkdir -p /opt/jiu/downloads
|
||
rm -f /opt/jiu/downloads/jiu-windows-* /opt/jiu/downloads/jiu-macos-* /opt/jiu/downloads/jiu-android-* /opt/jiu/downloads/jiu-android.apk 2>/dev/null || true
|
||
[ -f /tmp/jiu-windows-x64-setup.exe ] && mv -f /tmp/jiu-windows-x64-setup.exe /opt/jiu/downloads/ || true
|
||
[ -f /tmp/jiu-macos-x64.zip ] && mv -f /tmp/jiu-macos-x64.zip /opt/jiu/downloads/ || true
|
||
[ -f /tmp/jiu-android.apk ] && mv -f /tmp/jiu-android.apk /opt/jiu/downloads/ || true
|
||
|
||
# Update jiu nginx config in the pangolin-edge reverse proxy.
|
||
# nginx now runs inside the `pangolin-edge` container (host networking), not on
|
||
# the host. Its /etc/nginx/conf.d is bind-mounted from the host dir below, so we
|
||
# write the config there and reload nginx *inside the container*.
|
||
cp /tmp/nginx-jiu.conf /home/ec2-user/pangolin/edge/conf.d/jiu.conf
|
||
docker exec pangolin-edge nginx -t && docker exec pangolin-edge 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"
|