mirror of
https://github.com/Sea-Haven-Industries/.github.git
synced 2026-09-30 03:43:10 +00:00
142 lines
5.3 KiB
YAML
142 lines
5.3 KiB
YAML
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
|
|
# Serialise per stack so two pushes cannot deploy over each other.
|
|
# stack-name is required and region always defaults, so the group is never
|
|
# empty; the pair is exactly what identifies a CloudFormation stack, so
|
|
# different stacks in the same caller repo still deploy in parallel.
|
|
# cancel-in-progress is FALSE on purpose: unlike CI, aborting midway can
|
|
# leave a stack mid-update.
|
|
concurrency:
|
|
group: cd-sam-${{ inputs.region }}-${{ inputs.stack-name }}
|
|
cancel-in-progress: false
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: ${{ inputs.python-version }}
|
|
|
|
- uses: aws-actions/setup-sam@89ddb14d60e682855e3fea4be85b3c56485de310 # v3.0.0
|
|
|
|
- uses: aws-actions/configure-aws-credentials@cbe3b392738ccf3f987d68400dafcf4b0624a56c # v6.2.4
|
|
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
|
|
# Env-var indirection (not inline expression interpolation) so shell
|
|
# metacharacters in the secret are never parsed as script; unquoted
|
|
# $PARAM_OVERRIDES deliberately word-splits multiple Key=Value pairs.
|
|
env:
|
|
PARAM_OVERRIDES: ${{ secrets.parameter-overrides }}
|
|
run: |
|
|
PARAMS=""
|
|
if [ -n "$PARAM_OVERRIDES" ]; then
|
|
PARAMS="--parameter-overrides $PARAM_OVERRIDES"
|
|
fi
|
|
# $PARAMS is deliberately unquoted: it is either empty (no overrides,
|
|
# so no flag at all) or "--parameter-overrides Key=Value [Key=Value…]",
|
|
# which must reach `sam deploy` as separate argv entries. Quoting it
|
|
# would pass one empty or one concatenated argument and break every
|
|
# parameterised deploy.
|
|
# shellcheck disable=SC2086
|
|
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."
|