diff --git a/.github/workflows/rainix-sol-scheduled.yaml b/.github/workflows/rainix-sol-scheduled.yaml new file mode 100644 index 0000000..2873ccd --- /dev/null +++ b/.github/workflows/rainix-sol-scheduled.yaml @@ -0,0 +1,51 @@ +name: rainix-sol-scheduled +# The same sol suite as `rainix.yaml`, on a clock instead of on a push. +# +# `RegistryDeployChainTest` asks whether every RELEASED registry is live, with +# the code its release froze, on every network in `supportedNetworks()`. That is +# the one check whose answer changes without this repo changing: a pin can go +# from true to false because a chain reorged past the deploy, because a release +# was declared for a deployment that never landed on one of the five networks, +# or because the code at the address is not the code the snapshot recorded. +# Push-triggered CI cannot see any of it — a deploy repo deliberately does not +# move between releases, so the push that would have asked can be months away, +# and the pins consumers are already using are wrong for all of that time. +# +# Scheduled runs use the default branch, which for this repo is precisely the +# branch that carries the current release's pins (`rainix-tag-release` commits +# the frozen snapshot and the released-suites declaration back to main), so the +# constants under test here are the ones consumers resolve. +# +# Separate workflow rather than a `schedule:` added to `rainix.yaml`: the two +# triggers answer different questions and a red run should say on sight which +# one asked. `workflow_dispatch` is here so the sweep can be run on demand — +# after a manual deploy, or to re-check a chain that was unreachable — without +# waiting for the next cron tick. +# +# The schedule lives in this repo and not in a rainix reusable because it cannot +# live anywhere else: `rainix-sol.yaml` is `on: workflow_call`, and GitHub only +# honours `schedule:` in the workflow that the event triggers. A reusable cannot +# schedule itself, so the trigger is necessarily consumer-side even though the +# jobs it runs are the shared rainix ones. +# +# Known gap, not fixed here: GitHub disables scheduled workflows in a PUBLIC +# repository after 60 days with no repository activity, and a release-frozen +# deploy repo is exactly the repo that goes quiet that long. When that happens +# the sweep stops silently and has to be re-enabled by hand, so a repo that has +# been idle past 60 days is not covered by this and `workflow_dispatch` is the +# only way to ask. +on: + schedule: + # Daily, 03:17 UTC. Off the hour deliberately: GitHub's scheduler delays or + # drops on-the-hour crons under load, since that is when everything queues. + - cron: "17 3 * * *" + workflow_dispatch: +jobs: + rainix-sol: + uses: rainlanguage/rainix/.github/workflows/rainix-sol.yaml@main + # `inherit`, like every other workflow in this repo. The alternative — an + # explicit per-secret list — is a second copy of rainix's declared secret + # set that nothing keeps in sync: a network added to `[rpc_endpoints]` and + # to rainix would fork on push and silently not fork here, which is the same + # class of invisible gap this workflow exists to close. + secrets: inherit diff --git a/CLAUDE.md b/CLAUDE.md index e04e3be..73427cb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,6 +49,16 @@ the block above is what they run. A fourth workflow, `Manual sol artifacts`, is `workflow_dispatch` only and is the on-chain deploy — nothing automatic ever broadcasts. +Those same three tasks run twice over: on every push (`rainix.yaml`) and daily +on a schedule (`rainix-sol-scheduled.yaml`, also `workflow_dispatch`). The +schedule exists for group 4 below — `RegistryDeployChainTest` is red on chain +state that changes with nobody touching this repo, and between releases this +repo is not touched for months, so a push is not an event that can be waited +for. Scheduled runs use the default branch, which carries the current release's +pins. GitHub disables scheduled workflows in a public repo after 60 days of no +repository activity, which a release-frozen deploy repo can reach; past that the +sweep has to be re-enabled by hand. + ## RPC Configuration Fork tests require RPC endpoints defined in `.env` (gitignored): @@ -340,7 +350,8 @@ Four groups, sorted by what they are anchored to: `supportedNetworks()`, every RELEASED version's derived address carries code with its derived code hash. The only check that catches "never deployed" or "not there any more", neither of which the repo can hold: both go false with - nobody touching it. + nobody touching it — which is why it is also run on a daily schedule + (`rainix-sol-scheduled.yaml`) and not only on push. Group 4 is released-only for the mirror image of group 2's reason. A release IS a deployment that happened; a candidate is what the next release will be, and diff --git a/README.md b/README.md index 88ccf44..67ccb1b 100644 --- a/README.md +++ b/README.md @@ -298,7 +298,12 @@ Three separate steps, in this order. Nothing automatic ever broadcasts. suite is live on every supported network, with the code that release froze. This repo has released none, so today it has nothing to check and passes; it gets a subject the moment step 3 freezes one, and is red from then until step - 1 has been run everywhere. That is the order these steps are in. + 1 has been run everywhere. That is the order these steps are in. It is not + asked once: + [`rainix-sol-scheduled`](.github/workflows/rainix-sol-scheduled.yaml) re-runs + the sol suite daily against `main`, because a released pin stops being true — + a chain rolls back, a network never got the deploy — without anything in this + repo changing to trigger a push build. 3. **Tag.** Push a `sol-v*` tag. `rainix-tag-release` regenerates the snapshot for the version the tag names, verifies the live chains against those fresh pins, publishes to Soldeer and commits the frozen snapshot back to `main`. It