deviant/scripts/deploy-deviant-navigate-st.sh
Local Dev f1acbbc7b2 brand: scaffold brand/ + ship deviant.navigate.st vhost deploy script
brand/ subdirectory:
- README.md — asset map, voice, mark rules
- palette.md — signal + editorial themes, typography (Fraunces / DM Sans / DM+JetBrains Mono), extracted from deviant_website.html :root vars
- icon/ — favicon.svg (64px), icon-512.svg (matches shells/app/public/icon.svg)
- logo/ — deviant-mark.svg (dark), deviant-mark-onlight.svg (inverted), deviant-wordmark.svg (mark + wordmark + tagline)
- social/ — og-1200x630.svg (Open Graph), twitter-card-1200x600.svg, avatar-400x400.svg (round-crop-safe)
- source/ — README on exporting to PNG via rsvg-convert / Inkscape / ImageMagick

scripts/deploy-deviant-navigate-st.sh:
- Idempotent 3-phase deploy: install :80 ACME block → issue LetsEncrypt cert via webroot → install :443 vhost. Clones the pattern used by checkers.x.navigate.st in the same nginx.conf.
- Edits /opt/checkers/nginx/nginx.conf IN PLACE (single-file bind-mount discipline per vps-access.md), takes a timestamped backup first, awk-injects each block only if absent.
- Verifies with curl + openssl s_client. Reads SSH_KEY/HOST env overrides.
2026-08-29 22:35:04 +02:00

145 lines
6.1 KiB
Bash

#!/usr/bin/env bash
# Add deviant.navigate.st as an ICANN mirror of the deviant.x wallet on the
# shared VPS (130.12.242.84). Idempotent: safe to re-run.
#
# Runs in three phases:
# Phase 1: patch nginx.conf with :80 ACME block + reload
# Phase 2: issue LetsEncrypt cert via webroot (--webroot -w /opt/checkers/nginx/html/deviant)
# Phase 3: patch nginx.conf with :443 vhost + reload + verify
#
# Prereqs:
# - DNS: deviant.navigate.st -> 130.12.242.84 (verified before running)
# - SSH key: ~/.ssh/checkers_deploy
#
# Pattern cloned from checkers.x.navigate.st in the same nginx.conf.
# Single-file bind mount: edits happen in place (cat > file), NOT sed -i / mv.
set -euo pipefail
SSH_KEY="${SSH_KEY:-$HOME/.ssh/checkers_deploy}"
HOST="${HOST:-root@130.12.242.84}"
NAME="deviant.navigate.st"
NGINX_CONF="/opt/checkers/nginx/nginx.conf"
WEBROOT="/opt/checkers/nginx/html/deviant"
CONTAINER="checkers-nginx-1"
log() { printf '\n\033[1;36m▸ %s\033[0m\n' "$*"; }
sshx() { ssh -i "$SSH_KEY" -o ConnectTimeout=10 "$HOST" "$@"; }
# ---------------------------------------------------------------------------
log "Phase 0 — DNS check"
getent hosts "$NAME" >/dev/null || { echo "ERROR: $NAME does not resolve"; exit 1; }
echo "$NAME resolves OK"
# ---------------------------------------------------------------------------
log "Phase 1 — install :80 ACME block + reload"
sshx "cp $NGINX_CONF ${NGINX_CONF}.bak.\$(date +%Y%m%d-%H%M%S)"
# If our block already present, skip.
if sshx "grep -q 'server_name deviant.navigate.st;' $NGINX_CONF"; then
echo "vhost blocks for $NAME already present, skipping Phase 1 insert"
else
# Insert the :80 block right after the existing testchipnet :80 block's closing brace.
# awk-based in-place: read current, emit new content, cat back over the file.
sshx "awk '
{ print }
/server_name testchipnet\\.duckdns\\.org;/ { in_test=1 }
in_test && /^ }$/ {
print \"\"
print \" # ── Deviant landing (deviant.navigate.st) — ICANN mirror of deviant.x ──\"
print \" server {\"
print \" listen 80;\"
print \" server_name deviant.navigate.st;\"
print \" location /.well-known/acme-challenge/ {\"
print \" root /usr/share/nginx/html/deviant;\"
print \" }\"
print \" location / { return 301 https://\\\$host\\\$request_uri; }\"
print \" }\"
in_test=0
}
' $NGINX_CONF > /tmp/nginx.conf.p1 && cat /tmp/nginx.conf.p1 > $NGINX_CONF && rm /tmp/nginx.conf.p1"
fi
log "nginx -t (phase 1)"
sshx "docker exec $CONTAINER nginx -t"
log "nginx -s reload (phase 1)"
sshx "docker exec $CONTAINER nginx -s reload"
# ---------------------------------------------------------------------------
log "Phase 2 — LetsEncrypt cert via webroot"
if sshx "[ -d /etc/letsencrypt/live/$NAME ]"; then
echo "cert already present, skipping issuance"
else
sshx "certbot certonly --non-interactive --agree-tos --register-unsafely-without-email --webroot -w $WEBROOT -d $NAME"
fi
# ---------------------------------------------------------------------------
log "Phase 3 — install :443 vhost + reload"
# If the :443 block for our name is already present, skip.
if sshx "awk '/server_name deviant.navigate.st;/ { c++ } END { exit (c>=2)?0:1 }' $NGINX_CONF"; then
echo ":443 block already present, skipping Phase 3 insert"
else
# Insert the :443 block right after the existing testchipnet :443 block's closing brace.
# The testchipnet :443 block's LAST line has "try_files \$uri \$uri/ /index.html; }" — its
# closing brace comes on the next indented line. We match the try_files line and the
# following " }" to know we're at that specific block's end.
sshx "awk '
{ buf[++n] = \$0 }
END {
done=0
for (i=1; i<=n; i++) {
print buf[i]
if (!done && buf[i] ~ /try_files .* \\/index\\.html; \\}\$/) {
# find the next line that is exactly \" }\" - that closes the testchipnet 443 block
j = i+1
while (j<=n && buf[j] !~ /^ \\}\$/) { print buf[j]; j++ }
if (j<=n) { print buf[j]; i = j }
print \"\"
print \" # -- Deviant landing (deviant.navigate.st) -- HTTPS mirror of deviant.x --\"
print \" server {\"
print \" listen 443 ssl;\"
print \" server_name deviant.navigate.st;\"
print \" ssl_certificate /etc/letsencrypt/live/deviant.navigate.st/fullchain.pem;\"
print \" ssl_certificate_key /etc/letsencrypt/live/deviant.navigate.st/privkey.pem;\"
print \" ssl_protocols TLSv1.2 TLSv1.3;\"
print \" ssl_ciphers HIGH:!aNULL:!MD5;\"
print \" ssl_session_cache shared:SSL:10m;\"
print \" root /usr/share/nginx/html/deviant;\"
print \" index index.html;\"
print \" location = /sw.js { add_header Cache-Control \\\"no-cache\\\"; }\"
print \" location = /index.html { add_header Cache-Control \\\"no-cache\\\"; }\"
print \" location = /manifest.webmanifest { default_type application/manifest+json; add_header Cache-Control \\\"no-cache\\\"; }\"
print \" location /assets/ { add_header Cache-Control \\\"public, max-age=31536000, immutable\\\"; }\"
print \" location / { add_header Cache-Control \\\"no-cache\\\"; try_files \\\$uri \\\$uri/ /index.html; }\"
print \" }\"
done=1
}
}
}
' $NGINX_CONF > /tmp/nginx.conf.p3 && cat /tmp/nginx.conf.p3 > $NGINX_CONF && rm /tmp/nginx.conf.p3"
fi
log "nginx -t (phase 3)"
sshx "docker exec $CONTAINER nginx -t"
log "nginx -s reload (phase 3)"
sshx "docker exec $CONTAINER nginx -s reload"
# ---------------------------------------------------------------------------
log "Verify"
sleep 2
echo "== HTTPS handshake =="
curl -sI "https://$NAME/" | head -6
echo
echo "== cert subject =="
echo | openssl s_client -connect "$NAME:443" -servername "$NAME" 2>/dev/null \
| openssl x509 -noout -subject -dates
echo
echo "== body =="
curl -s "https://$NAME/" | grep -oE '<title>[^<]+</title>' | head -1
log "Done. https://$NAME/ is live."