Fix api-domain-secondary: gate Route53 records on CreateRoute53Record… #19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy Newsletter Lambda Stack | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - 'infra/newsletter-lambdas/**' | |
| - 'infra/newsletter.yml' | |
| - 'infra/newsletter-secondary.yml' | |
| - 'infra/api-domain.yml' | |
| - 'infra/api-domain-secondary.yml' | |
| - '.github/workflows/newsletter-deploy.yml' | |
| workflow_dispatch: # Allow manual triggering | |
| permissions: | |
| id-token: write # Required for OIDC | |
| contents: read | |
| env: | |
| AWS_REGION: us-east-1 | |
| AWS_REGION_SECONDARY: us-east-2 | |
| ARTIFACTS_BUCKET: micahwalter-newsletter-artifacts | |
| ARTIFACTS_BUCKET_SECONDARY: micahwalter-newsletter-artifacts-secondary | |
| ARTIFACTS_PREFIX: newsletter/lambda | |
| STACK_NAME: micahwalter-newsletter | |
| STACK_NAME_SECONDARY: micahwalter-newsletter-secondary | |
| # All functions deployed to primary; dispatch is excluded from secondary | |
| FUNCTIONS_PRIMARY: subscribe confirm unsubscribe email dispatch formtoken health | |
| FUNCTIONS_SECONDARY: subscribe confirm unsubscribe email formtoken health | |
| jobs: | |
| deploy: | |
| name: Build and deploy newsletter Lambda stacks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 2 # Need previous commit to detect changed files | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: infra/newsletter-lambdas/go.mod | |
| cache-dependency-path: infra/newsletter-lambdas/go.sum | |
| - name: Detect what changed | |
| id: changed | |
| run: | | |
| CHANGED=$(git diff --name-only HEAD~1 HEAD) | |
| echo "Changed files:" | |
| echo "$CHANGED" | |
| if echo "$CHANGED" | grep -q '^infra/newsletter\.yml$'; then | |
| echo "primary_template_changed=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "primary_template_changed=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| if echo "$CHANGED" | grep -q '^infra/newsletter-secondary\.yml$'; then | |
| echo "secondary_template_changed=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "secondary_template_changed=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| if echo "$CHANGED" | grep -q '^infra/api-domain\.yml$'; then | |
| echo "api_domain_changed=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "api_domain_changed=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| if echo "$CHANGED" | grep -q '^infra/api-domain-secondary\.yml$'; then | |
| echo "api_domain_secondary_changed=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "api_domain_secondary_changed=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Build Lambda functions | |
| working-directory: infra/newsletter-lambdas | |
| run: make build | |
| - name: Configure AWS credentials via OIDC | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ secrets.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| # ----------------------------------------------------------------------- | |
| # Upload Lambda zips to both regions' artifact buckets | |
| # ----------------------------------------------------------------------- | |
| - name: Upload Lambda zips to primary S3 bucket (us-east-1) | |
| working-directory: infra/newsletter-lambdas | |
| run: | | |
| for fn in ${{ env.FUNCTIONS_PRIMARY }}; do | |
| echo " uploading ${fn}.zip to us-east-1..." | |
| aws s3 cp dist/${fn}.zip \ | |
| s3://${{ env.ARTIFACTS_BUCKET }}/${{ env.ARTIFACTS_PREFIX }}/${fn}.zip \ | |
| --region ${{ env.AWS_REGION }} | |
| done | |
| - name: Upload Lambda zips to secondary S3 bucket (us-east-2) | |
| working-directory: infra/newsletter-lambdas | |
| run: | | |
| for fn in ${{ env.FUNCTIONS_SECONDARY }}; do | |
| echo " uploading ${fn}.zip to us-east-2..." | |
| aws s3 cp dist/${fn}.zip \ | |
| s3://${{ env.ARTIFACTS_BUCKET_SECONDARY }}/${{ env.ARTIFACTS_PREFIX }}/${fn}.zip \ | |
| --region ${{ env.AWS_REGION_SECONDARY }} | |
| done | |
| # ----------------------------------------------------------------------- | |
| # CloudFormation stack deploys (only when templates change) | |
| # ----------------------------------------------------------------------- | |
| - name: Deploy api-domain stack (us-east-1, api-domain.yml changed) | |
| if: steps.changed.outputs.api_domain_changed == 'true' | |
| run: | | |
| # Retrieve primary API GW regional domain from the newsletter stack output | |
| # to activate/refresh the Route 53 health check configuration. | |
| PRIMARY_API_DOMAIN=$(aws cloudformation describe-stacks \ | |
| --stack-name ${{ env.STACK_NAME }} \ | |
| --region ${{ env.AWS_REGION }} \ | |
| --query "Stacks[0].Outputs[?OutputKey=='ApiRegionalDomainName'].OutputValue" \ | |
| --output text 2>/dev/null || echo "") | |
| PARAMS="HostedZoneId=Z05804121WRFHZZEYWGT5" | |
| if [ -n "$PRIMARY_API_DOMAIN" ]; then | |
| PARAMS="$PARAMS PrimaryApiRegionalDomain=$PRIMARY_API_DOMAIN" | |
| echo "Activating failover routing with health check on: $PRIMARY_API_DOMAIN" | |
| else | |
| echo "Newsletter stack not found — deploying with simple routing" | |
| fi | |
| aws cloudformation deploy \ | |
| --stack-name micahwalter-api-domain \ | |
| --template-file infra/api-domain.yml \ | |
| --region ${{ env.AWS_REGION }} \ | |
| --parameter-overrides $PARAMS | |
| - name: Deploy api-domain-secondary stack (us-east-2, api-domain-secondary.yml changed) | |
| if: steps.changed.outputs.api_domain_secondary_changed == 'true' | |
| run: | | |
| aws cloudformation deploy \ | |
| --stack-name micahwalter-api-domain-secondary \ | |
| --template-file infra/api-domain-secondary.yml \ | |
| --region ${{ env.AWS_REGION_SECONDARY }} \ | |
| --parameter-overrides HostedZoneId=Z05804121WRFHZZEYWGT5 | |
| - name: Deploy primary newsletter stack via CloudFormation (newsletter.yml changed) | |
| if: steps.changed.outputs.primary_template_changed == 'true' | |
| run: | | |
| aws cloudformation deploy \ | |
| --stack-name ${{ env.STACK_NAME }} \ | |
| --template-file infra/newsletter.yml \ | |
| --region ${{ env.AWS_REGION }} \ | |
| --capabilities CAPABILITY_NAMED_IAM \ | |
| --parameter-overrides \ | |
| LambdaArtifactsBucket=${{ env.ARTIFACTS_BUCKET }} \ | |
| LambdaArtifactsKeyPrefix=${{ env.ARTIFACTS_PREFIX }} | |
| # After primary deploys, refresh api-domain failover config with the | |
| # (potentially new) API GW regional domain. | |
| PRIMARY_API_DOMAIN=$(aws cloudformation describe-stacks \ | |
| --stack-name ${{ env.STACK_NAME }} \ | |
| --region ${{ env.AWS_REGION }} \ | |
| --query "Stacks[0].Outputs[?OutputKey=='ApiRegionalDomainName'].OutputValue" \ | |
| --output text) | |
| echo "Refreshing api-domain failover config with: $PRIMARY_API_DOMAIN" | |
| aws cloudformation deploy \ | |
| --stack-name micahwalter-api-domain \ | |
| --template-file infra/api-domain.yml \ | |
| --region ${{ env.AWS_REGION }} \ | |
| --parameter-overrides \ | |
| HostedZoneId=Z05804121WRFHZZEYWGT5 \ | |
| PrimaryApiRegionalDomain=$PRIMARY_API_DOMAIN | |
| - name: Deploy secondary newsletter stack via CloudFormation (newsletter-secondary.yml changed) | |
| if: steps.changed.outputs.secondary_template_changed == 'true' | |
| run: | | |
| aws cloudformation deploy \ | |
| --stack-name ${{ env.STACK_NAME_SECONDARY }} \ | |
| --template-file infra/newsletter-secondary.yml \ | |
| --region ${{ env.AWS_REGION_SECONDARY }} \ | |
| --capabilities CAPABILITY_NAMED_IAM \ | |
| --parameter-overrides \ | |
| LambdaArtifactsBucket=${{ env.ARTIFACTS_BUCKET_SECONDARY }} \ | |
| LambdaArtifactsKeyPrefix=${{ env.ARTIFACTS_PREFIX }} | |
| # ----------------------------------------------------------------------- | |
| # Lambda code-only updates (when only Go source changed, not templates) | |
| # Runs in both regions for faster deploys. | |
| # ----------------------------------------------------------------------- | |
| - name: Update primary function code only (Go source changed, no template change) | |
| if: > | |
| steps.changed.outputs.primary_template_changed == 'false' && | |
| steps.changed.outputs.secondary_template_changed == 'false' && | |
| steps.changed.outputs.api_domain_changed == 'false' && | |
| steps.changed.outputs.api_domain_secondary_changed == 'false' | |
| run: | | |
| for fn in ${{ env.FUNCTIONS_PRIMARY }}; do | |
| echo " updating newsletter-${fn} (us-east-1)..." | |
| aws lambda update-function-code \ | |
| --function-name newsletter-${fn} \ | |
| --s3-bucket ${{ env.ARTIFACTS_BUCKET }} \ | |
| --s3-key ${{ env.ARTIFACTS_PREFIX }}/${fn}.zip \ | |
| --region ${{ env.AWS_REGION }} \ | |
| --no-cli-pager | |
| done | |
| - name: Update secondary function code only (Go source changed, no template change) | |
| if: > | |
| steps.changed.outputs.primary_template_changed == 'false' && | |
| steps.changed.outputs.secondary_template_changed == 'false' && | |
| steps.changed.outputs.api_domain_changed == 'false' && | |
| steps.changed.outputs.api_domain_secondary_changed == 'false' | |
| run: | | |
| for fn in ${{ env.FUNCTIONS_SECONDARY }}; do | |
| echo " updating newsletter-${fn} (us-east-2)..." | |
| aws lambda update-function-code \ | |
| --function-name newsletter-${fn} \ | |
| --s3-bucket ${{ env.ARTIFACTS_BUCKET_SECONDARY }} \ | |
| --s3-key ${{ env.ARTIFACTS_PREFIX }}/${fn}.zip \ | |
| --region ${{ env.AWS_REGION_SECONDARY }} \ | |
| --no-cli-pager | |
| done | |
| # ----------------------------------------------------------------------- | |
| # Summary | |
| # ----------------------------------------------------------------------- | |
| - name: Deployment summary | |
| run: | | |
| PRIMARY_CHANGED="${{ steps.changed.outputs.primary_template_changed }}" | |
| SECONDARY_CHANGED="${{ steps.changed.outputs.secondary_template_changed }}" | |
| API_DOMAIN_CHANGED="${{ steps.changed.outputs.api_domain_changed }}" | |
| API_DOMAIN_SEC_CHANGED="${{ steps.changed.outputs.api_domain_secondary_changed }}" | |
| if [ "$PRIMARY_CHANGED" = "true" ] || [ "$SECONDARY_CHANGED" = "true" ] || \ | |
| [ "$API_DOMAIN_CHANGED" = "true" ] || [ "$API_DOMAIN_SEC_CHANGED" = "true" ]; then | |
| echo "Deploy mode: CloudFormation stack update(s)" | |
| else | |
| echo "Deploy mode: Lambda function code update (both regions)" | |
| fi | |
| echo "" | |
| echo "Functions deployed (primary):" | |
| for fn in ${{ env.FUNCTIONS_PRIMARY }}; do | |
| size=$(stat -c%s infra/newsletter-lambdas/dist/${fn}.zip 2>/dev/null || echo "?") | |
| echo " newsletter-${fn} (${size} bytes)" | |
| done | |
| echo "" | |
| echo "Functions deployed (secondary):" | |
| for fn in ${{ env.FUNCTIONS_SECONDARY }}; do | |
| size=$(stat -c%s infra/newsletter-lambdas/dist/${fn}.zip 2>/dev/null || echo "?") | |
| echo " newsletter-${fn} (${size} bytes)" | |
| done |