Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 20 additions & 86 deletions .github/workflows/rainix-tag-release.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: rainix-tag-release
# Tag-triggered release for DEPLOY repos (deploy + publish + snapshot together),
# the counterpart to rainix-autopublish's merge-driven publish for LIBRARY repos.
# Tag-triggered release for DEPLOY repos (verify + publish + snapshot; the deploy
# itself stays manual — see below). The counterpart to rainix-autopublish's
# merge-driven publish for LIBRARY repos.
#
# The two lifecycles are mutually exclusive and a repo is strictly one or the
# other:
Expand Down Expand Up @@ -31,16 +32,15 @@ name: rainix-tag-release
# Soldeer, and commits the new (append-only) snapshot back to main so the daily
# drift sweep always has the current release's pins to check.
#
# A release is deploy + publish + snapshot as one act. The `deploy` job broadcasts
# every suite in dependency order (one forge run per suite, the Zoltu
# nonce-isolation the manual dispatch already enforces) by fanning
# rainix-manual-sol-artifacts over `deploy-suites` at max-parallel 1 — composing
# the existing deploy reusable rather than re-implementing its broadcast. The
# `release` job then `needs:` it, so publish/snapshot only run once the chain
# actually carries the code the pins name. Deploy is independent of the snapshot
# regeneration (both derive from the same deterministic bytecode), so it needs no
# shared filesystem with the release job — the regen recomputes the pins and the
# verify gate confirms they now resolve on-chain.
# The on-chain DEPLOY is deliberately NOT part of this workflow. A deploy is
# flaky by nature — per-network, funds-dependent, and at the mercy of RPC
# archive/fork behaviour — and a flaky, retry-prone operation must not gate a
# one-shot tag publish, where a single transient failure would block the release
# entirely. So the deploy stays the repo's own human-driven
# rainix-manual-sol-artifacts dispatch, run BEFORE tagging. This workflow does not
# broadcast; it ATTESTS that the broadcast already landed: the `verify` step (the
# repo's fork suite) checks prod exists at the freshly regenerated pins, and a
# release whose pins don't resolve on-chain fails there and publishes nothing.
on:
workflow_call:
inputs:
Expand Down Expand Up @@ -70,46 +70,11 @@ on:
required: false
type: string
default: main
deploy-suites:
description: >-
JSON array of deploy suites IN DEPENDENCY ORDER, e.g. `["stox-receipt", "stox-wrapped-token-vault"]`. Each is broadcast in a separate forge run (Zoltu nonce isolation) sequentially. A later suite that references an earlier one fails loud on the on-chain dep-codehash check if run out of order, so ordering is enforced by the deploy, not just by this list.
required: true
type: string
deploy-script:
description: >-
Fully qualified forge deploy script (`path:Contract`), passed to rainix-manual-sol-artifacts. Defaults to the conventional `script/Deploy.sol:Deploy`.
required: false
type: string
default: script/Deploy.sol:Deploy
deploy-verify:
description: >-
Whether the deploy passes `--verify` to forge. Set false when broadcasting pinned historical creation code that no longer matches current source (Etherscan would reject it). Default true.
required: false
type: boolean
default: true
secrets:
PUBLISH_PRIVATE_KEY:
# A deploy key whose push events (unlike GITHUB_TOKEN pushes) trigger the
# downstream git-clean / rainix-sol workflows on the commit-back to main.
required: false
PRIVATE_KEY:
# The on-chain DEPLOYMENT key (distinct from PUBLISH_PRIVATE_KEY, the git
# deploy key). Broadcasts the suites.
required: false
EXPLORER_VERIFICATION_KEY:
required: false
CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY:
required: false
CI_DEPLOY_BASE_ETHERSCAN_API_KEY:
required: false
CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY:
required: false
CI_DEPLOY_ETHEREUM_ETHERSCAN_API_KEY:
required: false
CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY:
required: false
CI_DEPLOY_FLARE_ETHERSCAN_API_KEY:
required: false
CI_GIT_EMAIL:
required: false
CI_GIT_USER:
Expand Down Expand Up @@ -138,9 +103,8 @@ jobs:
# The release tag must point at a commit already merged to the release branch.
# `on: push: tags` fires for ANY tag, including one cut from an unmerged branch;
# without this the later rebase would replay that branch's unreviewed commits
# onto main and push them, and the deploy would broadcast unreviewed bytecode.
# Gate both `deploy` and `release` on it. All refs come from built-in env vars,
# never interpolated into the shell, to avoid template injection.
# onto main and push them. Gate `release` on it. All refs come from built-in env
# vars, never interpolated into the shell, to avoid template injection.
guard:
runs-on: ubuntu-latest
steps:
Expand All @@ -164,40 +128,8 @@ jobs:
echo "::error::tag $GITHUB_REF_NAME ($GITHUB_SHA) is not on origin/$MAIN — refusing to release an unmerged commit" >&2
exit 1
fi
# Broadcast every suite in dependency order, one forge run each (Zoltu nonce
# isolation), by fanning the existing deploy reusable over `deploy-suites` at
# max-parallel 1. Runs before `release`, so publish/snapshot only happen once
# the chain carries the code the pins name. `verify:false`-capable via the
# `deploy-verify` input for pinned historical bytecode.
deploy:
needs: guard
strategy:
max-parallel: 1
matrix:
suite: ${{ fromJSON(inputs.deploy-suites) }}
uses: rainlanguage/rainix/.github/workflows/rainix-manual-sol-artifacts.yaml@main
with:
suite: ${{ matrix.suite }}
script: ${{ inputs.deploy-script }}
verify: ${{ inputs.deploy-verify }}
secrets:
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
EXPLORER_VERIFICATION_KEY: ${{ secrets.EXPLORER_VERIFICATION_KEY }}
RPC_URL_ARBITRUM_FORK: ${{ secrets.RPC_URL_ARBITRUM_FORK }}
RPC_URL_BASE_FORK: ${{ secrets.RPC_URL_BASE_FORK }}
RPC_URL_BASE_SEPOLIA_FORK: ${{ secrets.RPC_URL_BASE_SEPOLIA_FORK }}
RPC_URL_ETHEREUM_FORK: ${{ secrets.RPC_URL_ETHEREUM_FORK }}
RPC_URL_FLARE_FORK: ${{ secrets.RPC_URL_FLARE_FORK }}
RPC_URL_POLYGON_FORK: ${{ secrets.RPC_URL_POLYGON_FORK }}
CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY }}
CI_DEPLOY_BASE_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_BASE_ETHERSCAN_API_KEY }}
CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY }}
CI_DEPLOY_ETHEREUM_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_ETHEREUM_ETHERSCAN_API_KEY }}
CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY }}
CI_DEPLOY_FLARE_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_FLARE_ETHERSCAN_API_KEY }}
release:
needs: deploy
needs: guard
runs-on: ubuntu-latest
# contents: write for the commit-back to main + the gh-release composite. No
# id-token: nothing here uses OIDC (Soldeer uses SOLDEER_API_TOKEN, the release
Expand Down Expand Up @@ -290,9 +222,11 @@ jobs:
# an already-released tag must never change (consumers pin its constants).
uses: rainlanguage/rainix/.github/actions/frozen-snapshots-append-only@main
- name: Verify live chain matches the fresh pins
# The `deploy` job (needs:) has broadcast every suite; this gate confirms
# it actually landed, so a snapshot of addresses the
# chain does not carry never gets published.
# The manual deploy was run before tagging; this is the attestation that it
# landed — the fork suite checks prod exists at the freshly regenerated
# pins, so a snapshot of addresses the chain does not carry never gets
# published. A transient fork/RPC failure here just fails the release
# (retry the tag); it publishes nothing and is not a broadcast.
env:
RPC_URL_ARBITRUM_FORK: ${{ secrets.RPC_URL_ARBITRUM_FORK }}
RPC_URL_BASE_FORK: ${{ secrets.RPC_URL_BASE_FORK }}
Expand Down
Loading