Skip to content

Deploy Network

Deploy Network #92

# Low-level workflow to deploy a single network
# This is called by other deployment workflows
name: Deploy Network
on:
workflow_call:
inputs:
network:
description: "Network to deploy (e.g., staging-public, testnet, next-net)"
required: true
type: string
aztec_docker_image:
description: "Full Aztec docker image (e.g., aztecprotocol/aztec:2.3.4). If not set, constructed from semver."
required: false
type: string
semver:
description: "Semver version (e.g., 2.3.4). Used to construct docker image if aztec_docker_image is not set."
required: false
type: string
ref:
description: "Git ref to checkout"
required: false
type: string
namespace:
description: "Kubernetes namespace override (optional, defaults to env file value)"
required: false
type: string
deploy_contracts:
description: "Whether to deploy contracts fresh (true for first patch, false to read from network config)"
required: false
type: boolean
default: false
ha_docker_image:
description: "Full docker image for HA validator nodes (optional, defaults to aztec docker image)"
required: false
type: string
source_tag:
description: "Source tag that triggered this deploy"
required: false
type: string
workflow_dispatch:
inputs:
network:
description: "Network to deploy (e.g., staging-public, staging-ignition, testnet, next-net)"
required: true
type: choice
options:
- next-net
- staging-public
- testnet
- mainnet
aztec_docker_image:
description: "Full Aztec docker image (e.g., aztecprotocol/aztec:2.3.4). If not set, constructed from semver."
required: false
type: string
semver:
description: "Semver version (e.g., 2.3.4). Used to construct docker image if aztec_docker_image is not set."
required: false
type: string
namespace:
description: "Kubernetes namespace override (optional, defaults to env file value)"
required: false
type: string
deploy_contracts:
description: "Whether to deploy contracts fresh (true for first patch, false to read from network config)"
required: false
type: boolean
default: false
ha_docker_image:
description: "Full docker image for HA validator nodes (optional, defaults to aztec docker image)"
required: false
type: string
source_tag:
description: "Source tag that triggered this deploy"
required: false
type: string
concurrency:
group: deploy-network-${{ inputs.network }}-${{ inputs.namespace || inputs.network }}-${{ inputs.aztec_docker_image || inputs.semver }}-${{ github.ref || github.ref_name }}
cancel-in-progress: true
jobs:
deploy-network:
runs-on: ubuntu-latest
env:
GOOGLE_APPLICATION_CREDENTIALS: /tmp/gcp-key.json
outputs:
cluster: ${{ steps.deploy-network.outputs.cluster }}
steps:
- name: Determine checkout ref
id: checkout-ref
run: |
# Use inputs.ref if provided (workflow_call), otherwise use github.ref
if [[ -n "${{ inputs.ref }}" ]]; then
echo "ref=${{ inputs.ref }}" >> $GITHUB_OUTPUT
else
echo "ref=${{ github.ref }}" >> $GITHUB_OUTPUT
fi
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
ref: ${{ steps.checkout-ref.outputs.ref }}
fetch-depth: 1
persist-credentials: false
submodules: recursive # Initialize git submodules for l1-contracts dependencies
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
- name: Validate inputs
run: |
# Validate network
if [[ ! -f "spartan/environments/${{ inputs.network }}.env" ]]; then
echo "Error: Environment file not found for network '${{ inputs.network }}'"
echo "Available networks:"
ls -1 spartan/environments/ | grep -v '\.local\.env$' || echo "No environment files found"
exit 1
fi
# Require at least one of aztec_docker_image or semver
if [[ -z "${{ inputs.aztec_docker_image }}" && -z "${{ inputs.semver }}" ]]; then
echo "Error: Either 'aztec_docker_image' or 'semver' must be provided"
exit 1
fi
# Validate semver format if provided
if [[ -n "${{ inputs.semver }}" ]]; then
if ! echo "${{ inputs.semver }}" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-.*)?$'; then
echo "Error: Invalid semver format '${{ inputs.semver }}'. Expected format: X.Y.Z or X.Y.Z-suffix"
exit 1
fi
fi
# Resolve the docker image
if [[ -n "${{ inputs.aztec_docker_image }}" ]]; then
AZTEC_DOCKER_IMAGE="${{ inputs.aztec_docker_image }}"
else
AZTEC_DOCKER_IMAGE="aztecprotocol/aztec:${{ inputs.semver }}"
fi
echo "AZTEC_DOCKER_IMAGE=$AZTEC_DOCKER_IMAGE" >> $GITHUB_ENV
# Only use the separate prover-agent image for official semver builds;
# for custom images, let the deploy script fall back to AZTEC_DOCKER_IMAGE
if [[ -n "${{ inputs.semver }}" ]]; then
echo "PROVER_AGENT_DOCKER_IMAGE=aztecprotocol/aztec-prover-agent:${{ inputs.semver }}" >> $GITHUB_ENV
fi
- name: Store the GCP key in a file
env:
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
run: |
set +x
umask 077
printf '%s' "$GCP_SA_KEY" > "$GOOGLE_APPLICATION_CREDENTIALS"
jq -e . "$GOOGLE_APPLICATION_CREDENTIALS" >/dev/null
- name: Setup GCP authentication
run: |
gcloud auth activate-service-account --key-file="$GOOGLE_APPLICATION_CREDENTIALS"
- name: Setup gcloud and install GKE auth plugin
uses: google-github-actions/setup-gcloud@v2
with:
install_components: "gke-gcloud-auth-plugin"
- name: Setup Terraform
uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1
with:
terraform_version: "1.7.5"
terraform_wrapper: false # Disable the wrapper that adds debug output, this messes with reading terraform output
- name: Setup SSH key for CI Redis
env:
BUILD_INSTANCE_SSH_KEY: ${{ secrets.BUILD_INSTANCE_SSH_KEY }}
run: |
if [ -n "${BUILD_INSTANCE_SSH_KEY:-}" ]; then
mkdir -p ~/.ssh
echo "${BUILD_INSTANCE_SSH_KEY}" | base64 --decode > ~/.ssh/build_instance_key
chmod 600 ~/.ssh/build_instance_key
fi
- name: Deploy network
id: deploy-network
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }}
RUN_ID: ${{ github.run_id }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}
REF_NAME: ${{ inputs.semver && format('v{0}', inputs.semver) || '' }}
GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
NAMESPACE: ${{ inputs.namespace }}
AZTEC_DOCKER_IMAGE: ${{ env.AZTEC_DOCKER_IMAGE }}
CREATE_ROLLUP_CONTRACTS: ${{ inputs.deploy_contracts == true && 'true' || '' }}
PROVER_AGENT_DOCKER_IMAGE: ${{ env.PROVER_AGENT_DOCKER_IMAGE || env.AZTEC_DOCKER_IMAGE }}
VALIDATOR_HA_DOCKER_IMAGE: ${{ inputs.ha_docker_image || '' }}
run: |
echo "Deploying network: ${{ inputs.network }}"
echo "Using image: $AZTEC_DOCKER_IMAGE"
echo "Using prover image: $PROVER_AGENT_DOCKER_IMAGE"
echo "Using branch/ref: ${{ steps.checkout-ref.outputs.ref }}"
cd spartan
./scripts/install_deps.sh
./scripts/network_deploy.sh "${{ inputs.network }}"
# need to source this for CLUSTER
source "./environments/${{ inputs.network }}.env"
if [ -n "$CLUSTER" ]; then
echo "cluster=$CLUSTER" >> $GITHUB_OUTPUT
else
echo "cluster=" >> $GITHUB_OUTPUT
fi
- name: Step summary
if: always()
run: |
{
echo "## Deploy Network"
echo ""
echo "| Item | Value |"
echo "|------|-------|"
echo "| Network | \`${{ inputs.network }}\` |"
echo "| Docker Image | \`${{ env.AZTEC_DOCKER_IMAGE }}\` |"
echo "| Ref | \`${{ steps.checkout-ref.outputs.ref }}\` |"
if [[ -n "${{ inputs.source_tag }}" ]]; then
echo "| Source Tag | [\`${{ inputs.source_tag }}\`](https://github.com/${{ github.repository }}/releases/tag/${{ inputs.source_tag }}) |"
fi
} >> "$GITHUB_STEP_SUMMARY"
- name: Notify Slack and dispatch ClaudeBox on failure
if: failure()
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
GH_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }}
run: |
if [ -z "${SLACK_BOT_TOKEN:-}" ]; then
echo "No SLACK_BOT_TOKEN, skipping notification"
exit 0
fi
CHANNEL="#alerts-${{ inputs.network }}"
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
TEXT="Deploy Network workflow FAILED for *${{ inputs.network }}* (image ${{ env.AZTEC_DOCKER_IMAGE }}): <${RUN_URL}|View Run> (🤖)"
# Post to Slack and capture timestamp for permalink
RESP=$(curl -sS -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-type: application/json" \
-d "$(jq -n --arg c "$CHANNEL" --arg t "$TEXT" '{channel:$c, text:$t}')")
echo "Slack response: $RESP"
TS=$(echo "$RESP" | jq -r '.ts // empty')
CHANNEL_ID=$(echo "$RESP" | jq -r '.channel // empty')
LINK=""
if [[ -n "$TS" && -n "$CHANNEL_ID" ]]; then
LINK="https://aztecprotocol.slack.com/archives/$CHANNEL_ID/p${TS//./}"
fi
# Dispatch ClaudeBox to investigate the failure
PROMPT="Deployment of ${{ inputs.network }} (image ${{ env.AZTEC_DOCKER_IMAGE }}) failed. \
Follow .claude/claudebox/deploy-investigation.md to investigate. \
GitHub Actions run: ${RUN_URL}. \
Network: ${{ inputs.network }}. \
Docker image: ${{ env.AZTEC_DOCKER_IMAGE }}. \
Git ref: ${{ steps.checkout-ref.outputs.ref }}. \
Namespace: ${{ inputs.namespace || inputs.network }}. \
Deploy contracts: ${{ inputs.deploy_contracts }}."
gh workflow run claudebox.yml \
-f prompt="$PROMPT" \
-f link="${LINK:-$RUN_URL}" \
-f target_ref="${{ steps.checkout-ref.outputs.ref }}" || true
update-irm:
needs: deploy-network
if: inputs.network == 'testnet' || inputs.network == 'mainnet'
uses: ./.github/workflows/deploy-irm.yml
secrets: inherit
with:
network: ${{ inputs.network }}
l1_network: ${{ inputs.network == 'mainnet' && 'mainnet' || 'sepolia' }}
cluster: ${{ needs.deploy-network.outputs.cluster }}