feat(deploy): add CROMERO dapp Phoenix deploy target #12
70
docs/03-deployment/CROMERO_DAPP_DEPLOYMENT.md
Normal file
70
docs/03-deployment/CROMERO_DAPP_DEPLOYMENT.md
Normal 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
|
||||
```
|
||||
@@ -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",
|
||||
|
||||
131
scripts/deployment/phoenix-deploy-cromero-from-workspace.sh
Executable file
131
scripts/deployment/phoenix-deploy-cromero-from-workspace.sh
Executable 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}"
|
||||
Reference in New Issue
Block a user