284 lines
9.4 KiB
Bash
284 lines
9.4 KiB
Bash
#!/usr/bin/env bash
|
|
# dev-env: Bitcoin/Lightning regtest docker environment
|
|
#
|
|
# Provides:
|
|
# regtest-start [env|pr:<branch>] [--seed|--keep]
|
|
# regtest-stop
|
|
# regtest-status
|
|
# regtest-logs [service]
|
|
# regtest-cli # source CLI helpers
|
|
# regtest # cd to regtest dir
|
|
#
|
|
# Ported from ~/dev/.devenv.d/40-regtest.sh. Reads paths from
|
|
# /etc/dev-env/config.sh. The bitSpire ATM reaches its Lightning
|
|
# backend through the LNbits nostr-native transport (the nostrrelay
|
|
# extension) in this same regtest — there is no separate ATM-backend
|
|
# container to start.
|
|
#
|
|
# This is sourced into interactive shells (function definitions) AND
|
|
# also dropped into the system path as standalone wrapper scripts so
|
|
# `regtest-start` works from a fresh non-interactive shell.
|
|
|
|
# Shared base config loader lives in /etc/dev-env/lib.sh; this module
|
|
# layers regtest-specific path defaults on top.
|
|
if [[ -r /etc/dev-env/lib.sh ]]; then
|
|
# shellcheck disable=SC1091
|
|
source /etc/dev-env/lib.sh
|
|
fi
|
|
|
|
_devenv_load_config() {
|
|
_devenv_source_config
|
|
REGTEST_DIR="${LOCAL_DIR:-$DEV_ROOT/local}/docker/regtest"
|
|
LNBITS_DIR="${LNBITS_DIR:-$DEV_ROOT/lnbits}"
|
|
UPSTREAM_PRS_DIR="${UPSTREAM_PRS_DIR:-$DEV_ROOT/upstream-prs}"
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Status helpers
|
|
#-------------------------------------------------------------------------------
|
|
|
|
_regtest_is_running() {
|
|
docker ps --filter "name=lnbits-lnd-4-1" --format '{{.Names}}' 2>/dev/null \
|
|
| grep -q lnbits-lnd-4-1
|
|
}
|
|
|
|
_wait_for_lnd4() {
|
|
local attempts=0
|
|
echo "Waiting for lnd-4..."
|
|
while ! docker exec lnbits-lnd-4-1 lncli --network=regtest --rpcserver=lnd-4:10009 getinfo &>/dev/null; do
|
|
if (( attempts >= 30 )); then
|
|
echo "lnd-4 failed to start"
|
|
return 1
|
|
fi
|
|
sleep 2
|
|
((attempts++))
|
|
done
|
|
echo "lnd-4 is ready"
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Start
|
|
#-------------------------------------------------------------------------------
|
|
# Usage: regtest-start [env|pr:<branch>] [--path <dir>] [--seed|--keep]
|
|
#
|
|
# env dev|main (worktree under ~/dev/lnbits/<env>)
|
|
# pr:<branch> ~/dev/upstream-prs/lnbits-<branch>
|
|
# --path <dir> arbitrary lnbits directory
|
|
# --seed copy lnbits-seed/ to lnbits/ before starting
|
|
# --keep keep existing data
|
|
regtest-start() {
|
|
_devenv_load_config
|
|
|
|
local env="" data_mode="fresh" custom_path=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--seed) data_mode="seed" ;;
|
|
--keep) data_mode="keep" ;;
|
|
--path) shift; custom_path="$1" ;;
|
|
dev|main) env="$1" ;;
|
|
pr:*)
|
|
local pr_branch="${1#pr:}"
|
|
custom_path="$UPSTREAM_PRS_DIR/lnbits-$pr_branch"
|
|
;;
|
|
*)
|
|
cat <<USAGE
|
|
Unknown argument: $1
|
|
Usage: regtest-start [dev|main|pr:<branch>] [--path <dir>] [--seed|--keep]
|
|
|
|
Environments:
|
|
dev, main ~/dev/lnbits/<env>
|
|
pr:<branch> ~/dev/upstream-prs/lnbits-<branch>
|
|
--path <dir> arbitrary lnbits directory
|
|
USAGE
|
|
return 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ ! -d "$REGTEST_DIR" ]]; then
|
|
echo "Regtest environment not found at $REGTEST_DIR"
|
|
echo "Run dev-env-bootstrap to clone it."
|
|
return 1
|
|
fi
|
|
|
|
# Determine lnbits source dir
|
|
local lnbits_path=""
|
|
if [[ -n "$custom_path" ]]; then
|
|
lnbits_path="$custom_path"
|
|
[[ -d "$lnbits_path" ]] || { echo "lnbits dir not found: $lnbits_path"; return 1; }
|
|
else
|
|
[[ -z "$env" ]] && env="dev"
|
|
lnbits_path="$LNBITS_DIR/$env"
|
|
[[ -d "$lnbits_path" ]] || { echo "lnbits worktree not found: $env"; return 1; }
|
|
fi
|
|
|
|
local lnbits_data="$REGTEST_DIR/data/lnbits"
|
|
local lnbits_seed="$REGTEST_DIR/data/lnbits-seed"
|
|
|
|
case "$data_mode" in
|
|
fresh)
|
|
if [[ -d "$lnbits_data" ]] && [[ -n "$(ls -A "$lnbits_data" 2>/dev/null)" ]]; then
|
|
echo "Existing lnbits data at $lnbits_data"
|
|
read -r -p "Wipe it? [y/N] " -n 1 REPLY; echo
|
|
[[ $REPLY =~ ^[Yy]$ ]] || { echo "Aborted."; return 1; }
|
|
docker run --rm -v "$lnbits_data":/data alpine sh -c "rm -rf /data/* /data/.*" 2>/dev/null || true
|
|
rmdir "$lnbits_data" 2>/dev/null || true
|
|
fi
|
|
mkdir -p "$lnbits_data"
|
|
;;
|
|
seed)
|
|
[[ -d "$lnbits_seed" ]] || { echo "Seed data not found at $lnbits_seed"; return 1; }
|
|
if [[ -d "$lnbits_data" ]] && [[ -n "$(ls -A "$lnbits_data" 2>/dev/null)" ]]; then
|
|
read -r -p "Overwrite with seed? [y/N] " -n 1 REPLY; echo
|
|
[[ $REPLY =~ ^[Yy]$ ]] || { echo "Aborted."; return 1; }
|
|
docker run --rm -v "$lnbits_data":/data alpine sh -c "rm -rf /data/* /data/.*" 2>/dev/null || true
|
|
rmdir "$lnbits_data" 2>/dev/null || true
|
|
fi
|
|
cp -r "$lnbits_seed" "$lnbits_data"
|
|
;;
|
|
keep)
|
|
mkdir -p "$lnbits_data"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo "Starting regtest..."
|
|
echo " lnbits path: $lnbits_path"
|
|
echo " data mode: $data_mode"
|
|
echo ""
|
|
|
|
echo "Building lnbits Docker image from $lnbits_path..."
|
|
docker build -t lnbits/lnbits "$lnbits_path"
|
|
|
|
(cd "$REGTEST_DIR" && ./start-regtest)
|
|
|
|
cat <<EOF
|
|
|
|
Regtest running:
|
|
LNbits: http://localhost:5001/
|
|
Mempool: http://localhost:8080/
|
|
Boltz: http://localhost:9001/
|
|
|
|
EOF
|
|
|
|
echo "Commands: regtest-stop, regtest-logs, regtest-cli, regtest-status"
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Stop
|
|
#-------------------------------------------------------------------------------
|
|
regtest-stop() {
|
|
_devenv_load_config
|
|
|
|
if [[ ! -d "$REGTEST_DIR" ]]; then
|
|
echo "Regtest environment not found"
|
|
return 1
|
|
fi
|
|
|
|
echo "Stopping regtest environment..."
|
|
(cd "$REGTEST_DIR"
|
|
# shellcheck disable=SC1091
|
|
source ./docker-scripts.sh
|
|
docker compose down -v)
|
|
echo "Regtest stopped"
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Status / logs / CLI
|
|
#-------------------------------------------------------------------------------
|
|
regtest-status() {
|
|
_devenv_load_config
|
|
|
|
echo "=== Shared Regtest Environment ==="
|
|
if _regtest_is_running; then
|
|
echo "Status: RUNNING"
|
|
echo ""
|
|
docker ps --filter "name=lnbits-" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | head -15
|
|
|
|
local balance
|
|
balance="$(docker exec lnbits-lnd-4-1 lncli --network=regtest walletbalance 2>/dev/null \
|
|
| grep -oP '"total_balance":\s*"\K[0-9]+' || echo 0)"
|
|
echo ""
|
|
echo "lnd-4 balance: $balance sats"
|
|
else
|
|
echo "Status: STOPPED"
|
|
echo "Start with: regtest-start"
|
|
fi
|
|
}
|
|
|
|
regtest-logs() {
|
|
_devenv_load_config
|
|
local service="${1:-}"
|
|
[[ -d "$REGTEST_DIR" ]] || { echo "Regtest not found"; return 1; }
|
|
if [[ -n "$service" ]]; then
|
|
(cd "$REGTEST_DIR" && docker compose logs -f "$service")
|
|
else
|
|
(cd "$REGTEST_DIR" && docker compose logs -f)
|
|
fi
|
|
}
|
|
|
|
regtest-cli() {
|
|
_devenv_load_config
|
|
[[ -f "$REGTEST_DIR/docker-scripts.sh" ]] || { echo "docker-scripts.sh missing"; return 1; }
|
|
echo "Sourcing regtest CLI helpers..."
|
|
# shellcheck disable=SC1091
|
|
source "$REGTEST_DIR/docker-scripts.sh"
|
|
cat <<EOF
|
|
|
|
Available commands:
|
|
bitcoin-cli-sim Bitcoin Core CLI
|
|
lncli-sim <1-4> LND node CLI
|
|
lightning-cli-sim <1-3> C-Lightning node CLI
|
|
boltzcli-sim Boltz client CLI
|
|
elements-cli-sim Elements/Liquid CLI
|
|
|
|
Examples:
|
|
bitcoin-cli-sim -generate 1
|
|
lncli-sim 1 getinfo
|
|
EOF
|
|
}
|
|
|
|
regtest() {
|
|
_devenv_load_config
|
|
[[ -d "$REGTEST_DIR" ]] && cd "$REGTEST_DIR" || { echo "Regtest not found at $REGTEST_DIR"; return 1; }
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Rebuild / restart the lnbits service (docker-compose.dev.yml workflow)
|
|
#-------------------------------------------------------------------------------
|
|
# regtest-lnbits-rebuild build (cached) + recreate the lnbits container
|
|
# regtest-lnbits-rebuild --clean build --no-cache + recreate (truly fresh image)
|
|
# regtest-lnbits-restart restart the container without rebuilding
|
|
#
|
|
# The cached build is the happy path: docker invalidates the source COPY layer
|
|
# when LNBITS_SRC content changes, so most rebuilds are fast. Use --clean when
|
|
# you've been mucking with the image itself.
|
|
|
|
_regtest_lnbits_compose_file() {
|
|
echo "$REGTEST_DIR/docker-compose.dev.yml"
|
|
}
|
|
|
|
regtest-lnbits-rebuild() {
|
|
_devenv_load_config
|
|
local compose_file build_args=()
|
|
compose_file="$(_regtest_lnbits_compose_file)"
|
|
[[ -f "$compose_file" ]] || { echo "Compose file not found: $compose_file"; return 1; }
|
|
|
|
if [[ "${1:-}" == "--clean" ]]; then
|
|
build_args+=(--no-cache)
|
|
fi
|
|
|
|
(cd "$REGTEST_DIR" \
|
|
&& docker compose -f "$compose_file" build "${build_args[@]}" lnbits \
|
|
&& docker compose -f "$compose_file" up -d --force-recreate lnbits)
|
|
}
|
|
|
|
regtest-lnbits-restart() {
|
|
_devenv_load_config
|
|
local compose_file
|
|
compose_file="$(_regtest_lnbits_compose_file)"
|
|
[[ -f "$compose_file" ]] || { echo "Compose file not found: $compose_file"; return 1; }
|
|
docker compose -f "$compose_file" restart lnbits
|
|
}
|