-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Support for drafted uploads is a long-standing feature request, dating back (at least) to 2015 with #726.
Since then there's been been a proposed PEP, PEP 694, which defines a new upload API. This API includes support for drafted uploads, among other larger features (like upload session management, resumption, and batched uploading).
In the interest of expedience, @DarkaMau, @facutuesca, and I (@woodruffw) are looking at adding drafting support to just the legacy API, in such a way that a future implementation of PEP 694 (such as one built on #8941 or #16277) can reuse the building blocks within a new endpoint.
To keep things tractable (and keep PRs small + reviewable), here's the units of work we're proposing:
-
Add
published: datetime | None = datetime.nowto theReleasemodel, with a schema migration. This can be picked/adapted from PEP 694 (resolved merge conflicts from PR #8941) #16277 -
Add a PyPI-specific metadata field (or HTTP request header) to the upload endpoint, with the following semantics:
- If the project or release is being created for the first time, sets
published = None - If the release already exists and was not already marked as a draft, fails the upload (since this suggests a mixed/confused state on the user's side)
The current work in Draft Release support in PyPI #8941/PEP 694 (resolved merge conflicts from PR #8941) #16277 uses the
Is-Draft: true | falseheader, but I propose we prefix this withX-PyPI-to avoid standard header conflicts, so we'd checkX-PyPI-Is-Draftor similar. - If the project or release is being created for the first time, sets
-
When
Release.published is None, we should not present it in the standard indices/APIs. Instead, we'll serve it at a temporary draft index. Draft Release support in PyPI #8941 has this as/simple/draft/<draft_hash>, but I propose we drop the hash and do this with a URL that mirrors the existing hierarchy:/simple/draft/<name>/<version>/or even just/simple/draft/<name>with all drafts for the project being aggregated. -
When
Release.published is None, the given release should not appear as the latest release on the web view. Instead, it should have presentation behavior similar to yanked releases: we can show it on the/project/<name>/#historytimeline, but with a badge indicating that it's a draft. When navigating to/project/<name>/<version>, draft versions should be rendered like normal versions but with two differences:- A warning flash/banner/element on the top of the page, similar to what appears when a release has been yanked
- The
pip install ...instructions should be specialized to show the draft index, per the step above
All told, I think this gives us a set of independent tasks to accomplish drafting. Our proposal is that we try and use as much of #8941 and #16277 here as possible, but do so via independent PRs to keep things as small as possible and reduce reviewer burden.
Separately, as client-side/tooling changes that'll need to occur:
-
twineand similar uploading tools will ideally learn a--draftor similar flag that sets the appropriate header
CC @di for thoughts, and thanks @warsaw and @alanbato for the work you've done on this already!