v2.2.1 — 添加健康检查 + Gitea Actions CI/CD 自动部署
This commit is contained in:
132
.gitea/workflows/deploy.yml
Normal file
132
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,132 @@
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: prod-deploy
|
||||
env:
|
||||
DEPLOY_BASE: /opt/ziwei-power
|
||||
REPO_URL: https://qiukai:${{ secrets.DEPLOY_TOKEN }}@git.qiukai.me/qiukai/ziwei-power.git
|
||||
SERVICE_NAME: ziwei-power
|
||||
steps:
|
||||
- name: Clone and deploy
|
||||
run: |
|
||||
set -e
|
||||
RELEASE_ID="${{ github.sha }}"
|
||||
RELEASE_DIR="${DEPLOY_BASE}/releases/${RELEASE_ID}"
|
||||
CLONE_DIR="/tmp/zw-deploy-${RELEASE_ID}"
|
||||
|
||||
echo "=== 1. Clone repository ==="
|
||||
rm -rf "${CLONE_DIR}"
|
||||
git clone --depth 1 --branch main "${REPO_URL}" "${CLONE_DIR}"
|
||||
|
||||
echo "=== 2. Prepare release directory ==="
|
||||
rm -rf "${RELEASE_DIR}"
|
||||
mkdir -p "${RELEASE_DIR}"
|
||||
|
||||
# Copy repo content to release dir (exclude .git, .env, venv, data)
|
||||
rsync -a --exclude='.git' \
|
||||
--exclude='.env' \
|
||||
--exclude='.env.local' \
|
||||
--exclude='.venv' \
|
||||
--exclude='data/' \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='.gitea' \
|
||||
"${CLONE_DIR}/" "${RELEASE_DIR}/"
|
||||
|
||||
echo "=== 2.5 Ensure .env secrets ==="
|
||||
mkdir -p "${DEPLOY_BASE}/shared"
|
||||
SHARED_ENV="${DEPLOY_BASE}/shared/.env"
|
||||
touch "${SHARED_ENV}"
|
||||
ensure_env_var() {
|
||||
key="$1"; val="$2"
|
||||
if [ -z "$val" ]; then
|
||||
echo " ! ${key} 未提供(CI secret 为空),跳过写入"
|
||||
return
|
||||
fi
|
||||
if ! grep -q "^${key}=" "${SHARED_ENV}"; then
|
||||
echo "${key}=${val}" >> "${SHARED_ENV}"
|
||||
echo " + 已写入 ${key}"
|
||||
else
|
||||
echo " = ${key} 已存在,保留原值"
|
||||
fi
|
||||
}
|
||||
ensure_env_var "SECRET_KEY" "${{ secrets.ZW_SECRET_KEY }}"
|
||||
|
||||
echo "=== 3. Link shared resources ==="
|
||||
mkdir -p "${RELEASE_DIR}/data"
|
||||
# .env from shared dir (not in git)
|
||||
ln -sfn "${DEPLOY_BASE}/shared/.env" "${RELEASE_DIR}/.env"
|
||||
|
||||
# Database dir symlink to persist across releases
|
||||
DB_DIR="$HOME/.workbuddy/data/ziwei-power"
|
||||
mkdir -p "${DB_DIR}"
|
||||
ln -sfn "${DB_DIR}" "${RELEASE_DIR}/data"
|
||||
|
||||
echo "=== 4. Setup Python venv ==="
|
||||
cd "${RELEASE_DIR}"
|
||||
python3 -m venv .venv
|
||||
. .venv/bin/activate
|
||||
pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
echo "=== 5. Setup systemd service ==="
|
||||
if ! systemctl is-enabled "${SERVICE_NAME}" >/dev/null 2>&1; then
|
||||
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
|
||||
[Unit]
|
||||
Description=ziwei-power 日课系统
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$USER
|
||||
WorkingDirectory=${DEPLOY_BASE}/current
|
||||
ExecStart=${DEPLOY_BASE}/current/.venv/bin/python ${DEPLOY_BASE}/current/app.py
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
Environment=PORT=5058
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl enable "${SERVICE_NAME}"
|
||||
fi
|
||||
|
||||
echo "=== 6. Restart service ==="
|
||||
ln -sfn "${RELEASE_DIR}" "${DEPLOY_BASE}/current"
|
||||
systemctl restart "${SERVICE_NAME}"
|
||||
sleep 3
|
||||
|
||||
echo "=== 7. Health check ==="
|
||||
for i in 1 2 3 4 5; do
|
||||
if curl -fsS http://127.0.0.1:5058/api/health >/dev/null 2>&1; then
|
||||
echo "Health check passed"
|
||||
break
|
||||
fi
|
||||
echo "Attempt $i: waiting for service..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Final verify
|
||||
if ! curl -fsS http://127.0.0.1:5058/api/health >/dev/null 2>&1; then
|
||||
echo "ERROR: Health check failed after 5 attempts"
|
||||
echo "Rolling back to previous release..."
|
||||
PREV=$(ls -t "${DEPLOY_BASE}/releases" | sed -n '2p')
|
||||
if [ -n "${PREV}" ]; then
|
||||
ln -sfn "${DEPLOY_BASE}/releases/${PREV}" "${DEPLOY_BASE}/current"
|
||||
systemctl restart "${SERVICE_NAME}"
|
||||
echo "Rolled back to ${PREV}"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== 8. Cleanup old releases ==="
|
||||
ls -dt "${DEPLOY_BASE}"/releases/*/ | tail -n +6 | xargs -r rm -rf
|
||||
|
||||
echo "=== 9. Cleanup temp ==="
|
||||
rm -rf "${CLONE_DIR}"
|
||||
|
||||
echo "=== Deploy complete: ${RELEASE_ID} ==="
|
||||
7
app.py
7
app.py
@@ -406,6 +406,13 @@ def api_calendar_sync_all():
|
||||
return jsonify({'ok': True, 'data': results, 'saved_days': saved_weeks})
|
||||
|
||||
|
||||
# ── 健康检查 ──────────────────────────────────────────
|
||||
|
||||
@app.route('/api/health')
|
||||
def api_health():
|
||||
return jsonify({'ok': True, 'service': 'ziwei-power'})
|
||||
|
||||
|
||||
# ── 启动 ──────────────────────────────────────────────
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user