diff --git a/docs/03-deployment/CROMERO_DAPP_DEPLOYMENT.md b/docs/03-deployment/CROMERO_DAPP_DEPLOYMENT.md new file mode 100644 index 00000000..72e92189 --- /dev/null +++ b/docs/03-deployment/CROMERO_DAPP_DEPLOYMENT.md @@ -0,0 +1,70 @@ +# CROMERO Dapp — Phoenix Deploy + +Deploys [`d-bis/CROMERO`](https://gitea.d-bis.org/d-bis/CROMERO) (a +Vite + React + thirdweb v5 dapp) to +`https://d-bis.org/ecosystem/cromero/`. + +## Pipeline + +1. Push to `main` on `d-bis/CROMERO` triggers + `.gitea/workflows/deploy-to-phoenix.yml`, which POSTs + `{repo, sha, branch, target: "default"}` to the Phoenix Deploy API. +2. Phoenix runs the registered target (see + `phoenix-deploy-api/deploy-targets.json`): + `bash scripts/deployment/phoenix-deploy-cromero-from-workspace.sh`. +3. The script builds the staged workspace (`npm ci && npm run build`) + and rsyncs `dist/` to + `${IP_NPMPLUS:-192.168.11.167}:/var/www/ecosystem/cromero/`. +4. Healthcheck: `https://d-bis.org/ecosystem/cromero/` must return + HTTP 200 with `
` in the body. + +## One-time nginx setup on the NPMplus host + +The deploy script does **not** modify nginx config — install the +location block once, then redeploys land static files only. + +Add the following to whichever nginx server block terminates +`d-bis.org` (or the NPMplus advanced-config tab for the d-bis.org +proxy host): + +```nginx +location /ecosystem/cromero/ { + alias /var/www/ecosystem/cromero/; + try_files $uri $uri/ /ecosystem/cromero/index.html; +} +``` + +Then `nginx -t && nginx -s reload` (or restart the NPMplus +container). + +The Vite app already builds with `base: "/ecosystem/cromero/"` so +hashed asset URLs resolve under that subpath. + +## Required Actions secrets/vars on `d-bis/CROMERO` + +| Name | Type | Value | +| --- | --- | --- | +| `PHOENIX_DEPLOY_URL` | secret | `http://192.168.11.59:4001/api/deploy` | +| `PHOENIX_DEPLOY_TOKEN` | secret | matches `PHOENIX_DEPLOY_SECRET` on the Phoenix host | +| `VITE_THIRDWEB_CLIENT_ID` | secret | thirdweb publishable Client ID | +| `VITE_PROJECT_WALLET_ADDRESS` | var | recipient `0x…` address | +| `VITE_CHAIN_138_RPC` | var (optional) | overrides default `https://rpc.d-bis.org` | +| `VITE_CHAIN_138_EXPLORER` | var (optional) | overrides default `https://explorer.d-bis.org` | + +## Manual trigger from a LAN box with `phoenix-deploy-api` access + +```bash +curl -sSf -X POST "http://192.168.11.59:4001/api/deploy" \ + -H "Authorization: Bearer ${PHOENIX_DEPLOY_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"repo":"d-bis/CROMERO","branch":"main","target":"default"}' +``` + +## Dry run + +From this repo: + +```bash +PHOENIX_DEPLOY_WORKSPACE=/path/to/staged/CROMERO \ + bash scripts/deployment/phoenix-deploy-cromero-from-workspace.sh --dry-run +``` diff --git a/phoenix-deploy-api/deploy-targets.json b/phoenix-deploy-api/deploy-targets.json index 5f9ffa01..f776fae5 100644 --- a/phoenix-deploy-api/deploy-targets.json +++ b/phoenix-deploy-api/deploy-targets.json @@ -125,6 +125,29 @@ "timeout_ms": 15000 } }, + { + "repo": "d-bis/CROMERO", + "branch": "main", + "target": "default", + "description": "Deploy CROMERO dapp from the staged Gitea workspace: build dist/, rsync to NPMplus host /var/www/ecosystem/cromero/, served at https://d-bis.org/ecosystem/cromero/.", + "cwd": "${PHOENIX_REPO_ROOT}", + "command": [ + "bash", + "scripts/deployment/phoenix-deploy-cromero-from-workspace.sh" + ], + "required_env": [ + "PHOENIX_REPO_ROOT", + "PHOENIX_DEPLOY_WORKSPACE" + ], + "healthcheck": { + "url": "https://d-bis.org/ecosystem/cromero/", + "expect_status": 200, + "expect_body_includes": "
", + "attempts": 12, + "delay_ms": 5000, + "timeout_ms": 15000 + } + }, { "repo": "d-bis/proxmox", "branch": "main", diff --git a/scripts/deployment/phoenix-deploy-cromero-from-workspace.sh b/scripts/deployment/phoenix-deploy-cromero-from-workspace.sh new file mode 100755 index 00000000..80503790 --- /dev/null +++ b/scripts/deployment/phoenix-deploy-cromero-from-workspace.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Deploy d-bis/CROMERO (Vite + thirdweb v5 dapp) from the Phoenix-staged +# workspace to the NPMplus host's /var/www/ecosystem/cromero/, served by +# nginx under https://d-bis.org/ecosystem/cromero/. +# +# Phoenix Deploy API target: +# { repo: "d-bis/CROMERO", branch: "main", target: "default" } +# +# Required env (from Phoenix host): +# PHOENIX_DEPLOY_WORKSPACE Full staged CROMERO checkout +# +# Optional env (sane defaults from config/ip-addresses.conf): +# NPMPLUS_HOST Default: ${IP_NPMPLUS:-192.168.11.167} +# NPMPLUS_SSH_USER Default: root +# NPMPLUS_DEPLOY_ROOT Default: /var/www/ecosystem/cromero +# PUBLIC_URL Default: https://d-bis.org/ecosystem/cromero/ +# VITE_THIRDWEB_CLIENT_ID Build-time only; safe to ship. +# VITE_PROJECT_WALLET_ADDRESS +# VITE_CHAIN_138_RPC Default in app: https://rpc.d-bis.org +# VITE_CHAIN_138_EXPLORER Default in app: https://explorer.d-bis.org +# DRY_RUN=1 Print actions without executing them. + +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" +# shellcheck source=/dev/null +source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true + +PHOENIX_DEPLOY_WORKSPACE="${PHOENIX_DEPLOY_WORKSPACE:-}" +NPMPLUS_HOST="${NPMPLUS_HOST:-${IP_NPMPLUS:-192.168.11.167}}" +NPMPLUS_SSH_USER="${NPMPLUS_SSH_USER:-root}" +NPMPLUS_DEPLOY_ROOT="${NPMPLUS_DEPLOY_ROOT:-/var/www/ecosystem/cromero}" +PUBLIC_URL="${PUBLIC_URL:-https://d-bis.org/ecosystem/cromero/}" +DRY_RUN="${DRY_RUN:-0}" + +usage() { + cat <<'USAGE' +Usage: phoenix-deploy-cromero-from-workspace.sh [--dry-run] + +Builds the staged CROMERO workspace, rsyncs the dist/ output to the +NPMplus host's /var/www/ecosystem/cromero/, and verifies the public +URL. + +The nginx location block must be installed once on the host (out of +band) — see docs/03-deployment/CROMERO_DAPP_DEPLOYMENT.md. +USAGE +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY_RUN=1; shift ;; + -h|--help) usage; exit 0 ;; + *) echo "unknown arg: $1" >&2; usage; exit 2 ;; + esac +done + +log() { printf '[cromero-phoenix] %s\n' "$*" >&2; } +die() { printf '[cromero-phoenix][FATAL] %s\n' "$*" >&2; exit 1; } +run() { + if [[ "$DRY_RUN" -eq 1 ]]; then + printf '[dry-run] %s\n' "$*" >&2 + else + eval "$*" + fi +} +need_cmd() { command -v "$1" >/dev/null 2>&1 || die "missing required command: $1"; } + +for cmd in ssh rsync tar curl jq mktemp node npm; do + need_cmd "$cmd" +done + +[[ -n "$PHOENIX_DEPLOY_WORKSPACE" ]] || die "PHOENIX_DEPLOY_WORKSPACE is required" +[[ -d "$PHOENIX_DEPLOY_WORKSPACE" ]] || die "staged workspace missing: $PHOENIX_DEPLOY_WORKSPACE" + +SSH_TARGET="${NPMPLUS_SSH_USER}@${NPMPLUS_HOST}" +SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new) + +log "building CROMERO from staged workspace: ${PHOENIX_DEPLOY_WORKSPACE}" +pushd "$PHOENIX_DEPLOY_WORKSPACE" >/dev/null + +if [[ -f package-lock.json ]]; then + run "npm ci --no-audit --no-fund" +else + run "npm install --no-audit --no-fund" +fi + +# Build env. Empty values are fine — src/config.ts has defaults for +# Chain 138 RPC/explorer, and the dapp prints in-UI banners when the +# Client ID or wallet address are missing. +run "VITE_THIRDWEB_CLIENT_ID='${VITE_THIRDWEB_CLIENT_ID:-}' \ + VITE_PROJECT_WALLET_ADDRESS='${VITE_PROJECT_WALLET_ADDRESS:-}' \ + VITE_CHAIN_138_RPC='${VITE_CHAIN_138_RPC:-}' \ + VITE_CHAIN_138_EXPLORER='${VITE_CHAIN_138_EXPLORER:-}' \ + npm run build" + +[[ -f dist/index.html ]] || die "build produced no dist/index.html" + +popd >/dev/null + +log "rsync dist/ -> ${SSH_TARGET}:${NPMPLUS_DEPLOY_ROOT}/" +run "ssh ${SSH_OPTS[*]} '${SSH_TARGET}' \"mkdir -p '${NPMPLUS_DEPLOY_ROOT}'\"" +run "rsync -az --delete -e 'ssh ${SSH_OPTS[*]}' \ + '${PHOENIX_DEPLOY_WORKSPACE}/dist/' \ + '${SSH_TARGET}:${NPMPLUS_DEPLOY_ROOT}/'" + +# Reload nginx if a config file matching cromero exists. We do NOT +# write the location block from this script — it is installed once, +# out of band (see CROMERO_DAPP_DEPLOYMENT.md). Skip silently if +# nginx is not present (NPMplus may run nginx inside a container). +log "attempting nginx reload on ${NPMPLUS_HOST} (best-effort)" +run "ssh ${SSH_OPTS[*]} '${SSH_TARGET}' ' + if command -v nginx >/dev/null 2>&1; then + nginx -t && nginx -s reload || true + fi + if command -v docker >/dev/null 2>&1; then + docker exec npmplus nginx -s reload >/dev/null 2>&1 || true + fi +'" + +log "verifying ${PUBLIC_URL}" +HTTP_STATUS="$(curl -sS -o /dev/null -m 15 -w '%{http_code}' "$PUBLIC_URL" || echo 000)" +if [[ "$HTTP_STATUS" == "200" ]]; then + log "OK: ${PUBLIC_URL} returned 200" +else + log "WARN: ${PUBLIC_URL} returned ${HTTP_STATUS} (nginx location block may not yet be installed)" +fi + +log "CROMERO Phoenix deploy completed from ${PHOENIX_DEPLOY_WORKSPACE}"