.github/.github/workflows/cd-cdk.yaml
Adam Moussa 937e4d8daa
Add QEMU, Node 24, conventions check, pre-flight, and health checks to workflows (#29)
* Add QEMU support to CI CDK workflow for cross-platform Docker builds

Mirrors the enable-qemu input from cd-cdk.yaml. Required when CDK
stacks use PythonFunction or other Docker-bundled constructs targeting
arm64 Lambda on x86 CI runners.

* Increase CI timeout for QEMU CDK builds

* Bump default Node.js version to 24 across all reusable workflows

npm 11 (Node 24) generates lockfileVersion 3 which breaks npm ci
on Node 22's npm 10 for repos with aws-cdk-lib bundled deps.

* Add lightweight conventions check to CI workflows

Validates README exists, .env in .gitignore, arm64 architecture,
and log retention in synthesized templates. Runs by default,
opt-out via run-conventions-check: false.

* Add pre-flight stack status checks to CD workflows

Blocks deploy if the CloudFormation stack is in ROLLBACK_COMPLETE,
FAILED, or IN_PROGRESS state. Prevents wasted deploy attempts on
stacks that need manual intervention.

* Add post-deploy health checks to CD workflows

Verifies stack status after deploy, prints outputs, and runs
project-specific scripts/health-check.sh if present.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-05-14 18:39:10 -04:00

127 lines
4.1 KiB
YAML

name: CD — CDK Deploy
on:
workflow_call:
inputs:
node-version:
description: "Node.js version to use"
type: string
default: "24"
python-version:
description: "Python version for Python CDK repos (leave empty for TypeScript CDK)"
type: string
default: ""
region:
description: "AWS region"
type: string
default: "us-east-1"
cdk-dir:
description: "Directory containing cdk.json"
type: string
default: "."
enable-qemu:
description: "Enable QEMU for cross-platform Docker builds (arm64 on x86 runners)"
type: boolean
default: false
stack-name:
description: "CloudFormation stack name (for pre-flight checks)"
type: string
default: ""
secrets:
deploy-role-arn:
description: "OIDC deploy role ARN"
required: true
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
if: ${{ inputs.enable-qemu }}
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- uses: actions/setup-python@v5
if: ${{ inputs.python-version != '' }}
with:
python-version: ${{ inputs.python-version }}
- name: Install Node dependencies
if: ${{ inputs.python-version == '' }}
run: npm ci
- name: Install Python dependencies
if: ${{ inputs.python-version != '' }}
run: |
for req in $(find . -name requirements.txt -not -path '*/node_modules/*'); do
pip install -r "$req"
done
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.deploy-role-arn }}
aws-region: ${{ inputs.region }}
- name: Pre-flight checks
if: ${{ inputs.stack-name != '' }}
run: |
echo "Pre-flight: checking stack ${{ inputs.stack-name }}..."
STATUS=$(aws cloudformation describe-stacks \
--stack-name "${{ inputs.stack-name }}" \
--query 'Stacks[0].StackStatus' --output text 2>/dev/null || echo "NOT_FOUND")
case "$STATUS" in
*ROLLBACK_COMPLETE|*FAILED)
echo "::error::Stack ${{ inputs.stack-name }} is in $STATUS — manual intervention required."
exit 1
;;
*IN_PROGRESS)
echo "::error::Stack ${{ inputs.stack-name }} has an operation in progress ($STATUS) — wait for it to complete."
exit 1
;;
NOT_FOUND)
echo "Pre-flight: stack not found — will be created on first deploy."
;;
*)
echo "Pre-flight: stack status is $STATUS — OK to deploy."
;;
esac
- name: CDK deploy
working-directory: ${{ inputs.cdk-dir }}
run: npx -y cdk deploy --all --require-approval never
- name: Post-deploy health check
if: ${{ inputs.stack-name != '' }}
run: |
echo "Health check: verifying stack ${{ inputs.stack-name }}..."
STATUS=$(aws cloudformation describe-stacks \
--stack-name "${{ inputs.stack-name }}" \
--query 'Stacks[0].StackStatus' --output text)
if [[ "$STATUS" != *"COMPLETE" ]] || [[ "$STATUS" == *"ROLLBACK"* ]]; then
echo "::error::Stack ${{ inputs.stack-name }} ended in $STATUS after deploy."
exit 1
fi
echo "Stack status: $STATUS"
echo "Stack outputs:"
aws cloudformation describe-stacks \
--stack-name "${{ inputs.stack-name }}" \
--query 'Stacks[0].Outputs[*].[OutputKey,OutputValue]' --output table
# Run project-specific health check if it exists
if [[ -f scripts/health-check.sh ]]; then
echo "Running project health check..."
bash scripts/health-check.sh "${{ inputs.stack-name }}" "${{ inputs.region }}"
fi
echo "Health check passed."