From f2ffe50e86e56364e1e973ac11eb5f9ff14fa255 Mon Sep 17 00:00:00 2001 From: Gorka Date: Tue, 7 Jul 2026 10:59:40 -0300 Subject: [PATCH 1/2] fix(quickstart): pin current build + force protocol 27 via SSOT The quickstart v640+ images (current :latest = v642-b1163.1) ship stellar-core v27 but default the --local network to protocol 25, which fails every contract upload with HostError(Context, InternalError). v639 (core v26) worked, hence the earlier :v639-b1103.1-latest pin. Fix forward instead of freezing on v639: move every quickstart usage onto the validated v642-b1163.1-latest build and force --protocol-version 27 (matches the bundled core and live testnet). Verified the full deploy flow clean on the current image with the flag. Single source of truth: stellar-pin.env holds the image tag + protocol. Read by the docker-compose.*.yml files, e2e/lifecycle/multi-asset compose (via ${STELLAR_QUICKSTART_IMAGE}/${STELLAR_PROTOCOL_VERSION}), test.sh, infra-up.sh, down.sh, lifecycle/provider.ts, and the reusable CI workflows (load step -> $GITHUB_ENV) so they cannot diverge. --protocol-version is marked a workaround at each site, to drop when a fixed image ships. Add quickstart-drift.yml: weekly + dispatch alarm that compares live testnet protocol to the pin and nags Discord (secrets.DISCORD_WEBHOOK_URL, dormant until set) on every run while testnet is ahead. --- .github/workflows/e2e-reusable.yml | 8 +++ .github/workflows/invite-gate-reusable.yml | 8 +++ .github/workflows/lifecycle-reusable.yml | 8 +++ .github/workflows/multi-asset-reusable.yml | 8 +++ .github/workflows/quickstart-drift.yml | 71 ++++++++++++++++++++++ docker-compose.e2e.yml | 7 ++- docker-compose.governance.yml | 7 ++- docker-compose.invite-gate.yml | 7 ++- docker-compose.lifecycle.yml | 7 ++- docker-compose.multi-asset.yml | 7 ++- docker-compose.otel.yml | 7 ++- docker-compose.playwright.yml | 7 ++- docker-compose.pos-instant.yml | 7 ++- down.sh | 8 +++ e2e/docker-compose.yml | 7 ++- infra-up.sh | 24 +++++--- lifecycle/docker-compose.yml | 7 ++- lifecycle/provider.ts | 33 +++++++++- multi-asset/docker-compose.yml | 7 ++- stellar-pin.env | 18 ++++++ test.sh | 10 +++ 21 files changed, 243 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/quickstart-drift.yml create mode 100644 stellar-pin.env diff --git a/.github/workflows/e2e-reusable.yml b/.github/workflows/e2e-reusable.yml index 8e66e20..0f09a8e 100644 --- a/.github/workflows/e2e-reusable.yml +++ b/.github/workflows/e2e-reusable.yml @@ -71,6 +71,14 @@ jobs: fi ls -la e2e/wasms/ + - name: Load Stellar quickstart pin (SSOT) + run: | + set -a + . "$GITHUB_WORKSPACE/stellar-pin.env" + set +a + echo "STELLAR_QUICKSTART_IMAGE=$STELLAR_QUICKSTART_IMAGE" >> "$GITHUB_ENV" + echo "STELLAR_PROTOCOL_VERSION=$STELLAR_PROTOCOL_VERSION" >> "$GITHUB_ENV" + - name: Run E2E tests env: STELLAR_CLI_IMAGE: ${{ env.REGISTRY }}/${{ env.ORG }}/stellar-cli:${{ inputs.stellar_cli_version }} diff --git a/.github/workflows/invite-gate-reusable.yml b/.github/workflows/invite-gate-reusable.yml index b25c87e..2a2ede1 100644 --- a/.github/workflows/invite-gate-reusable.yml +++ b/.github/workflows/invite-gate-reusable.yml @@ -129,6 +129,14 @@ jobs: mkdir -p "$GITHUB_WORKSPACE/playwright/freighter-extension" cp -r extension/build/. "$GITHUB_WORKSPACE/playwright/freighter-extension/" + - name: Load Stellar quickstart pin (SSOT) + run: | + set -a + . "$GITHUB_WORKSPACE/stellar-pin.env" + set +a + echo "STELLAR_QUICKSTART_IMAGE=$STELLAR_QUICKSTART_IMAGE" >> "$GITHUB_ENV" + echo "STELLAR_PROTOCOL_VERSION=$STELLAR_PROTOCOL_VERSION" >> "$GITHUB_ENV" + - name: Run invite-gate tests env: PROVIDER_CONSOLE_PATH: ./provider-console diff --git a/.github/workflows/lifecycle-reusable.yml b/.github/workflows/lifecycle-reusable.yml index fee2778..593c070 100644 --- a/.github/workflows/lifecycle-reusable.yml +++ b/.github/workflows/lifecycle-reusable.yml @@ -75,6 +75,14 @@ jobs: fi ls -la e2e/wasms/ + - name: Load Stellar quickstart pin (SSOT) + run: | + set -a + . "$GITHUB_WORKSPACE/stellar-pin.env" + set +a + echo "STELLAR_QUICKSTART_IMAGE=$STELLAR_QUICKSTART_IMAGE" >> "$GITHUB_ENV" + echo "STELLAR_PROTOCOL_VERSION=$STELLAR_PROTOCOL_VERSION" >> "$GITHUB_ENV" + - name: Run Lifecycle E2E env: PROVIDER_IMAGE: ${{ inputs.provider_image_override != '' && inputs.provider_image_override || format('{0}/{1}/provider-platform:{2}', env.REGISTRY, env.ORG, inputs.provider_version) }} diff --git a/.github/workflows/multi-asset-reusable.yml b/.github/workflows/multi-asset-reusable.yml index 606b835..43fd47f 100644 --- a/.github/workflows/multi-asset-reusable.yml +++ b/.github/workflows/multi-asset-reusable.yml @@ -84,6 +84,14 @@ jobs: fi ls -la e2e/wasms/ + - name: Load Stellar quickstart pin (SSOT) + run: | + set -a + . "$GITHUB_WORKSPACE/stellar-pin.env" + set +a + echo "STELLAR_QUICKSTART_IMAGE=$STELLAR_QUICKSTART_IMAGE" >> "$GITHUB_ENV" + echo "STELLAR_PROTOCOL_VERSION=$STELLAR_PROTOCOL_VERSION" >> "$GITHUB_ENV" + - name: Run Multi-Asset E2E env: PROVIDER_IMAGE: ${{ inputs.provider_image_override != '' && inputs.provider_image_override || format('{0}/{1}/provider-platform:{2}', env.REGISTRY, env.ORG, inputs.provider_version) }} diff --git a/.github/workflows/quickstart-drift.yml b/.github/workflows/quickstart-drift.yml new file mode 100644 index 0000000..159d455 --- /dev/null +++ b/.github/workflows/quickstart-drift.yml @@ -0,0 +1,71 @@ +name: Quickstart protocol drift alarm + +# Warns when Stellar testnet advances past the protocol our quickstart pin is +# frozen to (STELLAR_PROTOCOL_VERSION in stellar-pin.env). While testnet is ahead +# of the pin, this posts to Discord on EVERY scheduled run — a weekly nag, no +# stored state — until the pin is bumped. +# +# Wired to secrets.DISCORD_WEBHOOK_URL but DORMANT until that secret is set +# out-of-band: with no secret it logs the drift and posts nothing. The webhook +# URL is read ONLY from the secret — never hardcoded, never from iac. + +on: + schedule: + - cron: "0 9 * * 1" # Mondays 09:00 UTC + workflow_dispatch: + +permissions: + contents: read + +jobs: + drift-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Compare testnet protocol to our pin + env: + DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} + run: | + set -euo pipefail + + # Our pinned protocol — single source of truth. + set -a + . ./stellar-pin.env + set +a + ours="$STELLAR_PROTOCOL_VERSION" + + # Testnet's live protocol: Horizon primary, Soroban RPC fallback. + testnet="$(curl -sf https://horizon-testnet.stellar.org/ \ + | jq -r '.current_protocol_version // empty' || true)" + if [ -z "$testnet" ]; then + testnet="$(curl -sf https://soroban-testnet.stellar.org \ + -X POST -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":1,"method":"getNetwork"}' \ + | jq -r '.result.protocolVersion // empty' || true)" + fi + + case "$testnet" in + ''|*[!0-9]*) echo "Could not read a numeric testnet protocol (got '$testnet'); skipping."; exit 0;; + esac + echo "Testnet protocol: $testnet | our pin: $ours" + + if [ "$testnet" -le "$ours" ]; then + echo "Pin is current (testnet $testnet <= pin $ours). No drift." + exit 0 + fi + echo "DRIFT: testnet ($testnet) is ahead of our pin ($ours)." + + if [ -z "${DISCORD_WEBHOOK_URL:-}" ]; then + echo "DISCORD_WEBHOOK_URL not set — alarm is dormant, not posting." + exit 0 + fi + + printf -v msg '%s\n%s\n%s' \ + ":warning: **Stellar quickstart pin is behind testnet**" \ + "Testnet is on protocol **$testnet**; local-dev is pinned to protocol **$ours**." \ + "Bump the pin in \`local-dev/stellar-pin.env\` (STELLAR_QUICKSTART_IMAGE + STELLAR_PROTOCOL_VERSION). It feeds the docker-compose.*.yml files, e2e/lifecycle/multi-asset compose, test.sh, infra-up.sh, down.sh and lifecycle/provider.ts." + + jq -n --arg content "$msg" '{content: $content}' \ + | curl -sf -X POST -H 'Content-Type: application/json' -d @- "$DISCORD_WEBHOOK_URL" \ + && echo "Posted drift alert to Discord." diff --git a/docker-compose.e2e.yml b/docker-compose.e2e.yml index e451cd1..3086652 100644 --- a/docker-compose.e2e.yml +++ b/docker-compose.e2e.yml @@ -3,8 +3,11 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} healthcheck: test: > curl -sf http://localhost:8000/soroban/rpc -X POST diff --git a/docker-compose.governance.yml b/docker-compose.governance.yml index 54288c0..40cbbca 100644 --- a/docker-compose.governance.yml +++ b/docker-compose.governance.yml @@ -3,8 +3,11 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} healthcheck: test: > curl -sf http://localhost:8000/soroban/rpc -X POST diff --git a/docker-compose.invite-gate.yml b/docker-compose.invite-gate.yml index 778c3ac..7c57445 100644 --- a/docker-compose.invite-gate.yml +++ b/docker-compose.invite-gate.yml @@ -7,8 +7,11 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} healthcheck: test: > curl -sf http://localhost:8000/soroban/rpc -X POST diff --git a/docker-compose.lifecycle.yml b/docker-compose.lifecycle.yml index ac8f697..87c31f4 100644 --- a/docker-compose.lifecycle.yml +++ b/docker-compose.lifecycle.yml @@ -4,8 +4,11 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} healthcheck: test: > curl -sf http://localhost:8000/soroban/rpc -X POST diff --git a/docker-compose.multi-asset.yml b/docker-compose.multi-asset.yml index a126c40..c1cf8e9 100644 --- a/docker-compose.multi-asset.yml +++ b/docker-compose.multi-asset.yml @@ -5,8 +5,11 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} healthcheck: test: > curl -sf http://localhost:8000/soroban/rpc -X POST diff --git a/docker-compose.otel.yml b/docker-compose.otel.yml index f6b42e1..9be1f55 100644 --- a/docker-compose.otel.yml +++ b/docker-compose.otel.yml @@ -3,8 +3,11 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} healthcheck: test: > curl -sf http://localhost:8000/soroban/rpc -X POST diff --git a/docker-compose.playwright.yml b/docker-compose.playwright.yml index 157caf3..d93af04 100644 --- a/docker-compose.playwright.yml +++ b/docker-compose.playwright.yml @@ -8,8 +8,11 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} healthcheck: test: > curl -sf http://localhost:8000/soroban/rpc -X POST diff --git a/docker-compose.pos-instant.yml b/docker-compose.pos-instant.yml index ce37ac8..3e11979 100644 --- a/docker-compose.pos-instant.yml +++ b/docker-compose.pos-instant.yml @@ -5,8 +5,11 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} healthcheck: test: > curl -sf http://localhost:8000/soroban/rpc -X POST diff --git a/down.sh b/down.sh index 7b20937..f879228 100755 --- a/down.sh +++ b/down.sh @@ -15,6 +15,14 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BASE_DIR="${BASE_DIR:-$(dirname "$SCRIPT_DIR")}" + +# Stellar quickstart pin (single source of truth) — exported so the lifecycle +# `docker compose down` below can interpolate the pinned image without warnings. +set -a +# shellcheck source=stellar-pin.env +. "$SCRIPT_DIR/stellar-pin.env" +set +a + PROVIDER_PLATFORM_PATH="${PROVIDER_PLATFORM_PATH:-$BASE_DIR/provider-platform}" COUNCIL_PLATFORM_PATH="${COUNCIL_PLATFORM_PATH:-$BASE_DIR/council-platform}" NETWORK_DASHBOARD_PLATFORM_PATH="${NETWORK_DASHBOARD_PLATFORM_PATH:-$BASE_DIR/network-dashboard-platform}" diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 6c001f2..9ff412b 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -17,8 +17,11 @@ services: retries: 15 stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} ports: - "8000:8000" healthcheck: diff --git a/infra-up.sh b/infra-up.sh index d06b9e3..a954410 100755 --- a/infra-up.sh +++ b/infra-up.sh @@ -30,6 +30,14 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BASE_DIR="${BASE_DIR:-$(dirname "$SCRIPT_DIR")}" + +# Stellar quickstart pin (image tag + local-network protocol) — single source of +# truth, shared with the compose files, test.sh, provider.ts and the drift alarm. +set -a +# shellcheck source=stellar-pin.env +. "$SCRIPT_DIR/stellar-pin.env" +set +a + PROVIDER_PLATFORM_PATH="${PROVIDER_PLATFORM_PATH:-$BASE_DIR/provider-platform}" PROVIDER_CONSOLE_PATH="${PROVIDER_CONSOLE_PATH:-$BASE_DIR/provider-console}" COUNCIL_CONSOLE_PATH="${COUNCIL_CONSOLE_PATH:-$BASE_DIR/council-console}" @@ -146,13 +154,15 @@ if curl -sf "http://localhost:${STELLAR_RPC_PORT}/soroban/rpc" -X POST \ info "Stellar RPC on port $STELLAR_RPC_PORT is healthy (shared)." else info "Stellar RPC not running, starting local network..." - # Pin to v639-b1103.1-latest — see local-dev PR #120: the rolling - # stellar/quickstart:testing tag (default for `stellar container start`) - # silently rejects all WASM uploads with HostError(Context, - # InternalError) since 2026-06-15. PR #120 pinned the docker-compose - # paths; this pins the CLI path used by up.sh. - stellar container start local --image-tag-override v639-b1103.1-latest 2>/dev/null \ - || stellar container start local 2>/dev/null \ + # Image tag + protocol come from stellar-pin.env (SSOT). --protocol-version is + # a WORKAROUND for the quickstart image's broken default (it selects protocol + # 25 on an image whose stellar-core is v27, which fails every WASM upload with + # HostError(Context, InternalError)); remove it when a fixed image ships. + stellar_tag="${STELLAR_QUICKSTART_IMAGE#stellar/quickstart:}" + stellar container start local \ + --image-tag-override "$stellar_tag" \ + --protocol-version "$STELLAR_PROTOCOL_VERSION" 2>/dev/null \ + || stellar container start local --image-tag-override "$stellar_tag" 2>/dev/null \ || true info "Waiting for Stellar RPC to be ready..." diff --git a/lifecycle/docker-compose.yml b/lifecycle/docker-compose.yml index 452a058..a086f80 100644 --- a/lifecycle/docker-compose.yml +++ b/lifecycle/docker-compose.yml @@ -1,7 +1,10 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} ports: - "8000:8000" healthcheck: diff --git a/lifecycle/provider.ts b/lifecycle/provider.ts index cdc4029..5748173 100644 --- a/lifecycle/provider.ts +++ b/lifecycle/provider.ts @@ -18,6 +18,35 @@ const PROVIDER_PORT = 3030; const NETWORK_PASSPHRASE = "Standalone Network ; February 2017"; +// Stellar quickstart pin (image tag + local-network protocol) — read from the +// single source of truth (stellar-pin.env at the repo root) so this local-runner +// path can't drift from the compose files / infra-up.sh. --protocol-version is a +// WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 +// image, which fails WASM upload); remove it when a fixed image ships. +function loadStellarPin(): { image: string; protocolVersion: string } { + const env = Deno.readTextFileSync( + new URL("../stellar-pin.env", import.meta.url), + ); + const vars: Record = {}; + for (const line of env.split("\n")) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const eq = trimmed.indexOf("="); + if (eq === -1) continue; + vars[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim(); + } + const image = vars["STELLAR_QUICKSTART_IMAGE"]; + const protocolVersion = vars["STELLAR_PROTOCOL_VERSION"]; + if (!image || !protocolVersion) { + throw new Error( + "stellar-pin.env missing STELLAR_QUICKSTART_IMAGE / STELLAR_PROTOCOL_VERSION", + ); + } + return { image, protocolVersion }; +} + +const STELLAR_PIN = loadStellarPin(); + export interface Infrastructure { rpcUrl: string; friendbotUrl: string; @@ -58,10 +87,12 @@ export async function startStellar(): Promise<{ STELLAR_CONTAINER, "-p", `${STELLAR_PORT}:8000`, - "stellar/quickstart:latest", + STELLAR_PIN.image, "--local", "--limits", "unlimited", + "--protocol-version", + STELLAR_PIN.protocolVersion, ]); // Wait for RPC health diff --git a/multi-asset/docker-compose.yml b/multi-asset/docker-compose.yml index bc20dd7..a90a358 100644 --- a/multi-asset/docker-compose.yml +++ b/multi-asset/docker-compose.yml @@ -11,8 +11,11 @@ services: stellar: - image: stellar/quickstart:v639-b1103.1-latest - command: --local --limits unlimited + # Pinned build + protocol come from stellar-pin.env (SSOT). --protocol-version is a + # WORKAROUND for the image's broken default (protocol 25 on a stellar-core v27 image), + # which fails contract upload; remove it when a fixed image ships. See stellar-pin.env. + image: ${STELLAR_QUICKSTART_IMAGE} + command: --local --limits unlimited --protocol-version ${STELLAR_PROTOCOL_VERSION} healthcheck: test: > curl -sf http://localhost:8000/soroban/rpc -X POST diff --git a/stellar-pin.env b/stellar-pin.env new file mode 100644 index 0000000..d73a603 --- /dev/null +++ b/stellar-pin.env @@ -0,0 +1,18 @@ +# Single source of truth for the Stellar quickstart image + local-network protocol. +# +# Read by: every docker-compose.*.yml (root) and e2e/, lifecycle/, multi-asset/ +# compose files; test.sh; infra-up.sh; down.sh; lifecycle/provider.ts; and the +# .github/workflows/quickstart-drift.yml alarm. Keep these two values here ONLY — +# every consumer sources this file so they cannot diverge. +# +# WORKAROUND: --protocol-version is passed to the local network because the +# quickstart image ships a broken default (it selects protocol 25 on an image +# whose bundled stellar-core is v27, which fails every contract upload with +# HostError(Context, InternalError)). Forcing the protocol to match the core +# makes uploads/deploys work. Remove the override once a future quickstart image +# ships a correct default. See ClickUp 86ca9wd2e. +# +# When testnet advances past STELLAR_PROTOCOL_VERSION, the drift alarm fires; +# bump the pin (image tag + protocol) here. +STELLAR_QUICKSTART_IMAGE=stellar/quickstart:v642-b1163.1-latest +STELLAR_PROTOCOL_VERSION=27 diff --git a/test.sh b/test.sh index 456c46c..2207dba 100755 --- a/test.sh +++ b/test.sh @@ -17,6 +17,14 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BASE_DIR="${BASE_DIR:-$(dirname "$SCRIPT_DIR")}" +# Stellar quickstart pin (image tag + local-network protocol) — single source of +# truth, shared with every compose file, infra-up.sh, provider.ts and the drift +# alarm. Exported so the docker compose invocations below interpolate it. +set -a +# shellcheck source=stellar-pin.env +. "$SCRIPT_DIR/stellar-pin.env" +set +a + RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -91,6 +99,8 @@ run_suite() { MOONLIGHT_PAY_PATH="${MOONLIGHT_PAY_PATH:-${BASE_DIR}/moonlight-pay}" \ PROVIDER_CONSOLE_PATH="${PROVIDER_CONSOLE_PATH:-${BASE_DIR}/provider-console}" \ COUNCIL_CONSOLE_PATH="${COUNCIL_CONSOLE_PATH:-${BASE_DIR}/council-console}" \ + STELLAR_QUICKSTART_IMAGE="$STELLAR_QUICKSTART_IMAGE" \ + STELLAR_PROTOCOL_VERSION="$STELLAR_PROTOCOL_VERSION" \ docker compose -f "$compose_file" -p "$project_name" up -d # Stream test-runner logs and wait for it to finish From a99082d93c5dae4f5499a1630fb6cc2a1241a417 Mon Sep 17 00:00:00 2001 From: Gorka Date: Tue, 7 Jul 2026 16:03:17 -0300 Subject: [PATCH 2/2] fix(ci): load quickstart pin SSOT in e2e.yml pull_request job e2e.yml runs docker compose inline on PRs (it does not call the reusable), so it also needs the SSOT loaded into $GITHUB_ENV before compose can interpolate ${STELLAR_QUICKSTART_IMAGE}/${STELLAR_PROTOCOL_VERSION}. --- .github/workflows/e2e.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 2ede620..9df7802 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -80,6 +80,14 @@ jobs: fi ls -la e2e/wasms/ + - name: Load Stellar quickstart pin (SSOT) + run: | + set -a + . "$GITHUB_WORKSPACE/stellar-pin.env" + set +a + echo "STELLAR_QUICKSTART_IMAGE=$STELLAR_QUICKSTART_IMAGE" >> "$GITHUB_ENV" + echo "STELLAR_PROTOCOL_VERSION=$STELLAR_PROTOCOL_VERSION" >> "$GITHUB_ENV" + - name: Run E2E tests env: STELLAR_CLI_IMAGE: ${{ env.REGISTRY }}/${{ env.ORG }}/stellar-cli:${{ steps.versions.outputs.stellar_cli }}