Skip to content

.github/workflows/manual_phala-envs-update-prod.yml #3

.github/workflows/manual_phala-envs-update-prod.yml

.github/workflows/manual_phala-envs-update-prod.yml #3

# Push sealed environment variables to a specific Phala CVM and start it (PROD).
#
# Prod-flavored sibling of manual_phala-envs-update.yml. Used during CVM
# migrations (e.g. prod6 → prod2) to bootstrap a freshly-provisioned chipotle-prod
# replica with the same live env vars CI would normally push during the
# tag-triggered deploy-prod-* flow.
#
# Differences from the next/staging variant:
# - Stripe LIVE keys (STRIPE_SECRET_KEY / STRIPE_PUBLISHABLE_KEY), not sandbox
# - Defaults: CERTBOT_DOMAIN=api.chipotle.litprotocol.com, GCP_PROJECT_ID=chipotle-prod
# - Encrypts env vars client-side and pushes via `phala envs update --encrypted-env`.
# The CLI's internal pubkey lookup is broken for Safe-owned apps (it passes
# kms_type "base" instead of a real slug like "kms-base-prod2"); see the
# same workaround in deploy-prod-1-propose.yml:307-383.
#
# Env block mirrors deploy-prod-1-propose.yml:341-372 (encryptEnvVars step).
#
# Required secrets:
# PHALA_CLOUD_API_KEY - Phala Cloud API key
# BASE_CHAIN_RPC - Base mainnet RPC URL
# GCP_SERVICE_ACCOUNT_JSON - GCP service account key
# STRIPE_SECRET_KEY - Stripe LIVE secret key
# STRIPE_PUBLISHABLE_KEY - Stripe LIVE publishable key
# CERTBOT_AWS_ACCESS_KEY_ID - Route 53 IAM access key
# CERTBOT_AWS_SECRET_ACCESS_KEY - Route 53 IAM secret key
# CERTBOT_AWS_ROLE_ARN - IAM role ARN for STS assumption
#
# Required vars:
# CERTBOT_AWS_REGION - AWS region for STS endpoint
name: Phala Envs Update + Start (Prod, manual)
permissions:
contents: read
on:
workflow_dispatch:
inputs:
cvm_id:
description: "Target CVM (UUID, app_id, instance_id, or name) — e.g. cvm_qwrMBqKl"
required: true
type: string
certbot_domain:
description: "CERTBOT_DOMAIN for dstack-ingress. Leave blank to skip ingress cert acquisition."
required: false
type: string
default: "api.chipotle.litprotocol.com"
gcp_project_id:
description: "GCP_PROJECT_ID for otel-collector"
required: false
type: string
default: "chipotle-prod"
kms_slug:
description: "KMS slug to fetch the encryption pubkey from (e.g. kms-base-prod2)"
required: false
type: string
default: "kms-base-prod2"
start:
description: "Start the CVM after pushing envs"
required: false
type: boolean
default: true
jobs:
envs-update:
runs-on: self-hosted
steps:
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Phala CLI
run: npm install -g phala
- name: Resolve CVM app_id
id: cvm
env:
PHALA_CLOUD_API_KEY: ${{ secrets.PHALA_CLOUD_API_KEY }}
run: |
set -euo pipefail
APP_ID=$(phala cvms get "${{ inputs.cvm_id }}" --json | jq -r '.app_id')
if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then
echo "::error::Could not resolve app_id for CVM ${{ inputs.cvm_id }}"
exit 1
fi
echo "Resolved app_id: $APP_ID"
echo "app_id=$APP_ID" >> "$GITHUB_OUTPUT"
- name: Fetch CVM encryption public key
id: pubkey
env:
PHALA_CLOUD_API_KEY: ${{ secrets.PHALA_CLOUD_API_KEY }}
KMS_SLUG: ${{ inputs.kms_slug }}
APP_ID: ${{ steps.cvm.outputs.app_id }}
run: |
set -euo pipefail
# Workaround: `phala envs update` passes kms_type ("base") to the
# /kms/{kms}/pubkey/{app_id} API, but the API expects a KMS slug
# (e.g. "kms-base-prod2"). Fetch the pubkey directly via the correct
# slug, then encrypt client-side.
echo "Fetching encryption pubkey via /kms/$KMS_SLUG/pubkey/$APP_ID ..."
PUBKEY_RESPONSE=$(phala api "/kms/$KMS_SLUG/pubkey/$APP_ID" --json)
PUBKEY=$(echo "$PUBKEY_RESPONSE" | jq -r '.public_key')
if [ -z "$PUBKEY" ] || [ "$PUBKEY" = "null" ]; then
echo "::error::Failed to extract public_key from KMS response: $PUBKEY_RESPONSE"
exit 1
fi
echo "Got encryption pubkey: ${PUBKEY:0:16}..."
echo "encrypt_pubkey=$PUBKEY" >> "$GITHUB_OUTPUT"
- name: Encrypt environment variables for CVM
id: encrypt
env:
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
STRIPE_PUBLISHABLE_KEY: ${{ secrets.STRIPE_PUBLISHABLE_KEY }}
GCP_SERVICE_ACCOUNT_JSON: ${{ secrets.GCP_SERVICE_ACCOUNT_JSON }}
BASE_CHAIN_RPC: ${{ secrets.BASE_CHAIN_RPC }}
CERTBOT_AWS_ACCESS_KEY_ID: ${{ secrets.CERTBOT_AWS_ACCESS_KEY_ID }}
CERTBOT_AWS_SECRET_ACCESS_KEY: ${{ secrets.CERTBOT_AWS_SECRET_ACCESS_KEY }}
CERTBOT_AWS_ROLE_ARN: ${{ secrets.CERTBOT_AWS_ROLE_ARN }}
CERTBOT_AWS_REGION: ${{ vars.CERTBOT_AWS_REGION }}
GCP_PROJECT_ID: ${{ inputs.gcp_project_id }}
CERTBOT_DOMAIN: ${{ inputs.certbot_domain }}
ENCRYPT_PUBKEY: ${{ steps.pubkey.outputs.encrypt_pubkey }}
run: |
set -euo pipefail
ENCRYPTED_ENV=$(node -e '
const path = require("path");
const prefix = require("child_process").execSync("npm prefix -g").toString().trim();
const { encryptEnvVars } = require(
require.resolve("@phala/cloud", { paths: [path.join(prefix, "lib/node_modules/phala")] })
);
const envs = [
{ key: "STRIPE_SECRET_KEY", value: process.env.STRIPE_SECRET_KEY },
{ key: "STRIPE_PUBLISHABLE_KEY", value: process.env.STRIPE_PUBLISHABLE_KEY },
{ key: "GCP_SERVICE_ACCOUNT_JSON", value: process.env.GCP_SERVICE_ACCOUNT_JSON },
{ key: "GCP_PROJECT_ID", value: process.env.GCP_PROJECT_ID },
{ key: "BASE_CHAIN_RPC", value: process.env.BASE_CHAIN_RPC },
{ key: "CERTBOT_DOMAIN", value: process.env.CERTBOT_DOMAIN },
{ key: "CERTBOT_AWS_ACCESS_KEY_ID", value: process.env.CERTBOT_AWS_ACCESS_KEY_ID },
{ key: "CERTBOT_AWS_SECRET_ACCESS_KEY", value: process.env.CERTBOT_AWS_SECRET_ACCESS_KEY },
{ key: "CERTBOT_AWS_ROLE_ARN", value: process.env.CERTBOT_AWS_ROLE_ARN },
{ key: "CERTBOT_AWS_REGION", value: process.env.CERTBOT_AWS_REGION },
];
encryptEnvVars(envs, process.env.ENCRYPT_PUBKEY).then(hex => process.stdout.write(hex));
')
if [ -z "$ENCRYPTED_ENV" ]; then
echo "::error::encryptEnvVars returned empty output"
exit 1
fi
echo "Encrypted env length: ${#ENCRYPTED_ENV} chars"
echo "encrypted_env=$ENCRYPTED_ENV" >> "$GITHUB_OUTPUT"
- name: Push sealed envs to CVM
env:
PHALA_CLOUD_API_KEY: ${{ secrets.PHALA_CLOUD_API_KEY }}
ENCRYPTED_ENV: ${{ steps.encrypt.outputs.encrypted_env }}
run: |
phala envs update "${{ inputs.cvm_id }}" --encrypted-env "$ENCRYPTED_ENV"
- name: Start CVM
if: inputs.start
env:
PHALA_CLOUD_API_KEY: ${{ secrets.PHALA_CLOUD_API_KEY }}
run: |
phala cvms start "${{ inputs.cvm_id }}"
- name: Show CVM status
env:
PHALA_CLOUD_API_KEY: ${{ secrets.PHALA_CLOUD_API_KEY }}
run: |
phala cvms get "${{ inputs.cvm_id }}"