Skip to content

[Feature]: Require PyPI publication for new public packages #793

Open

Description

@zhongkechen

What would you like?

Add a required PR check that blocks a new public Python distribution from merging until its exact package name has been published to production PyPI by AWS.

Adding a package directory and a pip install example should not depend on a maintainer remembering to complete the first publication. A check should catch this before merge and explain which package needs publishing. This also needs to cover a rename of an existing distribution and a previously local-only package becoming public.

Here, a public module means an independently installable Python distribution identified by project.name in a package's pyproject.toml. Adding a Python import module inside an existing distribution should not require a separate PyPI project.

Possible Implementation

Add a small metadata-checking script and a GitHub Actions workflow that runs for pull requests.

  1. Determine which packages are public. Scan package manifests and compare the PR base and head. Use an explicit, reviewed public/local-only classification, ideally shared with the publishing workflow. Require every package manifest to be classified so a new public package cannot escape the check simply because it has not been added to the publish matrix. Examples and conformance packages can be explicitly local-only. Treat a new normalized distribution name, a rename, or a local-only-to-public transition as a new public package. Detect conflicts between the classification and the publishing configuration.

    Do not use [tool.hatch.publish.index] disable = true as the public/local-only signal: this repository's GitHub workflow builds with Hatch and uploads through a separate PyPI action, so that flag does not prevent publication.

  2. Check production PyPI. Normalize names according to Python packaging rules (case-insensitive, with runs of -, _, and . treated equivalently). Query PyPI's JSON/Simple APIs for each newly public name. Require a matching project name and at least one non-yanked published distribution file. An HTTP 404, an empty project, or a pending Trusted Publisher is not sufficient. Check that some release exists; do not require the version being developed in the PR to be published already, otherwise ordinary version bumps would be blocked.

  3. Verify who published it. An HTTP 200 is not enough to show that a name belongs to this project. For newly public packages, verify PyPI publication attestations/provenance against the expected AWS repository and trusted publishing identity. Do not accept author/email fields or project URLs as ownership evidence. Missing or mismatched evidence should require maintainer action rather than produce a passing check.

  4. Make failures useful. Report the package path, normalized name, PyPI URL, and reason for failure in the job summary. Distinguish an unpublished package from a registry/network error. Use bounded timeouts and retries for transient failures, and fail if verification still cannot complete. Re-running the check after a maintainer publishes the package should pass without changing the PR just to refresh the result.

  5. Run the check safely and enforce it. The PR workflow should read metadata and public registry data without installing, importing, or building the package under review. It should work for fork PRs with read-only permissions and no publishing credentials or OIDC write permission. Emit a stable check result even when a PR adds no public packages, and configure it as a required check in the repository's merge rules. Support merge_group if the repository uses a merge queue.

  6. Document first publication. Update RELEASING.md with a maintainer-controlled way to publish a reviewed initial release before the new public package merges, including a minimal name-reservation release where appropriate. This avoids a circular dependency between the merge gate and the existing release process. The PR check itself must never publish packages. A pending publisher only passes once an actual release has been published and verified.

Acceptance criteria

  • Adding an unpublished public distribution fails the required PR check with an actionable message.
  • Renaming a public distribution or promoting a local-only package checks the new public name.
  • An unclassified package or a conflict with the publishing configuration fails instead of being silently skipped.
  • A package with verified AWS publication passes, including after rerunning a previously failed check once publication completes.
  • A project with no release files, only yanked files, or missing/mismatched publisher evidence fails.
  • PEP 503-equivalent spellings are treated as the same name; moving a directory without changing the distribution identity does not create a false positive.
  • Existing-package version bumps, internal Python modules, and explicitly local-only packages do not require a new PyPI publication.
  • Timeouts, rate limits, invalid responses, and PyPI server errors are tested and cannot turn into a passing result after retries are exhausted.
  • Fork PRs can run the check without secrets, and PR-controlled package code is never executed by it.
  • Tests cover base/head detection, classification, renames, normalization, publication/provenance responses, and registry failures using fixtures. A small workflow-level check verifies that script failure produces a failed required status.
  • The merge rule is enabled and the first-publication procedure is documented.

Is this a breaking change?

No SDK API or runtime change. It adds a merge requirement for new public distribution names.

Does this require an RFC?

No. The package-classification format and first-publication procedure can be reviewed in the implementation PR.

Additional Context

Related: #716 (Improve the monorepo release process). That issue covers release selection, publication, and post-publish verification. This issue adds the earlier PR gate for introducing a public package name.

Relevant files:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgithub_actionsPull requests that update GitHub Actions codeneeds-triageIssue needs triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions