setup-ec2-staging.sh
bash
sha256:3ff9c9863a9891bdcde71b4a43228f66d0493e38b7cc1d09fe9eb7de774046b2
feat: add repair-commit wire endpoint (API parity with repa…
Opus 4.8
minor
⚠ breaking
1 day ago
| 1 | #!/usr/bin/env bash |
| 2 | # Run this ON the staging EC2 instance (via SSH) after provisioning. |
| 3 | # Installs Docker and nginx, then configures the Cloudflare Origin Certificate. |
| 4 | # No Certbot or Let's Encrypt — Cloudflare terminates SSL at the edge. |
| 5 | # |
| 6 | # Prerequisites: |
| 7 | # 1. Generate a Cloudflare Origin Certificate for staging.musehub.ai: |
| 8 | # Cloudflare Dashboard → staging.musehub.ai → SSL/TLS → Origin Server |
| 9 | # → Create Certificate → RSA 2048 → 15 years |
| 10 | # Save certificate → /etc/ssl/cloudflare/origin.pem |
| 11 | # Save private key → /etc/ssl/cloudflare/origin.key |
| 12 | # |
| 13 | # 2. In Cloudflare: SSL/TLS → Overview → set mode to "Full (Strict)" |
| 14 | # |
| 15 | # Usage (from your Mac): |
| 16 | # scp -i ~/.ssh/musehub-key.pem -r deploy/ ubuntu@<STAGING_IP>:/home/ubuntu/ |
| 17 | # # Copy cert files: |
| 18 | # scp -i ~/.ssh/musehub-key.pem origin.pem origin.key ubuntu@<STAGING_IP>:/home/ubuntu/ |
| 19 | # ssh -i ~/.ssh/musehub-key.pem ubuntu@<STAGING_IP> |
| 20 | # chmod +x ~/deploy/setup-ec2-staging.sh && ~/deploy/setup-ec2-staging.sh |
| 21 | # |
| 22 | # After this script completes, sync the code and start the stack: |
| 23 | # rsync -az --exclude='.env' --exclude='node_modules/' --exclude='__pycache__/' \ |
| 24 | # --exclude='.muse/' --exclude='*.pyc' \ |
| 25 | # -e 'ssh -i ~/.ssh/musehub-key.pem' \ |
| 26 | # ~/musehub/ ubuntu@<STAGING_IP>:/opt/musehub/ |
| 27 | # ssh -i ~/.ssh/musehub-key.pem ubuntu@<STAGING_IP> \ |
| 28 | # 'cd /opt/musehub && sudo docker compose up -d --build' |
| 29 | |
| 30 | set -euo pipefail |
| 31 | |
| 32 | DOMAIN="staging.musehub.ai" |
| 33 | APP_DIR="/opt/musehub" |
| 34 | |
| 35 | echo "==> [1/7] System update" |
| 36 | sudo apt-get update -qq && sudo apt-get upgrade -y -qq |
| 37 | |
| 38 | echo "==> [2/7] Install Docker" |
| 39 | curl -fsSL https://get.docker.com | sudo sh |
| 40 | sudo usermod -aG docker ubuntu |
| 41 | echo " Docker installed." |
| 42 | |
| 43 | echo "==> [3/7] Install nginx" |
| 44 | sudo apt-get install -y -qq nginx |
| 45 | |
| 46 | echo "==> [4/7] Install Cloudflare Origin Certificate" |
| 47 | sudo mkdir -p /etc/ssl/cloudflare |
| 48 | |
| 49 | # Copy certs from home dir if present (scp'd in before running this script) |
| 50 | if [ -f ~/origin.pem ] && [ -f ~/origin.key ]; then |
| 51 | sudo cp ~/origin.pem /etc/ssl/cloudflare/origin.pem |
| 52 | sudo cp ~/origin.key /etc/ssl/cloudflare/origin.key |
| 53 | sudo chmod 644 /etc/ssl/cloudflare/origin.pem |
| 54 | sudo chmod 640 /etc/ssl/cloudflare/origin.key |
| 55 | sudo chown root:root /etc/ssl/cloudflare/origin.pem /etc/ssl/cloudflare/origin.key |
| 56 | echo " Origin certificate installed." |
| 57 | else |
| 58 | echo "" |
| 59 | echo " !! CERT FILES NOT FOUND — install them manually before nginx will start:" |
| 60 | echo " scp origin.pem origin.key ubuntu@<IP>:/home/ubuntu/" |
| 61 | echo " sudo cp ~/origin.pem /etc/ssl/cloudflare/origin.pem" |
| 62 | echo " sudo cp ~/origin.key /etc/ssl/cloudflare/origin.key" |
| 63 | echo " sudo chmod 640 /etc/ssl/cloudflare/origin.key" |
| 64 | echo " sudo nginx -s reload" |
| 65 | echo "" |
| 66 | fi |
| 67 | |
| 68 | echo "==> [5/7] Configure nginx" |
| 69 | |
| 70 | # Blue-green active-port file: deploy.sh rewrites this and does nginx -s reload |
| 71 | # to switch slots with zero downtime. Starts pointing at blue (1337). |
| 72 | echo "server 127.0.0.1:1337;" | sudo tee /etc/nginx/musehub-active-port > /dev/null |
| 73 | |
| 74 | # Install the Cloudflare-ready nginx config, substituting the domain |
| 75 | sudo sed "s/DOMAIN_PLACEHOLDER/$DOMAIN/g" \ |
| 76 | "$(dirname "$0")/nginx-cf.conf" \ |
| 77 | | sudo tee /etc/nginx/sites-available/musehub-staging > /dev/null |
| 78 | |
| 79 | sudo ln -sf /etc/nginx/sites-available/musehub-staging /etc/nginx/sites-enabled/musehub-staging |
| 80 | sudo rm -f /etc/nginx/sites-enabled/default |
| 81 | sudo nginx -t |
| 82 | sudo systemctl reload nginx |
| 83 | echo " nginx configured for $DOMAIN" |
| 84 | |
| 85 | echo "==> [6/7] Create app directory" |
| 86 | sudo mkdir -p "$APP_DIR" |
| 87 | sudo chown ubuntu:ubuntu "$APP_DIR" |
| 88 | echo " $APP_DIR ready (code will be rsynced from your Mac)" |
| 89 | |
| 90 | echo "==> [7/7] Create staging .env" |
| 91 | if [ ! -f "$APP_DIR/.env" ]; then |
| 92 | DB_PASSWORD=$(openssl rand -hex 16) |
| 93 | WEBHOOK_SECRET_KEY=$(python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" 2>/dev/null || openssl rand -base64 32) |
| 94 | RUNNER_TOKEN=$(openssl rand -hex 32) |
| 95 | |
| 96 | cat > "$APP_DIR/.env" << EOF |
| 97 | DEBUG=false |
| 98 | DATABASE_URL=postgresql+asyncpg://musehub:${DB_PASSWORD}@postgres:5432/musehub |
| 99 | DB_PASSWORD=${DB_PASSWORD} |
| 100 | CORS_ORIGINS=["https://staging.musehub.ai"] |
| 101 | WEBHOOK_SECRET_KEY=${WEBHOOK_SECRET_KEY} |
| 102 | MUSEHUB_ALLOWED_ORIGINS=["staging.musehub.ai"] |
| 103 | RUNNER_TOKEN=${RUNNER_TOKEN} |
| 104 | EOF |
| 105 | echo " .env created" |
| 106 | echo "" |
| 107 | echo " !! SAVE THESE — they are in $APP_DIR/.env !!" |
| 108 | cat "$APP_DIR/.env" |
| 109 | echo "" |
| 110 | else |
| 111 | echo " .env already exists, skipping" |
| 112 | fi |
| 113 | |
| 114 | echo "" |
| 115 | echo "============================================================" |
| 116 | echo " STAGING SETUP COMPLETE" |
| 117 | echo "============================================================" |
| 118 | echo " Domain : https://$DOMAIN" |
| 119 | echo " App dir : $APP_DIR" |
| 120 | echo " Secrets : $APP_DIR/.env (back these up!)" |
| 121 | echo "" |
| 122 | echo " Next: sync code from your Mac and start the stack:" |
| 123 | echo " rsync -az --exclude='.env' --exclude='node_modules/' --exclude='__pycache__/' \\" |
| 124 | echo " --exclude='.muse/' --exclude='*.pyc' \\" |
| 125 | echo " -e 'ssh -i ~/.ssh/musehub-key.pem' \\" |
| 126 | echo " ~/musehub/ ubuntu@\$(hostname -I | awk '{print \$1}'):/opt/musehub/" |
| 127 | echo "" |
| 128 | echo " cd /opt/musehub && sudo docker compose up -d postgres musehub-runner" |
| 129 | echo " sudo docker compose build musehub" |
| 130 | echo " sudo docker compose run --rm --no-deps musehub alembic upgrade head" |
| 131 | echo " sudo docker run -d --name musehub-blue --network musehub-internal \\" |
| 132 | echo " --env-file .env -e SKIP_MIGRATIONS=1 \\" |
| 133 | echo " -e DATABASE_URL=postgresql+asyncpg://musehub:\$(grep ^DB_PASSWORD .env | cut -d= -f2)@postgres:5432/musehub \\" |
| 134 | echo " -v musehub_data:/data -p 127.0.0.1:1337:1337 --restart unless-stopped musehub/musehub" |
| 135 | echo " bash deploy/deploy.sh --init # sets .active-slot=blue" |
| 136 | echo "" |
| 137 | echo " Subsequent deploys (zero downtime):" |
| 138 | echo " rsync ... && bash deploy/deploy.sh" |
| 139 | echo "============================================================" |
File History
1 commit
sha256:3ff9c9863a9891bdcde71b4a43228f66d0493e38b7cc1d09fe9eb7de774046b2
feat: add repair-commit wire endpoint (API parity with repa…
Opus 4.8
minor
⚠
1 day ago