#!/usr/bin/env bash set -euo pipefail ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P) FAILURES=0 fail() { printf 'FAIL: %s\n' "$1" >&2; FAILURES=$((FAILURES + 1)); } have() { local name=$1 link=$2 if ! command -v "$name" >/dev/null 2>&1; then fail "$name is missing — install from $link" return 1 fi } exact_version() { local name=$1 expected=$2 actual=$3 link=$4 if [[ "$actual" == "$expected" ]]; then printf 'ok: %s %s\n' "$name" "$actual" else fail "$name must be $expected (found $actual) — install from $link"; fi } port_free() { local port=$1 if (exec 9<>"/dev/tcp/127.0.0.1/$port") 2>/dev/null; then fail "127.0.0.1:$port is occupied; stop that external service before starting the demo" else printf 'ok: 127.0.0.1:%s is available\n' "$port" fi } printf '%s\n' 'Checking the pinned V1 demo environment (read-only).' if have git https://git-scm.com/downloads; then printf 'ok: %s\n' "$(git --version)"; fi if have forge https://getfoundry.sh; then exact_version forge 1.7.1 "$(forge --version | sed -nE 's/^forge Version: ([^ ]+).*/\1/p')" https://getfoundry.sh; fi if have anvil https://getfoundry.sh; then exact_version anvil 1.7.1 "$(anvil --version | sed -nE 's/^anvil Version: ([^ ]+).*/\1/p')" https://getfoundry.sh; fi if have cast https://getfoundry.sh; then exact_version cast 1.7.1 "$(cast --version | sed -nE 's/^cast Version: ([^ ]+).*/\1/p')" https://getfoundry.sh; fi if have node https://nodejs.org/en/download; then exact_version Node 24.18.0 "$(node --version | sed 's/^v//')" https://nodejs.org/en/download; fi if have npm https://docs.npmjs.com/downloading-and-installing-node-js-and-npm; then exact_version npm 11.17.0 "$(npm --version)" https://docs.npmjs.com/downloading-and-installing-node-js-and-npm; fi if have bash https://www.gnu.org/software/bash/; then printf 'ok: %s\n' "$(bash --version | sed -n '1p')"; fi if have make https://www.gnu.org/software/make/; then printf 'ok: %s\n' "$(make --version | sed -n '1p')"; fi if have curl https://curl.se/download.html; then printf 'ok: %s\n' "$(curl --version | sed -n '1p')"; fi if git -C "$ROOT" submodule status --recursive | while IFS= read -r line; do [[ "$line" == ' '* ]] || exit 1; done; then printf '%s\n' 'ok: recursive Git submodules are initialized at recorded commits' else fail 'recursive Git submodules are missing or differ from recorded commits — run make setup' fi if [[ -d "$ROOT/node_modules" ]]; then printf '%s\n' 'ok: root node_modules is installed'; else fail 'root node_modules is missing — run make setup'; fi if [[ -d "$ROOT/web/node_modules" ]]; then printf '%s\n' 'ok: web node_modules is installed'; else fail 'web node_modules is missing — run make setup'; fi if command -v node >/dev/null 2>&1 && [[ -d "$ROOT/node_modules" ]]; then node "$ROOT/tools/check-upgrades-cli.mjs" || fail 'the pinned offline OpenZeppelin upgrades CLI is unavailable — run make setup' fi for path in "$ROOT" "$ROOT/deployments" "$ROOT/web/public" "$ROOT/web/src/generated"; do if [[ -d "$path" && -w "$path" ]]; then printf 'ok: writable runtime directory %s\n' "${path#"$ROOT/"}"; else fail "runtime directory is not writable: $path"; fi done if [[ -d "$ROOT/.demo" ]]; then [[ -w "$ROOT/.demo" ]] || fail "$ROOT/.demo is not writable"; fi port_free 8545 port_free 5173 for variable in BASE_SEPOLIA_RPC_URL BASE_SEPOLIA_ACCOUNT; do if [[ -n ${!variable:-} ]]; then printf 'optional: %s is configured\n' "$variable"; else printf 'optional: %s is not configured (local demo unaffected)\n' "$variable"; fi done if ((FAILURES)); then printf 'Doctor found %d problem(s).\n' "$FAILURES" >&2; exit 1; fi printf '%s\n' 'Doctor passed.'