Open-Source Wikis

/

uv

/

Crates

/

uv-publish

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 outcome

Multipart 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's HashReader.
  • 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

Entry points for modification

  • A new auth flow — extend TrustedPublishingService for new OIDC providers, or add a branch to credential resolution in lib.rs.
  • A new index quirkerror.rs and lib.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 ProgressReader and ReaderStream wiring.

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

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

uv-publish – uv wiki | Factory