Harden kraiken-lib watch loop and confirm host-built dist propagation (#38)

- expand scripts/watch-kraiken-lib.sh to watch atomic rename events, validate required tools, and gracefully restart only the containers that mount kraiken-
  lib/dist
  - verify the host-built dist is mounted read-only inside each service and observe live rebuild + restart behavior under inotify
  - run the local podman stack, exercise the watcher by editing kraiken-lib/src/helpers.ts, and confirm GraphQL responds through Caddy after restarts

resolves #33

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: johba <johba@harb.eth>
Reviewed-on: https://codeberg.org/johba/harb/pulls/38
This commit is contained in:
johba 2025-10-02 14:33:59 +02:00
parent b4c829e4d6
commit a29ca1a26a
10 changed files with 100 additions and 35 deletions

View file

@ -34,7 +34,8 @@
- Ponder stores data in `.ponder/`; drop the directory if schema changes break migrations. - Ponder stores data in `.ponder/`; drop the directory if schema changes break migrations.
- Keep git clean before committing; never leave commented-out code or untested changes. - Keep git clean before committing; never leave commented-out code or untested changes.
- **ES Modules**: The entire stack uses ES modules. kraiken-lib, txnBot, Ponder, and web-app all require `"type": "module"` in package.json and use `import` syntax. - **ES Modules**: The entire stack uses ES modules. kraiken-lib, txnBot, Ponder, and web-app all require `"type": "module"` in package.json and use `import` syntax.
- **Podman Named Volumes**: `kraiken-lib/dist/` is mounted as a named volume (`harb_kraiken-dist`). After changing module format or build configuration, delete the volume with `podman volume rm harb_kraiken-dist` and restart to force a clean rebuild. - **kraiken-lib Build**: Run `./scripts/build-kraiken-lib.sh` before `podman-compose up` so containers mount a fresh `kraiken-lib/dist` from the host.
- **Live Reload**: `scripts/watch-kraiken-lib.sh` rebuilds on file changes (requires inotify-tools) and restarts dependent containers automatically.
## Handy Commands ## Handy Commands
- `foundryup` - update Foundry toolchain. - `foundryup` - update Foundry toolchain.

View file

@ -3,6 +3,12 @@ set -euo pipefail
ROOT_DIR=/workspace ROOT_DIR=/workspace
LANDING_DIR=$ROOT_DIR/landing LANDING_DIR=$ROOT_DIR/landing
REQUIRED_DIST="$ROOT_DIR/kraiken-lib/dist/index.js"
if [[ ! -f "$REQUIRED_DIST" ]]; then
echo "[landing-entrypoint] ERROR: Run ./scripts/build-kraiken-lib.sh before starting containers" >&2
exit 1
fi
cd "$LANDING_DIR" cd "$LANDING_DIR"
echo "[landing-entrypoint] Installing dependencies..." echo "[landing-entrypoint] Installing dependencies..."

View file

@ -12,6 +12,12 @@ done
cd "$PONDER_WORKDIR" cd "$PONDER_WORKDIR"
REQUIRED_DIST="$ROOT_DIR/kraiken-lib/dist/index.js"
if [[ ! -f "$REQUIRED_DIST" ]]; then
echo "[ponder-entrypoint] ERROR: Run ./scripts/build-kraiken-lib.sh before starting containers" >&2
exit 1
fi
echo "[ponder-entrypoint] Installing dependencies..." echo "[ponder-entrypoint] Installing dependencies..."
npm install --no-save --loglevel error 2>&1 || { npm install --no-save --loglevel error 2>&1 || {
echo "[ponder-entrypoint] npm install failed, trying with --force" echo "[ponder-entrypoint] npm install failed, trying with --force"

View file

@ -4,42 +4,16 @@ set -euo pipefail
ROOT_DIR=/workspace ROOT_DIR=/workspace
TXNBOT_ENV_FILE=$ROOT_DIR/tmp/podman/txnBot.env TXNBOT_ENV_FILE=$ROOT_DIR/tmp/podman/txnBot.env
BOT_DIR=$ROOT_DIR/services/txnBot BOT_DIR=$ROOT_DIR/services/txnBot
KRAIKEN_LIB_DIR=$ROOT_DIR/kraiken-lib REQUIRED_DIST=$ROOT_DIR/kraiken-lib/dist/index.js
while [[ ! -f "$TXNBOT_ENV_FILE" ]]; do while [[ ! -f "$TXNBOT_ENV_FILE" ]]; do
echo "[txn-bot-entrypoint] waiting for env file" echo "[txn-bot-entrypoint] waiting for env file"
sleep 2 sleep 2
done done
# Build kraiken-lib first if [[ ! -f "$REQUIRED_DIST" ]]; then
echo "[txn-bot-entrypoint] Building kraiken-lib..." echo "[txn-bot-entrypoint] ERROR: Run ./scripts/build-kraiken-lib.sh before starting containers" >&2
cd "$KRAIKEN_LIB_DIR" exit 1
# Install dependencies if needed
if [[ ! -d node_modules ]]; then
echo "[txn-bot-entrypoint] Installing kraiken-lib dependencies..."
npm install --loglevel error
fi
# Install TypeScript if not present
if [[ ! -f node_modules/.bin/tsc ]]; then
echo "[txn-bot-entrypoint] Installing TypeScript..."
npm install --loglevel error typescript
fi
# Build TypeScript files (dist is now a volume, writable by node user)
echo "[txn-bot-entrypoint] Compiling TypeScript..."
if [[ ! -f dist/index.js ]]; then
echo "[txn-bot-entrypoint] Running tsc to compile kraiken-lib..."
./node_modules/.bin/tsc 2>&1 | head -20 || {
echo "[txn-bot-entrypoint] TypeScript compilation had some issues, checking if files were created..."
}
# Verify the main file exists
if [[ ! -f dist/index.js ]]; then
echo "[txn-bot-entrypoint] ERROR: Failed to compile kraiken-lib"
exit 1
fi
fi fi
cd "$BOT_DIR" cd "$BOT_DIR"

View file

@ -11,6 +11,12 @@ while [[ ! -f "$CONTRACT_ENV" ]]; do
sleep 2 sleep 2
done done
REQUIRED_DIST="$ROOT_DIR/kraiken-lib/dist/index.js"
if [[ ! -f "$REQUIRED_DIST" ]]; then
echo "[frontend-entrypoint] ERROR: Run ./scripts/build-kraiken-lib.sh before starting containers" >&2
exit 1
fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "$CONTRACT_ENV" source "$CONTRACT_ENV"

View file

@ -40,7 +40,7 @@ Shared TypeScript helpers used by the landing app, txnBot, and other services to
- `"moduleResolution": "node"` - Enable proper module resolution - `"moduleResolution": "node"` - Enable proper module resolution
- `"rootDir": "./src"` - Ensure flat output structure in `dist/` - `"rootDir": "./src"` - Ensure flat output structure in `dist/`
- **Build Output**: Running `npx tsc` produces ES module `.js` files in `dist/` that can be consumed by both browser (Vite) and Node.js (≥14 with `"type": "module"`). - **Build Output**: Running `npx tsc` produces ES module `.js` files in `dist/` that can be consumed by both browser (Vite) and Node.js (≥14 with `"type": "module"`).
- **Container Volumes**: In Podman/Docker setups, `dist/` is often a named volume. After changing module format or imports, delete the volume (`podman volume rm harb_kraiken-dist`) before restarting to force a clean rebuild. - **Container Mount**: Podman/Docker services now bind-mount `dist/` read-only from the host. Run `./scripts/build-kraiken-lib.sh` before `podman-compose up` or keep `scripts/watch-kraiken-lib.sh` running to rebuild automatically.
## Quality Guidelines ## Quality Guidelines
- Keep helpers pure and side-effect free; they should accept explicit dependencies. - Keep helpers pure and side-effect free; they should accept explicit dependencies.

View file

@ -49,6 +49,7 @@ services:
entrypoint: ["/workspace/containers/ponder-dev-entrypoint.sh"] entrypoint: ["/workspace/containers/ponder-dev-entrypoint.sh"]
volumes: volumes:
- .:/workspace:z - .:/workspace:z
- ./kraiken-lib/dist:/workspace/kraiken-lib/dist:ro,z
- ponder-node-modules:/workspace/services/ponder/node_modules - ponder-node-modules:/workspace/services/ponder/node_modules
working_dir: /workspace working_dir: /workspace
environment: environment:
@ -69,6 +70,7 @@ services:
entrypoint: ["/workspace/containers/webapp-dev-entrypoint.sh"] entrypoint: ["/workspace/containers/webapp-dev-entrypoint.sh"]
volumes: volumes:
- .:/workspace:z - .:/workspace:z
- ./kraiken-lib/dist:/workspace/kraiken-lib/dist:ro,z
- webapp-node-modules:/workspace/web-app/node_modules - webapp-node-modules:/workspace/web-app/node_modules
working_dir: /workspace working_dir: /workspace
environment: environment:
@ -86,6 +88,7 @@ services:
entrypoint: ["/workspace/containers/landing-dev-entrypoint.sh"] entrypoint: ["/workspace/containers/landing-dev-entrypoint.sh"]
volumes: volumes:
- .:/workspace:z - .:/workspace:z
- ./kraiken-lib/dist:/workspace/kraiken-lib/dist:ro,z
- landing-node-modules:/workspace/landing/node_modules - landing-node-modules:/workspace/landing/node_modules
working_dir: /workspace working_dir: /workspace
environment: environment:
@ -103,9 +106,9 @@ services:
entrypoint: ["/workspace/containers/txn-bot-entrypoint.sh"] entrypoint: ["/workspace/containers/txn-bot-entrypoint.sh"]
volumes: volumes:
- .:/workspace:z - .:/workspace:z
- ./kraiken-lib/dist:/workspace/kraiken-lib/dist:ro,z
- txn-node-modules:/workspace/services/txnBot/node_modules - txn-node-modules:/workspace/services/txnBot/node_modules
- kraiken-node-modules:/workspace/kraiken-lib/node_modules - kraiken-node-modules:/workspace/kraiken-lib/node_modules
- kraiken-dist:/workspace/kraiken-lib/dist
working_dir: /workspace working_dir: /workspace
depends_on: depends_on:
- anvil - anvil
@ -140,4 +143,3 @@ volumes:
ponder-node-modules: ponder-node-modules:
txn-node-modules: txn-node-modules:
kraiken-node-modules: kraiken-node-modules:
kraiken-dist:

14
scripts/build-kraiken-lib.sh Executable file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/../kraiken-lib"
if [[ ! -d node_modules || ! -x node_modules/.bin/tsc ]]; then
if ! npm install --silent; then
echo "Warning: npm install failed; continuing with existing node_modules" >&2
fi
fi
./node_modules/.bin/tsc
echo "kraiken-lib built"

56
scripts/watch-kraiken-lib.sh Executable file
View file

@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
WATCH_DIR="$ROOT_DIR/kraiken-lib/src"
SERVICES=(harb_ponder_1 harb_webapp_1 harb_landing_1 harb_txn-bot_1)
if ! command -v inotifywait >/dev/null 2>&1; then
echo "Error: inotifywait not found. Install inotify-tools." >&2
exit 1
fi
if ! command -v podman >/dev/null 2>&1; then
echo "Error: podman not found on PATH." >&2
exit 1
fi
cd "$ROOT_DIR"
restart_services() {
local missing=0
local running
mapfile -t running < <(podman ps --format '{{.Names}}')
for service in "${SERVICES[@]}"; do
local found=1
for name in "${running[@]}"; do
if [[ "$name" == "$service" ]]; then
found=0
break
fi
done
if (( found != 0 )); then
echo "Warning: $service not running; skipping restart" >&2
missing=1
continue
fi
if ! podman restart "$service" >/dev/null; then
echo "Warning: failed to restart $service" >&2
missing=1
fi
done
if [[ $missing -eq 0 ]]; then
echo "Services restarted"
fi
}
inotifywait -m -r -e close_write,create,delete,moved_to,moved_from "$WATCH_DIR" |
while read -r _directory _events filename; do
echo "kraiken-lib changed: $filename"
./scripts/build-kraiken-lib.sh
restart_services || true
done

View file

@ -29,7 +29,7 @@ Ponder-based indexer that records Kraiken protocol activity and exposes the Grap
- **Virtual Module Errors**: If you see `Failed to load url ponder:registry/ponder:schema/ponder:api`, check: - **Virtual Module Errors**: If you see `Failed to load url ponder:registry/ponder:schema/ponder:api`, check:
1. `DATABASE_URL` is properly set and accessible 1. `DATABASE_URL` is properly set and accessible
2. kraiken-lib ABIs exist (`onchain/out/Kraiken.sol/Kraiken.json`) 2. kraiken-lib ABIs exist (`onchain/out/Kraiken.sol/Kraiken.json`)
3. kraiken-lib TypeScript is built (`kraiken-lib/dist/index.js`) 3. `./scripts/build-kraiken-lib.sh` has been run so `kraiken-lib/dist/index.js` exists
4. `ponder-env.d.ts` is writable by the container user (chmod 666 or pre-create it) 4. `ponder-env.d.ts` is writable by the container user (chmod 666 or pre-create it)
5. Ponder version is 0.13.8+ (earlier versions had virtual module generation bugs) 5. Ponder version is 0.13.8+ (earlier versions had virtual module generation bugs)
- **PostgreSQL Connection**: Requires `DATABASE_URL` env var; Ponder falls back to PGlite if not set. The entrypoint must export this before Ponder starts. - **PostgreSQL Connection**: Requires `DATABASE_URL` env var; Ponder falls back to PGlite if not set. The entrypoint must export this before Ponder starts.