|
#!/bin/bash |
|
# Beast Dev Deployment Script |
|
# Usage: beast_deploy.sh <branch> [app] [expiry] |
|
# beast_deploy.sh --redeploy <deployment_id> |
|
# beast_deploy.sh --extend <deployment_id> <date> |
|
# beast_deploy.sh --status <deployment_id> |
|
# beast_deploy.sh --delete <deployment_id> |
|
# beast_deploy.sh --list [app] |
|
# |
|
# Examples: |
|
# beast_deploy.sh astupka/rsq-ai-guidance # create new (rails-backend, 2 day) |
|
# beast_deploy.sh astupka/rsq-ai-guidance ml-ai-gateway # create new for different app |
|
# beast_deploy.sh astupka/rsq-ai-guidance rails-backend 86400 # 1 day expiry |
|
# beast_deploy.sh --redeploy 23607 # redeploy existing instance |
|
# beast_deploy.sh --extend 23607 2026-03-25 # extend expiry to date |
|
# beast_deploy.sh --status 23607 # check deployment status |
|
# beast_deploy.sh --delete 23607 # delete instance |
|
# beast_deploy.sh --list # list active rails-backend deployments |
|
# beast_deploy.sh --list ml-ai-gateway # list active ml-ai-gateway deployments |
|
# |
|
# Environment variables (optional): |
|
# BEAST_USER_EMAIL - defaults to git config user.email |
|
# AWS_CMD - defaults to `aws` on PATH |
|
# AWS_PROFILE - defaults to techdev-ci-prod |
|
# |
|
# Requires: AWS SSO session (aws sso login --profile $AWS_PROFILE) |
|
|
|
set -eo pipefail |
|
|
|
# App name → Blueprint ID lookup |
|
blueprint_for() { |
|
case "$1" in |
|
rails-backend) echo 127 ;; |
|
rails-frontend) echo 125 ;; |
|
ml-ai-gateway) echo 291 ;; |
|
mms) echo 34 ;; |
|
catalog) echo 23 ;; |
|
profiles) echo 1 ;; |
|
sales) echo 56 ;; |
|
checkout) echo 51 ;; |
|
groups) echo 37 ;; |
|
stores) echo 22 ;; |
|
content-review-queue) echo 133 ;; |
|
creative-services) echo 7 ;; |
|
order-fulfillment-service) echo 141 ;; |
|
decorator-gateway-service) echo 121 ;; |
|
decorator-service) echo 110 ;; |
|
shipping-service) echo 89 ;; |
|
production-engine) echo 95 ;; |
|
promptr) echo 294 ;; |
|
langfuse) echo 285 ;; |
|
notes) echo 50 ;; |
|
journal-service) echo 113 ;; |
|
accounts-service) echo 11 ;; |
|
asset-service) echo 19 ;; |
|
fulfillment-products-service) echo 171 ;; |
|
supplier-service) echo 109 ;; |
|
notification-service) echo 111 ;; |
|
schedule-service) echo 192 ;; |
|
order-service) echo 138 ;; |
|
*) echo "" ;; |
|
esac |
|
} |
|
|
|
USER_EMAIL="${BEAST_USER_EMAIL:-$(git config user.email)}" |
|
AWS_CMD="${AWS_CMD:-$(command -v aws)}" |
|
AWS_PROFILE="${AWS_PROFILE:-techdev-ci-prod}" |
|
|
|
get_api_key() { |
|
"$AWS_CMD" ssm get-parameter \ |
|
--name "/services/k8s-staging-env-manager/BEAST_API_KEY" \ |
|
--with-decryption --profile "$AWS_PROFILE" \ |
|
--query 'Parameter.Value' --output text |
|
} |
|
|
|
poll_until_ready() { |
|
local DEPLOYMENT_ID="$1" |
|
local BEAST_API_KEY="$2" |
|
|
|
echo "Polling for READY status..." |
|
while true; do |
|
sleep 30 |
|
STATE=$(curl -s "https://beast.in.customink.com/deployment/$DEPLOYMENT_ID" \ |
|
-H "Authorization: ApiKey $BEAST_API_KEY" \ |
|
-H "X-User-Email: $USER_EMAIL" | \ |
|
python3 -c "import json,sys; d=json.load(sys.stdin); c=d['components'][0]; print(c['state'], c['hostname'])" 2>/dev/null) |
|
|
|
COMP_STATE=$(echo "$STATE" | awk '{print $1}') |
|
HOSTNAME=$(echo "$STATE" | awk '{print $2}') |
|
|
|
echo " $(date +%H:%M:%S) — $COMP_STATE ($HOSTNAME)" |
|
|
|
if [ "$COMP_STATE" = "READY" ]; then |
|
echo "" |
|
echo "Deployment READY!" |
|
echo " Instance: $HOSTNAME" |
|
echo " Console: k8s use d → select $HOSTNAME → k8s console" |
|
exit 0 |
|
elif [ "$COMP_STATE" = "ERROR" ] || [ "$COMP_STATE" = "FAILED" ]; then |
|
echo "ERROR: Deployment failed" |
|
exit 1 |
|
fi |
|
done |
|
} |
|
|
|
# Handle --redeploy |
|
if [ "${1:-}" = "--redeploy" ]; then |
|
DEPLOYMENT_ID="${2:?Usage: beast_deploy.sh --redeploy <deployment_id>}" |
|
BEAST_API_KEY="$(get_api_key)" |
|
|
|
echo "Redeploying deployment $DEPLOYMENT_ID..." |
|
RESPONSE=$(curl -s -X PUT "https://beast.in.customink.com/deployment/$DEPLOYMENT_ID/redeploy" \ |
|
-H "Content-Type: application/json" \ |
|
-H "Authorization: ApiKey $BEAST_API_KEY" \ |
|
-H "X-User-Email: $USER_EMAIL") |
|
|
|
NEW_STATE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('state', 'UNKNOWN'))" 2>/dev/null) |
|
echo "Redeploy triggered: state=$NEW_STATE" |
|
echo "" |
|
|
|
poll_until_ready "$DEPLOYMENT_ID" "$BEAST_API_KEY" |
|
exit 0 |
|
fi |
|
|
|
# Handle --extend |
|
if [ "${1:-}" = "--extend" ]; then |
|
DEPLOYMENT_ID="${2:?Usage: beast_deploy.sh --extend <deployment_id> <date> (e.g. 2026-03-25)}" |
|
EXTEND_DATE="${3:?Usage: beast_deploy.sh --extend <deployment_id> <date> (e.g. 2026-03-25)}" |
|
BEAST_API_KEY="$(get_api_key)" |
|
|
|
EXPIRES="${EXTEND_DATE}T23:59:59.000Z" |
|
echo "Extending deployment $DEPLOYMENT_ID to $EXTEND_DATE..." |
|
RESPONSE=$(curl -s -X PATCH "https://beast.in.customink.com/deployment/$DEPLOYMENT_ID" \ |
|
-H "Content-Type: application/json" \ |
|
-H "Authorization: ApiKey $BEAST_API_KEY" \ |
|
-H "X-User-Email: $USER_EMAIL" \ |
|
-d "{\"expiresAt\": \"$EXPIRES\"}") |
|
|
|
NEW_EXPIRY=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); c=d['components'][0]; print(f'{c[\"hostname\"]}: expires {d[\"expiresAt\"]}')" 2>/dev/null) |
|
echo " $NEW_EXPIRY" |
|
exit 0 |
|
fi |
|
|
|
# Handle --status |
|
if [ "${1:-}" = "--status" ]; then |
|
DEPLOYMENT_ID="${2:?Usage: beast_deploy.sh --status <deployment_id>}" |
|
BEAST_API_KEY="$(get_api_key)" |
|
|
|
curl -s "https://beast.in.customink.com/deployment/$DEPLOYMENT_ID" \ |
|
-H "Authorization: ApiKey $BEAST_API_KEY" \ |
|
-H "X-User-Email: $USER_EMAIL" | \ |
|
python3 -c " |
|
import json,sys |
|
d = json.load(sys.stdin) |
|
c = d['components'][0] |
|
print(f'ID: {d[\"id\"]}') |
|
print(f'Host: {c[\"hostname\"]}') |
|
print(f'Branch: {c[\"gitBranch\"]}') |
|
print(f'State: {d[\"state\"]} / {c[\"state\"]}') |
|
print(f'Expires: {d[\"expiresAt\"]}') |
|
" |
|
exit 0 |
|
fi |
|
|
|
# Handle --list |
|
if [ "${1:-}" = "--list" ]; then |
|
APP_NAME="${2:-rails-backend}" |
|
BLUEPRINT_ID="$(blueprint_for "$APP_NAME")" |
|
if [ -z "$BLUEPRINT_ID" ]; then |
|
echo "ERROR: Unknown app '$APP_NAME'" |
|
exit 1 |
|
fi |
|
BEAST_API_KEY="$(get_api_key)" |
|
|
|
echo "Active $APP_NAME deployments (blueprint $BLUEPRINT_ID):" |
|
echo "" |
|
curl -s "https://beast.in.customink.com/deployment-blueprint/$BLUEPRINT_ID" \ |
|
-H "Authorization: ApiKey $BEAST_API_KEY" \ |
|
-H "X-User-Email: $USER_EMAIL" | \ |
|
python3 -c " |
|
import json, sys |
|
data = json.load(sys.stdin) |
|
deps = data.get('deployments', []) |
|
active = [d for d in deps if d.get('state') in ('READY', 'IN_PROGRESS')] |
|
if not active: |
|
print(' No active deployments.') |
|
sys.exit(0) |
|
for d in active: |
|
comps = d.get('components', []) |
|
for c in comps: |
|
print(f' ID: {d[\"id\"]} {d[\"state\"]:12s} {c[\"hostname\"]:40s} branch: {c[\"gitBranch\"]}') |
|
print(f' expires: {d[\"expiresAt\"]} user: {d.get(\"createdBy\", {}).get(\"email\", \"?\")}') |
|
print() |
|
" |
|
exit 0 |
|
fi |
|
|
|
# Handle --delete |
|
if [ "${1:-}" = "--delete" ]; then |
|
DEPLOYMENT_ID="${2:?Usage: beast_deploy.sh --delete <deployment_id>}" |
|
BEAST_API_KEY="$(get_api_key)" |
|
|
|
echo "Deleting deployment $DEPLOYMENT_ID..." |
|
RESPONSE=$(curl -s -X DELETE "https://beast.in.customink.com/deployment/$DEPLOYMENT_ID" \ |
|
-H "Authorization: ApiKey $BEAST_API_KEY" \ |
|
-H "X-User-Email: $USER_EMAIL") |
|
|
|
STATE=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('state', d))" 2>/dev/null) |
|
echo "Delete triggered: state=$STATE" |
|
exit 0 |
|
fi |
|
|
|
# Handle create (default) |
|
BRANCH="${1:?Usage: beast_deploy.sh <branch> [app] [expiry] OR --redeploy <id> OR --delete <id>}" |
|
APP_NAME="${2:-rails-backend}" |
|
EXPIRY="${3:-172800}" |
|
|
|
BLUEPRINT_ID="$(blueprint_for "$APP_NAME")" |
|
if [ -z "$BLUEPRINT_ID" ]; then |
|
echo "ERROR: Unknown app '$APP_NAME'" |
|
exit 1 |
|
fi |
|
|
|
BEAST_API_KEY="$(get_api_key)" |
|
|
|
echo "Creating Beast deployment..." |
|
echo " App: $APP_NAME (blueprint $BLUEPRINT_ID)" |
|
echo " Branch: $BRANCH" |
|
echo " User: $USER_EMAIL" |
|
echo " Expiry: $((EXPIRY / 3600))h" |
|
|
|
RESPONSE=$(curl -s -X POST "https://beast.in.customink.com/deployment" \ |
|
-H "Content-Type: application/json" \ |
|
-H "Authorization: ApiKey $BEAST_API_KEY" \ |
|
-H "X-User-Email: $USER_EMAIL" \ |
|
-d "{\"deploymentBlueprintId\": $BLUEPRINT_ID, \"branches\": {\"$APP_NAME\": \"$BRANCH\"}, \"timeToExpire\": $EXPIRY, \"metadata\": {}, \"configuration\": {}}") |
|
|
|
DEPLOYMENT_ID=$(echo "$RESPONSE" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])" 2>/dev/null) |
|
HOSTNAME=$(echo "$RESPONSE" | python3 -c "import json,sys; print(json.load(sys.stdin)['components'][0]['hostname'])" 2>/dev/null) |
|
|
|
if [ -z "$DEPLOYMENT_ID" ]; then |
|
echo "ERROR: Failed to create deployment" |
|
echo "$RESPONSE" |
|
exit 1 |
|
fi |
|
|
|
echo "Deployment created: ID=$DEPLOYMENT_ID, Host=$HOSTNAME" |
|
echo "Beast UI: https://beast.in.customink.com/#/deployment-blueprint/$BLUEPRINT_ID/deployments" |
|
echo "" |
|
echo "Save this ID to redeploy later: beast_deploy.sh --redeploy $DEPLOYMENT_ID" |
|
echo "" |
|
|
|
poll_until_ready "$DEPLOYMENT_ID" "$BEAST_API_KEY" |