name: CD — SAM Deploy on: workflow_call: inputs: python-version: description: "Python version to use" type: string default: "3.12" stack-name: description: "CloudFormation stack name" type: string required: true sam-template: description: "Path to SAM template file" type: string default: "template.yaml" region: description: "AWS region" type: string default: "us-east-1" cfn-role-arn: description: "CloudFormation execution role ARN" type: string required: true secrets: deploy-role-arn: description: "OIDC deploy role ARN" required: true parameter-overrides: description: "SAM parameter overrides (e.g. 'Key1=Value1 Key2=Value2')" required: false permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v7 - uses: actions/setup-python@v6 with: python-version: ${{ inputs.python-version }} - uses: aws-actions/setup-sam@89ddb14d60e682855e3fea4be85b3c56485de310 # v3 - uses: aws-actions/configure-aws-credentials@517a711dbcd0e402f90c77e7e2f81e849156e31d # v6 with: role-to-assume: ${{ secrets.deploy-role-arn }} aws-region: ${{ inputs.region }} - name: Pre-flight checks 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: SAM build run: sam build --template ${{ inputs.sam-template }} - name: SAM deploy run: | PARAMS="" if [ -n "${{ secrets.parameter-overrides }}" ]; then PARAMS="--parameter-overrides ${{ secrets.parameter-overrides }}" fi sam deploy \ --stack-name ${{ inputs.stack-name }} \ --template-file .aws-sam/build/template.yaml \ --resolve-s3 \ --capabilities CAPABILITY_IAM \ --no-confirm-changeset \ --no-fail-on-empty-changeset \ --role-arn ${{ inputs.cfn-role-arn }} \ $PARAMS - name: Post-deploy health check 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."