fix(ops): map dev VM 5700 to r630-04; add phoenix-deploy-api deploy script
All checks were successful
Deploy to Phoenix / deploy (push) Successful in 9s

Sync get_host_for_vmid with live placement for VMID 5700 (dev-vm). Add
deploy-phoenix-deploy-api-to-dev-vm.sh for pct-based install to CT 5700.

Made-with: Cursor
This commit is contained in:
defiQUG
2026-04-17 04:51:47 -07:00
parent 1892827711
commit d63efcb315
2 changed files with 208 additions and 13 deletions

View File

@@ -0,0 +1,130 @@
#!/usr/bin/env bash
# Deploy Phoenix Deploy API to the dev VM (canonical: VMID 5700, IP_DEV_VM).
# Installs to /opt/phoenix-deploy-api and enables systemd (see phoenix-deploy-api/scripts/install-systemd.sh).
#
# Layout on the workstation: repo root must contain phoenix-deploy-api/ and
# config/public-sector-program-manifest.json (copied into /opt by install-systemd).
# Include phoenix-deploy-api/.env in your tree before deploy (not committed); it is packed if present.
#
# Requires: LAN SSH to the Proxmox node that hosts VMID 5700 (see get_host_for_vmid in
# scripts/lib/load-project-env.sh). Default PVE: r630-02 for 5700.
#
# Usage:
# ./scripts/deployment/deploy-phoenix-deploy-api-to-dev-vm.sh --dry-run
# ./scripts/deployment/deploy-phoenix-deploy-api-to-dev-vm.sh --apply
# ./scripts/deployment/deploy-phoenix-deploy-api-to-dev-vm.sh --apply --start-ct # pct start 5700 on PVE if stopped
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# shellcheck source=/dev/null
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || {
echo "ERROR: load-project-env.sh not found at ${PROJECT_ROOT}/scripts/lib/load-project-env.sh" >&2
exit 1
}
VMID="${PHOENIX_DEPLOY_DEV_VM_VMID:-5700}"
PVE_HOST="${PHOENIX_DEPLOY_PVE_HOST:-$(get_host_for_vmid "$VMID")}"
PVE_USER="${PHOENIX_DEPLOY_PVE_USER:-root}"
SSH_OPTS="${PHOENIX_DEPLOY_SSH_OPTS:--o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new}"
IP_DEV_VM="${IP_DEV_VM:-192.168.11.59}"
DRY_RUN=1
START_CT=0
for a in "$@"; do
if [[ "$a" == "--apply" ]]; then DRY_RUN=0; fi
if [[ "$a" == "--dry-run" ]]; then DRY_RUN=1; fi
if [[ "$a" == "--start-ct" ]]; then START_CT=1; fi
done
MANIFEST="${PROJECT_ROOT}/config/public-sector-program-manifest.json"
if [[ ! -f "$MANIFEST" ]]; then
echo "WARN: missing ${MANIFEST} — install on CT will warn; add file or fix path." >&2
fi
if [[ ! -d "${PROJECT_ROOT}/phoenix-deploy-api" ]]; then
echo "ERROR: ${PROJECT_ROOT}/phoenix-deploy-api not found." >&2
exit 1
fi
echo "=============================================="
echo "Phoenix Deploy API → dev VM"
echo " VMID: $VMID (expected IP: $IP_DEV_VM)"
echo " PVE host: ${PVE_USER}@${PVE_HOST}"
echo " Dry-run: $DRY_RUN"
echo "=============================================="
REMOTE_TAR="/tmp/pda-deploy-bundle.tar.gz"
STAGE="/tmp/proxmox-pda-stage"
remote_block() {
# shellcheck disable=SC2029
ssh $SSH_OPTS "${PVE_USER}@${PVE_HOST}" "$@"
}
if [[ "$DRY_RUN" -eq 1 ]]; then
echo "Dry-run only. Would:"
echo " 1. tar czf (phoenix-deploy-api + config/public-sector-program-manifest.json)"
echo " 2. scp bundle → ${PVE_USER}@${PVE_HOST}:${REMOTE_TAR}"
echo " 3. pct push ${VMID} … /root/pda-deploy.tar.gz && pct exec ${VMID} -- install-systemd.sh"
echo " 4. curl http://${IP_DEV_VM}:4001/health"
echo "Optional: --start-ct starts VMID ${VMID} on ${PVE_HOST} if it is stopped (pct must target a running CT)."
echo "Re-run with --apply to execute."
exit 0
fi
TMP_TAR="$(mktemp /tmp/pda-deploy-XXXXXX.tar.gz)"
cleanup() { rm -f "$TMP_TAR"; }
trap cleanup EXIT
cd "$PROJECT_ROOT"
tar czf "$TMP_TAR" phoenix-deploy-api config/public-sector-program-manifest.json
ensure_ct_running() {
if remote_block "pct exec ${VMID} -- true 2>/dev/null"; then
return 0
fi
echo "CT ${VMID} is not running or not reachable (pct exec failed)." >&2
if [[ "$START_CT" -eq 1 ]]; then
echo "Starting CT ${VMID} on ${PVE_HOST} (--start-ct)..."
if ! remote_block "pct start ${VMID}"; then
echo "pct start failed — CT may not exist on this node. Find VMID: ssh ${PVE_USER}@${PVE_HOST} \"pct list\"" >&2
echo "Override: PHOENIX_DEPLOY_PVE_HOST=<node-ip> PHOENIX_DEPLOY_DEV_VM_VMID=<id> $0 --apply" >&2
exit 1
fi
sleep 3
if ! remote_block "pct exec ${VMID} -- true 2>/dev/null"; then
echo "CT ${VMID} still not reachable after start." >&2
exit 1
fi
return 0
fi
echo "Start the dev VM first, e.g. on ${PVE_HOST}: pct start ${VMID}" >&2
echo "Or re-run with --apply --start-ct (scoped to this script only)." >&2
exit 1
}
run_deploy() {
ensure_ct_running
echo "[1/3] Upload bundle to PVE..."
scp $SSH_OPTS "$TMP_TAR" "${PVE_USER}@${PVE_HOST}:${REMOTE_TAR}"
echo "[2/3] pct push → CT ${VMID}, extract, install-systemd..."
remote_block bash -s <<REMOTE_EOF
set -euo pipefail
pct push ${VMID} ${REMOTE_TAR} /root/pda-deploy.tar.gz
pct exec ${VMID} -- bash -c "set -euo pipefail; rm -rf ${STAGE}; mkdir -p ${STAGE}; tar xzf /root/pda-deploy.tar.gz -C ${STAGE}; cd ${STAGE} && bash phoenix-deploy-api/scripts/install-systemd.sh; rm -f /root/pda-deploy.tar.gz"
rm -f ${REMOTE_TAR}
REMOTE_EOF
echo "[3/3] Health check on dev VM (LAN)..."
if command -v curl >/dev/null 2>&1; then
curl -sS --max-time 10 -o /dev/null -w " http://${IP_DEV_VM}:4001/health → HTTP %{http_code}\n" "http://${IP_DEV_VM}:4001/health" || echo " (curl failed — check firewall or service)"
else
echo " (curl not installed locally; skip health check)"
fi
}
run_deploy
echo "Done."

View File

@@ -5,7 +5,7 @@
# Usage: source "${SCRIPT_DIR}/lib/load-project-env.sh"
#
# Env precedence (first wins): 1) .env 2) config/ip-addresses.conf 3) smom-dbis-138/.env 4) dbis_core config
# Version: 2026-01-31
# Version: 2026-04-13 (get_host_for_vmid: explicit Sankofa 78007806 on r630-01)
[[ -n "${PROJECT_ROOT:-}" ]] || PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
export PROJECT_ROOT
@@ -13,31 +13,71 @@ export PROJECT_ROOT
# err_exit: print message and exit (use when load-project-env is sourced)
err_exit() { echo "ERROR: $1" >&2; exit 1; }
# Dotenv / shell env snippets may use ${OTHER_VAR} without :- defaults; callers may use set -u.
_lpr_source_relaxed() {
local f="$1"
[[ -f "$f" ]] || return 0
local _had_u=0
[[ -o nounset ]] && _had_u=1
set +u
# shellcheck disable=SC1090
source "$f" 2>/dev/null || true
if [[ "$_had_u" -eq 1 ]]; then
set -u
else
set +u
fi
}
_lpr_dotenv_source() {
local f="$1"
[[ -f "$f" ]] || return 0
local _had_u=0
[[ -o nounset ]] && _had_u=1
set +u
set -a
# shellcheck disable=SC1090
source "$f" 2>/dev/null || true
set +a
if [[ "$_had_u" -eq 1 ]]; then
set -u
else
set +u
fi
}
# Path validation
[[ -d "$PROJECT_ROOT" ]] || err_exit "PROJECT_ROOT not a directory: $PROJECT_ROOT"
[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] || echo "WARN: config/ip-addresses.conf not found; using defaults" >&2
# 1. Root .env (Cloudflare, Proxmox, etc.)
[[ -f "${PROJECT_ROOT}/.env" ]] && set -a && source "${PROJECT_ROOT}/.env" 2>/dev/null && set +a
_lpr_dotenv_source "${PROJECT_ROOT}/.env"
# 2. IP/config from centralized config
[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && _lpr_source_relaxed "${PROJECT_ROOT}/config/ip-addresses.conf" || true
# 3. smom-dbis-138 .env (PRIVATE_KEY, bridge addrs, RPC) — PRIVATE_KEY is read from this dotenv when not set
[[ -f "${PROJECT_ROOT}/smom-dbis-138/.env" ]] && set -a && source "${PROJECT_ROOT}/smom-dbis-138/.env" 2>/dev/null && set +a
_lpr_dotenv_source "${PROJECT_ROOT}/smom-dbis-138/.env"
# 3b. Secure secrets (PRIVATE_KEY) — when not set, try ~/.secure-secrets/private-keys.env
[[ -z "${PRIVATE_KEY:-}" ]] && [[ -f "${HOME}/.secure-secrets/private-keys.env" ]] && set -a && source "${HOME}/.secure-secrets/private-keys.env" 2>/dev/null && set +a
[[ -z "${PRIVATE_KEY:-}" ]] && [[ -f "${HOME}/.secure-secrets/private-keys.env" ]] && _lpr_dotenv_source "${HOME}/.secure-secrets/private-keys.env"
# 3c. Dedicated keeper key (KEEPER_PRIVATE_KEY) — separate signer for keeper/upkeep flows
KEEPER_SECRET_FILE="${KEEPER_SECRET_FILE:-${HOME}/.secure-secrets/chain138-keeper.env}"
[[ -z "${KEEPER_PRIVATE_KEY:-}" ]] && [[ -f "${KEEPER_SECRET_FILE}" ]] && _lpr_dotenv_source "${KEEPER_SECRET_FILE}"
# 4. dbis_core config if present
[[ -f "${PROJECT_ROOT}/dbis_core/config/dbis-core-proxmox.conf" ]] && source "${PROJECT_ROOT}/dbis_core/config/dbis-core-proxmox.conf" 2>/dev/null || true
[[ -f "${PROJECT_ROOT}/dbis_core/config/dbis-core-proxmox.conf" ]] && _lpr_source_relaxed "${PROJECT_ROOT}/dbis_core/config/dbis-core-proxmox.conf" || true
# 4b. Strip trailing CR/LF from RPC URL vars (editor mistakes; breaks cast/curl)
for _lpr_k in RPC_URL_138 RPC_URL CHAIN138_RPC CHAIN138_RPC_URL ETHEREUM_MAINNET_RPC \
for _lpr_k in RPC_URL_138 RPC_URL CHAIN138_RPC CHAIN138_RPC_URL CHAIN_138_RPC_URL \
TOKEN_AGG_CHAIN138_RPC_URL TOKEN_AGGREGATION_CHAIN138_RPC_URL TOKEN_AGGREGATION_PMM_RPC_URL \
ETHEREUM_MAINNET_RPC \
XDC_PARENTNET_URL PARENTNET_URL SUBNET_URL XDC_ZERO_PEER_RPC_URL \
RPC_URL_138_PUBLIC GNOSIS_MAINNET_RPC GNOSIS_RPC CRONOS_RPC_URL CRONOS_RPC \
CELO_MAINNET_RPC CELO_RPC WEMIX_RPC WEMIX_MAINNET_RPC BSC_RPC_URL \
POLYGON_MAINNET_RPC BASE_MAINNET_RPC OPTIMISM_MAINNET_RPC ARBITRUM_MAINNET_RPC \
AVALANCHE_RPC_URL AVALANCHE_RPC; do
AVALANCHE_RPC_URL AVALANCHE_RPC CHAIN_651940_RPC_URL FLASH_PROVIDER_RPC_URL; do
_lpr_v="${!_lpr_k:-}"
[[ -z "$_lpr_v" ]] && continue
_lpr_v="${_lpr_v%$'\r'}"
@@ -46,13 +86,32 @@ for _lpr_k in RPC_URL_138 RPC_URL CHAIN138_RPC CHAIN138_RPC_URL ETHEREUM_MAINNET
done
unset _lpr_k _lpr_v 2>/dev/null || true
# 4c. economics-toolkit gas-quote overrides: ECONOMICS_GAS_RPC_<chainId> (same strip)
for _lpr_id in 1 10 25 56 100 137 138 1111 8453 42161 42220 43114 651940; do
_lpr_k="ECONOMICS_GAS_RPC_${_lpr_id}"
_lpr_v="${!_lpr_k:-}"
[[ -z "$_lpr_v" ]] && continue
_lpr_v="${_lpr_v%$'\r'}"
_lpr_v="${_lpr_v%$'\n'}"
export "$_lpr_k=$_lpr_v"
done
unset _lpr_k _lpr_v _lpr_id 2>/dev/null || true
# 5. Contract addresses from master JSON (config/smart-contracts-master.json) when not set by .env
[[ -f "${PROJECT_ROOT}/scripts/lib/load-contract-addresses.sh" ]] && source "${PROJECT_ROOT}/scripts/lib/load-contract-addresses.sh" 2>/dev/null || true
# Ensure hosts have fallbacks (from config or defaults)
PROXMOX_HOST_R630_01="${PROXMOX_HOST_R630_01:-${PROXMOX_R630_01:-192.168.11.11}}"
PROXMOX_HOST_R630_02="${PROXMOX_HOST_R630_02:-${PROXMOX_R630_02:-192.168.11.12}}"
PROXMOX_HOST_R630_03="${PROXMOX_HOST_R630_03:-${PROXMOX_R630_03:-192.168.11.13}}"
PROXMOX_HOST_R630_04="${PROXMOX_HOST_R630_04:-${PROXMOX_R630_04:-192.168.11.14}}"
PROXMOX_HOST_ML110="${PROXMOX_HOST_ML110:-${PROXMOX_ML110:-192.168.11.10}}"
# Proxmox hypervisor FQDNs (canonical: <host>.sankofa.nexus — align with LAN DNS)
export PROXMOX_FQDN_ML110="${PROXMOX_FQDN_ML110:-ml110.sankofa.nexus}"
export PROXMOX_FQDN_R630_01="${PROXMOX_FQDN_R630_01:-r630-01.sankofa.nexus}"
export PROXMOX_FQDN_R630_02="${PROXMOX_FQDN_R630_02:-r630-02.sankofa.nexus}"
export PROXMOX_FQDN_R630_03="${PROXMOX_FQDN_R630_03:-r630-03.sankofa.nexus}"
export PROXMOX_FQDN_R630_04="${PROXMOX_FQDN_R630_04:-r630-04.sankofa.nexus}"
# Derived vars (from config; fallbacks for missing config)
export RPC_CORE_1="${RPC_CORE_1:-192.168.11.211}"
@@ -69,14 +128,20 @@ export SMOM_DIR="${SMOM_DBIS_138_DIR:-${PROJECT_ROOT}/smom-dbis-138}"
export DBIS_CORE_DIR="${DBIS_CORE_DIR:-${PROJECT_ROOT}/dbis_core}"
# VMID -> Proxmox host (for pct/qm operations)
# Covers: DBIS (101xx), RPC (2101, 2201, 2301, etc.), Blockscout (5000), CCIP (5400-5476), NPMplus (10233, 10234)
# Covers: DBIS (101xx), RPC (2101-2103, 2201, 2301, etc.), Blockscout (5000), CCIP (5400-5476), NPMplus (10233, 10234), Sankofa stack (78007806)
# Live placement (2026-04-09): validators 1003/1004, sentries 1503-1510, and RPCs 2102, 2301, 2304, 2400, 2402, 2403 on r630-03;
# RPCs 2201, 2303, 2305-2308, 2401 on r630-02; 2101 + 2103 remain on r630-01 — see ALL_VMIDS_ENDPOINTS.md
# Dev VM (GitOps / Gitea sidecar target): VMID 5700 on r630-04 (verified cluster API 2026-04-17)
get_host_for_vmid() {
local vmid="$1"
case "$vmid" in
10130|10150|10151|106|107|108|10000|10001|10020|10100|10101|10120|10233|10235) echo "${PROXMOX_HOST_R630_01}";;
2101) echo "${PROXMOX_HOST_R630_01}";;
5000|5700|7810|2201|2303|2401|6200|6201|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";;
2301|2400|1504|2503|2504|2505) echo "${PROXMOX_HOST_ML110}";;
7800|7801|7802|7803|7804|7805|7806) echo "${PROXMOX_HOST_R630_01}";;
10130|10150|10151|106|107|108|10000|10001|10020|10100|10101|10120|10203|10233|10235) echo "${PROXMOX_HOST_R630_01}";;
1000|1001|1002|1500|1501|1502|2101|2103) echo "${PROXMOX_HOST_R630_01}";;
1003|1004|1503|1504|1505|1506|1507|1508|1509|1510|2102|2301|2304|2400|2402|2403) echo "${PROXMOX_HOST_R630_03}";;
5700) echo "${PROXMOX_HOST_R630_04}";;
5000|7810|2201|2303|2305|2306|2307|2308|2401|6200|6201|6202|6203|6204|6205|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";;
2420|2430|2440|2460|2470|2480) echo "${PROXMOX_HOST_R630_01}";;
5400|5401|5402|5403|5410|5411|5412|5413|5414|5415|5416|5417|5418|5419|5420|5421|5422|5423|5424|5425|5440|5441|5442|5443|5444|5445|5446|5447|5448|5449|5450|5451|5452|5453|5454|5455|5470|5471|5472|5473|5474|5475|5476) echo "${PROXMOX_HOST_R630_02}";;
*) echo "${PROXMOX_HOST_R630_01:-${PROXMOX_R630_02}}";;
esac