Changelog¶
2026.8.14.1¶
Order packages in the simple index by version order not alphabetical.
2026.8.14¶
Do not stop at first page of releases.
Project pages now list files in PEP 440 version order, oldest first — the lexical filename sort put
1.10.0before1.2.0. A version’s wheels and sdist stay adjacent, pre-releases sort before their release, and files whose version cannot be parsed sort last, matching the PEP 700versionslist, which was already ordered this way.Allow wild card specifiers for repositories within orgs in the config file.
Deployment targets:
target: cloudflarewrites Cloudflare Pages cache headers andtarget: nginxwrites a server snippet, through a documentedghr_pypi.targetsplugin interface.assets: redirect: serve private repositories without mirroring. Every index link points at the site’s own_assets/paths, the build emits an allow-list of the published assets beside them, and thecloudflaretarget ships a token-holding Cloudflare Worker that authenticates the whole site and redirects each asset request to GitHub’s short-lived signed URL. Targets opt in with asupports_redirectattribute; any target that does not is refused the mode.The
nginxtarget now supportsassets: redirecttoo, serving private release assets on stock nginx with no additional modules — no njs and noauth_request. It emits an/_assets/location gated byauth_basicthat proxies to GitHub’s asset API and returns the signed 302 unfollowed, plusghr-pypi-assets.conf— the allow-list as a generatedmap,included athttplevel becausemapis valid nowhere else. Neither file contains the token or a password; both areincluded from paths the build deliberately does not write.New
missing_metadatakey (extract|warn, defaultextract), accepted only underassets: redirect: it decides whether a wheel whose release carries no.metadataasset is downloaded once so the sidecar can be written into the site — PEP 658 metadata for a private repository without mirroring its wheels.Added
ghr-pypi webhook --index-repo OWNER/NAME [--out DIRECTORY], which writes a standalone Cloudflare Worker (pluswrangler.tomland aSETUP.mdchecklist). Pointed at a GitHub organization webhook, it validates each delivery’s HMAC signature and turns a release published in any repository the hook covers into arepository_dispatchthat rebuilds the index. Independent oftarget.The Pages workflow now also triggers on
repository_dispatchof typeghr-pypi-rebuild, which is what the receiver — or a releasing repository’s own workflow — sends. See “How do I rebuild when another repository releases?” for all three ways to keep an aggregating index fresh.The tutorials now assume a repository whose release process already attaches wheels to its Releases, and none of them writes a package for you. There are four: GitHub Pages, indexing other repositories, and a private index under
assets: redirecton each of the two redirectors — a Cloudflare Worker and stock nginx. The nginx one replaces the static-site walkthrough; content negotiation moved to “How do I serve the PEP 691 JSON API?”.New
auth: githubmode for thecloudflaretarget underassets: redirect: clients authenticate with their own fine-grained GitHub tokens as the Basic-auth password. Served pages require the token to read agate_repository(default: the repository the build runs in), and each download forwards the client’s token to GitHub, which answers per repository — per-user identity and revocation, per-repository access control, and a Worker that stores no secret at all. Targets opt in with asupports_github_authattribute, exactly assupports_redirectworks.The redirector no longer caches GitHub’s signed URLs (in either auth mode): under per-user tokens a cached URL minted with one user’s authorization would answer another user’s request, and rather than key the cache per token it is gone — one extra GitHub subrequest per download.
The GitHub token is now named
GHR_PYPI_TOKENeverywhere it appears: the CLI reads that environment variable first (GITHUB_TOKENstill works as a fallback, so workflows passing the built-ingithub.tokenare unaffected), and the Cloudflare redirector and webhook receiver read it as their secret name — a breaking rename for existing deployments, which must re-bind the secret under the new name on their next deploy. The old name was overloaded: exportingGITHUB_TOKENin a shell hijacksghand VSCode git auth, and a GitHub Actions secret cannot even be calledGITHUB_TOKEN.The Cloudflare tutorial is now turnkey: it starts from the cloudflare-pypi-template repository, whose single idempotent workflow creates the Pages project, binds the Worker’s secrets, builds, and deploys — entirely from GitHub Actions, with nothing run locally. The index rebuilds daily by default; the tutorial closes with per-repository
repository_dispatchfor instant rebuilds, and the organization webhook receiver moved to “How do I rebuild when another repository releases?”.Raised the minimum
typerto 0.18. click 8.3 moved required-option enforcement, and typer 0.16/0.17 paired with it stop reporting a missing required option at all, passingNoneinto the command instead —ghr-pypi webhookwith no--index-repoexited 1 complaining about a repository namedNonerather than 2 with a usage message.Deprecated: the
mirrorkey is nowassets: link | mirror.mirror: truestill works and warns.
2026.8.8¶
Support yank/exclude.
repositoriesentries may use anfnmatchpattern in the name half —yourorg/*indexes every repository the token can read in that owner — with a newexclude_repositorieskey to subtract from expansions.Every GitHub list endpoint is now paginated: repositories with more than 100 releases are read in full.
The positional
REPOargument takes zero or more repositories and defaults to$GITHUB_REPOSITORY;--outdefaults to_siteand the config file’srepositorieskey is optional. A GitHub Pages workflow can now runghr-pypi indexwith no arguments.Breaking: the index build is now the
indexsubcommand —ghr-pypi OWNER/NAMEbecomesghr-pypi index OWNER/NAME. Bareghr-pypiprints help.Added
ghr-pypi extract-meta PATH..., which writes each wheel’s PEP 658 core metadata to<wheel>.metadatafor upload as a release asset.
2026.8.6¶
Initial release.
Aggregate releases from multiple repositories via
--config(YAML).Template override hooks: a config-specified directory and
builtin/-prefixed block inheritance.Use GitHub’s asset digests instead of downloading to hash;
missing_digestconfig policy for digest-less assets.Emit a PEP 691/700 JSON Simple API alongside the HTML index, controlled by the
formatsconfig key.mirrormode: download assets into the site (private-repo support, self-contained output, incremental re-builds).Serve PEP 658/714 core metadata: extracted from mirrored wheels, passed through from
.metadatarelease assets, with per-repository coverage warnings.Release workflow publishes each wheel’s PEP 658
.metadataas a release asset.yankedandexcludeconfig keys, keyed by project then version:yankedmarks files PEP 592 yanked (data-yankedin the HTML, ayankedkey in the JSON) while leaving them installable by exact pin;excludekeeps them out of the index entirely.Versions are computed at build time from git tags; no version strings are stored in the repository.
Added a full Diátaxis documentation manual: tutorials, how-to guides, and reference.