Skip to content

Commit 7d5ba7b

Browse files
committed
(maybe) add purge action
1 parent 5cfe875 commit 7d5ba7b

File tree

5 files changed

+54
-25
lines changed

5 files changed

+54
-25
lines changed

.github/workflows/purge-cache.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Purge Fastly Cache
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths: ['static/**', 'templates/**', '**/templates/**']
7+
workflow_dispatch:
8+
inputs:
9+
target:
10+
description: 'Surrogate key to purge'
11+
required: true
12+
default: 'pydotorg-app'
13+
type: choice
14+
options: [pydotorg-app, downloads, events, sponsors, jobs]
15+
16+
permissions: {}
17+
18+
jobs:
19+
purge:
20+
runs-on: ubuntu-latest
21+
env:
22+
KEY: ${{ inputs.target || 'pydotorg-app' }}
23+
steps:
24+
- name: Purge ${{ env.KEY }}
25+
run: |
26+
curl -fsS -X POST \
27+
"https://api.fastly.com/service/${{ secrets.FASTLY_SERVICE_ID }}/purge/${{ env.KEY }}" \
28+
-H "Fastly-Key: ${{ secrets.FASTLY_API_KEY }}"

apps/downloads/models.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,10 @@ def promote_latest_release(sender, instance, **kwargs):
290290
def purge_fastly_download_pages(sender, instance, **kwargs):
291291
"""Purge Fastly caches so new Downloads show up more quickly.
292292
293-
Uses surrogate key purging to clear ALL pages under /downloads/ in one request,
294-
including dynamically added pages like /downloads/android/, /downloads/ios/, etc.
295-
Falls back to individual URL purges if surrogate key purging is not configured.
293+
Uses surrogate key purging to attempt to clear ALL pages under /downloads/
294+
in one request, including dynamically added pages like /downloads/android/,
295+
/downloads/ios/, etc. Independently purges a set of specific non-/downloads/
296+
URLs via individual URL purges.
296297
"""
297298
# Don't purge on fixture loads
298299
if kwargs.get("raw", False):

fastly/utils.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ def purge_url(path):
2222

2323

2424
def purge_surrogate_key(key):
25-
"""
26-
Purge all Fastly cached content tagged with a surrogate key.
25+
"""Purge all Fastly cached content tagged with a surrogate key.
2726
2827
Common keys (set by GlobalSurrogateKey middleware):
2928
- 'pydotorg-app': Purges entire site
@@ -37,13 +36,13 @@ def purge_surrogate_key(key):
3736
if settings.DEBUG:
3837
return None
3938

40-
api_key = getattr(settings, 'FASTLY_API_KEY', None)
41-
service_id = getattr(settings, 'FASTLY_SERVICE_ID', None)
39+
api_key = getattr(settings, "FASTLY_API_KEY", None)
40+
service_id = getattr(settings, "FASTLY_SERVICE_ID", None)
4241
if not api_key or not service_id:
4342
return None
4443

45-
response = requests.post(
46-
f'https://api.fastly.com/service/{service_id}/purge/{key}',
47-
headers={'Fastly-Key': api_key},
44+
return requests.post(
45+
f"https://api.fastly.com/service/{service_id}/purge/{key}",
46+
headers={"Fastly-Key": api_key},
47+
timeout=30,
4848
)
49-
return response

pydotorg/middleware.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ def __init__(self, get_response):
2929
"""Store the get_response callable."""
3030
self.get_response = get_response
3131

32-
def _get_section_key(self, path):
32+
def get_section_key(self, path):
3333
"""Extract section surrogate key from URL path.
3434
3535
Examples:
3636
/downloads/ -> downloads
3737
/downloads/release/python-3141/ -> downloads
3838
/events/python-events/ -> events
3939
/ -> None
40+
4041
"""
4142
parts = path.strip("/").split("/")
4243
if parts and parts[0]:
@@ -50,7 +51,7 @@ def __call__(self, request):
5051
if hasattr(settings, "GLOBAL_SURROGATE_KEY"):
5152
keys.append(settings.GLOBAL_SURROGATE_KEY)
5253

53-
section_key = self._get_section_key(request.path)
54+
section_key = self.get_section_key(request.path)
5455
if section_key:
5556
keys.append(section_key)
5657

pydotorg/tests/test_middleware.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,21 @@ def test_redirects(self):
2626

2727

2828
class GlobalSurrogateKeyTests(TestCase):
29-
def test_get_section_key(self):
29+
def testget_section_key(self):
3030
"""Test section key extraction from URL paths."""
31-
middleware = GlobalSurrogateKey(lambda r: None)
31+
middleware = GlobalSurrogateKey(lambda _: None)
3232

33-
self.assertEqual(middleware._get_section_key("/downloads/"), "downloads")
34-
self.assertEqual(middleware._get_section_key("/downloads/release/python-3141/"), "downloads")
35-
self.assertEqual(middleware._get_section_key("/events/"), "events")
36-
self.assertEqual(middleware._get_section_key("/events/python-events/123/"), "events")
37-
self.assertEqual(middleware._get_section_key("/sponsors/"), "sponsors")
33+
self.assertEqual(middleware.get_section_key("/downloads/"), "downloads")
34+
self.assertEqual(middleware.get_section_key("/downloads/release/python-3141/"), "downloads")
35+
self.assertEqual(middleware.get_section_key("/events/"), "events")
36+
self.assertEqual(middleware.get_section_key("/events/python-events/123/"), "events")
37+
self.assertEqual(middleware.get_section_key("/sponsors/"), "sponsors")
3838

39-
# returns None
40-
self.assertIsNone(middleware._get_section_key("/"))
39+
# returns None
40+
self.assertIsNone(middleware.get_section_key("/"))
4141

42-
self.assertEqual(middleware._get_section_key("/downloads"), "downloads")
43-
self.assertEqual(middleware._get_section_key("downloads/"), "downloads")
42+
self.assertEqual(middleware.get_section_key("/downloads"), "downloads")
43+
self.assertEqual(middleware.get_section_key("downloads/"), "downloads")
4444

4545
@override_settings(GLOBAL_SURROGATE_KEY="pydotorg-app")
4646
def test_surrogate_key_header_includes_section(self):
@@ -58,4 +58,4 @@ def test_surrogate_key_header_homepage(self):
5858
response = self.client.get("/")
5959
self.assertTrue(response.has_header("Surrogate-Key"))
6060
surrogate_key = response["Surrogate-Key"]
61-
self.assertIn("pydotorg-app", surrogate_key)
61+
self.assertEqual(surrogate_key, "pydotorg-app")

0 commit comments

Comments
 (0)