Repository: pypi/warehouse

Issue:
Temporarily Purge Legacy PyPI
Uploading should temporarily purge legacy PyPI, currently if someone uploads to Warehouse they need to do something like:

```
$ curl -XPURGE https://pypi.python.org/simple/<name>/
$ curl -XPURGE https://pypi.python.org/pypi/<name>/json
```

To make sure that their upload shows up on PyPI without it having to fall out of the cache. Or they could go and hit save on PyPI legacy at least once to trigger the same thing. This is less than optimal and we should just temporarily do the purges for folks until legacy is retired.



Discussion:

Resolving patch:
diff --git a/warehouse/config.py b/warehouse/config.py
--- a/warehouse/config.py
+++ b/warehouse/config.py
@@ -123,6 +123,7 @@ def configure(settings=None):
     maybe_set(settings, "warehouse.theme", "WAREHOUSE_THEME")
     maybe_set(settings, "warehouse.domain", "WAREHOUSE_DOMAIN")
     maybe_set(settings, "forklift.domain", "FORKLIFT_DOMAIN")
+    maybe_set(settings, "warehouse.legacy_domain", "WAREHOUSE_LEGACY_DOMAIN")
     maybe_set(settings, "site.name", "SITE_NAME", default="Warehouse")
     maybe_set(settings, "aws.key_id", "AWS_ACCESS_KEY_ID")
     maybe_set(settings, "aws.secret_key", "AWS_SECRET_ACCESS_KEY")
diff --git a/warehouse/forklift/legacy.py b/warehouse/forklift/legacy.py
--- a/warehouse/forklift/legacy.py
+++ b/warehouse/forklift/legacy.py
@@ -21,6 +21,7 @@
 import packaging.requirements
 import packaging.version
 import pkg_resources
+import requests
 import wtforms
 import wtforms.validators
 from rfc3986 import uri_reference
@@ -944,6 +945,17 @@ def file_upload(request):
                 },
             )
 
+        # TODO: Once we no longer have the legacy code base running PyPI we can
+        #       go ahead and delete this tiny bit of shim code, since it only
+        #       exists to purge stuff on legacy PyPI when uploaded to Warehouse
+        old_domain = request.registry.settings.get("warehouse.legacy_domain")
+        if old_domain:
+            request.tm.get().addAfterCommitHook(
+                requests.post,
+                args=["https://{}/pypi".format(old_domain)],
+                kws={"data": {":action": "purge", "project": project.name}},
+            )
+
     return Response()
 
 


Test patch:
diff --git a/tests/unit/forklift/test_legacy.py b/tests/unit/forklift/test_legacy.py
--- a/tests/unit/forklift/test_legacy.py
+++ b/tests/unit/forklift/test_legacy.py
@@ -21,6 +21,7 @@
 import pkg_resources
 import pretend
 import pytest
+import requests
 
 from pyblake2 import blake2b
 from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden
@@ -1652,6 +1653,50 @@ def test_upload_succeeds_creates_project(self, pyramid_config, db_request):
             ),
         ]
 
+    def test_upload_purges_legacy(self, pyramid_config, db_request,
+                                  monkeypatch):
+        pyramid_config.testing_securitypolicy(userid=1)
+
+        user = UserFactory.create()
+
+        filename = "{}-{}.tar.gz".format("example", "1.0")
+
+        db_request.user = user
+        db_request.POST = MultiDict({
+            "metadata_version": "1.2",
+            "name": "example",
+            "version": "1.0",
+            "filetype": "sdist",
+            "md5_digest": "335c476dc930b959dda9ec82bd65ef19",
+            "content": pretend.stub(
+                filename=filename,
+                file=io.BytesIO(b"A fake file."),
+                type="application/tar",
+            ),
+        })
+
+        storage_service = pretend.stub(store=lambda path, filepath, meta: None)
+        db_request.find_service = lambda svc: storage_service
+        db_request.client_addr = "10.10.10.10"
+
+        tm = pretend.stub(
+            addAfterCommitHook=pretend.call_recorder(lambda *a, **kw: None),
+        )
+        db_request.tm = pretend.stub(get=lambda: tm)
+
+        db_request.registry.settings["warehouse.legacy_domain"] = "example.com"
+
+        resp = legacy.file_upload(db_request)
+
+        assert resp.status_code == 200
+        assert tm.addAfterCommitHook.calls == [
+            pretend.call(
+                requests.post,
+                args=["https://example.com/pypi"],
+                kws={"data": {":action": "purge", "project": "example"}},
+            ),
+        ]
+
     def test_fails_without_user(self, pyramid_config, pyramid_request):
         pyramid_config.testing_securitypolicy(userid=None)