feat(deploy): add CROMERO dapp Phoenix deploy target #12

Merged
nsatoshi merged 1 commits from devin/1777157605-cromero-deploy-target into master 2026-04-26 00:11:50 +00:00
3 changed files with 224 additions and 0 deletions

View File

@@ -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 `<div id="root">` 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
```

View File

@@ -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": "<div id=\"root\">",
"attempts": 12,
"delay_ms": 5000,
"timeout_ms": 15000
}
},
{
"repo": "d-bis/proxmox",
"branch": "main",

View File

@@ -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}"