name: CI on: push: branches: - main pull_request: branches: - main schedule: - cron: "0 2 * * *" # 每天凌晨 2 点触发定时备份 jobs: build-deploy: runs-on: CDCD # 合并 Job:统一使用带 Docker 的 Node 镜像 container: dockerreg.eazygame.cn/node-with-docker:22 env: SKIP_ENV_VALIDATION: "1" NEXT_TELEMETRY_DISABLED: "1" steps: - name: Checkout uses: actions/checkout@v3 # 1. 增加 Cache 策略,显著加快 npm ci 速度 - name: Cache npm dependencies uses: actions/cache@v3 id: npm-cache with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- # 【保留增强】配置代理,确保 npm install 能通 - name: Configure npm proxy run: | GATEWAY_IP=$(ip route show | grep default | awk '{print $3}') echo "Detected Docker Gateway: $GATEWAY_IP" if [ -z "$GATEWAY_IP" ]; then echo "Warning: Could not detect gateway IP, falling back to 172.17.0.1" GATEWAY_IP="172.17.0.1" fi PROXY_URL="http://$GATEWAY_IP:7890" echo "Using Proxy: $PROXY_URL" # 设置 npm 代理 npm config set proxy "$PROXY_URL" npm config set https-proxy "$PROXY_URL" # 设置环境变量供后续步骤使用 echo "http_proxy=$PROXY_URL" >> $GITHUB_ENV echo "https_proxy=$PROXY_URL" >> $GITHUB_ENV echo "HTTP_PROXY=$PROXY_URL" >> $GITHUB_ENV echo "HTTPS_PROXY=$PROXY_URL" >> $GITHUB_ENV - name: Install dependencies run: npm ci - name: Lint run: npm run lint - name: Typecheck run: npm run typecheck - name: Install Playwright Chromium run: npx playwright install chromium - name: Integration tests run: npm run test:integration - name: E2E full regression tests run: npm run test:e2e # 2. 增加 Next.js 构建缓存 - name: Cache Next.js build uses: actions/cache@v3 with: path: | ~/.npm ${{ github.workspace }}/.next/cache # Generate a new cache whenever packages or source files change. key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }} restore-keys: | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}- - name: Build run: npm run build - name: Prepare standalone build run: | mkdir -p .next/standalone/public mkdir -p .next/standalone/.next/static cp -r public/* .next/standalone/public/ cp -r .next/static/* .next/standalone/.next/static/ cp Dockerfile .next/standalone/Dockerfile # 【核心变更】合并 Deploy 步骤,直接构建镜像,无需 artifact - name: Deploy to Docker run: | # 1. 进入 standalone 目录 cd .next/standalone # 2. 构建镜像 (使用 standalone 目录下的 Dockerfile) echo "Building Docker image from standalone..." docker build --no-cache --pull -t nextjs-app . # 3. 优雅停止 docker stop nextjs-app || true docker rm nextjs-app || true # 4. 运行容器 # 使用你后来补充的完整配置 (包含 network 和 NEXTAUTH) docker run -d \ --init \ -p 8015:3000 \ --restart unless-stopped \ --name nextjs-app \ --network 1panel-network \ -e NODE_ENV=production \ -e DATABASE_URL=${{ secrets.DATABASE_URL }} \ -e NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET }} \ -e NEXTAUTH_URL=${{ secrets.NEXTAUTH_URL }} \ -e NEXT_TELEMETRY_DISABLED=1 \ nextjs-app echo "Deploy complete!" security-scan: runs-on: ubuntu-latest needs: build-deploy steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci # 1. npm audit(保留) - name: npm audit run: | npm audit --audit-level=moderate || true npm audit --json > audit-report.json || true continue-on-error: true # 2. Snyk 扫描(深度依赖分析) - name: Run Snyk to check for vulnerabilities uses: snyk/actions/node@master env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: --severity-threshold=high --sarif-file-output=snyk.sarif continue-on-error: true # 3. Trivy 文件系统扫描(扫描项目代码和依赖) - name: Trivy FS Scan run: | trivy fs --format json --output trivy-fs-report.json --exit-code 0 . trivy fs --format table --exit-code 0 . continue-on-error: true # 4. OWASP ZAP 基线扫描(扫描部署后的应用) - name: OWASP ZAP Baseline Scan uses: zaproxy/action-baseline@v0.10.0 with: target: ${{ secrets.NEXTAUTH_URL || 'http://localhost:8015' }} cmd_options: '-a -j' continue-on-error: true # 5. 上传所有报告(失败不阻塞,但生成报告) - uses: actions/upload-artifact@v3 if: always() with: name: security-reports path: | audit-report.json trivy-fs-report.json snyk.sarif scheduled-backup: if: github.event_name == 'schedule' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run database backup env: DATABASE_URL: ${{ secrets.DATABASE_URL }} BACKUP_DIR: ./backups run: | chmod +x scripts/backup-db.sh ./scripts/backup-db.sh - name: Verify backup integrity env: DATABASE_URL: ${{ secrets.DATABASE_URL }} BACKUP_DIR: ./backups run: | chmod +x scripts/backup-verify.sh ./scripts/backup-verify.sh - name: Sync backup to offsite storage env: DATABASE_URL: ${{ secrets.DATABASE_URL }} BACKUP_DIR: ./backups BACKUP_OFFSITE_BACKEND: ${{ secrets.BACKUP_OFFSITE_BACKEND }} BACKUP_OFFSITE_REMOTE: ${{ secrets.BACKUP_OFFSITE_REMOTE }} BACKUP_OFFSITE_BUCKET: ${{ secrets.BACKUP_OFFSITE_BUCKET }} BACKUP_OFFSITE_ACCESS_KEY: ${{ secrets.BACKUP_OFFSITE_ACCESS_KEY }} BACKUP_OFFSITE_SECRET_KEY: ${{ secrets.BACKUP_OFFSITE_SECRET_KEY }} BACKUP_OFFSITE_REGION: ${{ secrets.BACKUP_OFFSITE_REGION }} run: | chmod +x scripts/backup-offsite-sync.sh ./scripts/backup-offsite-sync.sh || echo "WARN: Offsite sync failed, continuing" - uses: actions/upload-artifact@v3 with: name: db-backup path: backups/ retention-days: 30 backup-verify: if: github.event_name == 'schedule' needs: scheduled-backup runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v3 with: name: db-backup path: backups/ - name: Verify backup integrity env: DATABASE_URL: ${{ secrets.DATABASE_URL }} BACKUP_DIR: ./backups run: | chmod +x scripts/backup-verify.sh ./scripts/backup-verify.sh - name: Run health check env: DATABASE_URL: ${{ secrets.DATABASE_URL }} BACKUP_DIR: ./backups HEALTH_CHECK_URL: ${{ secrets.HEALTH_CHECK_URL }} run: | chmod +x scripts/health-check.sh ./scripts/health-check.sh > health-report.json || true - uses: actions/upload-artifact@v3 if: always() with: name: backup-verify-report path: | backups/ health-report.json retention-days: 7 weekly-dr-drill: if: github.event_name == 'schedule' && github.run_attempt % 7 == 0 needs: backup-verify runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run disaster recovery drill env: DATABASE_URL: ${{ secrets.DATABASE_URL }} BACKUP_DIR: ./backups DR_DRILL_TEST_DB: next_edu_dr_drill run: | chmod +x scripts/dr-drill.sh ./scripts/dr-drill.sh || echo "WARN: DR drill failed, see report" - uses: actions/upload-artifact@v3 if: always() with: name: dr-drill-report path: docs/dr/reports/ retention-days: 90