This repository has been archived on 2026-06-22. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
lnbits-sensei/modules/dev-env/scripts/regtest.sh
Padreug e38d313db2 feat(dev-env): backport matured dev-env implementation from /etc/nixos
Replace the stub dev-env with the real, working implementation that grew
in the reference machine config — de-identified for the public scaffold.

Nix layer:
- options.nix: full project schema (url/upstream/fork/category/
  worktreeRoot/worktrees{branch,path,remote}/isClone/deployFlakeInput),
  deploy.targets, github.forkUser, writeDirenvHints. Drops the
  forgejo-URL block + deploy-flake auto-derivation (incoherent in a
  scaffold that uses explicit per-project urls).
- lib.nix: mkProject + worktreePath/bareRepoPath/projectRemotes,
  generalized to the explicit-url model (origin falls back to upstream).
- config.nix: renders /etc/dev-env/{config.sh,projects.json,
  tmux-sessions.json}, installs helpers via writeShellScriptBin, loads
  shell functions into interactive shells, wires the git pre-commit hook.

Scripts (config-driven, read /etc/dev-env at runtime):
- bootstrap.sh, nav.sh, worktree.sh, pr-helpers.sh, rebase.sh,
  status.sh, deploy.sh, regtest.sh, tmux-launch.sh.
- Stripped aiolabs/forgejo/bitspire/lamassu/webapp hardcoding; the
  github-fork remote is renamed 'fork' to match git.remotes vocabulary.
- Removes the dev.sh stub (the matured impl uses discrete commands +
  shell functions, not a unified 'dev' CLI).

presets/example.nix: a worked, generic project list replacing the
identity-specific aiolabs preset. tests/smoke.nix + flake checks
exercise the schema; 'nix flake check' is green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 21:18:49 +02:00

267 lines
9.1 KiB
Bash

#!/usr/bin/env bash
# dev-env: Bitcoin/Lightning regtest docker environment
#
# Provides:
# regtest-start [worktree|pr:<branch>] [--path <dir>] [--seed|--keep]
# regtest-stop
# regtest-status
# regtest-logs [service]
# regtest-cli # source CLI helpers
# regtest # cd to regtest dir
#
# Reads paths from /etc/dev-env/config.sh. Wraps the
# `lnbits/legend-regtest-enviroment` compose stack (or your fork of it,
# set via devEnv.regtest.repoUrl). Container names and ports below assume
# that stack's layout — adjust if you've customised it.
#
# This file is sourced into interactive shells (function definitions) AND
# dropped into the system path as standalone wrapper scripts so
# `regtest-start` works from a fresh non-interactive shell.
_devenv_load_config() {
if [[ -r /etc/dev-env/config.sh ]]; then
# shellcheck disable=SC1091
source /etc/dev-env/config.sh
fi
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}"
# Node used for readiness/balance checks. Override if your stack
# names containers differently.
REGTEST_LND="${REGTEST_LND:-lnbits-lnd-4-1}"
}
#-------------------------------------------------------------------------------
# Status helpers
#-------------------------------------------------------------------------------
_regtest_is_running() {
docker ps --filter "name=$REGTEST_LND" --format '{{.Names}}' 2>/dev/null \
| grep -q "$REGTEST_LND"
}
#-------------------------------------------------------------------------------
# Start
#-------------------------------------------------------------------------------
# Usage: regtest-start [worktree|pr:<branch>] [--path <dir>] [--seed|--keep]
#
# worktree an lnbits worktree name (under ~/dev/lnbits/<name>)
# 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" ;;
pr:*)
local pr_branch="${1#pr:}"
custom_path="$UPSTREAM_PRS_DIR/lnbits-$pr_branch"
;;
-*)
cat <<USAGE
Unknown argument: $1
Usage: regtest-start [worktree|pr:<branch>] [--path <dir>] [--seed|--keep]
worktree an lnbits worktree name (under ~/dev/lnbits/<name>)
pr:<branch> ~/dev/upstream-prs/lnbits-<branch>
--path <dir> arbitrary lnbits directory
USAGE
return 1
;;
*) env="$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 "=== 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 "$REGTEST_LND" lncli --network=regtest walletbalance 2>/dev/null \
| grep -oP '"total_balance":\s*"\K[0-9]+' || echo 0)"
echo ""
echo "$REGTEST_LND 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
}