dgb-wallet/ops/push.sh

101 lines
4.1 KiB
Bash

#!/usr/bin/env bash
# Push a build to the wallet VPS from the dev box. This is the routine
# deploy — deploy-wallet-web.sh is the one-time bootstrap.
#
# ops/push.sh web bundle + daemon sources, restart daemon
# ops/push.sh web web bundle only (no daemon restart)
# ops/push.sh daemon daemon sources only, restart daemon
#
# What it does:
# web — `npm run build -w @dgb-wallet/web-ui`, tar dist/ to the VPS,
# atomic swap into $SITE_ROOT/web/, verify the served bundle
# hash matches the local build, and warn if the inline script's
# CSP hash no longer matches the live Content-Security-Policy.
# daemon — ship packages/p2p/{src,bin} and packages/core/{src,dist}
# (the daemon runs from source via tsx and imports core's
# dist), chown to the service user, restart dgb-daemon, wait
# for /health.
#
# Requires an `ssh` host alias (default: silentmode) with root access.
set -euo pipefail
HOST="${DGB_VPS_HOST:-silentmode}"
REPO_DIR="${DGB_VPS_REPO:-/opt/dgb-wallet}"
SITE_ROOT="${DGB_VPS_SITE_ROOT:-/var/www/dgb-wallet}"
SNI_HOST="${DGB_SNI_HOST:-digibyte.x}"
WHAT="${1:-all}"
here="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$here"
log() { printf '\n\033[1;36m>>> %s\033[0m\n' "$*"; }
push_web() {
log "building web-ui"
# Plain production build → relative ./assets base + runtime <base> fix.
# Never DGB_UI_BASE=/web/ here (see deploy-wallet-web.sh).
MSYS_NO_PATHCONV=1 npm run build -w @dgb-wallet/web-ui >/dev/null
local js
js="$(ls packages/web-ui/dist/assets/index-*.js | head -1)"
local local_hash
local_hash="$(sha256sum "$js" | cut -c1-64)"
log "shipping bundle → $HOST:$SITE_ROOT/web/"
tar -cz -C packages/web-ui/dist . | ssh "$HOST" "set -e
mkdir -p '$SITE_ROOT/web.new'
tar -xz -C '$SITE_ROOT/web.new'
chown -R www-data:www-data '$SITE_ROOT/web.new'
rm -rf '$SITE_ROOT/web.old'
mv '$SITE_ROOT/web' '$SITE_ROOT/web.old'
mv '$SITE_ROOT/web.new' '$SITE_ROOT/web'
rm -rf '$SITE_ROOT/web.old'"
log "verifying served bundle"
local served
served="$(ssh "$HOST" "curl -sk --resolve $SNI_HOST:443:127.0.0.1 https://$SNI_HOST/web/ | grep -o 'index-[A-Za-z0-9_-]*\\.js' | head -1")"
local remote_hash
remote_hash="$(ssh "$HOST" "sha256sum '$SITE_ROOT/web/assets/$served' | cut -c1-64")"
if [[ "$remote_hash" != "$local_hash" ]]; then
echo "ERROR: served $served hash $remote_hash != local $local_hash" >&2
exit 1
fi
echo " served $served — hash matches"
# CSP hash drift check: the inline <base>-fix script in index.html is
# pinned by sha256 in the live nginx Content-Security-Policy.
local want live_csp
want="$(node packages/web-ui/scripts/csp-hash.mjs)"
live_csp="$(ssh "$HOST" "curl -sk -D - -o /dev/null --resolve $SNI_HOST:443:127.0.0.1 https://$SNI_HOST/web/ | tr -d '\\r' | grep -i '^content-security-policy' || true")"
if [[ -n "$live_csp" && "$live_csp" != *"$want"* ]]; then
echo "WARNING: live CSP does not contain the current inline-script hash ($want)." >&2
echo " Update ops/nginx-digibyte-x.conf + /etc/angie/http.d/digibyte-x.conf (Angie serves 443), or the wallet will not load." >&2
else
echo " CSP hash OK ($want)"
fi
}
push_daemon() {
log "typechecking p2p"
(cd packages/p2p && npx tsc --noEmit -p .)
log "shipping daemon sources → $HOST:$REPO_DIR"
tar -cz packages/p2p/src packages/p2p/bin packages/p2p/native/dgbpow.wasm packages/core/src packages/core/dist packages/core/package.json packages/p2p/package.json \
| ssh "$HOST" "set -e
tar -xz -C '$REPO_DIR'
chown -R dgb-wallet:dgb-wallet '$REPO_DIR/packages/p2p' '$REPO_DIR/packages/core'
systemctl restart dgb-daemon
for i in \$(seq 1 30); do
if curl -s -m 3 http://127.0.0.1:5174/health >/dev/null; then break; fi
sleep 2
done
systemctl is-active dgb-daemon
curl -s -m 5 http://127.0.0.1:5174/health | head -c 140; echo"
}
case "$WHAT" in
all) push_web; push_daemon ;;
web) push_web ;;
daemon) push_daemon ;;
*) echo "usage: ops/push.sh [all|web|daemon]" >&2; exit 1 ;;
esac
log "done"