Skip to content

Publish Python packages

Build the distribution first, then upload its files to the repository’s PyPI upload host.

From the Python project root:

Terminal window
rvs art select platform/releases
rvs uv build

Review the generated files in dist/ before uploading them.

After signing in with rvs, either wrap Twine:

Terminal window
rvs art select platform/releases
rvs twine upload dist/*

rvs supplies temporary access without writing a token to .pypirc.

Before uploading, rvs shows the destination and files and asks for confirmation. Use rvs twine --rvs-yes upload dist/* only in a guarded automation job.

Terminal window
rvs art select platform/releases
rvs uv publish

Use rvs uv --rvs-yes publish only in a guarded automation job.

Both wrappers select the upload URL and provide temporary authentication without writing credentials into project files, shell history, or package-tool settings.

  1. Open the repository in Ravenstash.
  2. Confirm the package name and version.
  3. Inspect the uploaded wheel or source-distribution files.
  4. Install the exact version from the private index.

To run uv publish or twine upload without a wrapper, mint a short-lived publish token:

Terminal window
RVS_NATIVE_TOKEN=$(rvs art token mint \
--target platform/releases \
--format pypi \
--access publish)
export RVS_NATIVE_TOKEN

Copy the current upload address from Ravenstash. The examples below use:

https://push.pypi.rvsta.sh/platform/releases/

Print only the upload address or generate a complete target-specific recipe:

Terminal window
rvs art endpoint --target platform/releases --format pypi --access publish
rvs art native config uv --target platform/releases --access publish
rvs art native config twine --target platform/releases

endpoint only prints the address. native config prints configuration and commands without writing files, minting a token, or running the package tool.

Configure the named index and its upload address in pyproject.toml:

pyproject.toml
[[tool.uv.index]]
name = "ravenstash"
url = "https://pypi.rvsta.sh/platform/releases/simple/"
publish-url = "https://push.pypi.rvsta.sh/platform/releases/"
default = true
authenticate = "always"
Terminal window
export UV_INDEX_RAVENSTASH_USERNAME=rvs
export UV_INDEX_RAVENSTASH_PASSWORD="$RVS_NATIVE_TOKEN"
uv build
uv publish --index ravenstash

Use Twine’s environment variables so the token is not saved in .pypirc or placed on the command line:

Terminal window
export TWINE_USERNAME=rvs
export TWINE_PASSWORD="$RVS_NATIVE_TOKEN"
export TWINE_REPOSITORY_URL="https://push.pypi.rvsta.sh/platform/releases/"
export TWINE_NON_INTERACTIVE=1
twine upload dist/*

Unset the credential variables after publication. See Use tokens with package tools for expiry, access, and rotation details.