#!/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 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 -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"