fix(env): safe dotenv sourcing under set -u; report API prefix fallback
- load-project-env: _lpr_dotenv_source / _lpr_source_relaxed so smom-dbis-138/.env
lines like ${ARBITRUM_MAINNET_RPC} do not abort scripts using set -u
- check-public-report-api: detect /token-aggregation vs apex /api/v1 for networks
- run-completable-tasks: enforce public report API (remove SKIP_EXIT bypass)
- Document verifier behavior in TOKEN_AGGREGATION_REPORT_API_RUNBOOK and verify README
Made-with: Cursor
This commit is contained in:
@@ -13,7 +13,7 @@ bash metamask-integration/chain138-snap/scripts/verify-snap-api-and-icons.sh htt
|
||||
|
||||
**Expected when working:** Token list and networks return valid JSON with `.tokens` and `.networks`.
|
||||
|
||||
**If you see "no .tokens" or "no .networks":** The `/api/v1/` path is likely proxied to Blockscout (or another backend) instead of token-aggregation. Proceed to §2.
|
||||
**If you see "no .tokens" or "no .networks":** The `/api/v1/` path is likely proxied to Blockscout (or another backend) instead of token-aggregation. Proceed to §2. **Repo check:** `scripts/verify/check-public-report-api.sh` tries apex `/api/v1/` first, then `/token-aggregation/api/v1/`, and uses whichever returns a `.networks` array.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -13,24 +13,57 @@ 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"
|
||||
|
||||
# 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 \
|
||||
|
||||
@@ -23,7 +23,7 @@ if $DRY_RUN; then
|
||||
echo " (optional: python3 -m pip install check-jsonschema — step 1 then validates config/dbis-institutional JSON Schemas too)"
|
||||
echo "2. On-chain check (138): SKIP_EXIT=1 bash scripts/verify/check-contracts-on-chain-138.sh || true"
|
||||
echo "3. All validation: bash scripts/verify/run-all-validation.sh --skip-genesis"
|
||||
echo "4. Public report API: SKIP_EXIT=1 bash scripts/verify/check-public-report-api.sh || true"
|
||||
echo "4. Public report API: bash scripts/verify/check-public-report-api.sh"
|
||||
echo "5. Reconcile .env: bash scripts/verify/reconcile-env-canonical.sh --print"
|
||||
echo ""
|
||||
echo "Run without --dry-run to execute. Exit 0 = success."
|
||||
@@ -50,7 +50,7 @@ echo ""
|
||||
|
||||
# 4. Emit canonical .env lines for reconciliation
|
||||
echo "[Step 4/5] Public report API / token-aggregation health..."
|
||||
SKIP_EXIT=1 bash scripts/verify/check-public-report-api.sh || true
|
||||
bash scripts/verify/check-public-report-api.sh
|
||||
echo ""
|
||||
|
||||
# 5. Emit canonical .env lines for reconciliation
|
||||
|
||||
@@ -29,7 +29,7 @@ One-line install (Debian/Ubuntu): `sudo apt install -y sshpass rsync dnsutils ip
|
||||
|
||||
- `backup-npmplus.sh` - Full NPMplus backup (database, API exports, certificates)
|
||||
- `check-contracts-on-chain-138.sh` - Check that Chain 138 deployed contracts have bytecode on-chain (`cast code` for 31 addresses; requires `cast` and RPC access). Use `[RPC_URL]` or env `RPC_URL_138`; `--dry-run` lists addresses only (no RPC calls); `SKIP_EXIT=1` to exit 0 when RPC unreachable.
|
||||
- `check-public-report-api.sh` - Verify that `explorer.d-bis.org/api/v1/report/*` and `/api/v1/networks` return token-aggregation JSON rather than Blockscout-style `/api/v1` responses. Use `SKIP_EXIT=1` for diagnostic-only mode. Set `SKIP_BRIDGE_ROUTES=0` to assert `/api/v1/bridge/routes`, and `SKIP_BRIDGE_PREFLIGHT=0` to assert `/api/v1/bridge/preflight` payload shape.
|
||||
- `check-public-report-api.sh` - Verify token-aggregation report + networks JSON (not Blockscout). Probes `/api/v1/networks` first, then `/token-aggregation/api/v1/networks`, and uses the working prefix for all checks. Use `SKIP_EXIT=1` for diagnostic-only mode. Set `SKIP_BRIDGE_ROUTES=0` / `SKIP_BRIDGE_PREFLIGHT=0` for bridge assertions.
|
||||
- `check-token-aggregation-chain138-api.sh` - Hits tokens, pools, quote, `bridge/routes`, `bridge/status`, `bridge/preflight`, and networks on both `/api/v1/*` and `/token-aggregation/api/v1/*`. `BASE_URL=https://explorer.d-bis.org` (default) or `http://192.168.11.140`.
|
||||
- `check-gru-transport-preflight.sh` - Operator-focused GRU runtime preflight. Calls `/api/v1/bridge/preflight`, prints blocked pairs with `eligibilityBlockers` / `runtimeMissingRequirements`, and fails unless all active pairs are runtime-ready or `ALLOW_BLOCKED=1` is set.
|
||||
- `check-cstar-v2-transport-stack.sh` - Predeploy Forge verifier for the `c* V2` bridge stack. Runs the base V2 token suite, legacy reserve-verifier compatibility suite, V2 reserve/verifier full L1/L2 round-trip suite, and the core `CWMultiTokenBridge` round-trip suite.
|
||||
|
||||
@@ -21,6 +21,26 @@ HAD_FAILURE=0
|
||||
log() { printf '%s\n' "$*"; }
|
||||
ok() { printf '[OK] %s\n' "$*"; }
|
||||
warn() { printf '[WARN] %s\n' "$*"; }
|
||||
|
||||
# Use apex /api/v1 first; if misrouted to Blockscout, try /token-aggregation/api/v1 (nginx layouts differ).
|
||||
detect_token_aggregation_prefix() {
|
||||
local path="/api/v1/networks"
|
||||
local prefix url response status body
|
||||
for prefix in "" "/token-aggregation"; do
|
||||
url="${BASE_URL%/}${prefix}${path}"
|
||||
response="$(curl -sSL --max-time 15 -w $'\n%{http_code}' "$url" 2>/dev/null)" || continue
|
||||
status="$(printf '%s' "$response" | tail -n 1)"
|
||||
body="$(printf '%s' "$response" | sed '$d')"
|
||||
if printf '%s' "$body" | jq -e 'type == "object" and has("message") and has("result") and has("status")' >/dev/null 2>&1; then
|
||||
continue
|
||||
fi
|
||||
if printf '%s' "$body" | jq -e 'type == "object" and (.networks | type == "array")' >/dev/null 2>&1; then
|
||||
printf '%s\n' "$prefix"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
printf '%s\n' ""
|
||||
}
|
||||
fail() {
|
||||
printf '[FAIL] %s\n' "$*"
|
||||
HAD_FAILURE=1
|
||||
@@ -70,29 +90,35 @@ check_json_shape() {
|
||||
|
||||
log "=== Public report API check ==="
|
||||
log "Base URL: $BASE_URL"
|
||||
TA_PREFIX="$(detect_token_aggregation_prefix)"
|
||||
if [[ -n "$TA_PREFIX" ]]; then
|
||||
log "Using token-aggregation path prefix: ${TA_PREFIX}/api/v1/"
|
||||
else
|
||||
log "Using token-aggregation path prefix: /api/v1/ (default; may still be Blockscout if both layouts fail shape test)"
|
||||
fi
|
||||
log ""
|
||||
|
||||
check_json_shape \
|
||||
"token-list" \
|
||||
"$BASE_URL/api/v1/report/token-list?chainId=138" \
|
||||
"${BASE_URL%/}${TA_PREFIX}/api/v1/report/token-list?chainId=138" \
|
||||
'type == "object" and (.tokens | type == "array")' \
|
||||
'object with .tokens[]'
|
||||
|
||||
check_json_shape \
|
||||
"coingecko report" \
|
||||
"$BASE_URL/api/v1/report/coingecko?chainId=138" \
|
||||
"${BASE_URL%/}${TA_PREFIX}/api/v1/report/coingecko?chainId=138" \
|
||||
'type == "object"' \
|
||||
'token-aggregation report JSON object'
|
||||
|
||||
check_json_shape \
|
||||
"cmc report" \
|
||||
"$BASE_URL/api/v1/report/cmc?chainId=138" \
|
||||
"${BASE_URL%/}${TA_PREFIX}/api/v1/report/cmc?chainId=138" \
|
||||
'type == "object"' \
|
||||
'token-aggregation report JSON object'
|
||||
|
||||
check_json_shape \
|
||||
"networks" \
|
||||
"$BASE_URL/api/v1/networks" \
|
||||
"${BASE_URL%/}${TA_PREFIX}/api/v1/networks" \
|
||||
'type == "object" and (.networks | type == "array")' \
|
||||
'object with .networks[]'
|
||||
|
||||
@@ -100,7 +126,7 @@ check_json_shape \
|
||||
if [[ "${SKIP_BRIDGE_ROUTES:-1}" != "1" ]]; then
|
||||
check_json_shape \
|
||||
"bridge-routes" \
|
||||
"$BASE_URL/api/v1/bridge/routes" \
|
||||
"${BASE_URL%/}${TA_PREFIX}/api/v1/bridge/routes" \
|
||||
'type == "object" and (.chain138Bridges | type == "object") and (.routes | type == "object")' \
|
||||
'object with .chain138Bridges and .routes'
|
||||
fi
|
||||
@@ -109,7 +135,7 @@ fi
|
||||
if [[ "${SKIP_BRIDGE_PREFLIGHT:-1}" != "1" ]]; then
|
||||
check_json_shape \
|
||||
"bridge-preflight" \
|
||||
"$BASE_URL/api/v1/bridge/preflight" \
|
||||
"${BASE_URL%/}${TA_PREFIX}/api/v1/bridge/preflight" \
|
||||
'type == "object" and (.gruTransport | type == "object") and (.gruTransport.summary.transportPairs | type == "number") and (.gruTransport.blockedPairs | type == "array")' \
|
||||
'object with .gruTransport.summary and .gruTransport.blockedPairs[]'
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user