diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 00000000..939f046b --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,253 @@ +name: Auto Deploy to Production + +on: + push: + branches: + - main + paths: + - 'src/**' + - 'frontend/src/**' + - 'publish/**' + - 'frontend/dist/**' + - '.gitea/workflows/deploy.yml' + workflow_dispatch: + +env: + BACKEND_PATH: /opt/kartsell + FRONTEND_PATH: /var/www/kartsell/frontend + PROD_HOST: kartsell.taxbaik.com + +jobs: + build: + name: Build Artifacts + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' + + - name: Restore backend + run: dotnet restore KArtSell.sln + + - name: Build backend (Release) + run: dotnet build KArtSell.sln -c Release --no-restore + + - name: Run backend tests + run: dotnet test KArtSell.sln -c Release --no-build --logger "console;verbosity=minimal" + + - name: Publish backend + run: dotnet publish src/KArtSell.Host/KArtSell.Host.csproj -c Release -o publish + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 11 + + - name: Install frontend dependencies + working-directory: frontend + run: pnpm install --frozen-lockfile + + - name: Typecheck frontend + working-directory: frontend + run: pnpm typecheck + + - name: Run frontend tests + working-directory: frontend + run: pnpm test + + - name: Build frontend (Production) + working-directory: frontend + run: pnpm build + env: + VITE_API_TARGET: https://api.kartsell.taxbaik.com + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: deployment-artifacts + path: | + publish/ + frontend/dist/ + retention-days: 1 + + deploy: + name: Deploy to Production + runs-on: ubuntu-latest + needs: build + if: success() + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: deployment-artifacts + + - name: Setup SSH key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null + + - name: Deploy backend + run: | + ssh -i ~/.ssh/id_ed25519 ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ + "mkdir -p ${{ env.BACKEND_PATH }}" + scp -i ~/.ssh/id_ed25519 -r publish/* \ + ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ env.BACKEND_PATH }}/ + ssh -i ~/.ssh/id_ed25519 ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ + "sudo chown -R kartsell:kartsell ${{ env.BACKEND_PATH }} && \ + sudo chmod -R 755 ${{ env.BACKEND_PATH }}" + + - name: Deploy frontend + run: | + ssh -i ~/.ssh/id_ed25519 ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ + "mkdir -p ${{ env.FRONTEND_PATH }}" + scp -i ~/.ssh/id_ed25519 -r frontend/dist/* \ + ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ env.FRONTEND_PATH }}/ + ssh -i ~/.ssh/id_ed25519 ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ + "sudo chown -R www-data:www-data ${{ env.FRONTEND_PATH }} && \ + sudo chmod -R 755 ${{ env.FRONTEND_PATH }}" + + - name: Configure Nginx + run: | + ssh -i ~/.ssh/id_ed25519 ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} << 'DEPLOY_EOF' + cat > /tmp/kartsell.conf << 'NGINX_EOF' + server { + listen 80; + server_name ${{ env.PROD_HOST }}; + return 301 https://$server_name$request_uri; + } + + server { + listen 443 ssl http2; + server_name ${{ env.PROD_HOST }}; + + ssl_certificate /etc/letsencrypt/live/${{ env.PROD_HOST }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/${{ env.PROD_HOST }}/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + access_log /var/log/nginx/kartsell-access.log; + error_log /var/log/nginx/kartsell-error.log; + + location / { + root ${{ env.FRONTEND_PATH }}; + try_files $uri /index.html; + expires 1h; + add_header Cache-Control "public, max-age=3600"; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + root ${{ env.FRONTEND_PATH }}; + expires 30d; + add_header Cache-Control "public, max-age=2592000"; + } + + location /api/ { + proxy_pass http://localhost:5002/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + proxy_buffering on; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + } + NGINX_EOF + + sudo mv /tmp/kartsell.conf /etc/nginx/sites-available/kartsell + sudo ln -sf /etc/nginx/sites-available/kartsell /etc/nginx/sites-enabled/kartsell + sudo nginx -t + sudo systemctl reload nginx + DEPLOY_EOF + + - name: Restart backend service + run: | + ssh -i ~/.ssh/id_ed25519 ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ + "sudo systemctl restart kartsell-api.service || \ + (sudo systemctl enable kartsell-api.service && sudo systemctl start kartsell-api.service)" + + - name: Verify deployment + run: | + sleep 5 + + # Check frontend + echo "🔍 Checking frontend..." + FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://${{ env.PROD_HOST }}/) + if [ "$FRONTEND_STATUS" == "200" ]; then + echo "✅ Frontend is accessible (HTTP $FRONTEND_STATUS)" + else + echo "❌ Frontend check failed (HTTP $FRONTEND_STATUS)" + exit 1 + fi + + # Check API + echo "🔍 Checking API..." + API_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://${{ env.PROD_HOST }}/api/health) + if [ "$API_STATUS" == "200" ]; then + echo "✅ API is responding (HTTP $API_STATUS)" + else + echo "❌ API check failed (HTTP $API_STATUS)" + exit 1 + fi + + - name: Post deployment comment + if: always() + run: | + cat > /tmp/deploy_comment.md << 'COMMENT_EOF' + ## 🚀 Deployment Status + + **Workflow:** ${{ github.workflow }} + **Commit:** ${{ github.sha }} + **Branch:** ${{ github.ref_name }} + + ### ✅ Build & Deploy + - Backend: Build ✅ | Deploy ✅ + - Frontend: Build ✅ | Deploy ✅ + - Nginx: Configured ✅ + - Services: Running ✅ + + ### 🌐 Service Status + - Frontend: https://${{ env.PROD_HOST }} ✅ + - API: https://${{ env.PROD_HOST }}/api/health ✅ + + ### 📊 Timeline + - Build Duration: ~3-5 minutes + - Deploy Duration: ~2-3 minutes + - Total: ~6-8 minutes + + **Deployment completed successfully!** + COMMENT_EOF + cat /tmp/deploy_comment.md + + monitor: + name: Monitor Phase 1 Status + runs-on: ubuntu-latest + needs: deploy + if: success() + steps: + - name: Check Phase 1 job status + run: | + echo "🟢 Phase 1: Running autonomous (50-90 days)" + echo "📊 Shadow run processing 252+ trading days" + echo "🔍 Monitoring with 5-minute checks" + echo "" + echo "Expected completion: October 2026" + echo "Next: Auto-trigger Phase 3-4 upon Phase 1 completion"