Skip to main content

max / makenotwork

5.7 KB · 174 lines History Blame Raw
1 #!/bin/bash
2 # Multithreaded Deployment Script
3 # Builds natively on astra (aarch64), deploys to /opt/multithreaded.
4 # Run from the multithreaded project root.
5 #
6 # Usage:
7 # ./deploy/deploy.sh # Build + deploy + restart
8 # ./deploy/deploy.sh --setup # First-time: create user, dirs, db, build, install, seed
9
10 set -e
11
12 # Configuration
13 SERVER="max@100.106.221.39"
14 REMOTE_DIR="/opt/multithreaded"
15 SRC_DIR="src/multithreaded"
16 BINARY_NAME="multithreaded"
17 DEPLOY_DIR="deploy"
18
19 # Check we're in the right directory
20 if [ ! -f "Cargo.toml" ] || ! grep -q 'name = "multithreaded"' Cargo.toml; then
21 echo "Error: Run this script from the MNW/multithreaded/ directory"
22 exit 1
23 fi
24
25 rsync_source() {
26 echo "[rsync] Uploading source to $SERVER:~/$SRC_DIR/..."
27 ssh $SERVER "mkdir -p ~/$SRC_DIR"
28 # node_modules is reinstalled on the build host by build.rs (npm ci) and is
29 # large, platform-specific, and regenerable. static/dist is NOT excluded:
30 # it is emitted there by the same build script during cargo build.
31 rsync -az --delete \
32 --exclude target/ \
33 --exclude .env \
34 --exclude .git/ \
35 --exclude frontend/node_modules/ \
36 ./ $SERVER:~/$SRC_DIR/
37
38 # Sync the sibling crates the manifests point at (`../shared/...` resolves
39 # the same way from ~/src/multithreaded on the build host).
40 #
41 # Read the list out of the manifests rather than hardcoding it. The hardcoded
42 # version listed docengine and livechat, which mt no longer takes by path,
43 # and omitted pom-contract, which it does: the build failed on the build host
44 # for a dependency the deploy script had never heard of. A list derived from
45 # the manifests cannot drift from them.
46 local dep_paths dep_path dep
47 dep_paths="$(grep -ho 'path = "\.\./[^"]*"' Cargo.toml crates/*/Cargo.toml \
48 | sed 's/path = "//; s/"$//' | sort -u)"
49 for dep_path in $dep_paths; do
50 dep="${dep_path#../}"
51 if [ -d "$dep_path" ]; then
52 echo "[rsync] Syncing sibling crate: $dep"
53 ssh $SERVER "mkdir -p ~/src/$dep"
54 rsync -az --delete --exclude target --exclude .git \
55 "$dep_path/" "$SERVER:~/src/$dep/"
56 else
57 echo "[rsync] ERROR: manifest points at $dep_path, which does not exist" >&2
58 exit 1
59 fi
60 done
61 echo "[rsync] Done"
62 }
63
64 build_remote() {
65 echo "[build] Building release on astra..."
66 ssh $SERVER "cd ~/$SRC_DIR && ~/.cargo/bin/cargo build --release 2>&1"
67 echo "[build] Done"
68 }
69
70 # static/ is deliberately not copied: it is compiled into the binary
71 # (src/static_assets.rs), so the binary IS the assets. migrations/ is copied for
72 # operator convenience only — sqlx::migrate!() embeds them at compile time too.
73 deploy_files() {
74 echo "[deploy] Stopping service + copying binary to $REMOTE_DIR..."
75 ssh $SERVER "
76 sudo systemctl stop multithreaded || true
77 sudo cp ~/$SRC_DIR/target/release/$BINARY_NAME $REMOTE_DIR/$BINARY_NAME
78 sudo chmod +x $REMOTE_DIR/$BINARY_NAME
79 sudo rsync -a --delete ~/$SRC_DIR/migrations/ $REMOTE_DIR/migrations/
80 sudo chown -R multithreaded:multithreaded $REMOTE_DIR
81 "
82 echo "[deploy] Done"
83 }
84
85 restart_app() {
86 echo "[restart] Restarting multithreaded..."
87 ssh $SERVER "sudo systemctl restart multithreaded"
88 sleep 2
89 echo ""
90 ssh $SERVER "sudo systemctl status multithreaded --no-pager"
91 echo ""
92 echo "[health] Checking..."
93 ssh $SERVER "curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:3400/"
94 }
95
96 first_time_setup() {
97 echo "=== First-Time Setup ==="
98
99 echo "[setup] Creating system user..."
100 ssh $SERVER "
101 sudo useradd --system --shell /usr/sbin/nologin --home-dir $REMOTE_DIR multithreaded 2>/dev/null || echo 'User already exists'
102 "
103
104 echo "[setup] Creating directories..."
105 ssh $SERVER "
106 sudo mkdir -p $REMOTE_DIR
107 sudo chown multithreaded:multithreaded $REMOTE_DIR
108 "
109
110 echo "[setup] Creating database role + database..."
111 ssh $SERVER "
112 sudo -u postgres createuser multithreaded 2>/dev/null || echo 'Role already exists'
113 createdb multithreaded 2>/dev/null || echo 'Database already exists'
114 sudo -u postgres psql -c 'GRANT ALL PRIVILEGES ON DATABASE multithreaded TO multithreaded;' 2>/dev/null
115 sudo -u postgres psql -d multithreaded -c 'GRANT ALL ON SCHEMA public TO multithreaded;' 2>/dev/null
116 "
117
118 rsync_source
119 build_remote
120
121 echo "[setup] Installing files..."
122 deploy_files
123
124 echo "[setup] Installing env file..."
125 ssh $SERVER "
126 sudo cp ~/$SRC_DIR/$DEPLOY_DIR/env.production $REMOTE_DIR/.env
127 sudo chmod 600 $REMOTE_DIR/.env
128 sudo chown multithreaded:multithreaded $REMOTE_DIR/.env
129 "
130
131 echo "[setup] Installing systemd service..."
132 ssh $SERVER "
133 sudo cp ~/$SRC_DIR/$DEPLOY_DIR/multithreaded.service /etc/systemd/system/multithreaded.service
134 sudo systemctl daemon-reload
135 sudo systemctl enable multithreaded
136 "
137
138 echo "[setup] Starting service..."
139 ssh $SERVER "sudo systemctl start multithreaded"
140 sleep 2
141
142 echo "[setup] Running seed..."
143 ssh $SERVER "
144 cd $REMOTE_DIR
145 sudo -u multithreaded $REMOTE_DIR/$BINARY_NAME --seed
146 "
147
148 echo ""
149 ssh $SERVER "sudo systemctl status multithreaded --no-pager"
150 echo ""
151 echo "[health] Checking..."
152 ssh $SERVER "curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:3400/"
153 }
154
155 case "${1:-deploy}" in
156 --setup)
157 first_time_setup
158 ;;
159 deploy|"")
160 echo "=== Deploy ==="
161 rsync_source
162 build_remote
163 deploy_files
164 restart_app
165 ;;
166 *)
167 echo "Usage: $0 [--setup]"
168 exit 1
169 ;;
170 esac
171
172 echo ""
173 echo "=== Done ==="
174