gabriel / musehub public
push.sh bash
189 lines 7.0 KB
Raw
sha256:a10adeeb7a0169cb9900f9806ed7a973047258abb6283724fe55e8eb68ff3f0a init: musehub initial commit Human 171 days ago
1 #!/usr/bin/env bash
2 # MuseHub deploy orchestrator — build, push to ECR, trigger blue-green via SSM.
3 #
4 # Usage:
5 # bash deploy/push.sh staging # deploy to staging only
6 # bash deploy/push.sh prod # deploy to prod only
7 # bash deploy/push.sh staging prod # staging first, then prod
8 #
9 # What it does:
10 # 1. Builds a linux/amd64 Docker image from the local repo.
11 # 2. Tags it with <commit-hash>-<timestamp> for traceability.
12 # 3. Pushes the image to ECR (musehub/musehub).
13 # 4. Sends an SSM command to each target instance to run deploy.sh,
14 # which pulls the image and performs a zero-downtime blue-green swap.
15 # 5. Polls SSM until the deploy completes or fails, streaming the output.
16 #
17 # Prerequisites (one-time, already done):
18 # - AWS CLI configured (musehub-infra as default profile)
19 # - Docker Desktop running
20 # - musehub-infra has ecr push permissions (musehub-ecr-push policy)
21 # - musehub-ec2-ssm role has ecr pull permissions (musehub-ecr-pull policy)
22 # - AWS CLI installed on instances (run deploy/bootstrap-instance.sh once)
23 #
24 # Rollback to a previous image:
25 # IMAGE_TAG=<previous-tag> bash deploy/push.sh staging
26 # (skips build+push, triggers SSM with the specified tag directly)
27
28 set -euo pipefail
29
30 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31 REPO_DIR="$(dirname "$SCRIPT_DIR")"
32
33 ECR_REGISTRY="992382692655.dkr.ecr.us-east-1.amazonaws.com"
34 ECR_REPO="musehub/musehub"
35 ECR_IMAGE="${ECR_REGISTRY}/${ECR_REPO}"
36 REGION="us-east-1"
37
38 STAGING_INSTANCE="i-07547cd20bee2dea5"
39 PROD_INSTANCE="i-0855d6efe7fa1a49d"
40
41 # ── Parse targets ─────────────────────────────────────────────────────────────
42
43 if [ $# -eq 0 ]; then
44 echo "Usage: bash deploy/push.sh [staging] [prod]"
45 echo " staging deploy to staging.musehub.ai"
46 echo " prod deploy to musehub.ai"
47 echo " staging prod staging first, then prod"
48 echo ""
49 echo "Rollback (skips build+push, redeploys a previous tag):"
50 echo " IMAGE_TAG=<tag> bash deploy/push.sh staging"
51 exit 1
52 fi
53
54 TARGETS=()
55 for arg in "$@"; do
56 case "$arg" in
57 staging|prod) TARGETS+=("$arg") ;;
58 *) echo "Unknown target: $arg (must be staging or prod)" >&2; exit 1 ;;
59 esac
60 done
61
62 # ── Helpers ───────────────────────────────────────────────────────────────────
63
64 log() { echo "[push] $*"; }
65 die() { echo "[push] ERROR: $*" >&2; exit 1; }
66
67 # ── Image tag ─────────────────────────────────────────────────────────────────
68
69 # If IMAGE_TAG is already set (rollback mode), skip build+push.
70 if [ -n "${IMAGE_TAG:-}" ]; then
71 log "Rollback mode — using existing tag: $IMAGE_TAG"
72 SKIP_BUILD=true
73 else
74 COMMIT_HASH=$(muse -C "$REPO_DIR" rev-parse HEAD --json 2>/dev/null \
75 | python3 -c "import sys,json; print(json.load(sys.stdin)['commit_id'][:8])" \
76 2>/dev/null || echo "local")
77 IMAGE_TAG="${COMMIT_HASH}-$(date +%Y%m%d%H%M%S)"
78 SKIP_BUILD=false
79 fi
80
81 FULL_IMAGE="${ECR_IMAGE}:${IMAGE_TAG}"
82 log "Target image: $FULL_IMAGE"
83
84 # ── Build ─────────────────────────────────────────────────────────────────────
85
86 if [ "$SKIP_BUILD" = false ]; then
87 log "[1/3] Building image for linux/amd64..."
88 docker build \
89 --platform linux/amd64 \
90 --tag "$FULL_IMAGE" \
91 --tag "${ECR_IMAGE}:latest" \
92 "$REPO_DIR"
93 log "Build complete."
94
95 # ── Push to ECR ───────────────────────────────────────────────────────────
96
97 log "[2/3] Logging in to ECR and pushing..."
98 aws ecr get-login-password --region "$REGION" | \
99 docker login --username AWS --password-stdin "$ECR_REGISTRY"
100 docker push "$FULL_IMAGE"
101 docker push "${ECR_IMAGE}:latest"
102 log "Push complete. Tag: $IMAGE_TAG"
103 else
104 log "[1/3] Skipping build (rollback mode)."
105 log "[2/3] Skipping push (rollback mode)."
106 fi
107
108 # ── Trigger deploy via SSM ────────────────────────────────────────────────────
109
110 log "[3/3] Triggering deploy on: ${TARGETS[*]}"
111
112 deploy_to() {
113 local env="$1"
114 local instance_id="$2"
115
116 log ""
117 log "→ Deploying to $env ($instance_id)..."
118
119 local cmd_id
120 cmd_id=$(aws ssm send-command \
121 --region "$REGION" \
122 --instance-ids "$instance_id" \
123 --document-name "AWS-RunShellScript" \
124 --parameters "commands=[
125 \"export ECR_IMAGE=${ECR_IMAGE}\",
126 \"export IMAGE_TAG=${IMAGE_TAG}\",
127 \"bash /opt/musehub/deploy/deploy.sh\"
128 ]" \
129 --comment "musehub ${IMAGE_TAG} → ${env}" \
130 --timeout-seconds 600 \
131 --query "Command.CommandId" \
132 --output text)
133
134 log " SSM command ID: $cmd_id"
135 log " Polling for completion (up to 10 min)..."
136
137 local status
138 for i in $(seq 1 120); do
139 sleep 5
140 status=$(aws ssm get-command-invocation \
141 --region "$REGION" \
142 --command-id "$cmd_id" \
143 --instance-id "$instance_id" \
144 --query "Status" \
145 --output text 2>/dev/null || echo "Pending")
146
147 case "$status" in
148 Success)
149 log " ✅ $env deploy succeeded."
150 echo ""
151 aws ssm get-command-invocation \
152 --region "$REGION" \
153 --command-id "$cmd_id" \
154 --instance-id "$instance_id" \
155 --query "StandardOutputContent" \
156 --output text 2>/dev/null || true
157 return 0
158 ;;
159 Failed|Cancelled|TimedOut|Cancelling)
160 log " ❌ $env deploy FAILED (status: $status)"
161 echo ""
162 aws ssm get-command-invocation \
163 --region "$REGION" \
164 --command-id "$cmd_id" \
165 --instance-id "$instance_id" \
166 --query "[StandardOutputContent, StandardErrorContent]" \
167 --output text 2>/dev/null || true
168 return 1
169 ;;
170 InProgress|Pending|Delayed)
171 printf "."
172 ;;
173 esac
174 done
175
176 die "$env deploy timed out after 10 min. Check SSM: $cmd_id"
177 }
178
179 for target in "${TARGETS[@]}"; do
180 case "$target" in
181 staging) deploy_to "staging" "$STAGING_INSTANCE" ;;
182 prod) deploy_to "prod" "$PROD_INSTANCE" ;;
183 esac
184 done
185
186 log ""
187 log "All done."
188 log " Tag: $IMAGE_TAG"
189 log " Image: $FULL_IMAGE"
File History 1 commit
sha256:a10adeeb7a0169cb9900f9806ed7a973047258abb6283724fe55e8eb68ff3f0a init: musehub initial commit Human 171 days ago