Skip to content

Commit b68a5eb

Browse files
onspeedhpclaude
andcommitted
ci(release): tagged-release workflow with verified-build hashes
Triggered by audit-frozen-v* and v*.*.* tags. Builds both mainnet and devnet SBF binaries, records SHA-256 hashes, asserts they differ (catches a regression of the dual-cluster mechanism), and uploads the binaries + IDL + a manifest as GitHub Release assets. Manifest captures the build environment (Solana CLI version, rustc version, commit SHA) and the reproduction commands so anyone can locally rebuild and confirm the published hashes match. audit-frozen-v* tags create draft + prerelease releases (the artifact is for the auditor, not for end-users); v*.*.* tags create normal releases. Production deploys must consume binaries from a release — see docs/MAINNET_DEPLOY.md. GITHUB_SHA + GITHUB_REF_NAME are passed through to cargo build-sbf so the embedded security.txt advertises the exact source revision. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 242122f commit b68a5eb

1 file changed

Lines changed: 153 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
name: release
2+
3+
# Triggered by pushing an `audit-frozen-v*` tag (or a `v*` semver release tag).
4+
# Builds the mainnet and devnet SBF binaries, records their SHA-256 hashes,
5+
# and attaches the .so files + a manifest to the GitHub Release.
6+
#
7+
# The release artifacts are the source of truth for what gets deployed to
8+
# mainnet — production deploys must use a binary downloaded from a release,
9+
# not a locally-built one. See docs/MAINNET_DEPLOY.md.
10+
11+
on:
12+
push:
13+
tags:
14+
- 'audit-frozen-v*'
15+
- 'v*.*.*'
16+
17+
jobs:
18+
release:
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 20
21+
permissions:
22+
contents: write # for creating GitHub Releases
23+
24+
steps:
25+
- uses: actions/checkout@v4
26+
with:
27+
# Full history so source_revision in security_txt embeds the right SHA.
28+
fetch-depth: 0
29+
30+
- name: Install Solana toolchain
31+
run: |
32+
sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"
33+
echo "$HOME/.local/share/solana/install/active_release/bin" >> "$GITHUB_PATH"
34+
35+
- name: Pin Solana CLI to the version declared in Cargo.toml
36+
run: |
37+
DECLARED=$(grep -A1 'workspace.metadata.cli' Cargo.toml | grep solana | sed -E 's/.*"([^"]+)".*/\1/')
38+
INSTALLED=$(solana --version | awk '{print $2}')
39+
echo "Declared: $DECLARED"
40+
echo "Installed: $INSTALLED"
41+
if [ "$DECLARED" != "$INSTALLED" ]; then
42+
echo "::warning::Installed Solana CLI ($INSTALLED) does not match declared ($DECLARED). Verified-build hashes may differ from what consumers reproduce."
43+
fi
44+
45+
- name: Cache cargo build
46+
uses: actions/cache@v4
47+
with:
48+
path: |
49+
~/.cargo/registry
50+
~/.cargo/git
51+
target
52+
key: release-${{ hashFiles('**/Cargo.lock') }}-${{ github.ref_name }}
53+
54+
- name: Build mainnet binary
55+
working-directory: program
56+
env:
57+
GITHUB_SHA: ${{ github.sha }}
58+
GITHUB_REF_NAME: ${{ github.ref_name }}
59+
run: cargo build-sbf --features mainnet
60+
61+
- name: Hash + stage mainnet artifact
62+
run: |
63+
mkdir -p release-artifacts
64+
cp target/deploy/lazorkit_program.so release-artifacts/lazorkit_program-mainnet.so
65+
MAINNET_SHA=$(shasum -a 256 release-artifacts/lazorkit_program-mainnet.so | awk '{print $1}')
66+
echo "MAINNET_SHA=$MAINNET_SHA" >> "$GITHUB_ENV"
67+
echo "mainnet sha256: $MAINNET_SHA"
68+
69+
- name: Build devnet binary
70+
working-directory: program
71+
env:
72+
GITHUB_SHA: ${{ github.sha }}
73+
GITHUB_REF_NAME: ${{ github.ref_name }}
74+
run: cargo build-sbf --features devnet
75+
76+
- name: Hash + stage devnet artifact
77+
run: |
78+
cp target/deploy/lazorkit_program.so release-artifacts/lazorkit_program-devnet.so
79+
DEVNET_SHA=$(shasum -a 256 release-artifacts/lazorkit_program-devnet.so | awk '{print $1}')
80+
echo "DEVNET_SHA=$DEVNET_SHA" >> "$GITHUB_ENV"
81+
echo "devnet sha256: $DEVNET_SHA"
82+
83+
- name: Verify binaries differ
84+
run: |
85+
if [ "$MAINNET_SHA" = "$DEVNET_SHA" ]; then
86+
echo "::error::mainnet and devnet binaries are identical — dual-cluster mechanism broken"
87+
exit 1
88+
fi
89+
90+
- name: Stage IDL + keypair
91+
run: |
92+
cp program/idl.json release-artifacts/idl.json
93+
# The keypair file is regenerated per build; useful as a record but
94+
# NOT for deployment (the actual mainnet keypair is held off-CI).
95+
if [ -f target/deploy/lazorkit_program-keypair.json ]; then
96+
cp target/deploy/lazorkit_program-keypair.json release-artifacts/build-keypair.json
97+
fi
98+
99+
- name: Write release manifest
100+
run: |
101+
cat > release-artifacts/MANIFEST.txt <<EOF
102+
LazorKit program-v2 release manifest
103+
tag: ${{ github.ref_name }}
104+
commit: ${{ github.sha }}
105+
built: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
106+
solana-cli: $(solana --version)
107+
rust-toolchain: $(rustc --version)
108+
109+
mainnet binary: lazorkit_program-mainnet.so
110+
mainnet sha256: $MAINNET_SHA
111+
mainnet program ID: LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi
112+
113+
devnet binary: lazorkit_program-devnet.so
114+
devnet sha256: $DEVNET_SHA
115+
devnet program ID: FLb7fyAtkfA4TSa2uYcAT8QKHd2pkoMHgmqfnXFXo7ao
116+
117+
To reproduce these hashes locally:
118+
git checkout ${{ github.ref_name }}
119+
cd program
120+
cargo build-sbf --features mainnet # → mainnet sha256 above
121+
cargo build-sbf --features devnet # → devnet sha256 above
122+
123+
To deploy: see docs/MAINNET_DEPLOY.md
124+
EOF
125+
cat release-artifacts/MANIFEST.txt
126+
127+
- name: Create GitHub Release
128+
uses: softprops/action-gh-release@v2
129+
with:
130+
name: ${{ github.ref_name }}
131+
tag_name: ${{ github.ref_name }}
132+
body: |
133+
**Tag:** `${{ github.ref_name }}`
134+
**Commit:** `${{ github.sha }}`
135+
136+
**Mainnet binary:** `lazorkit_program-mainnet.so`
137+
**Mainnet sha256:** `${{ env.MAINNET_SHA }}`
138+
**Mainnet program ID:** `LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi` (slot shared with `lazorkit-protocol`)
139+
140+
**Devnet binary:** `lazorkit_program-devnet.so`
141+
**Devnet sha256:** `${{ env.DEVNET_SHA }}`
142+
**Devnet program ID:** `FLb7fyAtkfA4TSa2uYcAT8QKHd2pkoMHgmqfnXFXo7ao`
143+
144+
See [`MANIFEST.txt`](./MANIFEST.txt) for build environment + reproduction
145+
commands and [`docs/MAINNET_DEPLOY.md`](../docs/MAINNET_DEPLOY.md) for
146+
deployment procedure.
147+
files: |
148+
release-artifacts/lazorkit_program-mainnet.so
149+
release-artifacts/lazorkit_program-devnet.so
150+
release-artifacts/idl.json
151+
release-artifacts/MANIFEST.txt
152+
draft: ${{ startsWith(github.ref_name, 'audit-frozen-') }}
153+
prerelease: ${{ startsWith(github.ref_name, 'audit-frozen-') }}

0 commit comments

Comments
 (0)