Deploy to Production #30
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 to Production | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| invoke_staging: | |
| description: "Invoke staging Lambda after deploy" | |
| type: boolean | |
| default: true | |
| permissions: | |
| id-token: write | |
| contents: read | |
| concurrency: | |
| group: deploy-production | |
| cancel-in-progress: false | |
| env: | |
| AWS_REGION: us-west-2 | |
| STAGING_FUNCTION: HNDigest-staging | |
| PROD_FUNCTION: HNDigest | |
| jobs: | |
| echo-inputs: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Echo Inputs | |
| run: | | |
| echo "invoke_staging: ${{ github.event.inputs.invoke_staging }}" | |
| build: | |
| name: Build | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/build-lambda | |
| # --- Staging Infrastructure --- | |
| staging-infra-plan: | |
| name: "Infra Plan (staging)" | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' | |
| defaults: | |
| run: | |
| working-directory: infrastructure/environments/staging | |
| outputs: | |
| has_changes: ${{ steps.plan.outputs.has_changes }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: aws-actions/configure-aws-credentials@v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - uses: opentofu/setup-opentofu@v1 | |
| with: | |
| tofu_wrapper: false | |
| - name: Initialize | |
| run: tofu init | |
| - name: Plan | |
| id: plan | |
| run: | | |
| set +e | |
| tofu plan -detailed-exitcode -out=tfplan > plan_output.txt 2>&1 | |
| EXIT_CODE=$? | |
| set -e | |
| cat plan_output.txt | |
| if [ $EXIT_CODE -eq 0 ]; then | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| elif [ $EXIT_CODE -eq 2 ]; then | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Plan failed with exit code $EXIT_CODE" | |
| exit 1 | |
| fi | |
| - name: Upload plan artifact | |
| if: steps.plan.outputs.has_changes == 'true' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: tfplan-staging | |
| path: infrastructure/environments/staging/tfplan | |
| retention-days: 1 | |
| staging-infra-apply: | |
| name: "Infra Apply (staging)" | |
| needs: staging-infra-plan | |
| if: needs.staging-infra-plan.outputs.has_changes == 'true' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: infrastructure/environments/staging | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: aws-actions/configure-aws-credentials@v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - uses: opentofu/setup-opentofu@v1 | |
| - name: Initialize | |
| run: tofu init | |
| - name: Download plan artifact | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: tfplan-staging | |
| path: infrastructure/environments/staging | |
| - name: Apply | |
| run: tofu apply -auto-approve tfplan | |
| - name: Invalidate CloudFront cache | |
| run: | | |
| DIST_ID=$(tofu output -raw cloudfront_distribution_id) | |
| aws cloudfront create-invalidation \ | |
| --distribution-id "$DIST_ID" \ | |
| --paths "/*" | |
| # --- Staging Code Deploy --- | |
| deploy-staging: | |
| name: Deploy to Staging | |
| needs: [staging-infra-apply, build] | |
| if: ${{ !cancelled() && !failure() }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - name: Download artifact | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: lambda-zips | |
| - name: Deploy to Digest Lambda | |
| run: | | |
| aws lambda update-function-code \ | |
| --function-name ${{ env.STAGING_FUNCTION }} \ | |
| --zip-file fileb://lambda.zip | |
| - name: Deploy to API Lambda | |
| run: | | |
| aws lambda update-function-code \ | |
| --function-name ${{ env.STAGING_FUNCTION }}-api \ | |
| --zip-file fileb://api-lambda.zip | |
| - name: Deploy to Bounce Handler Lambda | |
| run: | | |
| aws lambda update-function-code \ | |
| --function-name ${{ env.STAGING_FUNCTION }}-bounce-handler \ | |
| --zip-file fileb://bounce-handler-lambda.zip | |
| - name: Wait for function update | |
| run: | | |
| aws lambda wait function-updated \ | |
| --function-name ${{ env.STAGING_FUNCTION }} | |
| aws lambda wait function-updated \ | |
| --function-name ${{ env.STAGING_FUNCTION }}-api | |
| aws lambda wait function-updated \ | |
| --function-name ${{ env.STAGING_FUNCTION }}-bounce-handler | |
| invoke-staging: | |
| name: Invoke Staging | |
| needs: deploy-staging | |
| if: ${{ !cancelled() && !failure() && inputs.invoke_staging }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - name: Invoke Lambda | |
| run: | | |
| echo "Invoking ${{ env.STAGING_FUNCTION }}..." | |
| aws lambda invoke \ | |
| --function-name ${{ env.STAGING_FUNCTION }} \ | |
| --log-type Tail \ | |
| --query 'LogResult' \ | |
| --output text \ | |
| response.json | base64 --decode | |
| echo "" | |
| echo "Response:" | |
| cat response.json | |
| # --- Production Infrastructure --- | |
| prod-infra-plan: | |
| name: "Infra Plan (production)" | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: infrastructure/environments/production | |
| outputs: | |
| has_changes: ${{ steps.plan.outputs.has_changes }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: aws-actions/configure-aws-credentials@v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - uses: opentofu/setup-opentofu@v1 | |
| with: | |
| tofu_wrapper: false | |
| - name: Initialize | |
| run: tofu init | |
| - name: Plan | |
| id: plan | |
| run: | | |
| set +e | |
| tofu plan -detailed-exitcode -out=tfplan > plan_output.txt 2>&1 | |
| EXIT_CODE=$? | |
| set -e | |
| cat plan_output.txt | |
| if [ $EXIT_CODE -eq 0 ]; then | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| elif [ $EXIT_CODE -eq 2 ]; then | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Plan failed with exit code $EXIT_CODE" | |
| exit 1 | |
| fi | |
| - name: Upload plan artifact | |
| if: steps.plan.outputs.has_changes == 'true' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: tfplan-production | |
| path: infrastructure/environments/production/tfplan | |
| retention-days: 1 | |
| # --- Production Approval + Deploy --- | |
| approve-prod: | |
| name: Approve Production | |
| needs: [prod-infra-plan, deploy-staging, invoke-staging] | |
| if: ${{ !cancelled() && !failure() }} | |
| runs-on: ubuntu-latest | |
| environment: production | |
| steps: | |
| - run: echo "Production deployment approved" | |
| prod-infra-apply: | |
| name: "Infra Apply (production)" | |
| needs: [approve-prod, prod-infra-plan] | |
| if: ${{ !cancelled() && !failure() && needs.prod-infra-plan.outputs.has_changes == 'true' }} | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: infrastructure/environments/production | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: aws-actions/configure-aws-credentials@v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - uses: opentofu/setup-opentofu@v1 | |
| - name: Initialize | |
| run: tofu init | |
| - name: Download plan artifact | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: tfplan-production | |
| path: infrastructure/environments/production | |
| - name: Apply | |
| run: tofu apply -auto-approve tfplan | |
| - name: Invalidate CloudFront cache | |
| run: | | |
| DIST_ID=$(tofu output -raw cloudfront_distribution_id) | |
| aws cloudfront create-invalidation \ | |
| --distribution-id "$DIST_ID" \ | |
| --paths "/*" | |
| deploy-prod: | |
| name: Deploy to Production | |
| needs: [approve-prod, prod-infra-apply] | |
| if: ${{ !cancelled() && !failure() }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - name: Download artifact | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: lambda-zips | |
| - name: Deploy to Digest Lambda | |
| run: | | |
| echo "Deploying to ${{ env.PROD_FUNCTION }}..." | |
| aws lambda update-function-code \ | |
| --function-name ${{ env.PROD_FUNCTION }} \ | |
| --zip-file fileb://lambda.zip | |
| - name: Deploy to API Lambda | |
| run: | | |
| echo "Deploying to ${{ env.PROD_FUNCTION }}-api..." | |
| aws lambda update-function-code \ | |
| --function-name ${{ env.PROD_FUNCTION }}-api \ | |
| --zip-file fileb://api-lambda.zip | |
| - name: Deploy to Bounce Handler Lambda | |
| run: | | |
| echo "Deploying to ${{ env.PROD_FUNCTION }}-bounce-handler..." | |
| aws lambda update-function-code \ | |
| --function-name ${{ env.PROD_FUNCTION }}-bounce-handler \ | |
| --zip-file fileb://bounce-handler-lambda.zip | |
| - name: Wait for function update | |
| run: | | |
| aws lambda wait function-updated \ | |
| --function-name ${{ env.PROD_FUNCTION }} | |
| aws lambda wait function-updated \ | |
| --function-name ${{ env.PROD_FUNCTION }}-api | |
| aws lambda wait function-updated \ | |
| --function-name ${{ env.PROD_FUNCTION }}-bounce-handler | |
| echo "Production Lambda updated successfully" |