Skip to content

Commit 2069391

Browse files
authored
Cache fixing by unit path invalidation instead of individual file listing (#1464)
Closes RaspberryPiFoundation/digital-editor-issues#1371 #### What changed This PR changes the deploy workflow to purge Cloudflare by deployed path prefix instead of by a hard-coded list of individual files. A staging deploy now purges: ```bash { "prefixes": [ "staging-editor-static.raspberrypi.org/branches/main/" ] } ``` while before: ```bash { "files": [ "https://staging-editor-static.raspberrypi.org/branches/main/web-component.html", "https://staging-editor-static.raspberrypi.org/branches/main/web-component.js", "https://staging-editor-static.raspberrypi.org/branches/main/scratch.html", "https://staging-editor-static.raspberrypi.org/branches/main/scratch.js", "https://staging-editor-static.raspberrypi.org/latest_version", "https://staging-editor-static.raspberrypi.org/branches/main/html-renderer.html", "https://staging-editor-static.raspberrypi.org/branches/main/html-renderer.js" ] } ``` The PR also uploads latest_version with Cache-Control: no-cache, because that file is intentionally updated in place. For production tags, before it purged individual files inside one release plus latest_version. Now it purges: ```bash { "prefixes": [ "editor-static.raspberrypi.org/releases/<tag>/", "editor-static.raspberrypi.org/latest_version" ] } ``` ## Why Staging should serve the latest deployed version of editor-ui after each deploy. The previous single-file purge targeted paths such as `web-component.js`, `scratch.js`, and `html-renderer.js`. That can be unreliable because Cloudflare may cache separate variants of the same URL when request headers differ, for example with different Origin headers. If the purge request does not match the cached variant, Cloudflare can continue serving an older file. Prefix purging clears the deployed build path as a unit. The next request after deploy fetches the updated files from the bucket, and Cloudflare then caches those files again as normal. This is a targeted deploy/cache invalidation fix. References: - Purge cache by prefix (https://developers.cloudflare.com/cache/how-to/purge-cache/purge_by_prefix/) - Cloudflare documents prefix purge for clearing a directory/path, reducing purge calls, and causing the next request to refetch from origin. - Purge Cached Content API (https://developers.cloudflare.com/api/resources/cache/methods/purge/) - Cache Keys (https://developers.cloudflare.com/cache/how-to/cache-keys/) - Purge by single-file (https://developers.cloudflare.com/cache/how-to/purge-cache/purge-by-single-file/) - Origin Cache Control (https://developers.cloudflare.com/cache/concepts/cache-control/) - Browser Cache TTL (https://developers.cloudflare.com/cache/how-to/edge-browser-cache-ttl/set-browser-ttl/) Idea: Longer term, we should move to content-hashed asset filenames with immutable caching, leaving only small stable entrypoints or manifests as revalidated files.
1 parent cc794bd commit 2069391

1 file changed

Lines changed: 49 additions & 3 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ jobs:
172172
exit 0
173173
fi
174174
175-
echo -n "${{ needs.setup-environment.outputs.deploy_dir }}" | aws s3 cp - s3://${{ secrets.AWS_S3_BUCKET }}/latest_version --endpoint ${{ secrets.AWS_ENDPOINT }} --content-type "text/plain"
175+
# Use no-cache so latest_version is revalidated after tag deploys.
176+
echo -n "${{ needs.setup-environment.outputs.deploy_dir }}" | aws s3 cp - s3://${{ secrets.AWS_S3_BUCKET }}/latest_version --endpoint ${{ secrets.AWS_ENDPOINT }} --content-type "text/plain" --cache-control "no-cache"
176177
env:
177178
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
178179
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -181,11 +182,52 @@ jobs:
181182
- name: Purge Cloudflare cache
182183
if: env.HAS_CLOUDFLARE_SECRETS == 'true'
183184
run: |
185+
# Cloudflare expects purge prefixes without the URL scheme.
186+
# For example, "https://editor-static.raspberrypi.org/releases/v1.2.3"
187+
# becomes "editor-static.raspberrypi.org/releases/v1.2.3".
188+
cloudflare_prefix() {
189+
local url="$1"
190+
191+
url="${url#https://}"
192+
url="${url#http://}"
193+
url="${url%/}"
194+
195+
printf "%s" "$url"
196+
}
197+
198+
# Purge the deployed web component and HTML renderer directories.
199+
# The trailing slash makes these directory prefixes, so Cloudflare
200+
# removes cached files under each deployed path.
201+
purge_prefixes=(
202+
"$(cloudflare_prefix "$PUBLIC_URL")/"
203+
"$(cloudflare_prefix "$HTML_RENDERER_URL")/"
204+
)
205+
206+
# Tagged releases update /latest_version, which is cached separately
207+
# from the release directory. Branch deploys do not touch this file.
208+
if [ "$REF_TYPE" = "tag" ]; then
209+
purge_prefixes+=("$(cloudflare_prefix "$BASE_URL")/latest_version")
210+
fi
211+
212+
# Build the API payload and de-duplicate prefixes. PUBLIC_URL and
213+
# HTML_RENDERER_URL are often the same, so unique avoids purging the
214+
# same prefix twice.
215+
purge_payload="$(
216+
jq -cn --args '
217+
$ARGS.positional
218+
| unique
219+
| {prefixes: .}
220+
' "${purge_prefixes[@]}"
221+
)"
222+
223+
echo "Purging Cloudflare cache prefixes:"
224+
jq -r '.prefixes[] | " - \(.)"' <<<"$purge_payload"
225+
184226
response="$(
185227
curl -sS --fail-with-body -X POST "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache" \
186228
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
187229
-H "Content-Type: application/json" \
188-
--data '{"files":["${{ needs.setup-environment.outputs.public_url }}/web-component.html", "${{ needs.setup-environment.outputs.public_url }}/web-component.js", "${{ needs.setup-environment.outputs.public_url }}/scratch.html", "${{ needs.setup-environment.outputs.public_url }}/scratch.js", "${{ inputs.base_url }}/latest_version", "${{ needs.setup-environment.outputs.html_renderer_url}}/html-renderer.html", "${{ needs.setup-environment.outputs.html_renderer_url}}/html-renderer.js"]}'
230+
--data "$purge_payload"
189231
)" || {
190232
echo "Cloudflare purge request failed:"
191233
echo "$response"
@@ -198,7 +240,11 @@ jobs:
198240
exit 1
199241
}
200242
201-
echo "Cloudflare purge succeeded for ${{ needs.setup-environment.outputs.public_url }}"
243+
echo "Cloudflare purge succeeded for $PUBLIC_URL"
202244
env:
203245
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
204246
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
247+
PUBLIC_URL: ${{ needs.setup-environment.outputs.public_url }}
248+
HTML_RENDERER_URL: ${{ needs.setup-environment.outputs.html_renderer_url }}
249+
BASE_URL: ${{ inputs.base_url }}
250+
REF_TYPE: ${{ github.ref_type }}

0 commit comments

Comments
 (0)