diff --git a/.github/workflows/rainix-autopublish.yaml b/.github/workflows/rainix-autopublish.yaml index 6d3bd70..fc3de26 100644 --- a/.github/workflows/rainix-autopublish.yaml +++ b/.github/workflows/rainix-autopublish.yaml @@ -41,6 +41,8 @@ on: required: false SOLDEER_API_TOKEN: required: false +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: release: if: ${{ !startsWith(github.event.head_commit.message, 'Package Release') }} @@ -123,7 +125,7 @@ jobs: # until that sibling is published — chicken-and-egg. If packaging # fails, treat the crate as changed and let the dependency-ordered # publish step sort it out (the sibling publishes first). - if ! nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo package -p "$CRATE" --allow-dirty --no-verify --quiet; then + if ! nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c cargo package -p "$CRATE" --allow-dirty --no-verify --quiet; then echo "gate $CRATE: cannot package yet (likely an unpublished intra-workspace dep) -> changed" changed=true continue @@ -152,7 +154,7 @@ jobs: if: ${{ inputs.npm-package != '' }} id: npm run: | - nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-node-shell -c bash -c ' + nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-node-shell -c bash -c ' OLD=$(npm view ${{ inputs.npm-package }}@latest dist.shasum 2>/dev/null || echo "none") NEW=$(npm pack --silent | xargs shasum | cut -c1-40) rm -f *.tgz @@ -191,7 +193,7 @@ jobs: LOCAL=$(awk -F\" '/^version[[:space:]]*=/ { print $2; exit }' foundry.toml) # Local package content: dry-run writes .zip into the cwd. rm -f ./*.zip - nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge soldeer push "$PKG~$LOCAL" --dry-run + nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge soldeer push "$PKG~$LOCAL" --dry-run NEW=$(nh "$(ls -t ./*.zip | head -1)"); rm -f ./*.zip if [ -z "$URL" ] || [ "$REMOTE" = "none" ]; then OLD="none"; else curl -fsSL "$URL" -o /tmp/soldeer_pub.zip; OLD=$(nh /tmp/soldeer_pub.zip); fi @@ -217,7 +219,7 @@ jobs: # gate on the exact merged commit. - name: Cargo test if: ${{ (inputs.crates != '' || inputs.crate != '') && (steps.cargo.outputs.changed == 'true' || steps.npm.outputs.changed == 'true' || steps.soldeer.outputs.changed == 'true') }} - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo test --workspace + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c cargo test --workspace # Bump versions. One cargo-release invocation bumps every listed crate and # rewrites their inter-crate version pins atomically in one commit. # @@ -233,12 +235,12 @@ jobs: set -euo pipefail PKGS="" for CRATE in $CRATES; do PKGS="$PKGS -p $CRATE"; done - nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo release --no-confirm --execute --no-tag --no-push --no-publish $PKGS ${{ inputs.level }} + nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c cargo release --no-confirm --execute --no-tag --no-push --no-publish $PKGS ${{ inputs.level }} git commit --amend -m "Package Release: cargo version bump ($CRATES)" - name: Bump NPM version if: ${{ inputs.npm-package != '' && steps.npm.outputs.changed == 'true' }} run: | - NEW=$(nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-node-shell -c npm version patch --no-git-tag-version) + NEW=$(nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-node-shell -c npm version patch --no-git-tag-version) echo "NPM_VERSION=$NEW" >> $GITHUB_ENV git add package.json package-lock.json git commit -m "Package Release npm-${NEW}" @@ -270,13 +272,13 @@ jobs: set -euo pipefail git fetch origin if ! git rebase "origin/${{ github.ref_name }}"; then - nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo generate-lockfile + nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c cargo generate-lockfile git add Cargo.lock GIT_EDITOR=true git rebase --continue fi if [ "${{ steps.cargo.outputs.changed }}" = "true" ]; then for CRATE in $CRATES; do - VER=$(nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo pkgid -p "$CRATE" | cut -d@ -f2) + VER=$(nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c cargo pkgid -p "$CRATE" | cut -d@ -f2) git tag "$CRATE-v$VER" done fi @@ -288,7 +290,7 @@ jobs: - name: NPM pack if: ${{ inputs.npm-package != '' && steps.npm.outputs.changed == 'true' }} run: | - TARBALL=$(nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-node-shell -c npm pack --silent) + TARBALL=$(nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-node-shell -c npm pack --silent) mv "$TARBALL" npm_package_${{ env.NPM_VERSION }}.tgz # Publishes. Crates publish in dependency order so each crate's deps are # already on the registry when it publishes. @@ -297,7 +299,7 @@ jobs: run: | set -euo pipefail for CRATE in $CRATES; do - nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo publish -p "$CRATE" + nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c cargo publish -p "$CRATE" done env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} @@ -312,14 +314,14 @@ jobs: if: ${{ inputs.soldeer-package != '' && steps.soldeer.outputs.changed == 'true' }} env: SOLDEER_API_TOKEN: ${{ secrets.SOLDEER_API_TOKEN }} - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge soldeer push "${{ inputs.soldeer-package }}~${{ steps.soldeer.outputs.next }}" + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge soldeer push "${{ inputs.soldeer-package }}~${{ steps.soldeer.outputs.next }}" # GitHub Releases. One per crate (loop), plus npm/soldeer. - name: GitHub Release (cargo) if: ${{ steps.cargo.outputs.changed == 'true' }} run: | set -euo pipefail for CRATE in $CRATES; do - VER=$(nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo pkgid -p "$CRATE" | cut -d@ -f2) + VER=$(nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c cargo pkgid -p "$CRATE" | cut -d@ -f2) gh release create "$CRATE-v$VER" --title "$CRATE-v$VER" --notes "Automated release of $CRATE $VER." || true done env: diff --git a/.github/workflows/rainix-copy-artifacts.yaml b/.github/workflows/rainix-copy-artifacts.yaml index 5e7f711..027f12f 100644 --- a/.github/workflows/rainix-copy-artifacts.yaml +++ b/.github/workflows/rainix-copy-artifacts.yaml @@ -1,6 +1,8 @@ name: rainix-copy-artifacts on: workflow_call: +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: copy-artifacts: runs-on: ubuntu-latest @@ -18,7 +20,7 @@ jobs: # keep the cache; copy-artifacts skips the save/restore overhead. - name: Install soldeer dependencies if: hashFiles('soldeer.lock') != '' - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge soldeer install + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge soldeer install # Currency-check every committed generated artifact by re-running each # consumer-provided codegen step. The final git diff fails if any # committed file has drifted from its source. The build-meta.sh hook is @@ -26,15 +28,15 @@ jobs: # filenames, meta type) varies per repo. - name: Regenerate meta artifacts if: hashFiles('script/build-meta.sh') != '' - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c ./script/build-meta.sh + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c ./script/build-meta.sh - name: Regenerate pointer artifacts if: hashFiles('script/BuildPointers.sol') != '' - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge script ./script/BuildPointers.sol + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge script ./script/BuildPointers.sol - name: Build Solidity - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge build + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge build - name: Copy forge artifacts into committed location if: hashFiles('script/CopyArtifacts.sol') != '' - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge script ./script/CopyArtifacts.sol --ffi + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge script ./script/CopyArtifacts.sol --ffi # Catch-all post-forge regen hook: consumer-supplied. Runs outside any # nix devshell so the script picks shells per command (subgraph-shell, # sol-shell, etc.) for whatever derived artifacts it emits. @@ -42,7 +44,7 @@ jobs: if: hashFiles('script/build.sh') != '' run: ./script/build.sh - name: Format (so generated artifacts match committed style) - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge fmt + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge fmt - name: Assert committed artifacts match freshly built run: | if ! git diff --exit-code; then diff --git a/.github/workflows/rainix-manual-sol-artifacts.yaml b/.github/workflows/rainix-manual-sol-artifacts.yaml index c64e551..c45a37d 100644 --- a/.github/workflows/rainix-manual-sol-artifacts.yaml +++ b/.github/workflows/rainix-manual-sol-artifacts.yaml @@ -41,6 +41,8 @@ on: required: false CI_DEPLOY_FLARE_ETHERSCAN_API_KEY: required: false +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: deploy: runs-on: ubuntu-latest @@ -64,9 +66,9 @@ jobs: foundry-full-${{ runner.os }}- - name: Install soldeer dependencies if: hashFiles('soldeer.lock') != '' - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge soldeer install - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge selectors up --all - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge script script/Deploy.sol:Deploy -vvvvv --slow --broadcast --verify + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge soldeer install + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge selectors up --all + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge script script/Deploy.sol:Deploy -vvvvv --slow --broadcast --verify env: DEPLOYMENT_SUITE: ${{ inputs.suite }} DEPLOYMENT_KEY: ${{ secrets.PRIVATE_KEY }} diff --git a/.github/workflows/rainix-rs-static.yaml b/.github/workflows/rainix-rs-static.yaml index a9f6d1c..ec374a1 100644 --- a/.github/workflows/rainix-rs-static.yaml +++ b/.github/workflows/rainix-rs-static.yaml @@ -1,6 +1,8 @@ name: rainix-rs-static on: workflow_call: +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: rs-static: runs-on: ubuntu-latest @@ -14,7 +16,7 @@ jobs: with: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - uses: rainlanguage/rainix/.github/actions/rust-cache@main - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c rainix-rs-static + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c rainix-rs-static # Run the rainix pre-commit hook bundle (taplo, the nix hooks, yamlfmt, # shellcheck, rustfmt) over all files — the same hooks a local commit # runs. rainix-rs-static only covers rustfmt + clippy, so a file that is @@ -24,4 +26,4 @@ jobs: # rather than re-paying runner setup. Reuses rust-shell, so prettier-rainix # (which needs the default shell's RAINIX_PRETTIER_BUNDLE_DIR) no-ops here; # frontend repos format svelte/ts via their own pipeline. - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c pre-commit run --all-files --show-diff-on-failure --color always + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c pre-commit run --all-files --show-diff-on-failure --color always diff --git a/.github/workflows/rainix-rs-test.yaml b/.github/workflows/rainix-rs-test.yaml index da4ae9a..e781038 100644 --- a/.github/workflows/rainix-rs-test.yaml +++ b/.github/workflows/rainix-rs-test.yaml @@ -1,6 +1,8 @@ name: rainix-rs-test on: workflow_call: +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: rs-test: strategy: @@ -18,4 +20,4 @@ jobs: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} gc-max-store-size-macos: 1G - uses: rainlanguage/rainix/.github/actions/rust-cache@main - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo test + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c cargo test diff --git a/.github/workflows/rainix-rs-wasm-test.yaml b/.github/workflows/rainix-rs-wasm-test.yaml index 0149174..ca1bd6d 100644 --- a/.github/workflows/rainix-rs-wasm-test.yaml +++ b/.github/workflows/rainix-rs-wasm-test.yaml @@ -1,6 +1,8 @@ name: rainix-rs-wasm-test on: workflow_call: +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: rs-wasm-test: runs-on: ubuntu-latest @@ -12,4 +14,4 @@ jobs: with: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - uses: rainlanguage/rainix/.github/actions/rust-cache@main - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c bash -c "CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner cargo test --target wasm32-unknown-unknown --workspace" + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c bash -c "CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner cargo test --target wasm32-unknown-unknown --workspace" diff --git a/.github/workflows/rainix-rs-wasm.yaml b/.github/workflows/rainix-rs-wasm.yaml index 3124061..9612145 100644 --- a/.github/workflows/rainix-rs-wasm.yaml +++ b/.github/workflows/rainix-rs-wasm.yaml @@ -1,6 +1,8 @@ name: rainix-rs-wasm on: workflow_call: +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: rs-wasm: runs-on: ubuntu-latest @@ -12,4 +14,4 @@ jobs: with: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - uses: rainlanguage/rainix/.github/actions/rust-cache@main - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo build -r --target wasm32-unknown-unknown --lib --workspace + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#rust-shell -c cargo build -r --target wasm32-unknown-unknown --lib --workspace diff --git a/.github/workflows/rainix-sol-legal.yaml b/.github/workflows/rainix-sol-legal.yaml index ccf52cd..1bb1c0d 100644 --- a/.github/workflows/rainix-sol-legal.yaml +++ b/.github/workflows/rainix-sol-legal.yaml @@ -1,6 +1,8 @@ name: rainix-sol-legal on: workflow_call: +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: legal: runs-on: ubuntu-latest @@ -13,5 +15,5 @@ jobs: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Install soldeer dependencies if: hashFiles('soldeer.lock') != '' - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge soldeer install - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c reuse lint + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge soldeer install + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c reuse lint diff --git a/.github/workflows/rainix-sol-static.yaml b/.github/workflows/rainix-sol-static.yaml index bce9da2..6b758fa 100644 --- a/.github/workflows/rainix-sol-static.yaml +++ b/.github/workflows/rainix-sol-static.yaml @@ -1,6 +1,8 @@ name: rainix-sol-static on: workflow_call: +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: static: runs-on: ubuntu-latest @@ -24,8 +26,8 @@ jobs: foundry-full-${{ runner.os }}- - name: Install soldeer dependencies if: hashFiles('soldeer.lock') != '' - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge soldeer install - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c slither . - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge fmt --check + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge soldeer install + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c slither . + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge fmt --check # Enforce Rain's one-contract-per-.sol-file convention (rainix#214). - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c rainix-sol-single-contract + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c rainix-sol-single-contract diff --git a/.github/workflows/rainix-sol-test.yaml b/.github/workflows/rainix-sol-test.yaml index 8b225d9..d4bc239 100644 --- a/.github/workflows/rainix-sol-test.yaml +++ b/.github/workflows/rainix-sol-test.yaml @@ -18,6 +18,8 @@ on: required: false RPC_URL_POLYGON_FORK: required: false +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: test: runs-on: ubuntu-latest @@ -53,5 +55,5 @@ jobs: foundry-full-${{ runner.os }}- - name: Install soldeer dependencies if: hashFiles('soldeer.lock') != '' - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge soldeer install - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge test -vvv + run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge soldeer install + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge test -vvv diff --git a/.github/workflows/rainix-subgraph-test.yaml b/.github/workflows/rainix-subgraph-test.yaml index 317be1a..752ef5d 100644 --- a/.github/workflows/rainix-subgraph-test.yaml +++ b/.github/workflows/rainix-subgraph-test.yaml @@ -1,6 +1,8 @@ name: rainix-subgraph-test on: workflow_call: +env: + RAINIX_SHA: 307bf27fcc5a410994f5a6a6a96527a64625c3da jobs: subgraph-test: runs-on: ubuntu-latest @@ -11,4 +13,4 @@ jobs: - uses: rainlanguage/rainix/.github/actions/nix-cachix-setup@main with: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#subgraph-shell -c subgraph-test + - run: nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#subgraph-shell -c subgraph-test diff --git a/CLAUDE.md b/CLAUDE.md index 430d48b..0de59fe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -101,13 +101,16 @@ flake. A full sha makes nix skip that API call and fetch the tarball directly. Authenticating the call does NOT help (it's a secondary limit, not missing auth); pinning is the fix. -**Every flake ref across every reusable shares ONE sha.** To bump the toolchain, -find-replace the old sha with the new across `.github/workflows/*.yaml`, then -sanity-check with `nix flake show github:rainlanguage/rainix/` that the -referenced devshells (`sol-shell`, `rust-shell`, `rust-node-shell`, -`subgraph-shell`) still resolve at it. Never add a bare unpinned -`github:rainlanguage/rainix#…` ref — it reintroduces the 429. (Single-sourcing -this repeated sha so a bump is one line is tracked in #248.) +**Every flake ref across every reusable shares ONE sha**, single-sourced via a +top-level `env: RAINIX_SHA:` in each reusable workflow file. Run steps reference +it as `github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#`. + +To bump the toolchain, find-replace the old sha with the new in +`env.RAINIX_SHA:` across all 11 `rainix-*.yaml` files, then sanity-check with +`nix flake show github:rainlanguage/rainix/` that the referenced +devshells (`sol-shell`, `rust-shell`, `rust-node-shell`, `subgraph-shell`) still +resolve at it. Never add a bare unpinned `github:rainlanguage/rainix#…` ref — it +reintroduces the 429. ## Code Style