From 8eca835860d0415f2029fda492caa711cac9c18d Mon Sep 17 00:00:00 2001 From: David Meister Date: Sun, 28 Jun 2026 09:26:53 +0000 Subject: [PATCH 1/3] feat(deploy): add shared check-published-deploy-constants script Parameterizes the deploy-constants completeness check that raindex currently hardcodes in script/check-published-deploy-constants.sh. Exposes check-published-deploy-constants on sol-shell PATH so any consumer repo can run it via: nix develop github:rainlanguage/rainix#sol-shell \ -c check-published-deploy-constants ... Co-Authored-By: Claude --- flake.nix | 11 ++++ lib/check-published-deploy-constants.sh | 60 +++++++++++++++++++ .../devshell/sol-shell/sol-tasks.test.bats | 5 ++ 3 files changed, 76 insertions(+) create mode 100644 lib/check-published-deploy-constants.sh diff --git a/flake.nix b/flake.nix index 23aa1fc..7daf0e2 100644 --- a/flake.nix +++ b/flake.nix @@ -273,6 +273,15 @@ additionalBuildInputs = [ pkgs.git ]; }; + rainix-check-deploy-constants = mkTask { + name = "check-published-deploy-constants"; + body = '' + set -euo pipefail + exec ${./lib/check-published-deploy-constants.sh} "$@" + ''; + additionalBuildInputs = [ pkgs.curl pkgs.gnugrep ]; + }; + rainix-rs-static = mkTask { name = "rainix-rs-static"; body = '' @@ -286,6 +295,7 @@ sol-tasks = [ rainix-sol-artifacts rainix-sol-single-contract + rainix-check-deploy-constants ]; rs-tasks = [ @@ -586,6 +596,7 @@ inherit rainix-sol-artifacts rainix-sol-single-contract + rainix-check-deploy-constants rainix-rs-static prettier-bundle sol-shell-test diff --git a/lib/check-published-deploy-constants.sh b/lib/check-published-deploy-constants.sh new file mode 100644 index 0000000..b26a718 --- /dev/null +++ b/lib/check-published-deploy-constants.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LicenseRef-DCL-1.0 +# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +# +# Checks that every version published to the soldeer registry for a given +# package has a full suite of pinned deploy constants in the specified Solidity +# file. For each published version and each constant prefix, asserts that both +# a DEPLOYED_ADDRESS_ and DEPLOYED_CODEHASH_ constant exist (where +# is the version string with dots replaced by underscores). +# +# Usage: +# check-published-deploy-constants [ ...] +# +# Arguments: +# soldeer-package soldeer package name to query (e.g. "raindex") +# deploy-lib-path path to the Solidity file holding the constants +# prefix... one or more constant name prefixes +# +# Output (always exits 0): +# OK every published version has its full constant suite +# MISSING: one or more expected constants are absent +# SKIP: the registry could not be reached (nothing verified) + +set -euo pipefail + +if [ "$#" -lt 3 ]; then + printf 'Usage: check-published-deploy-constants [...]\n' >&2 + exit 1 +fi + +package="$1" +lib="$2" +shift 2 + +versions=$( + curl -fsS "https://api.soldeer.xyz/api/v1/revision?project_name=${package}" 2>/dev/null \ + | grep -oE '"version":"[0-9][0-9.]*"' | cut -d'"' -f4 | sort -u +) || true + +if [ -z "$versions" ]; then + printf 'SKIP: could not fetch published soldeer versions' + exit 0 +fi + +missing="" +for v in $versions; do + suffix=$(printf '%s' "$v" | tr . _) + for p in "$@"; do + for kind in ADDRESS CODEHASH; do + name="${p}_${kind}_${suffix}" + grep -qE "constant ${name} =" "$lib" || missing="${missing} ${name}" + done + done +done + +if [ -n "$missing" ]; then + printf 'MISSING:%s' "$missing" +else + printf 'OK' +fi diff --git a/test/bats/devshell/sol-shell/sol-tasks.test.bats b/test/bats/devshell/sol-shell/sol-tasks.test.bats index 298eab0..510917e 100644 --- a/test/bats/devshell/sol-shell/sol-tasks.test.bats +++ b/test/bats/devshell/sol-shell/sol-tasks.test.bats @@ -8,3 +8,8 @@ run command -v rainix-sol-artifacts [ "$status" -eq 0 ] } + +@test "check-published-deploy-constants should be available on PATH" { + run command -v check-published-deploy-constants + [ "$status" -eq 0 ] +} From c882a42ff1837d7db9b5179ed743393d8ae331e1 Mon Sep 17 00:00:00 2001 From: David Meister Date: Sun, 28 Jun 2026 13:05:15 +0000 Subject: [PATCH 2/3] fix(ci): rainix-check-shell [3b-attempt] nixfmt format additionalBuildInputs list Co-Authored-By: Claude --- flake.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 7daf0e2..f822d8b 100644 --- a/flake.nix +++ b/flake.nix @@ -279,7 +279,10 @@ set -euo pipefail exec ${./lib/check-published-deploy-constants.sh} "$@" ''; - additionalBuildInputs = [ pkgs.curl pkgs.gnugrep ]; + additionalBuildInputs = [ + pkgs.curl + pkgs.gnugrep + ]; }; rainix-rs-static = mkTask { From 8931c98ae19a9816b03f82babcb257f677d107a5 Mon Sep 17 00:00:00 2001 From: David Meister Date: Sun, 2 Aug 2026 13:14:34 +0000 Subject: [PATCH 3/3] test(deploy): cover check-published-deploy-constants and split fetch from parse CodeRabbit flagged that the `|| true` on the whole version pipeline collapsed a successful fetch that yielded no parseable versions into the connectivity `SKIP: could not fetch published soldeer versions`, masking parser/API drift as an unreachable registry. Split the two. The curl invocation is now guarded on its own, so only a real request failure reports the connectivity SKIP. A reachable registry whose response yields no versions gets its own `SKIP: no versions parsed from registry response`, which is still a zero-exit SKIP (nothing was verified) but names a cause an operator can act on. Also fixes the tracked file mode. The flake task execs the script directly from its nix store path, and the store canonicalizes the git mode, so a 644 file becomes 0444 and the task dies with EACCES. Marking it 755 makes the store copy 0555. A bats case asserts the exec bit so this cannot silently regress. `cut`, `sort` and `tr` in the version pipeline came from the ambient PATH, which mkTask only prefixes rather than replaces; coreutils is now a declared build input of the task. Documents why a malformed invocation keeps its nonzero exit. The OK/MISSING/SKIP success-only contract describes REGISTRY state, which must never red a consumer pipeline. Fewer than three arguments is a caller bug, not a registry state, and a miswired CI step must fail loud rather than skip forever. New bats suite covers the exec bit, the usage exit, both SKIP branches, OK, and MISSING across multiple prefixes, with curl stubbed so no test touches the network. Wired into the default-shell-test task alongside the existing suites. Co-Authored-By: Claude --- flake.nix | 3 + lib/check-published-deploy-constants.sh | 24 +++++- ...check-published-deploy-constants.test.bats | 85 +++++++++++++++++++ 3 files changed, 108 insertions(+), 4 deletions(-) mode change 100644 => 100755 lib/check-published-deploy-constants.sh create mode 100644 test/bats/task/check-published-deploy-constants.test.bats diff --git a/flake.nix b/flake.nix index f822d8b..08ce7a3 100644 --- a/flake.nix +++ b/flake.nix @@ -282,6 +282,8 @@ additionalBuildInputs = [ pkgs.curl pkgs.gnugrep + # cut/sort/tr in the version-parsing pipeline. + pkgs.coreutils ]; }; @@ -394,6 +396,7 @@ bats test/bats/task/subgraph-build.test.bats bats test/bats/task/subgraph-deploy-version.test.bats bats test/bats/task/sol-single-contract.test.bats + bats test/bats/task/check-published-deploy-constants.test.bats ''; additionalBuildInputs = [ pkgs.bats ] ++ sol-build-inputs ++ node-build-inputs; }; diff --git a/lib/check-published-deploy-constants.sh b/lib/check-published-deploy-constants.sh old mode 100644 new mode 100755 index b26a718..fbe13c7 --- a/lib/check-published-deploy-constants.sh +++ b/lib/check-published-deploy-constants.sh @@ -16,10 +16,17 @@ # deploy-lib-path path to the Solidity file holding the constants # prefix... one or more constant name prefixes # -# Output (always exits 0): +# Output for a well-formed invocation (always exits 0, so registry state never +# reds a consumer pipeline): # OK every published version has its full constant suite # MISSING: one or more expected constants are absent -# SKIP: the registry could not be reached (nothing verified) +# SKIP: nothing was verified; the reason distinguishes an +# unreachable registry from a reachable registry whose +# response yielded no parseable versions +# +# A malformed invocation (fewer than 3 arguments) is a caller bug, not a +# registry state: usage goes to stderr and the exit status is 1, so a miswired +# CI step fails loud instead of silently skipping forever. set -euo pipefail @@ -32,13 +39,22 @@ package="$1" lib="$2" shift 2 +if ! response=$(curl -fsS "https://api.soldeer.xyz/api/v1/revision?project_name=${package}" 2>/dev/null); then + printf 'SKIP: could not fetch published soldeer versions' + exit 0 +fi + +# grep exits non-zero on zero matches; that is the legitimate +# "registry reachable but no versions parsed" case (e.g. a package with no +# published releases yet), kept distinct from the connectivity SKIP above so +# parser/API drift is never masked as an unreachable registry. versions=$( - curl -fsS "https://api.soldeer.xyz/api/v1/revision?project_name=${package}" 2>/dev/null \ + printf '%s' "$response" \ | grep -oE '"version":"[0-9][0-9.]*"' | cut -d'"' -f4 | sort -u ) || true if [ -z "$versions" ]; then - printf 'SKIP: could not fetch published soldeer versions' + printf 'SKIP: no versions parsed from registry response' exit 0 fi diff --git a/test/bats/task/check-published-deploy-constants.test.bats b/test/bats/task/check-published-deploy-constants.test.bats new file mode 100644 index 0000000..3685225 --- /dev/null +++ b/test/bats/task/check-published-deploy-constants.test.bats @@ -0,0 +1,85 @@ +setup() { + TESTDIR="$(mktemp -d)" + # Stub curl on PATH so no test touches the network. STUB_CURL_FAIL simulates + # an unreachable registry; otherwise the stub prints the file named by + # STUB_CURL_PAYLOAD as the registry response. + mkdir -p "$TESTDIR/bin" + cat > "$TESTDIR/bin/curl" << 'EOF' +#!/usr/bin/env bash +if [ -n "${STUB_CURL_FAIL:-}" ]; then + exit 22 +fi +cat "$STUB_CURL_PAYLOAD" +EOF + chmod +x "$TESTDIR/bin/curl" + PATH="$TESTDIR/bin:$PATH" + SCRIPT="./lib/check-published-deploy-constants.sh" +} + +teardown() { + rm -rf "$TESTDIR" +} + +_write_payload() { + STUB_CURL_PAYLOAD="$TESTDIR/payload.json" + export STUB_CURL_PAYLOAD + cat > "$STUB_CURL_PAYLOAD" +} + +# The flake task execs the script's nix store copy directly, and the store +# canonicalizes the git file mode (644 -> 0444, 755 -> 0555), so the tracked +# file must carry the exec bit or the task dies with EACCES. +@test "script is executable" { + [ -x "$SCRIPT" ] +} + +@test "malformed invocation prints usage to stderr and exits 1" { + run "$SCRIPT" only-two-args "$TESTDIR/lib.sol" + [ "$status" -eq 1 ] + [[ "$output" == *"Usage: check-published-deploy-constants"* ]] +} + +@test "unreachable registry prints connectivity SKIP and exits 0" { + export STUB_CURL_FAIL=1 + run "$SCRIPT" somepkg "$TESTDIR/lib.sol" DEPLOYED + [ "$status" -eq 0 ] + [ "$output" = "SKIP: could not fetch published soldeer versions" ] +} + +@test "reachable registry with no parseable versions prints a distinct SKIP and exits 0" { + _write_payload << 'EOF' +{"data":[],"status":"success"} +EOF + run "$SCRIPT" somepkg "$TESTDIR/lib.sol" DEPLOYED + [ "$status" -eq 0 ] + [ "$output" = "SKIP: no versions parsed from registry response" ] +} + +@test "prints OK when every published version has its full constant suite" { + _write_payload << 'EOF' +{"data":[{"version":"1.0.0"},{"version":"1.2.3"}]} +EOF + cat > "$TESTDIR/lib.sol" << 'EOF' +address constant DEPLOYED_ADDRESS_1_0_0 = address(1); +bytes32 constant DEPLOYED_CODEHASH_1_0_0 = bytes32(0); +address constant DEPLOYED_ADDRESS_1_2_3 = address(2); +bytes32 constant DEPLOYED_CODEHASH_1_2_3 = bytes32(0); +EOF + run "$SCRIPT" somepkg "$TESTDIR/lib.sol" DEPLOYED + [ "$status" -eq 0 ] + [ "$output" = "OK" ] +} + +@test "prints MISSING with each absent constant name across prefixes and exits 0" { + _write_payload << 'EOF' +{"data":[{"version":"1.0.0"}]} +EOF + cat > "$TESTDIR/lib.sol" << 'EOF' +address constant OBV2_ADDRESS_1_0_0 = address(1); +bytes32 constant OBV2_CODEHASH_1_0_0 = bytes32(0); +address constant OBV3_ADDRESS_1_0_0 = address(2); +EOF + run "$SCRIPT" somepkg "$TESTDIR/lib.sol" OBV2 OBV3 + [ "$status" -eq 0 ] + [ "$output" = "MISSING: OBV3_CODEHASH_1_0_0" ] +}