Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a74ae80
feat: add rotating group tips system with admin controls
claude Feb 23, 2026
9d23185
feat: disable GitHub hosting, add VPS-only runtime guard, add deploy.sh
claude Feb 23, 2026
b7ec945
fix: replace git pull with fetch+reset --hard in /deploy command and …
claude Feb 23, 2026
dfab1f4
fix: deploy only on successful PR merges — prevent revert/accidental …
claude Feb 23, 2026
c4ded2c
chore: resolve merge conflicts with origin/main
claude Feb 23, 2026
ecbc0e7
feat: deploy source tags + per-step admin notifications + SSH retry
claude Feb 23, 2026
9461c2c
fix: guard git fetch/reset — restart service on git failure
claude Feb 23, 2026
a8f7b44
fix: remove production environment gate from CI — fix 1s failure
claude Feb 23, 2026
790e8a9
feat: username confirmation, affiliate reminders, 30 SC bonus info, a…
claude Feb 23, 2026
5210111
feat: age gate hardening, free-to-play messaging, Help sub-menu, full…
claude Feb 23, 2026
7fd2c29
Merge branch 'main' into claude/update-prod-runner-script-Lq2gS
gamblecodezcom Feb 23, 2026
9b66dc0
fix: address all 5 CodeAnt AI nitpicks from PR #66
claude Feb 23, 2026
5a81126
fix: deploy step-notifications and GitHub Actions pipeline
claude Feb 23, 2026
e6c4be2
ci: overhaul deploy job with inline commands, env approval gate, and …
claude Feb 23, 2026
4c7e3e1
feat: extend data model for upcoming feature upgrades (Phase 1)
claude Feb 23, 2026
24cb46b
feat: implement all 26 bot feature upgrades
claude Feb 23, 2026
85e7a01
fix: guard journalctl vacuum pipeline against non-fatal failure
claude Feb 23, 2026
9765a19
fix: harden disk-protect APP_DIR, .env parser, ERR trap, and YAML syntax
claude Feb 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 135 additions & 85 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,33 +151,33 @@ jobs:
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 }}\`"
MSG="🚀 *Deploy Starting*"$'\n'
MSG+="Repo: \`${{ github.repository }}\`"$'\n'
MSG+="Branch: \`${{ github.ref_name }}\`"$'\n'
MSG+="Commit: \`${{ steps.meta.outputs.commit_hash }}\`"$'\n'
MSG+="Version: \`${{ steps.meta.outputs.version }}\`"$'\n'
MSG+="Mode: \`${DEPLOY_MODE}\`"$'\n'
MSG+="By: \`${{ github.actor }}\`"
./scripts/notify-telegram.sh "$MSG"

# ---------------------------------------------------------------------------
# Job 2: Deploy to VPS — SSH into VPS and run deploy.sh
# Job 2: Deploy to VPS — pull latest main, restart service, health check
# Only triggers after quality gates pass on a valid PR merge to main.
# ---------------------------------------------------------------------------
deploy:
name: Deploy to VPS
runs-on: ubuntu-latest
needs: quality-gates
environment: Production
if: needs.quality-gates.result == 'success'
environment:
name: production

env:
DEPLOY_PASS: ${{ secrets.DEPLOY_PASS }}

steps:
- uses: actions/checkout@v4

- name: Install sshpass (password SSH fallback)
- name: Install sshpass (password fallback)
run: sudo apt-get install -y sshpass

- name: Setup SSH
Expand All @@ -187,9 +187,12 @@ By: \`${{ github.actor }}\`"
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
Expand All @@ -199,13 +202,28 @@ By: \`${{ github.actor }}\`"
ConnectTimeout 15
EOF

- name: Notify — deploy job started
env:
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
chmod +x scripts/notify-telegram.sh
MSG="🚀 *Deploy Job Started*"$'\n'
MSG+="Repo: \`${{ github.repository }}\`"$'\n'
MSG+="Branch: \`${{ github.ref_name }}\`"$'\n'
MSG+="Commit: \`${{ needs.quality-gates.outputs.commit_hash }}\`"$'\n'
MSG+="Version: \`${{ needs.quality-gates.outputs.version }}\`"$'\n'
MSG+="Actor: \`${{ github.actor }}\`"
./scripts/notify-telegram.sh "$MSG"

- 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
env:
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
ssh deploy_target "
set -e
Expand All @@ -223,49 +241,89 @@ By: \`${{ github.actor }}\`"
echo 'Checking memory/disk...'
free -m || true
df -h . || true

echo 'Checking disk free threshold...'
FREE_KB=\$(df . | awk 'NR==2 {print \$4}')
FREE_MB=\$((FREE_KB / 1024))
if [ \"\$FREE_MB\" -lt 100 ]; then
echo 'WARNING: Less than 100MB free disk space (' \$FREE_MB 'MB). Proceeding with caution.'
else
echo \"✅ Disk: \${FREE_MB}MB free\"
fi
"

- name: Deploy via deploy.sh (with password fallback)
chmod +x scripts/notify-telegram.sh
MSG="📋 *Pre-Deploy Health Snapshot*"$'\n'
MSG+="Path: \`/var/www/html/Runewager\`"$'\n'
MSG+="Commit (pre): \`${{ steps.pre.outputs.sha }}\`"
./scripts/notify-telegram.sh "$MSG"

- name: Deploy via deploy.sh (key first, then password fallback)
env:
SERVER_HOST: ${{ secrets.SERVER_HOST }}
SERVER_USER: ${{ secrets.SERVER_USER }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
DEPLOY_CMD="bash /var/www/html/Runewager/deploy.sh github"
chmod +x scripts/notify-telegram.sh

DEPLOY_CMD='
set -e
cd /var/www/html/Runewager

echo "Pulling latest main..."
git fetch origin main
git reset --hard origin/main

echo "Ensuring .env exists and is preserved..."
if [ ! -f .env ]; then
echo "ERROR: .env missing on VPS" >&2
exit 1
fi

echo "Stopping systemd service (if running)..."
sudo systemctl stop runewager.service || true

echo "Killing any stray Node/bot processes..."
pkill -f "Runewager" || true
pkill -f "index.js" || true
pkill -f "node" || true

echo "Installing production dependencies..."
npm ci --omit=dev

echo "Starting systemd service cleanly..."
sudo systemctl daemon-reload || true
sudo systemctl start runewager.service

echo "Checking systemd status..."
sudo systemctl is-active --quiet runewager.service
'

MSG="🔑 *Deploy Phase — SSH Key Attempt*"$'\n'"Host: \`${SERVER_HOST}\`"
./scripts/notify-telegram.sh "$MSG"

# First attempt: SSH with key
if ssh deploy_target "$DEPLOY_CMD"; then
echo "✅ Deployed via SSH key"
MSG="✅ *Deploy Succeeded via SSH Key*"$'\n'"Host: \`${SERVER_HOST}\`"
./scripts/notify-telegram.sh "$MSG"
else
echo "⚠️ SSH key attempt failed — waiting 120 seconds before password retry…"
echo "⚠️ SSH key failed — waiting 120 seconds before password retry..."
MSG="⚠️ *SSH Key Failed — Waiting 2 Minutes Before Password Fallback*"$'\n'"Host: \`${SERVER_HOST}\`"
./scripts/notify-telegram.sh "$MSG"
sleep 120

# Second attempt: password fallback via sshpass
if sshpass -p "$DEPLOY_PASS" ssh \
-o StrictHostKeyChecking=no \
-o ConnectTimeout=15 \
"${SERVER_USER}@${SERVER_HOST}" "$DEPLOY_CMD"; then
echo "✅ Deployed via password fallback"
MSG="✅ *Deploy Succeeded via Password Fallback*"$'\n'"Host: \`${SERVER_HOST}\`"
./scripts/notify-telegram.sh "$MSG"
else
echo "❌ Both SSH attempts failed — sending admin alert"
chmod +x scripts/notify-telegram.sh
./scripts/notify-telegram.sh "❌ Deploy failed — SSH could not connect to VPS after key + password retry. Manual intervention required."
echo "❌ Both SSH attempts failed"
MSG="❌ *Deploy Failed — SSH Key + Password Fallback Both Failed*"$'\n'
MSG+="Host: \`${SERVER_HOST}\`"$'\n'
MSG+="Action required: manual SSH + service check."
./scripts/notify-telegram.sh "$MSG"
exit 1
fi
fi

- name: Download deploy report from VPS
if: always()
run: |
ssh deploy_target "cat /tmp/deploy-report.txt 2>/dev/null || echo 'No deploy report file found on server.'" \
ssh deploy_target "cat /tmp/deploy-report.txt 2>/dev/null || echo 'No deploy report file found.'" \
> /tmp/deploy-report.txt || echo 'No deploy report available.' > /tmp/deploy-report.txt

- name: Upload deploy report as artifact
Expand All @@ -277,42 +335,56 @@ By: \`${{ github.actor }}\`"
if-no-files-found: ignore
retention-days: 7

- name: Notify success
- name: Post-deploy health check (30s delay)
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 }}\`"
./scripts/notify-telegram.sh "⏳ *Post-Deploy Health Check in 30s*"
sleep 30

HEALTH_STATUS="FAIL"
if ssh deploy_target "curl -fsS http://127.0.0.1:3000/health" >/dev/null 2>&1; then
HEALTH_STATUS="OK"
fi

MSG="📡 *Post-Deploy Health Check*"$'\n'
MSG+="Status: \`${HEALTH_STATUS}\`"$'\n'
MSG+="Commit: \`${{ needs.quality-gates.outputs.commit_hash }}\`"
./scripts/notify-telegram.sh "$MSG"

- 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}\`
Reason: Deployment failed — see GitHub Actions logs for full stack trace."

MSG="❌ *Deploy Failed — Starting Rollback*"$'\n'"Previous SHA: \`${PRE}\`"
./scripts/notify-telegram.sh "$MSG"

ssh deploy_target "
cd /var/www/html/Runewager
if [ \"$PRE\" != \"none\" ] && [ -n \"$PRE\" ]; then
git reset --hard $PRE
git reset --hard \"$PRE\"
npm ci --omit=dev || true
systemctl restart runewager.service || bash prod-run.sh restart || true

sudo systemctl stop runewager.service || true
pkill -f \"Runewager\" || true
pkill -f \"index.js\" || true
pkill -f \"node\" || true

sudo systemctl daemon-reload || true
sudo systemctl start runewager.service || true
else
echo 'No previous SHA recorded, skipping git rollback'
fi
"

./scripts/notify-telegram.sh "🔄 *Rollback Complete*
Restored to: \`${PRE}\`"
MSG="🔄 *Rollback Complete*"$'\n'"Restored to: \`${PRE}\`"
./scripts/notify-telegram.sh "$MSG"

- name: Final deploy report
if: always()
Expand All @@ -323,50 +395,28 @@ Restored to: \`${PRE}\`"
COMMIT="${{ needs.quality-gates.outputs.commit_hash }}"
VERSION="${{ needs.quality-gates.outputs.version }}"
TIME="${{ needs.quality-gates.outputs.deploy_time }}"
DURATION="${{ needs.quality-gates.outputs.duration_seconds }}"

REPORT_BODY=$(cat /tmp/deploy-report.txt 2>/dev/null || echo "No deploy report available.")

if [ "$STATUS" = "success" ]; then
MSG="🎉 *Final Deploy Report*
Status: ✅ Success
Commit: \`$COMMIT\`
Version: \`$VERSION\`
Time: \`$TIME\`
Duration: \`${DURATION}s\`

$REPORT_BODY"
MSG="🎉 *Final Deploy Report*"$'\n'
MSG+="Status: ✅ Success"$'\n'
MSG+="Commit: \`$COMMIT\`"$'\n'
MSG+="Version: \`$VERSION\`"$'\n'
MSG+="Time: \`$TIME\`"$'\n\n'
MSG+="$REPORT_BODY"
else
MSG="⚠️ *Final Deploy Report*
Status: ❌ Failure
Commit: \`$COMMIT\`
Version: \`$VERSION\`
Time: \`$TIME\`
Duration: \`${DURATION}s\`
Reason: Deployment failed — see GitHub Actions logs.

$REPORT_BODY"
MSG="⚠️ *Final Deploy Report*"$'\n'
MSG+="Status: ❌ Failure"$'\n'
MSG+="Commit: \`$COMMIT\`"$'\n'
MSG+="Version: \`$VERSION\`"$'\n'
MSG+="Time: \`$TIME\`"$'\n\n'
MSG+="$REPORT_BODY"
fi

chmod +x scripts/notify-telegram.sh
./scripts/notify-telegram.sh "$MSG"

- name: Post-deploy health check (30s delay)
if: success()
env:
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
echo "Waiting 30 seconds before post-deploy health check..."
sleep 30
HEALTH_STATUS="FAIL"
if ssh deploy_target "curl -fsS http://127.0.0.1:3000/health" >/dev/null 2>&1; then
HEALTH_STATUS="OK"
fi
chmod +x scripts/notify-telegram.sh
./scripts/notify-telegram.sh "📡 *Post-Deploy Health Check (30s)*
Status: \`${HEALTH_STATUS}\`
Commit: \`${{ needs.quality-gates.outputs.commit_hash }}\`"

# ---------------------------------------------------------------------------
# Job 3: Dry-run report (only when dry_run=true)
# ---------------------------------------------------------------------------
Expand All @@ -384,12 +434,12 @@ Commit: \`${{ needs.quality-gates.outputs.commit_hash }}\`"
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
chmod +x scripts/notify-telegram.sh
./scripts/notify-telegram.sh "🧪 *Dry Run Only — No VPS Changes*
Repo: \`${{ github.repository }}\`
Branch: \`${{ github.ref_name }}\`
Commit: \`${{ needs.quality-gates.outputs.commit_hash }}\`
Version: \`${{ needs.quality-gates.outputs.version }}\`
Time: \`${{ needs.quality-gates.outputs.deploy_time }}\`
By: \`${{ github.actor }}\`

Quality gates passed. VPS was NOT modified."
MSG="🧪 *Dry Run Only — No VPS Changes*"$'\n'
MSG+="Repo: \`${{ github.repository }}\`"$'\n'
MSG+="Branch: \`${{ github.ref_name }}\`"$'\n'
MSG+="Commit: \`${{ needs.quality-gates.outputs.commit_hash }}\`"$'\n'
MSG+="Version: \`${{ needs.quality-gates.outputs.version }}\`"$'\n'
MSG+="Time: \`${{ needs.quality-gates.outputs.deploy_time }}\`"$'\n'
MSG+="By: \`${{ github.actor }}\`"$'\n\n'
MSG+="Quality gates passed. VPS was NOT modified."
./scripts/notify-telegram.sh "$MSG"
Loading
Loading