Merge branch 'main' into claude/update-prod-runner-script-Lq2gS #16
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy | ||
| on: | ||
| push: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
| permissions: | ||
| contents: read | ||
| env: | ||
| BOT_TOKEN: ${{ secrets.BOT_TOKEN }} | ||
| AFFILIATE_SOURCE: GambleCodez | ||
| ADMIN_IDS: "6668510825" | ||
| DISCORD_CODE_GENERATION_IMAGE_URL: "https://github.com/gamblecodezcom/Runewager/blob/38c1bbab2d954238829852981514ce92c49d8b10/images/discord_code_generation.png" | ||
| DISCORD_VERIFY_IMAGE_URL: "https://github.com/gamblecodezcom/Runewager/blob/38c1bbab2d954238829852981514ce92c49d8b10/images/discord_verify.png" | ||
| MAX_ANALYTICS_EVENTS: "2000" | ||
| MAX_ONBOARDING_STEPS_HISTORY: "500" | ||
| MINI_APP_CLAIM_URL: "https://t.me/RuneWager_bot/claim" | ||
| MINI_APP_PLAY_URL: "https://t.me/RuneWager_bot/Play" | ||
| MINI_APP_PROFILE_URL: "https://t.me/RuneWager_bot/profile" | ||
| PORT: "3000" | ||
| PROMO_AMOUNT_SC: "3.5" | ||
| PROMO_BONUS_RULE: "Bonus added once your account is verified and meets eligibility requirements. Limit once per account/IP. New users are defined as accounts that have never redeemed a promo code before." | ||
| PROMO_CLAIM_LIMIT: "600" | ||
| PROMO_CODE: SPORTS3.5 | ||
| PROMO_ENTRY_IMAGE_URL: "https://github.com/gamblecodezcom/Runewager/blob/225428f813f2b3caf74f9716fff4b13ef0b74992/images/promo_entry.png" | ||
| RW_DISCORD_JOIN: "https://discord.gg/runewagers" | ||
| RW_DISCORD_LINK: "https://discord.com/channels/1100486422395355197/1249181934811349052" | ||
| RW_DISCORD_SUPPORT: "https://discord.com/channels/1100486422395355197/1249182067296567338" | ||
| DEPLOY_MODE: normal | ||
| jobs: | ||
| quality-gates: | ||
| name: Quality Gates | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| commit_hash: ${{ steps.meta.outputs.commit_hash }} | ||
| deploy_time: ${{ steps.meta.outputs.deploy_time }} | ||
| version: ${{ steps.meta.outputs.version }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '20' | ||
| cache: 'npm' | ||
| - name: Install deps | ||
| run: npm ci | ||
| - name: Build metadata | ||
| id: meta | ||
| run: | | ||
| echo "commit_hash=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" | ||
| echo "deploy_time=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >> "$GITHUB_OUTPUT" | ||
| echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT" | ||
| - name: Syntax check | ||
| run: npm run check | ||
| - name: Tests | ||
| run: npm test | ||
| - name: Security audit (non-blocking) | ||
| run: npm audit --audit-level=high || true | ||
| - name: Notify deploy start | ||
| env: | ||
| BOT_TOKEN: ${{ secrets.BOT_TOKEN }} | ||
| run: | | ||
| chmod +x scripts/notify-telegram.sh | ||
| ./scripts/notify-telegram.sh \ | ||
| "π *Deploy Starting* | ||
| Repo: \`${{ github.repository }}\` | ||
| Branch: \`${{ github.ref_name }}\` | ||
| Commit: \`${{ steps.meta.outputs.commit_hash }}\` | ||
| Version: \`${{ steps.meta.outputs.version }}\` | ||
| Mode: \`${DEPLOY_MODE}\` | ||
| By: \`${{ github.actor }}\`" | ||
| deploy: | ||
| name: Deploy to VPS | ||
| runs-on: ubuntu-latest | ||
| needs: quality-gates | ||
| environment: | ||
| name: production | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Setup SSH | ||
| env: | ||
| SERVER_KEY: ${{ secrets.SERVER_KEY }} | ||
| SERVER_HOST: ${{ secrets.SERVER_HOST }} | ||
| run: | | ||
| mkdir -p ~/.ssh | ||
| chmod 700 ~/.ssh | ||
| printf '%s\n' "$SERVER_KEY" > ~/.ssh/deploy_key | ||
| chmod 600 ~/.ssh/deploy_key | ||
| ssh-keyscan -H "$SERVER_HOST" >> ~/.ssh/known_hosts | ||
| cat >> ~/.ssh/config <<EOF | ||
| Host deploy_target | ||
| HostName $SERVER_HOST | ||
| User ${{ secrets.SERVER_USER }} | ||
| IdentityFile ~/.ssh/deploy_key | ||
| StrictHostKeyChecking yes | ||
| ConnectTimeout 15 | ||
| EOF | ||
| - name: Save pre-deploy SHA | ||
| id: pre | ||
| run: | | ||
| SHA=$(ssh deploy_target "cd /var/www/html/Runewager && git rev-parse HEAD || echo none") | ||
| echo "sha=$SHA" >> "$GITHUB_OUTPUT" | ||
| - name: Pre-deploy health snapshot | ||
| run: | | ||
| ssh deploy_target " | ||
| set -e | ||
| cd /var/www/html/Runewager | ||
| echo 'Checking .env exists...' | ||
| if [ ! -f .env ]; then | ||
| echo 'ERROR: .env missing on VPS' >&2 | ||
| exit 1 | ||
| fi | ||
| echo 'Checking port 3000 usage...' | ||
| lsof -i :3000 || true | ||
| echo 'Checking memory/disk...' | ||
| free -m || true | ||
| df -h . || true | ||
| " | ||
| - name: Deploy (git pull origin main only) | ||
| run: | | ||
| ssh deploy_target " | ||
| set -e | ||
| cd /var/www/html/Runewager | ||
| git fetch origin main | ||
| git reset --hard origin/main | ||
| echo 'Updated to:' \$(git rev-parse --short HEAD) | ||
| " | ||
| - name: Install deps on VPS | ||
| run: | | ||
| ssh deploy_target " | ||
| cd /var/www/html/Runewager | ||
| npm ci --omit=dev | ||
| " | ||
| - name: Validate Node + Telegram + mini-apps | ||
| env: | ||
| BOT_TOKEN: ${{ secrets.BOT_TOKEN }} | ||
| run: | | ||
| ssh deploy_target " | ||
| set -e | ||
| cd /var/www/html/Runewager | ||
| echo 'Checking Node version...' | ||
| NODE_VER=\$(node -v) | ||
| NODE_MAJOR=\$(echo \$NODE_VER | sed 's/v\([0-9]*\).*/\1/') | ||
| if [ \"\$NODE_MAJOR\" -lt 20 ]; then | ||
| echo 'ERROR: Node 20+ required, found' \$NODE_VER | ||
| exit 1 | ||
| fi | ||
| echo 'Checking Telegram API...' | ||
| curl -fsS --max-time 5 \"https://api.telegram.org/bot${{ secrets.BOT_TOKEN }}/getMe\" >/dev/null || { | ||
| echo 'ERROR: Telegram API not reachable' | ||
| exit 1 | ||
| } | ||
| echo 'Checking mini-app endpoints...' | ||
| curl -fsS --max-time 5 '${{ env.MINI_APP_PLAY_URL }}' >/dev/null || echo 'WARN: MINI_APP_PLAY_URL not responding' | ||
| curl -fsS --max-time 5 '${{ env.MINI_APP_CLAIM_URL }}' >/dev/null || echo 'WARN: MINI_APP_CLAIM_URL not responding' | ||
| curl -fsS --max-time 5 '${{ env.MINI_APP_PROFILE_URL }}' >/dev/null || echo 'WARN: MINI_APP_PROFILE_URL not responding' | ||
| " | ||
| - name: Wait 15 seconds | ||
| run: sleep 15 | ||
| - name: Restart service (systemd β prod-run.sh fallback) | ||
| id: restart | ||
| run: | | ||
| ssh deploy_target " | ||
| cd /var/www/html/Runewager | ||
| echo 'Trying systemd restart...' | ||
| if systemctl restart runewager.service 2>/dev/null; then | ||
| sleep 5 | ||
| if systemctl is-active runewager.service >/dev/null; then | ||
| echo 'Systemd restart OK' | ||
| exit 0 | ||
| fi | ||
| fi | ||
| echo 'Systemd failed β trying prod-run.sh restart...' | ||
| if bash prod-run.sh restart 2>/dev/null; then | ||
| echo 'prod-run.sh restart OK' | ||
| exit 0 | ||
| fi | ||
| echo 'Both restart methods failed' | ||
| exit 1 | ||
| " | ||
| - name: Health + status check | ||
| run: | | ||
| ssh deploy_target " | ||
| set -e | ||
| cd /var/www/html/Runewager | ||
| if [ -x ./health-check.sh ]; then | ||
| ./health-check.sh --status | ||
| else | ||
| curl -fsS http://127.0.0.1:3000/health >/dev/null | ||
| fi | ||
| " | ||
| - name: Notify success | ||
| if: success() | ||
| env: | ||
| BOT_TOKEN: ${{ secrets.BOT_TOKEN }} | ||
| run: | | ||
| chmod +x scripts/notify-telegram.sh | ||
| ./scripts/notify-telegram.sh \ | ||
| "β *Deploy Successful* | ||
| Commit: \`${{ needs.quality-gates.outputs.commit_hash }}\` | ||
| Version: \`${{ needs.quality-gates.outputs.version }}\` | ||
| Time: \`${{ needs.quality-gates.outputs.deploy_time }}\`" | ||
| - name: Rollback on failure | ||
| if: failure() | ||
| env: | ||
| BOT_TOKEN: ${{ secrets.BOT_TOKEN }} | ||
| run: | | ||
| PRE="${{ steps.pre.outputs.sha }}" | ||
| chmod +x scripts/notify-telegram.sh | ||
| ./scripts/notify-telegram.sh \ | ||
| "β *Deploy Failed β Rolling Back* | ||
| Previous SHA: \`${PRE}\`" | ||
| ssh deploy_target " | ||
| cd /var/www/html/Runewager | ||
| if [ \"$PRE\" != \"none\" ] && [ -n \"$PRE\" ]; then | ||
| git reset --hard $PRE | ||
| npm ci --omit=dev || true | ||
| systemctl restart runewager.service || bash prod-run.sh restart || true | ||
| else | ||
| echo 'No previous SHA recorded, skipping git rollback' | ||
| fi | ||
| " | ||
| ./scripts/notify-telegram.sh \ | ||
| "π *Rollback Complete* | ||
| Restored to: \`${PRE}\`" | ||