chore(release): stage v1.7.55-alpha
This commit is contained in:
@@ -10,8 +10,10 @@ required_containers=(
|
||||
"bitcoin-knots"
|
||||
"electrumx"
|
||||
"lnd"
|
||||
"archy-mempool-db"
|
||||
"mempool-api"
|
||||
"mempool"
|
||||
"filebrowser"
|
||||
"archy-bitcoin-ui"
|
||||
"archy-lnd-ui"
|
||||
"archy-electrs-ui"
|
||||
@@ -26,6 +28,18 @@ container_running() {
|
||||
podman inspect --format '{{.State.Running}}' "$name" 2>/dev/null
|
||||
}
|
||||
|
||||
bitcoin_rpc() {
|
||||
curl -fsS --max-time 60 \
|
||||
--user "archipelago:$(cat /var/lib/archipelago/secrets/bitcoin-rpc-password)" \
|
||||
--data-binary '{"jsonrpc":"1.0","id":"required-stack","method":"getblockchaininfo","params":[]}' \
|
||||
-H 'content-type: text/plain;' \
|
||||
http://127.0.0.1:8332/
|
||||
}
|
||||
|
||||
bitcoin_json() {
|
||||
python3 -c 'import json,sys; r=json.load(sys.stdin)["result"]; print(r[sys.argv[1]])' "$1"
|
||||
}
|
||||
|
||||
@test "required containers are present" {
|
||||
local names
|
||||
names="$(podman_names)"
|
||||
@@ -43,9 +57,29 @@ container_running() {
|
||||
}
|
||||
|
||||
@test "bitcoin-knots RPC responds" {
|
||||
run sh -lc 'podman exec bitcoin-knots bitcoin-cli -rpcuser=archipelago -rpcpassword="$(cat /var/lib/archipelago/secrets/bitcoin-rpc-password)" getblockchaininfo'
|
||||
run bitcoin_rpc
|
||||
[ "$status" -eq 0 ]
|
||||
echo "$output" | jq -e '.chain == "main" and (.blocks >= 0)' >/dev/null
|
||||
echo "$output" | python3 -c 'import json,sys; r=json.load(sys.stdin)["result"]; assert r["chain"] == "main" and r["blocks"] >= 0'
|
||||
}
|
||||
|
||||
@test "bitcoin backend is synced archival for electrumx/lnd gate" {
|
||||
run bitcoin_rpc
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
local pruned ibd blocks headers
|
||||
pruned="$(echo "$output" | bitcoin_json pruned)"
|
||||
ibd="$(echo "$output" | bitcoin_json initialblockdownload)"
|
||||
blocks="$(echo "$output" | bitcoin_json blocks)"
|
||||
headers="$(echo "$output" | bitcoin_json headers)"
|
||||
|
||||
if [ "$pruned" = "True" ] || [ "$pruned" = "true" ]; then
|
||||
echo "bitcoin is pruned (blocks=$blocks headers=$headers); electrumx cannot index pruned historical blocks"
|
||||
return 1
|
||||
fi
|
||||
if [ "$ibd" = "True" ] || [ "$ibd" = "true" ]; then
|
||||
echo "bitcoin is still in initial block download (blocks=$blocks headers=$headers)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
@test "electrumx TCP port accepts connections" {
|
||||
@@ -59,7 +93,17 @@ PY
|
||||
}
|
||||
|
||||
@test "lnd CLI getinfo succeeds" {
|
||||
run sh -lc 'podman exec lnd lncli --tlscertpath /root/.lnd/tls.cert --macaroonpath /root/.lnd/data/chain/bitcoin/mainnet/readonly.macaroon --rpcserver localhost:10009 getinfo >/dev/null'
|
||||
run sh -lc 'timeout 60 podman exec lnd lncli --tlscertpath /root/.lnd/tls.cert --macaroonpath /root/.lnd/data/chain/bitcoin/mainnet/readonly.macaroon --rpcserver localhost:10009 getinfo >/dev/null'
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "lnd REST port accepts connections" {
|
||||
run python3 - <<'PY'
|
||||
import socket
|
||||
s = socket.create_connection(("127.0.0.1", 18080), 3)
|
||||
s.close()
|
||||
print("ok")
|
||||
PY
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@@ -79,6 +123,11 @@ PY
|
||||
}
|
||||
|
||||
@test "lnd ui responds" {
|
||||
run curl -fsS "http://127.0.0.1:8081/"
|
||||
run curl -fsS "http://127.0.0.1:18083/"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "filebrowser responds" {
|
||||
run curl -fsS "http://127.0.0.1:8083/"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ ARCHY_APPS="${ARCHY_APPS:-}"
|
||||
ARCHY_TIMEOUT="${ARCHY_TIMEOUT:-900}"
|
||||
ARCHY_STABILITY_SECONDS="${ARCHY_STABILITY_SECONDS:-5}"
|
||||
ARCHY_ALLOW_BITCOIN_SWAP="${ARCHY_ALLOW_BITCOIN_SWAP:-0}"
|
||||
ARCHY_APP_CATALOG="${ARCHY_APP_CATALOG:-}"
|
||||
ARCHY_PRUNED_NODE="${ARCHY_PRUNED_NODE:-auto}"
|
||||
|
||||
if [[ -z "$ARCHY_HOST" || -z "$ARCHY_PASSWORD" ]]; then
|
||||
echo "ARCHY_HOST and ARCHY_PASSWORD are required" >&2
|
||||
@@ -37,6 +39,7 @@ fi
|
||||
BASE_URL="${ARCHY_SCHEME}://${ARCHY_HOST}"
|
||||
SESSION=""
|
||||
CSRF=""
|
||||
CATALOG_FILE=""
|
||||
|
||||
ALL_APPS=(
|
||||
bitcoin-knots
|
||||
@@ -65,6 +68,69 @@ ALL_APPS=(
|
||||
gitea
|
||||
)
|
||||
|
||||
ARCHIVAL_ONLY_APPS=(
|
||||
electrumx
|
||||
mempool
|
||||
)
|
||||
|
||||
app_in_list() {
|
||||
local needle="$1"
|
||||
shift
|
||||
local item
|
||||
for item in "$@"; do
|
||||
[[ "$item" == "$needle" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
fetch_catalog() {
|
||||
CATALOG_FILE=$(mktemp)
|
||||
if [[ -n "$ARCHY_APP_CATALOG" ]]; then
|
||||
cp "$ARCHY_APP_CATALOG" "$CATALOG_FILE"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if curl -skfL --connect-timeout 8 -m 30 "${BASE_URL}/api/app-catalog" -o "$CATALOG_FILE" \
|
||||
&& jq -e '.apps | length > 0' "$CATALOG_FILE" >/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
curl -skfL --connect-timeout 8 -m 30 "${BASE_URL}/catalog.json" -o "$CATALOG_FILE"
|
||||
jq -e '.apps | length > 0' "$CATALOG_FILE" >/dev/null
|
||||
}
|
||||
|
||||
catalog_app_ids() {
|
||||
jq -r '.apps[] | select((.dockerImage // "") != "") | .id' "$CATALOG_FILE"
|
||||
}
|
||||
|
||||
catalog_app_json() {
|
||||
local app="$1"
|
||||
[[ -n "$CATALOG_FILE" && -r "$CATALOG_FILE" ]] || return 1
|
||||
jq -c --arg app "$app" '
|
||||
.registry as $registry
|
||||
| .apps[]
|
||||
| select(.id == $app)
|
||||
| .dockerImage = (if ((.dockerImage // "") | contains("/")) then .dockerImage else ($registry + "/" + .dockerImage) end)
|
||||
' "$CATALOG_FILE" | head -n 1
|
||||
}
|
||||
|
||||
is_pruned_node() {
|
||||
case "$ARCHY_PRUNED_NODE" in
|
||||
1|true|yes) return 0 ;;
|
||||
0|false|no) return 1 ;;
|
||||
esac
|
||||
|
||||
local pass body
|
||||
pass=$(ssh "${ARCHY_HOST}" 'sudo cat /var/lib/archipelago/secrets/bitcoin-rpc-password 2>/dev/null || cat /var/lib/archipelago/secrets/bitcoin-rpc-password 2>/dev/null' 2>/dev/null || true)
|
||||
[[ -n "$pass" ]] || return 1
|
||||
body=$(curl -fsS --max-time 20 \
|
||||
--user "archipelago:${pass}" \
|
||||
--data-binary '{"jsonrpc":"1.0","id":"remote-lifecycle","method":"getblockchaininfo","params":[]}' \
|
||||
-H 'content-type: text/plain;' \
|
||||
"http://${ARCHY_HOST}:8332/" 2>/dev/null || true)
|
||||
printf '%s' "$body" | jq -e '.result.pruned == true' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
image_for() {
|
||||
case "$1" in
|
||||
bitcoin-knots) echo "146.59.87.168:3000/lfg2025/bitcoin-knots:latest" ;;
|
||||
@@ -341,7 +407,7 @@ probe_lnd_wallet_connect() {
|
||||
(.cert_base64url | type == "string" and length > 100) and
|
||||
(.macaroon_base64url | type == "string" and length > 50) and
|
||||
(.tor_onion | type == "string" and test("^[a-z2-7]+\\.onion$")) and
|
||||
(.rest_port == 8080) and
|
||||
(.rest_port == 18080) and
|
||||
(.grpc_port == 10009)
|
||||
' >/dev/null || {
|
||||
echo "lnd connect info incomplete: $info" >&2
|
||||
@@ -371,12 +437,29 @@ probe_electrum_wallet_connect() {
|
||||
}
|
||||
|
||||
install_app() {
|
||||
local app="$1" image params
|
||||
image=$(image_for "$app")
|
||||
params=$(jq -nc --arg id "$app" --arg img "$image" '{id:$id,dockerImage:$img,version:"latest"}')
|
||||
local app="$1" app_json image params
|
||||
app_json=$(catalog_app_json "$app" || true)
|
||||
if [[ -n "$app_json" ]]; then
|
||||
params=$(printf '%s' "$app_json" | jq -c '{id, dockerImage, version, containerConfig} | with_entries(select(.value != null))')
|
||||
else
|
||||
image=$(image_for "$app")
|
||||
params=$(jq -nc --arg id "$app" --arg img "$image" '{id:$id,dockerImage:$img,version:"latest"}')
|
||||
fi
|
||||
rpc_result package.install "$params" >/dev/null
|
||||
}
|
||||
|
||||
expect_archival_blocked_install() {
|
||||
local app="$1" app_json resp err params
|
||||
app_json=$(catalog_app_json "$app")
|
||||
params=$(printf '%s' "$app_json" | jq -c '{id, dockerImage, version, containerConfig} | with_entries(select(.value != null))')
|
||||
resp=$(rpc_call package.install "$params")
|
||||
err=$(printf '%s' "$resp" | jq -r '.error.message // empty')
|
||||
if [[ "$err" != *"Requires an archival Bitcoin node"* && "$err" != *"requires an archival Bitcoin node"* && "$err" != *"running pruned Bitcoin"* ]]; then
|
||||
echo "expected archival Bitcoin block for $app, got: $resp" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
start_app() { rpc_result package.start "$(jq -nc --arg id "$1" '{id:$id}')" >/dev/null; }
|
||||
stop_app() { rpc_result package.stop "$(jq -nc --arg id "$1" '{id:$id}')" >/dev/null; }
|
||||
restart_app() { rpc_result package.restart "$(jq -nc --arg id "$1" '{id:$id}')" >/dev/null; }
|
||||
@@ -405,6 +488,11 @@ full_lifecycle_app() {
|
||||
echo "skip bitcoin-core: set ARCHY_ALLOW_BITCOIN_SWAP=1 to test mutually-exclusive Bitcoin implementation"
|
||||
return 0
|
||||
fi
|
||||
if app_in_list "$app" "${ARCHIVAL_ONLY_APPS[@]}" && is_pruned_node; then
|
||||
echo "== $app: expect archival Bitcoin block =="
|
||||
expect_archival_blocked_install "$app"
|
||||
return $?
|
||||
fi
|
||||
echo "== $app: install =="
|
||||
install_app "$app" || return 1
|
||||
wait_not_installing "$app" || return 1
|
||||
@@ -451,11 +539,16 @@ full_lifecycle_app() {
|
||||
apps=()
|
||||
if [[ -n "$ARCHY_APPS" ]]; then
|
||||
IFS=',' read -r -a apps <<< "$ARCHY_APPS"
|
||||
fetch_catalog || true
|
||||
elif [[ "$ARCHY_FULL_LIFECYCLE" == "1" ]]; then
|
||||
echo "ARCHY_FULL_LIFECYCLE=1 requires ARCHY_APPS to avoid installing unqualified catalog apps" >&2
|
||||
exit 2
|
||||
fetch_catalog
|
||||
mapfile -t apps < <(catalog_app_ids)
|
||||
else
|
||||
apps=("${ALL_APPS[@]}")
|
||||
if fetch_catalog; then
|
||||
mapfile -t apps < <(catalog_app_ids)
|
||||
else
|
||||
apps=("${ALL_APPS[@]}")
|
||||
fi
|
||||
fi
|
||||
|
||||
rpc_login
|
||||
|
||||
Reference in New Issue
Block a user