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>
267 lines
9.1 KiB
Bash
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
|
|
}
|