Skip to content

Commit 3f68b1d

Browse files
Merge pull request #1295 from devsimze/feat/deploy-dryrun-mainnet-rollback-versioning
feat: add deploy dry-run, mainnet CI target, rollback procedure, and versioning policy
2 parents 5c123cd + 9f89377 commit 3f68b1d

6 files changed

Lines changed: 726 additions & 132 deletions

File tree

.github/workflows/soroban-deploy.yml

Lines changed: 172 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,48 @@
11
name: Soroban Runtime Guard Deployment
22

33
on:
4-
# Pruned to manual-only: this workflow was persistently red on push/PR/schedule.
5-
# Run on demand from the Actions tab.
6-
workflow_dispatch: {}
4+
workflow_dispatch:
5+
inputs:
6+
target:
7+
description: 'Deployment target network'
8+
required: true
9+
default: 'testnet'
10+
type: choice
11+
options:
12+
- testnet
13+
- mainnet
14+
dry_run:
15+
description: 'Perform a dry run without broadcasting'
16+
required: true
17+
default: 'true'
18+
type: choice
19+
options:
20+
- 'true'
21+
- 'false'
22+
ref:
23+
description: 'Git ref to deploy (branch, tag, or SHA)'
24+
required: false
25+
default: ''
26+
type: string
27+
728
env:
829
CARGO_TERM_COLOR: always
930
RUST_BACKTRACE: 1
1031
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
1132

1233
jobs:
13-
build-and-deploy:
14-
name: Build & Deploy Runtime Guard Wrapper
34+
# ── Shared build — produces the WASM artifact ──────────────────────────────
35+
build:
36+
name: Build Runtime Guard Wrapper
1537
runs-on: ubuntu-latest
1638
permissions:
1739
contents: read
18-
deployments: write
19-
checks: write
2040

2141
steps:
2242
- name: Checkout repository
2343
uses: actions/checkout@v6
44+
with:
45+
ref: ${{ github.event.inputs.ref || github.ref }}
2446

2547
- name: Install stable Rust toolchain
2648
uses: dtolnay/rust-toolchain@stable
@@ -70,6 +92,43 @@ jobs:
7092
fi
7193
echo "WASM size: $(du -h "$WASM_PATH" | cut -f1)"
7294
echo "WASM_PATH=$WASM_PATH" >> $GITHUB_ENV
95+
echo "WASM_HASH=$(sha256sum "$WASM_PATH" | awk '{print $1}')" >> $GITHUB_ENV
96+
97+
- name: Upload WASM artifact
98+
uses: actions/upload-artifact@v6
99+
with:
100+
name: runtime-guard-wrapper-wasm
101+
path: target/wasm32-unknown-unknown/release/runtime_guard_wrapper.wasm
102+
retention-days: 7
103+
104+
# ── Testnet deploy (existing path) ─────────────────────────────────────────
105+
testnet-deploy:
106+
name: Deploy to Soroban Testnet
107+
runs-on: ubuntu-latest
108+
needs: build
109+
if: github.event.inputs.target == 'testnet'
110+
permissions:
111+
contents: read
112+
deployments: write
113+
checks: write
114+
115+
steps:
116+
- name: Checkout repository
117+
uses: actions/checkout@v6
118+
119+
- name: Install Soroban CLI
120+
run: |
121+
sudo apt-get update
122+
sudo apt-get install -y libdbus-1-dev libudev-dev pkg-config
123+
export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig:$PKG_CONFIG_PATH
124+
echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH" >> $GITHUB_ENV
125+
cargo install --locked soroban-cli || true
126+
127+
- name: Download WASM artifact
128+
uses: actions/download-artifact@v6
129+
with:
130+
name: runtime-guard-wrapper-wasm
131+
path: target/wasm32-unknown-unknown/release/
73132

74133
- name: Show Soroban network info
75134
run: |
@@ -83,81 +142,77 @@ jobs:
83142
echo "present=true" >> "$GITHUB_OUTPUT"
84143
else
85144
echo "present=false" >> "$GITHUB_OUTPUT"
86-
echo "::warning::SOROBAN_SECRET_KEY secret is not configured — deployment steps will be skipped. Add the secret to the repository to enable live deployments."
145+
echo "::warning::SOROBAN_SECRET_KEY secret is not configured — deployment steps will be skipped."
87146
fi
88147
env:
89148
SOROBAN_SECRET_KEY: ${{ secrets.SOROBAN_SECRET_KEY }}
90149

91150
- name: Deploy to Soroban testnet (Dry Run)
92151
if: github.event.inputs.dry_run == 'true' && steps.check-key.outputs.present == 'true'
93152
run: |
94-
bash scripts/deploy-soroban-testnet.sh \
153+
bash scripts/deploy.sh \
95154
--network testnet \
96155
--dry-run \
97-
--debug
156+
--wasm target/wasm32-unknown-unknown/release/runtime_guard_wrapper.wasm
98157
env:
99158
SOROBAN_SECRET_KEY: ${{ secrets.SOROBAN_SECRET_KEY }}
100-
SOROBAN_ACCOUNT_ID: ${{ secrets.SOROBAN_ACCOUNT_ID }}
101159

102160
- name: Deploy to Soroban testnet
103161
if: github.event.inputs.dry_run != 'true' && steps.check-key.outputs.present == 'true'
104162
run: |
105-
bash scripts/deploy-soroban-testnet.sh \
163+
bash scripts/deploy.sh \
106164
--network testnet \
107-
--interval 300 \
108-
--no-continuous \
109-
--debug
165+
--wasm target/wasm32-unknown-unknown/release/runtime_guard_wrapper.wasm
110166
env:
111167
SOROBAN_SECRET_KEY: ${{ secrets.SOROBAN_SECRET_KEY }}
112-
SOROBAN_ACCOUNT_ID: ${{ secrets.SOROBAN_ACCOUNT_ID }}
113168
timeout-minutes: 30
114169

170+
- name: Continuous validation
171+
if: github.event.inputs.dry_run != 'true' && steps.check-key.outputs.present == 'true'
172+
run: |
173+
echo "Running continuous validation checks..."
174+
if [ -f ".deployment-manifest.json" ]; then
175+
CONTRACTS=$(jq -r '.deployments[].contract_id' .deployment-manifest.json)
176+
for CID in $CONTRACTS; do
177+
echo "Validating: $CID"
178+
if soroban contract invoke --id "$CID" --network testnet -- health_check; then
179+
echo "✓ Health check passed"
180+
else
181+
echo "✗ Health check failed"
182+
exit 1
183+
fi
184+
done
185+
fi
186+
env:
187+
SOROBAN_SECRET_KEY: ${{ secrets.SOROBAN_SECRET_KEY }}
188+
SOROBAN_ACCOUNT_ID: ${{ secrets.SOROBAN_ACCOUNT_ID }}
189+
115190
- name: Upload deployment manifest
116191
if: always()
117192
uses: actions/upload-artifact@v6
118193
with:
119-
name: deployment-manifest-${{ github.run_id }}
194+
name: deployment-manifest-testnet-${{ github.run_id }}
120195
path: .deployment-manifest.json
121196
retention-days: 30
122197

123-
- name: Upload deployment log
124-
if: always()
125-
uses: actions/upload-artifact@v6
126-
with:
127-
name: deployment-log-${{ github.run_id }}
128-
path: .deployment.log
129-
retention-days: 30
130-
131-
- name: Parse deployment results
132-
if: always()
133-
run: |
134-
if [ -f ".deployment-manifest.json" ]; then
135-
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
136-
echo "" >> $GITHUB_STEP_SUMMARY
137-
jq '.deployments[] | "- **\(.name)**: `\(.contract_id)` (\(.status))"' \
138-
.deployment-manifest.json >> $GITHUB_STEP_SUMMARY
139-
echo "" >> $GITHUB_STEP_SUMMARY
140-
echo "Last updated: $(jq -r '.last_updated' .deployment-manifest.json)" >> $GITHUB_STEP_SUMMARY
141-
fi
142-
143-
continuous-validation:
144-
name: Continuous Validation
198+
# ── Mainnet deploy (gated by GitHub Environment + manual approval) ───────
199+
mainnet-deploy:
200+
name: Deploy to Soroban Mainnet
145201
runs-on: ubuntu-latest
146-
needs: build-and-deploy
147-
if: success() && github.ref == 'refs/heads/main'
202+
needs: build
203+
if: github.event.inputs.target == 'mainnet'
204+
environment:
205+
name: mainnet
206+
url: https://stellar.expert/explorer
148207
permissions:
149208
contents: read
150209
deployments: write
210+
checks: write
151211

152212
steps:
153213
- name: Checkout repository
154214
uses: actions/checkout@v6
155215

156-
- name: Download deployment manifest
157-
uses: actions/download-artifact@v6
158-
with:
159-
name: deployment-manifest-${{ github.run_id }}
160-
161216
- name: Install Soroban CLI
162217
run: |
163218
sudo apt-get update
@@ -166,95 +221,106 @@ jobs:
166221
echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH" >> $GITHUB_ENV
167222
cargo install --locked soroban-cli || true
168223
169-
- name: Run continuous validation checks
224+
- name: Download WASM artifact
225+
uses: actions/download-artifact@v6
226+
with:
227+
name: runtime-guard-wrapper-wasm
228+
path: target/wasm32-unknown-unknown/release/
229+
230+
- name: Show Soroban network info
170231
run: |
171-
echo "Running continuous validation checks..."
172-
232+
soroban network ls
233+
soroban network info --network mainnet
234+
235+
- name: Check mainnet deployment secrets
236+
id: check-key
237+
run: |
238+
if [[ -n "${SOROBAN_MAINNET_SECRET_KEY:-}" ]]; then
239+
echo "present=true" >> "$GITHUB_OUTPUT"
240+
else
241+
echo "present=false" >> "$GITHUB_OUTPUT"
242+
echo "::error::SOROBAN_MAINNET_SECRET_KEY is not configured — aborting."
243+
exit 1
244+
fi
245+
env:
246+
SOROBAN_MAINNET_SECRET_KEY: ${{ secrets.SOROBAN_MAINNET_SECRET_KEY }}
247+
248+
- name: Dry-run first (preflight simulation)
249+
run: |
250+
bash scripts/deploy.sh \
251+
--network mainnet \
252+
--confirm-mainnet \
253+
--dry-run \
254+
--wasm target/wasm32-unknown-unknown/release/runtime_guard_wrapper.wasm
255+
env:
256+
SOROBAN_SECRET_KEY: ${{ secrets.SOROBAN_MAINNET_SECRET_KEY }}
257+
258+
- name: Deploy to Soroban mainnet
259+
run: |
260+
bash scripts/deploy.sh \
261+
--network mainnet \
262+
--confirm-mainnet \
263+
--wasm target/wasm32-unknown-unknown/release/runtime_guard_wrapper.wasm
264+
env:
265+
SOROBAN_SECRET_KEY: ${{ secrets.SOROBAN_MAINNET_SECRET_KEY }}
266+
timeout-minutes: 30
267+
268+
- name: Continuous validation
269+
run: |
270+
echo "Running mainnet validation checks..."
173271
if [ -f ".deployment-manifest.json" ]; then
174272
CONTRACTS=$(jq -r '.deployments[].contract_id' .deployment-manifest.json)
175-
176-
for CONTRACT_ID in $CONTRACTS; do
177-
echo ""
178-
echo "Validating contract: $CONTRACT_ID"
179-
180-
# Health check
181-
if soroban contract invoke \
182-
--id "$CONTRACT_ID" \
183-
--network testnet \
184-
-- health_check; then
185-
echo "✓ Health check passed for $CONTRACT_ID"
273+
for CID in $CONTRACTS; do
274+
echo "Validating: $CID"
275+
if soroban contract invoke --id "$CID" --network mainnet -- health_check; then
276+
echo "✓ Health check passed"
186277
else
187-
echo "✗ Health check failed for $CONTRACT_ID"
278+
echo "✗ Health check failed"
188279
exit 1
189280
fi
190-
191-
# Get stats
192-
if soroban contract invoke \
193-
--id "$CONTRACT_ID" \
194-
--network testnet \
195-
-- get_stats 2>/dev/null; then
196-
echo "✓ Stats retrieved for $CONTRACT_ID"
197-
fi
198281
done
199282
fi
200283
env:
201-
SOROBAN_SECRET_KEY: ${{ secrets.SOROBAN_SECRET_KEY }}
202-
SOROBAN_ACCOUNT_ID: ${{ secrets.SOROBAN_ACCOUNT_ID }}
284+
SOROBAN_SECRET_KEY: ${{ secrets.SOROBAN_MAINNET_SECRET_KEY }}
203285

204-
- name: Generate validation report
286+
- name: Upload deployment manifest
205287
if: always()
206-
run: |
207-
echo "## Continuous Validation Report" >> $GITHUB_STEP_SUMMARY
208-
echo "" >> $GITHUB_STEP_SUMMARY
209-
echo "- Timestamp: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY
210-
echo "- Network: testnet" >> $GITHUB_STEP_SUMMARY
211-
echo "- Status: Success" >> $GITHUB_STEP_SUMMARY
288+
uses: actions/upload-artifact@v6
289+
with:
290+
name: deployment-manifest-mainnet-${{ github.run_id }}
291+
path: .deployment-manifest.json
292+
retention-days: 90
212293

213-
notification:
214-
name: Send Deployment Notification
294+
# ── Summary ──────────────────────────────────────────────────────────────
295+
summary:
296+
name: Deployment Summary
215297
runs-on: ubuntu-latest
216-
needs: [build-and-deploy, continuous-validation]
298+
needs: [build, testnet-deploy, mainnet-deploy]
217299
if: always()
218300
permissions:
219301
checks: write
220302

221303
steps:
222-
- name: Determine status
304+
- name: Determine overall status
223305
id: status
224306
run: |
225-
if [ "${{ needs.build-and-deploy.result }}" = "success" ]; then
307+
if [[ '${{ needs.testnet-deploy.result }}' == 'success' || '${{ needs.mainnet-deploy.result }}' == 'success' ]]; then
226308
echo "DEPLOYMENT_STATUS=✅ Success" >> $GITHUB_OUTPUT
227309
echo "DEPLOYMENT_COLOR=0x28a745" >> $GITHUB_OUTPUT
228-
else
310+
elif [[ '${{ needs.testnet-deploy.result }}' == 'failure' || '${{ needs.mainnet-deploy.result }}' == 'failure' ]]; then
229311
echo "DEPLOYMENT_STATUS=❌ Failed" >> $GITHUB_OUTPUT
230312
echo "DEPLOYMENT_COLOR=0xdc3545" >> $GITHUB_OUTPUT
313+
else
314+
echo "DEPLOYMENT_STATUS=⏭️ Skipped" >> $GITHUB_OUTPUT
315+
echo "DEPLOYMENT_COLOR=0x6c757d" >> $GITHUB_OUTPUT
231316
fi
232317
233-
- name: Create deployment status check
234-
uses: actions/github-script@v8
235-
with:
236-
script: |
237-
github.rest.checks.create({
238-
owner: context.repo.owner,
239-
repo: context.repo.repo,
240-
name: 'Soroban Runtime Guard Deployment',
241-
head_sha: context.sha,
242-
status: '${{ needs.build-and-deploy.result }}' === 'success' ? 'completed' : 'completed',
243-
conclusion: '${{ needs.build-and-deploy.result }}' === 'success' ? 'success' : 'failure',
244-
output: {
245-
title: 'Deployment ${{ steps.status.outputs.DEPLOYMENT_STATUS }}',
246-
summary: 'Runtime guard wrapper contract has been deployed to Soroban testnet',
247-
text: 'Check the deployment artifacts for detailed logs and manifests.'
248-
}
249-
});
250-
251318
- name: Post deployment summary
252319
run: |
253-
echo "## 🚀 Soroban Runtime Guard Deployment Complete" >> $GITHUB_STEP_SUMMARY
320+
echo "## 🚀 Soroban Runtime Guard Deployment" >> $GITHUB_STEP_SUMMARY
254321
echo "" >> $GITHUB_STEP_SUMMARY
255322
echo "**Status**: ${{ steps.status.outputs.DEPLOYMENT_STATUS }}" >> $GITHUB_STEP_SUMMARY
256-
echo "**Network**: testnet" >> $GITHUB_STEP_SUMMARY
323+
echo "**Target**: ${{ github.event.inputs.target || 'testnet' }}" >> $GITHUB_STEP_SUMMARY
324+
echo "**Dry Run**: ${{ github.event.inputs.dry_run || 'true' }}" >> $GITHUB_STEP_SUMMARY
257325
echo "**Timestamp**: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY
258326
echo "**Commit**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
259-
echo "" >> $GITHUB_STEP_SUMMARY
260-
echo "[View Artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
See [VERSIONING_POLICY.md](./VERSIONING_POLICY.md) for the detailed policy on what constitutes a breaking change for CLI flags, output schemas, and rule sets.
78

89
## Format Guidelines
910

0 commit comments

Comments
 (0)