From 022d2bfb7b07c4102367521aa760b0a28c432b28 Mon Sep 17 00:00:00 2001 From: Leigh <351529+leighmcculloch@users.noreply.github.com> Date: Wed, 17 Jun 2026 06:36:39 +0000 Subject: [PATCH] add workflow to auto-update testnet settings Add a scheduled GitHub Actions workflow and a supporting .scripts/update-config-settings script that regenerate, for the protocol version testnet is currently running, both testnet.json (from `stellar network settings`) and unlimited.json (testnet.json with the protocol's unlimited overrides applied), sorted for stable output, and open a PR when the committed values drift. A protocol the repo has no directory for yet is created and seeded from the most recent prior protocol. --- .github/workflows/update-config-settings.yml | 58 +++++++++++++ .scripts/update-config-settings | 86 ++++++++++++++++++++ local/core/etc/config-settings/README.md | 2 + 3 files changed, 146 insertions(+) create mode 100644 .github/workflows/update-config-settings.yml create mode 100755 .scripts/update-config-settings diff --git a/.github/workflows/update-config-settings.yml b/.github/workflows/update-config-settings.yml new file mode 100644 index 000000000..6c9f62fe9 --- /dev/null +++ b/.github/workflows/update-config-settings.yml @@ -0,0 +1,58 @@ +name: Update Config Settings + +on: + schedule: + - cron: '0 12 * * 1' # Weekly, Monday + workflow_dispatch: + +jobs: + + update: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + + - name: Install stellar-cli + uses: stellar/stellar-cli@v26.1.0 + + - name: Update config settings + id: update + run: | + dir="$(./.scripts/update-config-settings)" + echo "dir=$dir" >> "$GITHUB_OUTPUT" + + - name: Create Pull Request + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DIR: ${{ steps.update.outputs.dir }} + run: | + if [ -z "$(git status --porcelain -- "$DIR")" ]; then + echo "Config settings already in sync" + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + hash="$(git hash-object "$DIR/testnet.json" | cut -c1-16)" + branch="update-config-settings/${hash}" + + # Check if PR already exists for this exact update + if gh pr list --head "$branch" --state open --json number --jq '.[0].number' | grep -q .; then + echo "PR already exists for this update" + exit 0 + fi + + git checkout -b "$branch" + git add "$DIR" + git commit -m "Update testnet config settings (${hash})" + git push -u origin "$branch" + + gh pr create \ + --title "Update testnet config settings (${hash})" \ + --body "This PR was automatically generated by the Update Config Settings workflow because the committed testnet config settings drifted from live testnet. Please review the value changes before merging." \ + --base main \ + --head "$branch" diff --git a/.scripts/update-config-settings b/.scripts/update-config-settings new file mode 100755 index 000000000..f63893a29 --- /dev/null +++ b/.scripts/update-config-settings @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# Regenerates the testnet config settings for the protocol version that testnet +# is currently running. +# +# Fetches testnet's upgradeable Soroban config settings as a ConfigUpgradeSet +# (the format consumed by `stellar-xdr encode --type ConfigUpgradeSet` in the +# start script) and writes them, sorted for stable output, to testnet.json for +# the protocol version in effect when retrieved. Internally-maintained, +# non-upgradeable settings are excluded (no --internal). +# +# unlimited.json is regenerated alongside it: it is testnet.json with a set of +# "unlimited" scalar overrides applied (cost-param arrays carry through from +# testnet unchanged). The overrides are taken from this protocol's own committed +# unlimited.json, preserving its hand-tuning; for a protocol the repo has no +# directory for yet, they are taken from the most recent prior protocol, and +# settings introduced by the new protocol keep their testnet values until tuned +# by hand in the resulting PR. +# +# Usage: ./.scripts/update-config-settings +set -euo pipefail + +config_settings_dir="local/core/etc/config-settings" + +protocol="$(stellar ledger latest --network testnet --output json | jq -r '.protocolVersion')" +if ! [[ "$protocol" =~ ^[0-9]+$ ]]; then + echo "error: could not determine testnet protocol version (got '$protocol')" >&2 + exit 1 +fi + +# stellar-cli's major version tracks the protocol it supports. If testnet has +# advanced beyond it, `stellar network settings` silently omits settings the +# older XDR doesn't know about, so fail loudly to prompt bumping the pinned CLI. +cli_protocol="$(stellar version | head -n1 | sed -E 's/^stellar ([0-9]+).*/\1/')" +if [ "$protocol" -gt "$cli_protocol" ]; then + echo "error: testnet is on protocol $protocol but stellar-cli supports protocol $cli_protocol; bump the pinned stellar-cli version" >&2 + exit 1 +fi + +dir="$config_settings_dir/p$protocol" +mkdir -p "$dir" + +# Pick the source of the unlimited overrides before overwriting anything: this +# protocol's own committed pair if present, else the most recent prior protocol. +override_t="" +override_u="" +if [ -f "$dir/testnet.json" ] && [ -f "$dir/unlimited.json" ]; then + override_t="$dir/testnet.json" + override_u="$dir/unlimited.json" +else + for ((v = protocol - 1; v >= 20; v--)); do + if [ -f "$config_settings_dir/p$v/testnet.json" ] && [ -f "$config_settings_dir/p$v/unlimited.json" ]; then + override_t="$config_settings_dir/p$v/testnet.json" + override_u="$config_settings_dir/p$v/unlimited.json" + break + fi + done +fi +if [ -z "$override_u" ]; then + echo "error: no protocol with testnet.json and unlimited.json to derive unlimited overrides from" >&2 + exit 1 +fi + +# Fetch testnet's settings, sorted for stable output. +testnet="$(stellar network settings --network testnet --output json | jq -S .)" + +# unlimited.json = testnet.json with the unlimited overrides (the leaf fields +# where the override source's unlimited.json differs from its testnet.json). +unlimited="$(printf '%s' "$testnet" | jq -S --slurpfile t "$override_t" --slurpfile u "$override_u" ' + ( $t[0].updated_entry | map({(keys[0]): .[keys[0]]}) | add ) as $T + | ( $u[0].updated_entry | map({(keys[0]): .[keys[0]]}) | add ) as $U + | { updated_entry: ( .updated_entry | map( + (keys[0]) as $n | .[$n] as $v + | { ($n): + ( if ($U[$n] == null) or ($T[$n] == null) then $v + elif ($U[$n] | type) == "object" + then reduce ($U[$n] | paths(scalars)) as $p + ($v; + if ($U[$n] | getpath($p)) != ($T[$n] | getpath($p)?) + then setpath($p; ($U[$n] | getpath($p))) else . end) + else (if $U[$n] != $T[$n] then $U[$n] else $v end) + end ) } ) ) } +')" + +printf '%s\n' "$testnet" > "$dir/testnet.json" +printf '%s\n' "$unlimited" > "$dir/unlimited.json" +echo "$dir" diff --git a/local/core/etc/config-settings/README.md b/local/core/etc/config-settings/README.md index 89c8883eb..08d95960f 100644 --- a/local/core/etc/config-settings/README.md +++ b/local/core/etc/config-settings/README.md @@ -1,2 +1,4 @@ # config-settings This directory contains Soroban settings upgrade files for each default protocol version (set with the arg `PROTOCOL_VERSION_DEFAULT`) specified on each on the three builds (`latest`, `testing`, and `future`). They need to be separated by protocol version because the number of cost types is dependant on protocol, and we specifically want to capture the cpu cost type changes we make outside of protocol boundaries. + +The `testnet.json` and `unlimited.json` for the protocol version testnet is currently running are kept up to date by the `Update Config Settings` workflow, which opens a PR when the committed values drift.