-
Notifications
You must be signed in to change notification settings - Fork 0
Syncing development with main after pipeline finalization #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3637789
e00c235
9099b8a
d80637b
5611c18
e75a13a
5cbce9d
32c7a65
bec36e7
3815722
b9ab596
95487f5
08c584b
8a2231f
beb3693
04b6634
bfb32b8
eb08c37
e64b34c
a1dcb8a
a808759
449d1b5
2cdf98e
6c88809
fd4a413
29220a1
7394607
2c55645
37f6fba
dd3edae
4ade494
dc98e6f
595e0fc
ec24cbf
fd9f3e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,9 +18,6 @@ jobs: | |||||||||||||||||||||||
| id-token: write | ||||||||||||||||||||||||
| contents: read | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| outputs: | ||||||||||||||||||||||||
| image_uri: ${{ steps.build.outputs.image_uri }} | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||
| - name: Checkout | ||||||||||||||||||||||||
| uses: actions/checkout@v4 | ||||||||||||||||||||||||
|
|
@@ -35,7 +32,6 @@ jobs: | |||||||||||||||||||||||
| uses: aws-actions/amazon-ecr-login@v2 | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Build & push image | ||||||||||||||||||||||||
| id: build | ||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||
| IMAGE_TAG: ${{ github.sha }} | ||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||
|
|
@@ -45,8 +41,6 @@ jobs: | |||||||||||||||||||||||
| docker build -f Dockerfile.prod -t "${IMAGE_URI}" . | ||||||||||||||||||||||||
| docker push "${IMAGE_URI}" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| echo "IMAGE_URI=${IMAGE_URI}" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| deploy: | ||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||
| needs: build | ||||||||||||||||||||||||
|
|
@@ -63,55 +57,66 @@ jobs: | |||||||||||||||||||||||
| aws-region: ${{ env.AWS_REGION }} | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Deploy to EC2 via SSM | ||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||
| IMAGE_URI: ${{ needs.build.outputs.image_uri }} | ||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ECR_REGISTRY="$(aws sts get-caller-identity --query Account --output text).dkr.ecr.${AWS_REGION}.amazonaws.com" | ||||||||||||||||||||||||
| IMAGE_URI="${ECR_REGISTRY}/${ECR_REPO}:${GITHUB_SHA}" | ||||||||||||||||||||||||
| echo "Deploying image ${IMAGE_URI} to instance" | ||||||||||||||||||||||||
| test -n "${IMAGE_URI}" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| COMMANDS=$(cat <<JSON | ||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||
| "set -euo pipefail", | ||||||||||||||||||||||||
| "AWS_REGION=${AWS_REGION}", | ||||||||||||||||||||||||
| "IMAGE_URI=${IMAGE_URI}", | ||||||||||||||||||||||||
| "CONTAINER_NAME=${CONTAINER_NAME}", | ||||||||||||||||||||||||
| "echo IMAGE_URI=$IMAGE_URI", | ||||||||||||||||||||||||
| "if ! command -v docker >/dev/null 2>&1; then sudo dnf -y install docker; sudo systemctl enable --now docker; fi", | ||||||||||||||||||||||||
| "sudo usermod -aG docker ec2-user || true", | ||||||||||||||||||||||||
| "ECR_REGISTRY=\${IMAGE_URI%%/*}", | ||||||||||||||||||||||||
| "aws ecr get-login-password --region $AWS_REGION | sudo docker login --username AWS --password-stdin \$ECR_REGISTRY", | ||||||||||||||||||||||||
| "DBURL=\$(aws ssm get-parameter --region $AWS_REGION --with-decryption --name /cgc-2026-prod/api/database_url --query Parameter.Value --output text)", | ||||||||||||||||||||||||
| "CLERK_SECRET=\$(aws ssm get-parameter --region $AWS_REGION --with-decryption --name /cgc-2026-prod/api/clerk_secret_key --query Parameter.Value --output text)", | ||||||||||||||||||||||||
| "sudo docker pull $IMAGE_URI", | ||||||||||||||||||||||||
| "sudo docker rm -f $CONTAINER_NAME || true", | ||||||||||||||||||||||||
| "sudo docker run -d --restart unless-stopped --name $CONTAINER_NAME -p 8080:8080 -e PORT=8080 -e DATABASE_URL=\"\$DBURL\" -e CLERK_SECRET_KEY=\"\$CLERK_SECRET\" $IMAGE_URI", | ||||||||||||||||||||||||
| "sleep 2", | ||||||||||||||||||||||||
| "curl -fsS http://localhost:8080/health" | ||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||
| JSON | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| COMMAND_ID=$(aws ssm send-command \ | ||||||||||||||||||||||||
| --region "${AWS_REGION}" \ | ||||||||||||||||||||||||
| --instance-ids "${EC2_INSTANCE_ID}" \ | ||||||||||||||||||||||||
| --document-name "AWS-RunShellScript" \ | ||||||||||||||||||||||||
| --parameters commands="[ | ||||||||||||||||||||||||
| "set -e", | ||||||||||||||||||||||||
| "AWS_REGION='${AWS_REGION}'", | ||||||||||||||||||||||||
| "IMAGE_URI='${IMAGE_URI}'", | ||||||||||||||||||||||||
| "CONTAINER_NAME='${CONTAINER_NAME}'", | ||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||
| "if ! command -v docker >/dev/null 2>&1; then sudo dnf -y install docker; sudo systemctl enable --now docker; fi", | ||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||
| "sudo usermod -aG docker ec2-user || true", | ||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||
| "# Login to ECR", | ||||||||||||||||||||||||
| "aws ecr get-login-password --region ${AWS_REGION} | sudo docker login --username AWS --password-stdin $(echo ${IMAGE_URI} | cut -d/ -f1)", | ||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||
| "# Fetch runtime secrets from SSM", | ||||||||||||||||||||||||
| "DBURL=$(aws ssm get-parameter --region ${AWS_REGION} --with-decryption --name /cgc-2026-prod/api/database_url --query Parameter.Value --output text)", | ||||||||||||||||||||||||
| "CLERK_SECRET=$(aws ssm get-parameter --region ${AWS_REGION} --with-decryption --name /cgc-2026-prod/api/clerk_secret_key --query Parameter.Value --output text)", | ||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||
| "# Pull new image", | ||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||
| "sudo docker pull ${IMAGE_URI}", | ||||||||||||||||||||||||
| "# Stop existing container (if any)", | ||||||||||||||||||||||||
| "sudo docker rm -f ${CONTAINER_NAME} || true", | ||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||
| "# Run container", | ||||||||||||||||||||||||
| "sudo docker run -d --restart unless-stopped --name \"${CONTAINER_NAME}\" -p 8080:8080 -e PORT=8080 -e DATABASE_URL=\"${DBURL}\" -e CLERK_SECRET_KEY=\"${CLERK_SECRET}\" \"${IMAGE_URI}\"", | ||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||
| "# Smoke check", | ||||||||||||||||||||||||
| "sleep 2", | ||||||||||||||||||||||||
| "curl -fsS http://localhost:8080/health" | ||||||||||||||||||||||||
| ]"" \ | ||||||||||||||||||||||||
| --parameters "commands=${COMMANDS}" \ | ||||||||||||||||||||||||
| --query 'Command.CommandId' \ | ||||||||||||||||||||||||
| --output text) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| sleep 2 | ||||||||||||||||||||||||
| aws ssm get-command-invocation \ | ||||||||||||||||||||||||
| - name: Verify health | ||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| PUBLIC_IP=$(aws ec2 describe-instances \ | ||||||||||||||||||||||||
| --region "${AWS_REGION}" \ | ||||||||||||||||||||||||
| --command-id "${COMMAND_ID}" \ | ||||||||||||||||||||||||
| --instance-id "${EC2_INSTANCE_ID}" \ | ||||||||||||||||||||||||
| --query 'StandardErrorContent' \ | ||||||||||||||||||||||||
| --output text | ||||||||||||||||||||||||
| --instance-ids "${EC2_INSTANCE_ID}" \ | ||||||||||||||||||||||||
| --query 'Reservations[0].Instances[0].PublicIpAddress' \ | ||||||||||||||||||||||||
| --output text) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| test "${PUBLIC_IP}" != "None" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| URL="http://${PUBLIC_IP}:8080/health" | ||||||||||||||||||||||||
| echo "Checking ${URL}" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| for i in {1..20}; do | ||||||||||||||||||||||||
| if curl -fsS "${URL}" >/dev/null; then | ||||||||||||||||||||||||
| echo "Health check passed" | ||||||||||||||||||||||||
| exit 0 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
| echo "Health not ready yet (attempt $i/20) — retrying..." | ||||||||||||||||||||||||
| sleep 3 | ||||||||||||||||||||||||
| done | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| echo "Health check failed" | ||||||||||||||||||||||||
|
Comment on lines
+118
to
+122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing When the loop exhausts all 20 retries, the step prints "Health check failed" but exits with status 0 (the exit code of 🐛 Proposed fix to exit non-zero on failure echo "Health check failed"
+ exit 1📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SSM command is sent but never awaited — deployment success/failure is unknown.
The step captures
COMMAND_IDbut never uses it. The workflow proceeds immediately without waiting for the SSM command to complete or checking its exit status. The deployment could fail silently while the health check runs.Add a wait loop to poll for command completion:
🛠️ Proposed fix to wait for SSM command completion
COMMAND_ID=$(aws ssm send-command \ --region "${AWS_REGION}" \ --instance-ids "${EC2_INSTANCE_ID}" \ --document-name "AWS-RunShellScript" \ --parameters "commands=${COMMANDS}" \ --query 'Command.CommandId' \ --output text) + + echo "Waiting for SSM command ${COMMAND_ID} to complete..." + for i in {1..60}; do + STATUS=$(aws ssm get-command-invocation \ + --region "${AWS_REGION}" \ + --command-id "${COMMAND_ID}" \ + --instance-id "${EC2_INSTANCE_ID}" \ + --query 'Status' \ + --output text 2>/dev/null || echo "Pending") + + case "${STATUS}" in + Success) + echo "SSM command succeeded" + break + ;; + Failed|Cancelled|TimedOut) + echo "SSM command failed with status: ${STATUS}" + aws ssm get-command-invocation \ + --region "${AWS_REGION}" \ + --command-id "${COMMAND_ID}" \ + --instance-id "${EC2_INSTANCE_ID}" \ + --query 'StandardErrorContent' \ + --output text + exit 1 + ;; + *) + echo "Status: ${STATUS} (attempt $i/60)" + sleep 5 + ;; + esac + done🤖 Prompt for AI Agents