Skip to main content

max / makenotwork

6.8 KB · 177 lines History Blame Raw
1 #!/bin/bash
2 # RETIRED 2026-07-31. Do not run this.
3 #
4 # This was the hand-deploy script for the MNW server before the Sando cutover.
5 # Deploys now go through Sando, the pipeline controller: it builds natively,
6 # runs the gate tiers, and swaps the release on the prod host. The operator
7 # procedure is in sando/deploy/README.md.
8 #
9 # Kept as a reference for what the old path did — the remote layout, the
10 # service restarts, the config files it pushed — not as a fallback. It
11 # cross-compiles (which is no longer allowed), it knows nothing about the gate
12 # tiers, and its assumptions about the prod host drift further every month. If
13 # Sando is broken, fix Sando.
14 #
15 # Original header follows.
16 #
17 # Makenotwork Deployment Script
18 # Cross-compiles for x86_64 Linux on macOS, uploads everything, restarts services.
19 # Run from the MNW/server directory.
20 #
21 # Usage:
22 # ./deploy/deploy.sh # Full deploy (build + upload + config + restart)
23 # ./deploy/deploy.sh --quick # Quick deploy (build + upload binary + restart app)
24 # ./deploy/deploy.sh --config # Config only (upload Caddyfile, systemd, error pages, backup script)
25 #
26 # Prerequisites (one-time):
27 # brew install zig
28 # cargo install cargo-zigbuild
29 # rustup target add x86_64-unknown-linux-gnu
30
31 set -e
32
33 # Configuration
34 SERVER="root@100.120.174.96"
35 SSH_PORT=2200
36 SSH_OPTS="-p $SSH_PORT"
37 SCP_OPTS="-P $SSH_PORT"
38 REMOTE_DIR="/opt/makenotwork"
39 BINARY_NAME="makenotwork"
40 TARGET="x86_64-unknown-linux-gnu"
41 DEPLOY_DIR="deploy"
42
43 # Check we're in the right directory
44 if [ ! -f "Cargo.toml" ]; then
45 echo "Error: Run this script from the MNW/server directory"
46 exit 1
47 fi
48
49 upload_config() {
50 echo "[config] Uploading configuration files..."
51 scp $SCP_OPTS $DEPLOY_DIR/Caddyfile $SERVER:/etc/caddy/Caddyfile
52 scp $SCP_OPTS $DEPLOY_DIR/makenotwork.service $SERVER:/etc/systemd/system/makenotwork.service
53 scp $SCP_OPTS $DEPLOY_DIR/backup-db.sh $SERVER:$REMOTE_DIR/backup-db.sh
54 ssh $SSH_OPTS $SERVER "chmod +x $REMOTE_DIR/backup-db.sh"
55
56 # Error pages
57 ssh $SSH_OPTS $SERVER "mkdir -p $REMOTE_DIR/error-pages"
58 scp $SCP_OPTS $DEPLOY_DIR/error-pages/*.html $SERVER:$REMOTE_DIR/error-pages/
59
60 # Git SSH and security config files
61 ssh $SSH_OPTS $SERVER "mkdir -p $REMOTE_DIR/deploy"
62 scp $SCP_OPTS $DEPLOY_DIR/sshd-git.conf $DEPLOY_DIR/fail2ban-sshd.conf $DEPLOY_DIR/setup-firewall.sh $SERVER:$REMOTE_DIR/deploy/
63 scp $SCP_OPTS $DEPLOY_DIR/setup-git-ssh.sh $DEPLOY_DIR/setup-ssh-keys.sh $SERVER:$REMOTE_DIR/deploy/ 2>/dev/null || true
64 # Scan-pipeline setup scripts (one-time, manually run on prod). See
65 # docs/scan-pipeline-audit.md § 8 for the rollout procedure.
66 scp $SCP_OPTS $DEPLOY_DIR/setup-clamav.sh $DEPLOY_DIR/setup-yara-rules.sh $SERVER:$REMOTE_DIR/deploy/
67 ssh $SSH_OPTS $SERVER "chmod +x $REMOTE_DIR/deploy/setup-firewall.sh $REMOTE_DIR/deploy/setup-git-ssh.sh $REMOTE_DIR/deploy/setup-ssh-keys.sh $REMOTE_DIR/deploy/setup-clamav.sh $REMOTE_DIR/deploy/setup-yara-rules.sh 2>/dev/null || true"
68
69 # Minify CSS for production (restore source on exit)
70 echo "[config] Minifying CSS..."
71 cp static/style.css static/style.css.src
72 restore_css() { [ -f static/style.css.src ] && mv static/style.css.src static/style.css; }
73 trap restore_css EXIT
74 npx --yes clean-css-cli -o static/style.css static/style.css.src
75 echo "[config] CSS: $(wc -c < static/style.css.src | tr -d ' ')B -> $(wc -c < static/style.css | tr -d ' ')B"
76
77 # Static assets (CSS, JS, fonts, images)
78 echo "[config] Uploading static assets..."
79 rsync -az --delete static/ $SERVER:$REMOTE_DIR/static/
80
81 # Restore unminified CSS
82 restore_css
83 trap - EXIT
84
85 # Documentation (public markdown files + UI examples)
86 echo "[config] Uploading documentation..."
87 rsync -az --delete site-docs/public/ $SERVER:$REMOTE_DIR/docs/public/
88 rsync -az --delete site-docs/examples/ $SERVER:$REMOTE_DIR/docs/examples/
89
90 # Business assumptions (source-of-truth for substituted figures in docs)
91 # Lives in the private docs store outside the repo (moved 2026-05-20).
92 rsync -az docs/business/assumptions.toml $SERVER:$REMOTE_DIR/docs/assumptions.toml
93
94 # Reload systemd and restart Caddy
95 ssh $SSH_OPTS $SERVER "systemctl daemon-reload && systemctl restart caddy"
96 echo "[config] Done"
97 }
98
99 build_binary() {
100 echo "[build] Cross-compiling for $TARGET..."
101 ulimit -n 65536 2>/dev/null || true
102 cargo zigbuild --release --target $TARGET
103 echo "[build] Done: target/$TARGET/release/$BINARY_NAME"
104 }
105
106 upload_binary() {
107 echo "[upload] Stopping service and uploading binary..."
108 ssh $SSH_OPTS $SERVER "systemctl stop makenotwork || true"
109 scp $SCP_OPTS target/$TARGET/release/$BINARY_NAME $SERVER:$REMOTE_DIR/$BINARY_NAME
110 ssh $SSH_OPTS $SERVER "chmod +x $REMOTE_DIR/$BINARY_NAME"
111 # Also upload mnw-admin binary (used for SSH key management)
112 if [ -f "target/$TARGET/release/mnw-admin" ]; then
113 scp $SCP_OPTS target/$TARGET/release/mnw-admin $SERVER:$REMOTE_DIR/mnw-admin
114 ssh $SSH_OPTS $SERVER "chmod +x $REMOTE_DIR/mnw-admin"
115 echo "[upload] mnw-admin binary uploaded"
116 fi
117 echo "[upload] Done"
118 }
119
120 send_restart_warning() {
121 echo "[warning] Sending 30s restart warning to users..."
122 local token
123 token=$(ssh $SSH_OPTS $SERVER "grep '^CLI_SERVICE_TOKEN=' $REMOTE_DIR/.env 2>/dev/null | cut -d= -f2-" | tr -d '\r\n')
124 if [ -z "$token" ]; then
125 echo "[warning] CLI_SERVICE_TOKEN not found in .env, skipping warning"
126 return 0
127 fi
128 local status
129 status=$(ssh $SSH_OPTS $SERVER "curl -s -o /dev/null -w '%{http_code}' -X POST http://127.0.0.1:3000/api/internal/restart-warning -H 'Authorization: Bearer $token' -H 'Content-Type: application/json' -d '{\"seconds\": 30}'")
130 if [ "$status" = "204" ]; then
131 echo "[warning] Restart warning sent, waiting 30s..."
132 sleep 30
133 else
134 echo "[warning] Warning request returned HTTP $status, continuing without delay"
135 fi
136 }
137
138 restart_app() {
139 echo "[restart] Restarting makenotwork..."
140 ssh $SSH_OPTS $SERVER "systemctl restart makenotwork"
141 sleep 1
142 echo ""
143 ssh $SSH_OPTS $SERVER "systemctl status makenotwork --no-pager"
144 echo ""
145 echo "[restart] Verifying app responds..."
146 ssh $SSH_OPTS $SERVER "curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:3000"
147 }
148
149 case "${1:-full}" in
150 --quick)
151 echo "=== Quick Deploy ==="
152 build_binary
153 send_restart_warning
154 upload_binary
155 restart_app
156 ;;
157 --config)
158 echo "=== Config Deploy ==="
159 upload_config
160 ;;
161 full|"")
162 echo "=== Full Deploy ==="
163 build_binary
164 upload_config
165 send_restart_warning
166 upload_binary
167 restart_app
168 ;;
169 *)
170 echo "Usage: $0 [--quick|--config]"
171 exit 1
172 ;;
173 esac
174
175 echo ""
176 echo "=== Deploy Complete ==="
177