name: Deploy to Production on: push: branches: [ main ] workflow_dispatch: env: DEPLOY_HOST: 172.17.0.1 # NOTE: Gitea와 운영서버가 같은 호스트에 있음 (hz-prod-01) # 구조: 공인 IP 178.104.200.7/quant → Nginx reverse proxy → localhost:5000 (quantengine) # 배포: .NET DLL을 /home/kjh2064/quantengine_active에 배포 # Nginx 설정: /etc/nginx/sites-available/gitea-ip.conf (이미 구성됨) DEPLOY_USER: kjh2064 DEPLOY_PATH: /home/kjh2064/quantengine_active SERVICE_NAME: quantengine 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 of current deployment..." ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF' set -e BACKUP_DIR="/home/kjh2064/quantengine_backup" BACKUP_NAME="quantengine_$(date +%Y%m%d_%H%M%S)" # Create backup without stopping the service (minimize downtime) mkdir -p $BACKUP_DIR if [ -d ${{ env.DEPLOY_PATH }} ]; then cp -r ${{ env.DEPLOY_PATH }} "$BACKUP_DIR/$BACKUP_NAME" echo "✅ Backup created: $BACKUP_DIR/$BACKUP_NAME" # Keep only last 5 backups BACKUP_COUNT=$(ls -1 $BACKUP_DIR | wc -l) if [ "$BACKUP_COUNT" -gt 5 ]; then OLD_BACKUPS=$(ls -1t $BACKUP_DIR | tail -n +6) for backup in $OLD_BACKUPS; do rm -rf "$BACKUP_DIR/$backup" done echo "🧹 Old backups cleaned" fi else echo "⚠️ No existing deployment found" 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 mkdir -p "$DEPLOY_PATH" # Extract new package tar -xzf "$ARCHIVE_NAME" -C "$DEPLOY_PATH" echo "✅ Package extracted to $DEPLOY_PATH" # Verify key files if [ -f "$DEPLOY_PATH/QuantEngine.Web.dll" ]; then echo "✅ QuantEngine.Web.dll verified" else echo "❌ QuantEngine.Web.dll not found!" exit 1 fi # Cleanup temp rm -rf /tmp/quant-deploy EOF - name: Restart Service run: | echo "🔄 Restarting quantengine service to apply changes (Downtime minimal)..." ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF' set -e # Restart service echo "⏹️ Restarting quantengine service..." sudo systemctl restart ${{ env.SERVICE_NAME }} sleep 3 # Check status if sudo systemctl is-active --quiet ${{ env.SERVICE_NAME }}; then echo "✅ ${{ env.SERVICE_NAME }} restarted successfully" sudo systemctl status ${{ env.SERVICE_NAME }} | head -5 else echo "❌ ${{ env.SERVICE_NAME }} failed to start" sudo systemctl status ${{ env.SERVICE_NAME }} exit 1 fi EOF - name: Health Check run: | echo "🧪 Running health checks..." # Wait for service to be ready (localhost:5000/quant/ through Nginx/Kestrel) for i in {1..30}; do HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ "http://127.0.0.1:5000/quant/" || echo "000") if [ "$HTTP_CODE" = "200" ]; then echo "✅ Health check passed (HTTP $HTTP_CODE at localhost:5000/quant/)" 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" echo "Service logs:" ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \ "sudo journalctl -u ${{ env.SERVICE_NAME }} -n 20" || true exit 1 fi - name: Verify Deployment run: | echo "📊 Verifying deployment..." # Check MudBlazor is loaded (via public IP) PUBLIC_IP="178.104.200.7" MUDBLAZOR_CHECK=$(curl -s "http://$PUBLIC_IP/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://$PUBLIC_IP/quant/" | grep -o "