Files
2026FIFAWorldCup/.gitea/workflows/cd.yaml
wooo 489baecbe8
Some checks failed
2026 World Cup Quant Platform - Production Deployment / Code Quality, Security Gate & Testing (push) Failing after 2m44s
2026 World Cup Quant Platform - Production Deployment / Deploy to Production VM via Gitea CD (push) Has been skipped
fix: restore backend runtime entrypoints
2026-06-18 13:00:28 +08:00

246 lines
8.8 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: 2026 World Cup Quant Platform - Production Deployment
on:
push:
branches:
- main
jobs:
test-and-lint:
name: Code Quality, Security Gate & Testing
runs-on: ewoooc-dedicated-runner
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Security policy gate
run: |
set -euo pipefail
echo "== 檢查禁止進入正式部署的臨時維運腳本 =="
forbidden_files="
iwooos_javae_monitor.sh
iwooos_autopatch.py
fix_guardian.py
fix_register.sh
fix_wazuh.sh
ops/harden-host.sh
"
for file in $forbidden_files; do
if [ -e "$file" ]; then
echo "禁止部署:$file 不得存在於正式產品 repo。"
exit 1
fi
done
echo "== 檢查硬編碼密碼、手工 SSH 修補與挖礦 IOC =="
if grep -RInE '(sshpass|sudo -S|WAZUH_PASS\s*=|YOUR_BOT_TOKEN|xmrig|kinsing|kdevtmpfsi|stratum|pool\.supportxmr\.com|221\.156\.167\.200|0936223270|Wooo-0936223270)' \
--exclude-dir=.git \
--exclude-dir=.gitea \
--exclude-dir=node_modules \
--exclude-dir=.next \
--exclude=package-lock.json \
--exclude='*.md' \
.; then
echo "禁止部署:偵測到硬編碼密碼、挖礦 IOC 或手工 SSH 修補痕跡。"
exit 1
fi
- name: Setup Python Environment
run: |
apt-get update -qq
apt-get install -y -qq python3-pip python3-venv
python3 -m venv venv
echo "PATH=$PWD/venv/bin:$PATH" >> $GITHUB_ENV
- name: Install Backend Dependencies
run: |
pip install -r platform/backend/requirements.txt pytest pip-audit
- name: Python dependency audit
run: pip-audit -r platform/backend/requirements.txt
- name: Run Backend Quant Engine Tests
run: |
if find platform/backend -type f \( -name 'test_*.py' -o -name '*_test.py' \) | grep -q .; then
pytest platform/backend
else
echo "未找到後端 pytest 測試檔,改以 Python 編譯檢查作為最低安全閘門。"
python -m compileall -q platform/backend/app
fi
- name: Validate Backend Runtime Entrypoints
env:
PYTHONPATH: platform/backend
DATABASE_URL: postgresql+asyncpg://fifa_user:ci-placeholder-db-password@127.0.0.1:5432/fifa2026
REDIS_URL: redis://:ci-placeholder-redis-password@127.0.0.1:6379/0
run: |
python - <<'PY'
import importlib
modules = [
'app.main',
'app.analytics.worldcup_seed',
'app.analytics.crawler',
'app.analytics.news_worker',
'app.analytics.agent_review_worker',
'app.analytics.daily_card_calendar_worker',
'app.analytics.fixtures_worker',
]
for module in modules:
importlib.import_module(module)
print(f'OK import {module}')
PY
- name: Setup Node.js Environment
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install Frontend Dependencies
run: |
cd platform/web
npm ci --legacy-peer-deps
- name: Frontend dependency audit
run: |
cd platform/web
npm audit --audit-level=high
- name: Run Frontend Linting
run: |
cd platform/web
npm run lint
- name: Run Frontend Production Build
env:
DATABASE_URL: postgresql://fifa_user:ci-placeholder-db-password@127.0.0.1:5432/fifa2026
NEXTAUTH_SECRET: ci-placeholder-nextauth-secret
NEXTAUTH_URL: https://2026fifa.wooo.work
ANALYTICS_BACKEND_URL: http://127.0.0.1:8000
run: |
cd platform/web
npm run build
- name: Validate Docker Compose
env:
DB_PASSWORD: ci-placeholder-db-password
REDIS_PASSWORD: ci-placeholder-redis-password
NEXTAUTH_SECRET: ci-placeholder-nextauth-secret
run: docker compose -f docker-compose.prod.yml config -q
deploy-docker:
name: Deploy to Production VM via Gitea CD
needs: test-and-lint
runs-on: ewoooc-dedicated-runner
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Install rsync and ssh
run: apt-get update -qq && apt-get install -y -qq rsync openssh-client
- name: Configure SSH Key
env:
PROD_SSH_PRIVATE_KEY: ${{ secrets.PROD_SSH_PRIVATE_KEY }}
PROD_SERVER_IP_SECRET: ${{ secrets.PROD_SERVER_IP }}
PROD_SERVER_USER_SECRET: ${{ secrets.PROD_SERVER_USER }}
run: |
set -euo pipefail
PROD_SERVER_IP="${PROD_SERVER_IP_SECRET:-192.168.0.188}"
PROD_SERVER_USER="${PROD_SERVER_USER_SECRET:-ollama}"
if [ -z "$PROD_SERVER_IP" ] || [ -z "$PROD_SERVER_USER" ]; then
echo "禁止部署:正式主機 IP 或使用者未設定。"
exit 1
fi
if [ -z "${PROD_SSH_PRIVATE_KEY:-}" ]; then
echo "禁止部署Gitea secret PROD_SSH_PRIVATE_KEY 未設定。"
exit 1
fi
echo "PROD_SERVER_IP=$PROD_SERVER_IP" >> "$GITHUB_ENV"
echo "PROD_SERVER_USER=$PROD_SERVER_USER" >> "$GITHUB_ENV"
echo "部署目標:$PROD_SERVER_USER@$PROD_SERVER_IP"
mkdir -p ~/.ssh
printf '%s\n' "$PROD_SSH_PRIVATE_KEY" > ~/.ssh/id_deploy
chmod 600 ~/.ssh/id_deploy
ssh-keyscan -T 10 "$PROD_SERVER_IP" >> ~/.ssh/known_hosts
- name: Sync Files to Production
run: |
set -euo pipefail
printf "%s\n" "${{ github.sha }}" > REVISION
rsync -az --delete --delay-updates -e "ssh -i ~/.ssh/id_deploy" \
--exclude='.git/' \
--exclude='.gitea/' \
--exclude='node_modules/' \
--exclude='.next/' \
--exclude='venv/' \
--exclude='__pycache__/' \
--exclude='.env' \
./ "$PROD_SERVER_USER@$PROD_SERVER_IP:/opt/fifa2026/current/"
- name: Prepare production environment file
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
THE_ODDS_API_KEY: ${{ secrets.THE_ODDS_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
NEMOTRON_API_BASE: ${{ secrets.NEMOTRON_API_BASE }}
OLLAMA_BASE_URL: ${{ secrets.OLLAMA_BASE_URL }}
run: |
set -euo pipefail
for required in DB_PASSWORD REDIS_PASSWORD NEXTAUTH_SECRET; do
if [ -z "${!required:-}" ]; then
echo "禁止部署Gitea secret $required 未設定。"
exit 1
fi
done
write_env_line() {
name="$1"
value="${!name:-}"
escaped=$(printf '%s' "$value" | sed "s/'/'\\''/g")
printf "%s='%s'\n" "$name" "$escaped"
}
write_optional_env_line() {
name="$1"
if [ -n "${!name:-}" ]; then
write_env_line "$name"
fi
}
umask 077
{
write_env_line DB_PASSWORD
write_env_line REDIS_PASSWORD
write_env_line NEXTAUTH_SECRET
write_optional_env_line THE_ODDS_API_KEY
write_optional_env_line GEMINI_API_KEY
write_optional_env_line NEMOTRON_API_BASE
write_optional_env_line OLLAMA_BASE_URL
} > .deploy.env
scp -i ~/.ssh/id_deploy .deploy.env "$PROD_SERVER_USER@$PROD_SERVER_IP:/opt/fifa2026/current/.env"
rm -f .deploy.env
- name: Restart Docker Containers
run: |
set -euo pipefail
ssh -i ~/.ssh/id_deploy "$PROD_SERVER_USER@$PROD_SERVER_IP" 'bash -se' <<'DEPLOY_SCRIPT'
set -euo pipefail
echo "[Deploy] Starting deployment for 2026fifa.wooo.work"
cd /opt/fifa2026/current
for file in iwooos_javae_monitor.sh iwooos_autopatch.py fix_guardian.py fix_register.sh fix_wazuh.sh ops/harden-host.sh; do
if [ -e "$file" ]; then
echo "[Deploy] Forbidden emergency script still exists on production: $file"
exit 1
fi
done
docker compose -f docker-compose.prod.yml config -q
docker compose -f docker-compose.prod.yml build --pull
docker compose -f docker-compose.prod.yml up -d --remove-orphans
docker image prune -f
echo "[Deploy] Deployment completed successfully."
DEPLOY_SCRIPT