Monorepo for Aesthetic.Computer aesthetic.computer
at main 186 lines 5.6 kB view raw
1#!/usr/bin/env fish 2# lith Deployment Script 3# Deploys the AC monolith (frontend + API) to DigitalOcean droplet 4 5set RED '\033[0;31m' 6set GREEN '\033[0;32m' 7set YELLOW '\033[1;33m' 8set NC '\033[0m' 9 10set SCRIPT_DIR (dirname (status --current-filename)) 11set REPO_ROOT (realpath "$SCRIPT_DIR/..") 12set VAULT_DIR "$SCRIPT_DIR/../aesthetic-computer-vault" 13set SSH_KEY "$VAULT_DIR/home/.ssh/id_rsa" 14set SERVICE_ENV "$VAULT_DIR/lith/.env" 15set LITH_USER "root" 16set REMOTE_DIR "/opt/ac" 17set DEFAULT_LITH_HOST "lith.aesthetic.computer" 18set DEFAULT_LITH_DROPLET_NAME "ac-lith" 19set TARGET_HOST $DEFAULT_LITH_HOST 20set TARGET_DROPLET_NAME $DEFAULT_LITH_DROPLET_NAME 21set LOCAL_BRANCH (git -C $REPO_ROOT branch --show-current 2>/dev/null) 22set TARGET_BRANCH $LOCAL_BRANCH 23 24if set -q LITH_HOST 25 set TARGET_HOST $LITH_HOST 26end 27 28if set -q LITH_DROPLET_NAME 29 set TARGET_DROPLET_NAME $LITH_DROPLET_NAME 30end 31 32if set -q DEPLOY_BRANCH 33 set TARGET_BRANCH $DEPLOY_BRANCH 34end 35 36if test -z "$TARGET_BRANCH" 37 set TARGET_BRANCH main 38end 39 40function ssh_ok --argument host 41 ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o ConnectTimeout=10 $LITH_USER@$host "echo ok" &>/dev/null 42end 43 44function get_do_token 45 if set -q DIGITALOCEAN_ACCESS_TOKEN 46 echo $DIGITALOCEAN_ACCESS_TOKEN 47 return 0 48 end 49 50 if set -q DO_TOKEN 51 echo $DO_TOKEN 52 return 0 53 end 54 55 for token_file in \ 56 "$VAULT_DIR/help/deploy.env" \ 57 "$VAULT_DIR/judge/deploy.env" \ 58 "$VAULT_DIR/oven/deploy.env" \ 59 "$VAULT_DIR/at/deploy.env" 60 if not test -f $token_file 61 continue 62 end 63 64 set token_line (rg -m1 '^DO_TOKEN=' $token_file) 65 if test -n "$token_line" 66 string replace -r '^DO_TOKEN=' '' -- $token_line 67 return 0 68 end 69 end 70 71 return 1 72end 73 74function get_lith_host_from_do 75 if not command -sq doctl 76 return 1 77 end 78 79 set do_token (get_do_token) 80 if test -z "$do_token" 81 return 1 82 end 83 84 set row (env DIGITALOCEAN_ACCESS_TOKEN="$do_token" \ 85 doctl compute droplet list --format Name,PublicIPv4 --no-header 2>/dev/null | \ 86 rg "^$TARGET_DROPLET_NAME\\s") 87 88 if test -z "$row" 89 return 1 90 end 91 92 set compact_row (string replace -ra '\s+' ' ' -- (string trim -- $row)) 93 set fields (string split ' ' -- $compact_row) 94 95 if test (count $fields) -lt 2 96 return 1 97 end 98 99 echo $fields[2] 100end 101 102# Check for required files 103if not test -f $SSH_KEY 104 echo -e "$RED x SSH key not found: $SSH_KEY$NC" 105 exit 1 106end 107 108if not test -f $SERVICE_ENV 109 echo -e "$RED x Service env not found: $SERVICE_ENV$NC" 110 exit 1 111end 112 113if not rg -q '^DEPLOY_SECRET=' $SERVICE_ENV 114 echo -e "$RED x DEPLOY_SECRET missing from $SERVICE_ENV$NC" 115 echo -e "$YELLOW lith reads this file via /opt/ac/system/.env on the server.$NC" 116 exit 1 117end 118 119# Test SSH connection 120echo -e "$GREEN-> Testing SSH connection to $TARGET_HOST...$NC" 121if not ssh_ok $TARGET_HOST 122 set fallback_host (get_lith_host_from_do) 123 124 if test -n "$fallback_host"; and test "$fallback_host" != "$TARGET_HOST" 125 echo -e "$YELLOW Falling back to DigitalOcean droplet $TARGET_DROPLET_NAME at $fallback_host.$NC" 126 set TARGET_HOST $fallback_host 127 end 128 129 if not ssh_ok $TARGET_HOST 130 echo -e "$RED x Cannot connect to $TARGET_HOST$NC" 131 exit 1 132 end 133end 134 135echo -e "$GREEN-> Connected to $TARGET_HOST.$NC" 136 137# Deploy from pushed git state only. This avoids production drift from local rsync overlays. 138echo -e "$GREEN-> Verifying origin/$TARGET_BRANCH...$NC" 139git -C $REPO_ROOT fetch origin $TARGET_BRANCH --quiet 140set ORIGIN_HEAD (git -C $REPO_ROOT rev-parse origin/$TARGET_BRANCH) 141 142if test "$LOCAL_BRANCH" = "$TARGET_BRANCH" 143 set LOCAL_HEAD (git -C $REPO_ROOT rev-parse HEAD) 144 if test "$LOCAL_HEAD" != "$ORIGIN_HEAD" 145 echo -e "$RED x Local $TARGET_BRANCH is ahead of origin/$TARGET_BRANCH.$NC" 146 echo -e "$YELLOW Push first. This deploy script no longer rsyncs uncommitted or unpushed code into production.$NC" 147 exit 1 148 end 149end 150 151echo -e "$GREEN-> Deploying branch $TARGET_BRANCH at $ORIGIN_HEAD...$NC" 152ssh -i $SSH_KEY $LITH_USER@$TARGET_HOST "\ 153cd $REMOTE_DIR && \ 154git fetch origin $TARGET_BRANCH --quiet && \ 155if git show-ref --verify --quiet refs/heads/$TARGET_BRANCH; then \ 156 git checkout $TARGET_BRANCH --quiet; \ 157else \ 158 git checkout -B $TARGET_BRANCH origin/$TARGET_BRANCH --quiet; \ 159fi && \ 160git reset --hard origin/$TARGET_BRANCH --quiet && \ 161git rev-parse HEAD > system/public/.commit-ref" 162 163# Upload env 164echo -e "$GREEN-> Uploading environment...$NC" 165# Note: lith.service reads EnvironmentFile=/opt/ac/system/.env, so the 166# canonical vault source lives at aesthetic-computer-vault/lith/.env and is 167# uploaded into system/.env on the remote host. 168scp -i $SSH_KEY $SERVICE_ENV $LITH_USER@$TARGET_HOST:$REMOTE_DIR/system/.env 169 170# Install deps 171echo -e "$GREEN-> Installing dependencies...$NC" 172ssh -i $SSH_KEY $LITH_USER@$TARGET_HOST "cd $REMOTE_DIR/lith && npm install --omit=dev && cd $REMOTE_DIR/system && npm install --omit=dev" 173 174# Install service file + Caddy config from the deployed checkout 175echo -e "$GREEN-> Updating service + Caddy config...$NC" 176ssh -i $SSH_KEY $LITH_USER@$TARGET_HOST "\ 177cp $REMOTE_DIR/lith/lith.service /etc/systemd/system/lith.service && \ 178cp $REMOTE_DIR/lith/Caddyfile /etc/caddy/Caddyfile && \ 179systemctl daemon-reload && \ 180systemctl reload caddy" 181 182# Restart lith service 183echo -e "$GREEN-> Restarting lith...$NC" 184ssh -i $SSH_KEY $LITH_USER@$TARGET_HOST "systemctl restart lith" 185 186echo -e "$GREEN-> Done. lith deployed to $TARGET_HOST$NC"