Skip to content
Merged
Show file tree
Hide file tree
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
31 changes: 30 additions & 1 deletion .github/workflows/publish-prerelease.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: Publish Beta Package

# Creating a `v*` tag is restricted to the dotns team by the `release tags` ruleset, and the
# job below pauses on the `releases` environment for a reviewer, so a release takes two
# distinct human actions: cutting the tag, and approving the run it starts.
on:
push:
tags:
Expand All @@ -23,6 +26,9 @@ concurrency:
jobs:
beta-release:
runs-on: ubuntu-latest
# Gated: this job reads the genesis owner key, which lives only on the `releases`
# environment, so every run waits for a reviewer's approval there.
environment: releases
# Baked into the genesis registry, so it decides which networks the artifact suits.
# Job-level: five steps name the file derived from it and must not disagree.
env:
Expand Down Expand Up @@ -87,6 +93,30 @@ jobs:

- uses: ./.github/actions/setup-foundry

# The environment pairs the key (secret) with its address (variable). Deriving one from
# the other before the build turns a mistyped key into a failed run instead of a genesis
# owned by an address nobody holds. The address is public; the key is never printed.
- name: Check the owner key against its declared address
env:
DOTNS_ADMIN_KEY: ${{ secrets.DOTNS_ADMIN_KEY }}
DOTNS_ADMIN_ADDRESS: ${{ vars.DOTNS_ADMIN_ADDRESS }}
run: |
set -euo pipefail
if [ -z "$DOTNS_ADMIN_KEY" ]; then
echo "::error::DOTNS_ADMIN_KEY is not configured on the releases environment"
exit 1
fi
if [ -z "$DOTNS_ADMIN_ADDRESS" ]; then
echo "::error::DOTNS_ADMIN_ADDRESS is not configured on the releases environment"
exit 1
fi
DERIVED="$(cast wallet address --private-key "$DOTNS_ADMIN_KEY")"
if [ "$(printf '%s' "$DERIVED" | tr 'A-Z' 'a-z')" != "$(printf '%s' "$DOTNS_ADMIN_ADDRESS" | tr 'A-Z' 'a-z')" ]; then
echo "::error::DOTNS_ADMIN_KEY derives to $DERIVED, the environment declares $DOTNS_ADMIN_ADDRESS"
exit 1
fi
echo "Owner key derives to the declared address $DERIVED"

- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.2.6"
Expand Down Expand Up @@ -125,7 +155,6 @@ jobs:
FOUNDRY_DISABLE_NIGHTLY_WARNING: "1"
FACTORY_DEPLOYER_KEY: ${{ secrets.FACTORY_DEPLOYER_KEY }}
DOTNS_ADMIN_KEY: ${{ secrets.DOTNS_ADMIN_KEY }}
DOTNS_ADMIN_MNEMONIC: ${{ secrets.DOTNS_ADMIN_MNEMONIC }}
run: bash scripts/genesis/build-genesis.sh release

- name: Extract ABIs
Expand Down
37 changes: 33 additions & 4 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: Publish Release Package

# Creating a `v*` tag is restricted to the dotns team by the `release tags` ruleset, and the
# job below pauses on the `releases` environment for a reviewer, so a release takes two
# distinct human actions: cutting the tag, and approving the run it starts.
on:
push:
tags:
Expand All @@ -23,6 +26,9 @@ concurrency:
jobs:
release:
runs-on: ubuntu-latest
# Gated: this job reads the genesis owner key, which lives only on the `releases`
# environment, so every run waits for a reviewer's approval there.
environment: releases
# Baked into the genesis registry, so it decides which networks the artifact suits.
# Job-level: five steps name the file derived from it and must not disagree.
env:
Expand Down Expand Up @@ -82,6 +88,30 @@ jobs:

- uses: ./.github/actions/setup-foundry

# The environment pairs the key (secret) with its address (variable). Deriving one from
# the other before the build turns a mistyped key into a failed run instead of a genesis
# owned by an address nobody holds. The address is public; the key is never printed.
- name: Check the owner key against its declared address
env:
DOTNS_ADMIN_KEY: ${{ secrets.DOTNS_ADMIN_KEY }}
DOTNS_ADMIN_ADDRESS: ${{ vars.DOTNS_ADMIN_ADDRESS }}
run: |
set -euo pipefail
if [ -z "$DOTNS_ADMIN_KEY" ]; then
echo "::error::DOTNS_ADMIN_KEY is not configured on the releases environment"
exit 1
fi
if [ -z "$DOTNS_ADMIN_ADDRESS" ]; then
echo "::error::DOTNS_ADMIN_ADDRESS is not configured on the releases environment"
exit 1
fi
DERIVED="$(cast wallet address --private-key "$DOTNS_ADMIN_KEY")"
if [ "$(printf '%s' "$DERIVED" | tr 'A-Z' 'a-z')" != "$(printf '%s' "$DOTNS_ADMIN_ADDRESS" | tr 'A-Z' 'a-z')" ]; then
echo "::error::DOTNS_ADMIN_KEY derives to $DERIVED, the environment declares $DOTNS_ADMIN_ADDRESS"
exit 1
fi
echo "Owner key derives to the declared address $DERIVED"

- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.2.6"
Expand Down Expand Up @@ -113,15 +143,14 @@ jobs:
# contracts, the deploy scripts and the CREATE3 factory key that fixes every address,
# and the artifact then ships from the same commit as the ABIs beside it.
#
# The owner key is taken from DOTNS_ADMIN_KEY if set, otherwise derived from
# DOTNS_ADMIN_MNEMONIC. It ends up owning the registry, resolvers, registrar, store factory
# and beacons in the genesis storage, so the script refuses to run without one.
# The owner key is DOTNS_ADMIN_KEY, checked against its declared address above. It
# ends up owning the registry, resolvers, registrar, store factory and beacons in the
# genesis storage, so the script refuses to run without it.
- name: Build pallet-revive genesis
env:
FOUNDRY_DISABLE_NIGHTLY_WARNING: "1"
FACTORY_DEPLOYER_KEY: ${{ secrets.FACTORY_DEPLOYER_KEY }}
DOTNS_ADMIN_KEY: ${{ secrets.DOTNS_ADMIN_KEY }}
DOTNS_ADMIN_MNEMONIC: ${{ secrets.DOTNS_ADMIN_MNEMONIC }}
run: bash scripts/genesis/build-genesis.sh release

- name: Extract ABIs
Expand Down
14 changes: 14 additions & 0 deletions RELEASE_ARTIFACTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ With `--tag vX.Y.Z` it additionally checks the chain's own declarations: `protoc

## Publishing

Both publish workflows run in the `releases` environment, because building a genesis reads the
key that owns every contract in it. The environment holds:

- `DOTNS_ADMIN_KEY` (secret): the genesis owner's private key.
- `DOTNS_ADMIN_ADDRESS` (variable): the address that key derives to. The workflow checks the
pair right after the toolchain is installed and fails the run on a mismatch, so a mistyped
key dies before anything is built.
- Required reviewers: the dotns team. Every release run pauses for one approval.
- Deployment refs: `master` (for `workflow_dispatch`) and `v[0-9]*` tags. A dispatch started
from any other branch stops at the environment gate.

Creating a `v*` tag is itself restricted to the dotns team by the `release tags` ruleset, so a
release takes two distinct human actions: cutting the tag, and approving the run it starts.

`deployments.json`, `release-manifest.json`, and `codehashes.json` are generated during the release by `scripts/js/release-metadata.mjs build`, from the committed deployment manifests and the build that just ran; `abi-diff.json` comes from `abidiff` against the previous release's published ABIs. Neither is committed: an address stored in two tracked files eventually disagrees with itself, so `deployments/<network>/<chain-id>.json` is the only tracked copy.

That file holds exactly one address per contract, the current one. Each deploy overwrites the entries it produces, so it tracks only the latest deployment for a network and never a history of them; previous address sets exist only in this repository's git history. It also carries no implementation addresses behind the UUPS proxies, and no record of which commit was deployed.
Expand Down
21 changes: 8 additions & 13 deletions scripts/genesis/build-genesis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ CANONICAL_MANIFEST="deployments/expected.json"
# does: this key ends up owning the registry, the resolvers, the registrar, the
# store factory and the beacons.
#
# Accepted, in order of precedence:
# DOTNS_ADMIN_KEY a raw private key — the admin credential this repo already holds
# DOTNS_ADMIN_MNEMONIC the admin mnemonic; index $DOTNS_ADMIN_INDEX (default 0)
# Accepted:
# DOTNS_ADMIN_KEY a raw private key — in CI it lives on the `releases` environment,
# behind a required reviewer, paired with a DOTNS_ADMIN_ADDRESS
# variable the workflow checks the key against before this runs
#
# Deliberately NOT accepted: DOTNS_MNEMONIC. That is an operational credential for driving
# the `dotns` CLI, not the contract admin, and quietly making it the owner of every
# contract in a genesis would be a hard mistake to spot.
# Deliberately NOT accepted: DOTNS_ADMIN_MNEMONIC and DOTNS_MNEMONIC. A second credential
# accepted here would let a different secret silently decide who owns every contract in a
# genesis, and a wrong owner is a hard mistake to spot. One explicit key, or a loud failure.
# Not DEPLOYER_KEY: that name is dotns-releases' own secret, and accepting it here
# would make which key owns a published genesis depend on which repo the build ran in.
ADMIN_KEY="${DOTNS_ADMIN_KEY:-}"
Expand Down Expand Up @@ -84,15 +85,9 @@ for tool in forge anvil cast node jq curl; do
command -v "$tool" >/dev/null 2>&1 || { echo "Error: $tool is not on PATH" >&2; exit 1; }
done

# Needs cast, so it happens after the check above.
if [ -z "$ADMIN_KEY" ] && [ -n "${DOTNS_ADMIN_MNEMONIC:-}" ]; then
ADMIN_KEY="$(cast wallet private-key --mnemonic "$DOTNS_ADMIN_MNEMONIC" "${DOTNS_ADMIN_INDEX:-0}")"
echo "Owner key derived from DOTNS_ADMIN_MNEMONIC, index ${DOTNS_ADMIN_INDEX:-0}."
fi

if [ -z "$ADMIN_KEY" ]; then
cat >&2 <<'MSG'
Error: no owner key. Set DOTNS_ADMIN_KEY or DOTNS_ADMIN_MNEMONIC.
Error: no owner key. Set DOTNS_ADMIN_KEY.

Whichever is given becomes the owner of every DotNS contract in the genesis
state, so this build refuses to fall back to a public dev key.
Expand Down
Loading