Python release checklist · Updated 2026-06-27

Python Packaging Publishing Checklist: pyproject.toml, Build Backends, Wheels, sdists, TestPyPI, Twine, uv, and Release Rollback

A practical release checklist for Python libraries and CLIs so uv migration, build metadata, wheel/sdist publishing, changelog, credentials, and rollback do not break at release time.

Python toolchain migration is not complete until publishing still works. A project can run perfectly with uv locally and still fail at release time because metadata, build backends, package data, wheels, sdists, credentials, TestPyPI, dependency constraints, or changelog rules were never tested.

This checklist covers the release-specific layer after the Python Toolchain Migration Checklist. uv can manage projects and build workflows, mise can pin uv and release tools, but publishing requires its own gate: package metadata, build backend behavior, wheel and sdist inspection, TestPyPI dry run, token handling, provenance notes, rollback plan, and post-release monitoring.

RepoDaily verdict

Do not replace a Python project release path just because local uv sync works. A publish-ready migration must prove clean build artifacts, correct metadata, reproducible wheels and sdists, safe credentials, TestPyPI or dry-run validation, changelog/version alignment, and a rollback or yanked-release plan.

Quick matrix

Release areaWhat to checkPass signalStop signal
Project metadataname, version, description, license, authors, classifiers, URLs, Python requiresMetadata renders correctly and matches README/changelogWrong package name, stale license, unsupported Python range
Build backendsetuptools, hatchling, flit, poetry-core, maturin, or uv build pathClean isolated build produces expected artifactsBuild depends on local files, hidden env, or old Poetry-only behavior
Wheel artifactWheel filename, tags, imports, package data, console scriptsFresh venv install imports and runs CLIMissing files, wrong tags, import failures, broken entry points
sdist artifactSource contents, pyproject, tests/docs policy, generated filessdist can rebuild wheel in isolationsdist excludes required package data or includes secrets
DependenciesRuntime deps, optional extras, upper/lower bounds, environment markersInstall works on supported Python versions and platformsUnpinned internal deps, broken extras, incompatible markers
CredentialsPyPI/TestPyPI token, secret storage, CI environment, maintainer accessToken scoped and stored in CI/secret managerToken in local shell history, repo, logs, or shared chat
Dry runTestPyPI or internal index publish/install cycleInstall from published test artifact succeedsOnly local build was tested
RollbackYank/delete policy, version bump rule, hotfix owner, incident notesTeam knows when to yank, patch, or publish fixNo plan for bad release

Publishing readiness scorecard

Score the release path before changing official publishing instructions.

Readiness area0 points1 point2 pointsReviewer question
MetadataIncomplete or staleMostly correctReviewed and matches docsWill PyPI show the right story?
ArtifactsNo artifact inspectionWheel only checkedWheel and sdist inspected and installedCan a clean environment use this package?
CI release pathManual laptop releasePartial CI automationOwned CI release job with scoped secretsWho can publish and how is it audited?
Dependency policyUnknown boundsSome version rulesBounds/extras/markers testedWhat versions and platforms are supported?
Dry runNoneLocal build onlyTestPyPI or internal index install testedDid we test the published artifact?
RollbackNo planInformal ownerYank/patch/notify playbook documentedWhat happens after a bad release?

30-minute Python publishing smoke test

Use this plan before migrating a package release path to uv or CI automation.

0–5 min: release inventory

Read pyproject, version source, build backend, package data rules, entry points, README, license, and current release docs.

Success checkThe current release path and metadata are visible.

5–12 min: clean build

Build wheel and sdist in a clean environment and list artifact names and contents.

Success checkBoth artifacts exist and contents look intentional.

12–18 min: install smoke test

Create a fresh environment, install from wheel or sdist, import the package, and run CLI/help or minimal function tests.

Success checkThe installed artifact works without source checkout assumptions.

18–24 min: publish dry run

Validate upload credentials and publish/install through TestPyPI or staging where possible.

Success checkThe published artifact can be installed from an index.

24–30 min: release decision

Decide migrate, keep old path, patch metadata, test longer, or assign release owner and rollback plan.

Success checkThe next release step has owner, evidence, and rollback path.

Publishing decision flow

  1. Classify the package: library, CLI, plugin, internal package, native-extension package, data package, or monorepo subpackage.
  2. Review `pyproject.toml`, build backend, version source, package data, entry points, dependency groups, optional extras, Python version support, license, and README rendering.
  3. Build wheel and sdist in a clean environment, then inspect contents before publishing anything.
  4. Install from the built wheel and sdist into a fresh environment and run import, CLI, and minimal smoke tests.
  5. Publish to TestPyPI or an internal dry-run index when possible, then install from that index to validate the actual published artifact.
  6. Only then update release docs or CI to use uv/mise-backed publishing; keep the old path until one successful release or explicit owner sign-off.

Scenario table

ScenarioRelease patternExtra guardrail
Pure Python libraryBuild wheel and sdist, inspect metadata, install both artifacts, run import smoke testCheck Python version range and dependency markers
Python CLI toolVerify console script entry points and fresh venv command executionRun help/version command from installed wheel
Package with data filesInspect wheel/sdist contents for package data and exclusionsConfirm no private data or generated junk is included
Native extension or Rust-backed packageBuild platform-specific artifacts and test tagsUse platform matrix or release tooling owner review
Internal package indexPublish to internal dry-run or staging index firstValidate credentials, index URL, and dependency resolution
Poetry-to-uv migrationKeep existing release path until build/publish parity is provenCompare metadata and artifact contents before switching
Emergency hotfixPatch version, minimal diff, clean artifact, install smoke test, publish, monitorDo not reuse a broken version number

Publishing risk checklist

Local success, published failure

A package can import locally because source paths are on disk. Test installed artifacts from wheel and sdist.

Metadata drift

Package metadata, README, changelog, license, and Python support can disagree unless reviewed together.

sdist surprise

Wheel-only testing can hide missing source files. A good sdist should rebuild the wheel in isolation.

Credential leakage

Publishing tokens should be scoped and stored in CI or a secret manager, never committed or pasted into chat.

Version-number trap

Once a version is published, reusing it is usually unsafe. Define patch/yank behavior before release.

Dependency overconfidence

Loose or wrong dependency bounds can break downstream users after install, not during local tests.

Publishing implementation patterns

Build in clean env

Use a clean environment or CI job so hidden local files and editable installs cannot mask packaging problems.

Inspect before publish

List wheel and sdist contents and check metadata before uploading to any index.

Install the artifact

Run smoke tests against the built/published artifact, not the source checkout.

TestPyPI or staging index

Use a dry-run index when feasible to validate upload and install behavior.

mise pins release tools

Use mise to pin uv, Python, twine, or other release tools when releases must be reproducible.

Release PR checklist

Every release PR should record version, changelog, artifacts tested, credentials path, publisher, and rollback owner.

FAQ

Short answers for Python maintainers publishing packages after toolchain migration.

Does uv replace Twine or PyPI release policy?

No. uv can support build and project workflows, but release policy still needs artifact inspection, credentials, dry run, changelog, and rollback rules.

Should we test both wheel and sdist?

Yes for published packages. Wheel and sdist failures differ, and downstream users may depend on either path.

When is TestPyPI useful?

Use it to validate upload/install behavior and metadata rendering before publishing a real public release.

Can a bad PyPI release be overwritten?

Do not rely on overwriting. Use a new version, yank when appropriate, publish a fix, and document the incident.

Related radar

Infrastructure & Runtime Radar

Related RepoDaily briefs

Sources

  1. Python Packaging User Guide
  2. Python Packaging: pyproject.toml
  3. PyPI help
  4. TestPyPI
  5. Twine documentation
  6. uv documentation
  7. uv build docs
  8. mise documentation

Feedback

Did this page help you make a decision?

Anonymous feedback helps RepoDaily improve what is actually useful.

Report outdated or missing evidence