name: Deploy to Production on: push: branches: [ main ] workflow_dispatch: env: DEPLOY_HOST: 192.168.123.100 # NOTE: Gitea와 운영서버가 같은 내부 네트워크(192.168.x.x)에 있으므로 내부 IP 사용 # 외부 접속은 공인 IP 178.104.200.7 사용 DEPLOY_USER: kjh2064 DEPLOY_PATH: /var/www/quant DOTNET_VERSION: '10.0.x' jobs: build-and-test: name: Build Release Package runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: "[GATE] Run Core Validations" run: | # CI 게이트: 핵심 검증 먼저 실행 echo "🔐 Running critical CI validations..." python3 tools/validate_no_direct_api_trading_v1.py || exit 1 python3 tools/validate_specs.py || exit 1 echo "✅ All critical validations passed" - name: Restore Dependencies run: dotnet restore src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj - name: Build Release run: | dotnet build src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj \ -c Release \ --no-restore \ -p:Version=1.0.${{ github.run_number }} - name: Run Unit Tests run: | if [ -d tests/unit ]; then dotnet test tests/unit \ -c Release \ --no-build \ --logger "trx;LogFileName=test-results.trx" \ || echo "⚠️ Some tests failed (non-blocking for web service)" fi - name: Publish Release Package run: | dotnet publish src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj \ -c Release \ --no-build \ -o ./publish-output echo "📦 Package size:" du -sh ./publish-output - name: Create Deployment Archive run: | cd publish-output tar -czf ../quant-engine-release-${{ github.run_number }}.tar.gz . cd .. ls -lh quant-engine-release-${{ github.run_number }}.tar.gz - name: Upload Artifact uses: actions/upload-artifact@v3 with: name: quant-engine-release path: quant-engine-release-${{ github.run_number }}.tar.gz retention-days: 30 deploy-to-prod: name: Deploy to Production Server needs: build-and-test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - name: Checkout Code uses: actions/checkout@v3 - name: Download Artifact uses: actions/download-artifact@v3 with: name: quant-engine-release - name: Setup SSH run: | mkdir -p ~/.ssh chmod 700 ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true - name: Create Backup run: | echo "📦 Creating backup on production server..." ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF' set -e BACKUP_DIR="/var/www/quant_backup" BACKUP_NAME="quant_backup_$(date +%Y%m%d_%H%M%S)" sudo mkdir -p $BACKUP_DIR if [ -d ${{ env.DEPLOY_PATH }}/publish ]; then sudo cp -r ${{ env.DEPLOY_PATH }}/publish "$BACKUP_DIR/$BACKUP_NAME" echo "✅ Backup created: $BACKUP_DIR/$BACKUP_NAME" # Keep only last 5 backups ls -t $BACKUP_DIR | tail -n +6 | xargs -I {} sudo rm -rf "$BACKUP_DIR/{}" echo "🧹 Old backups cleaned" else echo "⚠️ No existing deployment found, skipping backup" fi EOF - name: Deploy Package run: | echo "📤 Deploying package to production..." ARCHIVE_NAME=$(ls -1 quant-engine-release-*.tar.gz | head -1) # Create temporary directory on remote ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \ "mkdir -p /tmp/quant-deploy && chmod 777 /tmp/quant-deploy" # Transfer archive scp -i ~/.ssh/id_ed25519 "$ARCHIVE_NAME" \ ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/quant-deploy/ echo "✅ Package transferred" - name: Extract and Install run: | echo "📦 Extracting and installing..." ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF' set -e DEPLOY_PATH="${{ env.DEPLOY_PATH }}" ARCHIVE_NAME=$(ls -1 /tmp/quant-deploy/quant-engine-release-*.tar.gz | head -1) # Create deployment directory sudo mkdir -p "$DEPLOY_PATH/publish" sudo chmod 777 "$DEPLOY_PATH/publish" # Extract new package tar -xzf "$ARCHIVE_NAME" -C "$DEPLOY_PATH/publish" echo "✅ Package extracted" # Set permissions sudo chown -R www-data:www-data "$DEPLOY_PATH/publish" sudo chmod -R 755 "$DEPLOY_PATH/publish" echo "✅ Permissions set" # Cleanup temp rm -rf /tmp/quant-deploy EOF - name: Restart Services run: | echo "🔄 Restarting services..." ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF' set -e # Restart nginx sudo systemctl restart nginx sleep 2 # Check status if sudo systemctl is-active --quiet nginx; then echo "✅ nginx restarted successfully" else echo "❌ nginx failed to start" sudo systemctl status nginx exit 1 fi EOF - name: Health Check run: | echo "🧪 Running health checks..." # Wait for service to be ready for i in {1..30}; do HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ http://${{ env.DEPLOY_HOST }}/quant/ || echo "000") if [ "$HTTP_CODE" = "200" ]; then echo "✅ Health check passed (HTTP $HTTP_CODE)" break fi echo "⏳ Waiting for service... (attempt $i/30, HTTP $HTTP_CODE)" sleep 2 done if [ "$HTTP_CODE" != "200" ]; then echo "❌ Health check failed after 60 seconds" exit 1 fi - name: Verify Deployment run: | echo "📊 Verifying deployment..." # Check MudBlazor is loaded MUDBLAZOR_CHECK=$(curl -s http://${{ env.DEPLOY_HOST }}/quant/ | grep -c "MudBlazor" || echo "0") if [ "$MUDBLAZOR_CHECK" -gt "0" ]; then echo "✅ MudBlazor UI loaded successfully" else echo "⚠️ MudBlazor might not be loaded correctly" fi # Get page title PAGE_TITLE=$(curl -s http://${{ env.DEPLOY_HOST }}/quant/ | grep -o "