astral-sh/uv
uv-publish
crates/uv-publish/ implements uv publish — uploading wheels and source distributions to
PyPI-compatible indexes. It supports plain username/password, keyring, and modern OIDC-based
trusted publishing (GitHub Actions, GitLab, pyx).
Purpose
Replacing twine with a faster, more secure default. Beyond the basic upload it handles:
- Multi-file glob patterns (
uv publish dist/*.whl dist/*.tar.gz). - Concurrent uploads with a semaphore.
- Multipart bodies and pre-signed S3-style URLs.
- Retries with exponential backoff (registry uploads are flaky).
- Trusted publishing token retrieval.
- Hash verification on the local artifact before upload.
- Status-code aware reporting (already-uploaded, conflicts, auth failures).
Directory layout
crates/uv-publish/src/
├── lib.rs # 49k chars: top-level publish + retry orchestration
├── error.rs # 27k chars: typed PublishError variants
└── trusted_publishing/ # OIDC providers
├── mod.rs # TrustedPublishingService trait, common bits
├── pypi.rs # PyPI flavor (api.pypi.org)
└── pyx.rs # pyx flavor (Astral's index)Key abstractions
| Type | File | Role |
|---|---|---|
publish_files (top-level fn) |
lib.rs |
The orchestrator. Globs, dedups, concurrency-limits, and uploads each artifact. |
PublishError |
error.rs |
Specific failure modes (no files, invalid filename, multipart failure, auth, conflict, unsupported response). |
TrustedPublishingService |
trusted_publishing/mod.rs |
Trait used by both PyPI and pyx flavors to mint short-lived API tokens from an OIDC ID token. |
TrustedPublishingToken |
trusted_publishing/mod.rs |
The minted token. |
PyPIPublishingService, PyxPublishingService |
trusted_publishing/pypi.rs, trusted_publishing/pyx.rs |
The two concrete implementations. |
RetryState |
lib.rs |
Retry budgeting per attempt. |
How it works
sequenceDiagram
participant CLI as uv publish
participant Publisher as uv-publish
participant Auth as uv-auth (keyring/.netrc/env)
participant TP as TrustedPublishingService
participant Index as PyPI / mirror
CLI->>Publisher: publish_files(globs, options)
Publisher->>Publisher: Resolve files, validate filenames
alt --trusted-publishing
Publisher->>TP: mint_token(audience, OIDC ID token)
TP-->>Publisher: TrustedPublishingToken
else
Publisher->>Auth: credentials_for_realm(index_url)
Auth-->>Publisher: Credentials (or prompt)
end
loop concurrent files
Publisher->>Index: POST multipart (wheel + metadata)
alt 200/201
Index-->>Publisher: success
else already-uploaded
Publisher-->>Publisher: warn + skip
else 5xx / network error
Publisher->>Publisher: retry with backoff
end
end
Publisher-->>CLI: per-file outcomeMultipart uploads
PyPI's upload API is a multipart POST: the wheel/sdist as one part, plus a set of metadata
fields (name, version, filetype, sha256, blake2_256, …). lib.rs builds the body using
reqwest::multipart::Part, streaming the file from disk via tokio_util::io::ReaderStream
and a ProgressReader (from uv-fs) so the user sees an upload progress bar.
Trusted publishing
GitHub Actions can mint OIDC ID tokens that say "this workflow run is for repo org/uv on
ref main." PyPI accepts those tokens and returns short-lived API tokens. trusted_publishing/pypi.rs
implements that exchange. The pyx flavor (pyx.rs) is the same shape but talks to Astral's
index.
To use it, uv publish --trusted-publishing always or the auto-detected default within a
GitHub Actions runner triggers the OIDC dance before any uploads happen.
Hash and metadata extraction
Before upload, uv reads the wheel/sdist locally and:
- Verifies it parses (
uv-distribution-filename,uv-metadata). - Hashes it with SHA-256 and BLAKE2b-256 via
uv-extract'sHashReader. - Reads
METADATA(or the equivalent for sdists) to populate the upload form.
This catches malformed local artifacts before the network round trip and makes the upload deterministic.
Retries
lib.rs wraps the upload in astral-reqwest-retry's ExponentialBackoff. Some failures are
intentionally not retried — a 4xx response usually indicates a permanent client error, while a
"file already uploaded" response is downgraded to a warning rather than a failure (depending
on the index's status-code conventions).
Integration points
uv-authfor non-OIDC credential resolution.uv-clientfor the underlying reqwest client and middleware.uv-distribution-filenameto validate that paths look like wheels/sdists.uv-metadatato readMETADATAfrom the local artifact.uv-redactedto render index URLs without leaking credentials.
Entry points for modification
- A new auth flow — extend
TrustedPublishingServicefor new OIDC providers, or add a branch to credential resolution inlib.rs. - A new index quirk —
error.rsandlib.rs's status-code mapping. The recent fix in #19146 ("Redact pre-signed upload URLs in verbose output") is a good example of the pattern. - Streaming improvements — the
ProgressReaderandReaderStreamwiring.
Key source files
| File | Purpose |
|---|---|
crates/uv-publish/src/lib.rs |
The orchestrator. |
crates/uv-publish/src/error.rs |
The typed error surface. |
crates/uv-publish/src/trusted_publishing/ |
OIDC token minting. |
See also
- features/publishing for an end-to-end walkthrough.
uv-authfor credential resolution.uv-keyringfor OS keyring backends.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.