From c0730d3d0d65ffde6fa16da0462049be2c358009 Mon Sep 17 00:00:00 2001 From: David Meister Date: Sun, 28 Jun 2026 16:18:40 +0000 Subject: [PATCH 1/3] chore(ci): pin rainix flake rev org-wide across the 10 remaining reusables Rolls the #245 pin to the rest of the reusables. Pins all 32 github:rainlanguage/rainix# refs to sha 307bf27f (the same sha the deploy reusable uses), so nix skips the api.github.com HEAD-resolution that GitHub burst-rate-limits (the org-wide CI 429). Pure sha-insertion; all 4 devshells (sol/rust/rust-node/subgraph) confirmed to resolve at the pinned sha. Bump the sha when the toolchain changes. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/rainix-autopublish.yaml | 24 ++++++++++---------- .github/workflows/rainix-copy-artifacts.yaml | 12 +++++----- .github/workflows/rainix-rs-static.yaml | 4 ++-- .github/workflows/rainix-rs-test.yaml | 2 +- .github/workflows/rainix-rs-wasm-test.yaml | 2 +- .github/workflows/rainix-rs-wasm.yaml | 2 +- .github/workflows/rainix-sol-legal.yaml | 4 ++-- .github/workflows/rainix-sol-static.yaml | 8 +++---- .github/workflows/rainix-sol-test.yaml | 4 ++-- .github/workflows/rainix-subgraph-test.yaml | 2 +- 10 files changed, 32 insertions(+), 32 deletions(-) diff --git a/.github/workflows/rainix-autopublish.yaml b/.github/workflows/rainix-autopublish.yaml index 01d4e62..6d3bd70 100644 --- a/.github/workflows/rainix-autopublish.yaml +++ b/.github/workflows/rainix-autopublish.yaml @@ -123,7 +123,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#rust-shell -c cargo package -p "$CRATE" --allow-dirty --no-verify --quiet; then + if ! nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 +152,7 @@ jobs: if: ${{ inputs.npm-package != '' }} id: npm run: | - nix develop github:rainlanguage/rainix#rust-node-shell -c bash -c ' + nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 +191,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#sol-shell -c forge soldeer push "$PKG~$LOCAL" --dry-run + nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 +217,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#rust-shell -c cargo test --workspace + run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 +233,12 @@ jobs: set -euo pipefail PKGS="" for CRATE in $CRATES; do PKGS="$PKGS -p $CRATE"; done - nix develop github:rainlanguage/rainix#rust-shell -c cargo release --no-confirm --execute --no-tag --no-push --no-publish $PKGS ${{ inputs.level }} + nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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#rust-node-shell -c npm version patch --no-git-tag-version) + NEW=$(nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 +270,13 @@ jobs: set -euo pipefail git fetch origin if ! git rebase "origin/${{ github.ref_name }}"; then - nix develop github:rainlanguage/rainix#rust-shell -c cargo generate-lockfile + nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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#rust-shell -c cargo pkgid -p "$CRATE" | cut -d@ -f2) + VER=$(nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo pkgid -p "$CRATE" | cut -d@ -f2) git tag "$CRATE-v$VER" done fi @@ -288,7 +288,7 @@ jobs: - name: NPM pack if: ${{ inputs.npm-package != '' && steps.npm.outputs.changed == 'true' }} run: | - TARBALL=$(nix develop github:rainlanguage/rainix#rust-node-shell -c npm pack --silent) + TARBALL=$(nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 +297,7 @@ jobs: run: | set -euo pipefail for CRATE in $CRATES; do - nix develop github:rainlanguage/rainix#rust-shell -c cargo publish -p "$CRATE" + nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo publish -p "$CRATE" done env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} @@ -312,14 +312,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#sol-shell -c forge soldeer push "${{ inputs.soldeer-package }}~${{ steps.soldeer.outputs.next }}" + run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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#rust-shell -c cargo pkgid -p "$CRATE" | cut -d@ -f2) + VER=$(nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 f209385..5e7f711 100644 --- a/.github/workflows/rainix-copy-artifacts.yaml +++ b/.github/workflows/rainix-copy-artifacts.yaml @@ -18,7 +18,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#sol-shell -c forge soldeer install + run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 +26,15 @@ jobs: # filenames, meta type) varies per repo. - name: Regenerate meta artifacts if: hashFiles('script/build-meta.sh') != '' - run: nix develop github:rainlanguage/rainix#sol-shell -c ./script/build-meta.sh + run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c ./script/build-meta.sh - name: Regenerate pointer artifacts if: hashFiles('script/BuildPointers.sol') != '' - run: nix develop github:rainlanguage/rainix#sol-shell -c forge script ./script/BuildPointers.sol + run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge script ./script/BuildPointers.sol - name: Build Solidity - run: nix develop github:rainlanguage/rainix#sol-shell -c forge build + run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c forge build - name: Copy forge artifacts into committed location if: hashFiles('script/CopyArtifacts.sol') != '' - run: nix develop github:rainlanguage/rainix#sol-shell -c forge script ./script/CopyArtifacts.sol --ffi + run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 +42,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#sol-shell -c forge fmt + run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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-rs-static.yaml b/.github/workflows/rainix-rs-static.yaml index b657907..a9f6d1c 100644 --- a/.github/workflows/rainix-rs-static.yaml +++ b/.github/workflows/rainix-rs-static.yaml @@ -14,7 +14,7 @@ jobs: with: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - uses: rainlanguage/rainix/.github/actions/rust-cache@main - - run: nix develop github:rainlanguage/rainix#rust-shell -c rainix-rs-static + - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 +24,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#rust-shell -c pre-commit run --all-files --show-diff-on-failure --color always + - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 d8dd043..da4ae9a 100644 --- a/.github/workflows/rainix-rs-test.yaml +++ b/.github/workflows/rainix-rs-test.yaml @@ -18,4 +18,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#rust-shell -c cargo test + - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#rust-shell -c cargo test diff --git a/.github/workflows/rainix-rs-wasm-test.yaml b/.github/workflows/rainix-rs-wasm-test.yaml index 9604fd1..0149174 100644 --- a/.github/workflows/rainix-rs-wasm-test.yaml +++ b/.github/workflows/rainix-rs-wasm-test.yaml @@ -12,4 +12,4 @@ jobs: with: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - uses: rainlanguage/rainix/.github/actions/rust-cache@main - - run: nix develop github:rainlanguage/rainix#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/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 046b50f..3124061 100644 --- a/.github/workflows/rainix-rs-wasm.yaml +++ b/.github/workflows/rainix-rs-wasm.yaml @@ -12,4 +12,4 @@ jobs: with: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - uses: rainlanguage/rainix/.github/actions/rust-cache@main - - run: nix develop github:rainlanguage/rainix#rust-shell -c cargo build -r --target wasm32-unknown-unknown --lib --workspace + - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#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 34a2c9e..ccf52cd 100644 --- a/.github/workflows/rainix-sol-legal.yaml +++ b/.github/workflows/rainix-sol-legal.yaml @@ -13,5 +13,5 @@ jobs: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Install soldeer dependencies if: hashFiles('soldeer.lock') != '' - run: nix develop github:rainlanguage/rainix#sol-shell -c forge soldeer install - - run: nix develop github:rainlanguage/rainix#sol-shell -c reuse lint + 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 diff --git a/.github/workflows/rainix-sol-static.yaml b/.github/workflows/rainix-sol-static.yaml index 68d538c..bce9da2 100644 --- a/.github/workflows/rainix-sol-static.yaml +++ b/.github/workflows/rainix-sol-static.yaml @@ -24,8 +24,8 @@ jobs: foundry-full-${{ runner.os }}- - name: Install soldeer dependencies if: hashFiles('soldeer.lock') != '' - run: nix develop github:rainlanguage/rainix#sol-shell -c forge soldeer install - - run: nix develop github:rainlanguage/rainix#sol-shell -c slither . - - run: nix develop github:rainlanguage/rainix#sol-shell -c forge fmt --check + 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 # Enforce Rain's one-contract-per-.sol-file convention (rainix#214). - - run: nix develop github:rainlanguage/rainix#sol-shell -c rainix-sol-single-contract + - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#sol-shell -c rainix-sol-single-contract diff --git a/.github/workflows/rainix-sol-test.yaml b/.github/workflows/rainix-sol-test.yaml index abf52ff..8b225d9 100644 --- a/.github/workflows/rainix-sol-test.yaml +++ b/.github/workflows/rainix-sol-test.yaml @@ -53,5 +53,5 @@ jobs: foundry-full-${{ runner.os }}- - name: Install soldeer dependencies if: hashFiles('soldeer.lock') != '' - run: nix develop github:rainlanguage/rainix#sol-shell -c forge soldeer install - - run: nix develop github:rainlanguage/rainix#sol-shell -c forge test -vvv + 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 diff --git a/.github/workflows/rainix-subgraph-test.yaml b/.github/workflows/rainix-subgraph-test.yaml index 22f5bed..317be1a 100644 --- a/.github/workflows/rainix-subgraph-test.yaml +++ b/.github/workflows/rainix-subgraph-test.yaml @@ -11,4 +11,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#subgraph-shell -c subgraph-test + - run: nix develop github:rainlanguage/rainix/307bf27fcc5a410994f5a6a6a96527a64625c3da#subgraph-shell -c subgraph-test From 1c0aed355533ef68b433d5599cdaa7927fe8fee3 Mon Sep 17 00:00:00 2001 From: David Meister Date: Sun, 28 Jun 2026 16:21:51 +0000 Subject: [PATCH 2/3] docs: document the CI flake-pin convention + bump procedure (refs #248) Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index a3482f9..938b3cc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -89,6 +89,26 @@ Defined in `.github/workflows/`: - **check-shell.yml** — verifies dev shell tools are available - **pr-assessment.yaml** — PR size assessment +### Flake-ref pinning in reusable workflows + +The reusable workflows (`.github/workflows/rainix-*.yaml`) invoke the dev shells +via `nix develop github:rainlanguage/rainix/#` — pinned to an +explicit commit **sha**, never the bare `github:rainlanguage/rainix#…` (HEAD) +form. Unpinned, nix resolves HEAD through `api.github.com/.../commits/HEAD`, +which GitHub **burst-rate-limits (429)** under CI load (the error body comes back +gzipped and nix mis-parses it as JSON) — this was the dominant org-wide CI 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.) + ## Code Style - Rust: `cargo fmt` and `cargo clippy` with all warnings denied From 7d8865cdab0dfc85e006617e426806610f9b5be0 Mon Sep 17 00:00:00 2001 From: David Meister Date: Sun, 28 Jun 2026 16:40:42 +0000 Subject: [PATCH 3/3] style: deno fmt CLAUDE.md prose wrap (80-col) Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 938b3cc..430d48b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -95,11 +95,11 @@ The reusable workflows (`.github/workflows/rainix-*.yaml`) invoke the dev shells via `nix develop github:rainlanguage/rainix/#` — pinned to an explicit commit **sha**, never the bare `github:rainlanguage/rainix#…` (HEAD) form. Unpinned, nix resolves HEAD through `api.github.com/.../commits/HEAD`, -which GitHub **burst-rate-limits (429)** under CI load (the error body comes back -gzipped and nix mis-parses it as JSON) — this was the dominant org-wide CI 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. +which GitHub **burst-rate-limits (429)** under CI load (the error body comes +back gzipped and nix mis-parses it as JSON) — this was the dominant org-wide CI +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