From 0f8d63ae5d10fdcf83e3c28913cd26f277e2e9f5 Mon Sep 17 00:00:00 2001 From: Erin O'Connell Date: Tue, 3 Nov 2026 12:11:00 -0700 Subject: [PATCH] ci(prod): invalidate CloudFront on deploy Prepare production for caching HTML at the edge, matching staging. Upload hashed assets first, sync everything else, invalidate paths with stable URLs and wait, then delete old assets, so live pages never reference a missing asset. Requires the PRODUCTION_AWS_CLOUDFRONT_DISTRIBUTION_ID secret and CloudFront invalidation permissions for the production deploy credentials. --- .github/workflows/deploy-prod.yml | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 28ece750..8f7f2b24 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -52,8 +52,30 @@ jobs: AWS_DEFAULT_REGION: ${{ secrets.STAGING_AWS_REGION }} - name: Deploy - run: aws s3 sync ./dist s3://${{ secrets.PRODUCTION_AWS_S3_BUCKET }}/tech-docs --delete + # Order matters: live pages must never reference an asset that has not + # been uploaded yet, or one that has already been deleted. + run: | + # Hashed filenames change with their content, so size is enough to skip existing files. + aws s3 sync ./dist/_astro "$S3_PREFIX/_astro" --size-only + aws s3 sync ./dist "$S3_PREFIX" --exclude "_astro/*" --delete + + # CloudFront caches rewritten URIs (/x/index.html), which these wildcards also match. + # Read top-level folders from the build so new sections are never missed. + # _astro is skipped because its filenames change with their content. + mapfile -t DIR_PATHS < <(cd dist && find . -mindepth 1 -maxdepth 1 -type d ! -name _astro ! -name '.*' -printf '/tech-docs/%P*\n') + INVALIDATION_ID=$(aws cloudfront create-invalidation \ + --distribution-id "$DISTRIBUTION_ID" \ + --paths "/tech-docs" "/tech-docs/" "/tech-docs/index.html" \ + "${DIR_PATHS[@]}" \ + "/tech-docs/sitemap-*" "/tech-docs/favicon.png" \ + --query Invalidation.Id --output text) + aws cloudfront wait invalidation-completed \ + --distribution-id "$DISTRIBUTION_ID" --id "$INVALIDATION_ID" + + aws s3 sync ./dist/_astro "$S3_PREFIX/_astro" --size-only --delete env: + S3_PREFIX: s3://${{ secrets.PRODUCTION_AWS_S3_BUCKET }}/tech-docs + DISTRIBUTION_ID: ${{ secrets.PRODUCTION_AWS_CLOUDFRONT_DISTRIBUTION_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.PRODUCTION_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.PRODUCTION_AWS_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: ${{ secrets.PRODUCTION_AWS_REGION }} \ No newline at end of file + AWS_DEFAULT_REGION: ${{ secrets.PRODUCTION_AWS_REGION }}