The final PyPI push is deliberately manual: artifact building and testing
are automated, but a human runs the upload and confirms each package. This
sequence was last exercised for 0.9.0.dev0.
The version is a literal in many coordinated places. Bump them all:
version =in every workspacepyproject.toml(root meta-package,python/pecos-rslib{,-llvm,-cuda,-exp},python/quantum-pecos, and thepython/selene-plugins/*packages)- Exact-version internal pins: the root's
quantum-pecos[cuda12/13]==...entries and quantum-pecos'specos-rslib==.../pecos-rslib-llvm==... - Regenerate both lockfiles:
uv lockat the root and inexp/zluppy/, using the same uv version CI pins in.github/workflows/(verify the diffs are version-lines-only)
Verify: git grep <old-version> returns nothing outside this file's
historical note; uv lock --check passes;
just python-ci-sync-test then python -c "import pecos; print(pecos.__version__)"
reports the new version.
Versioning convention: a user-visible default-behavior change gets a minor
bump (e.g. 0.8.x -> 0.9.0.dev0); additive-only work increments the dev/patch
number.
Merge the bump PR to dev, then wait for all post-merge workflows on the
merge commit to pass -- including the long ones (python-core is the full
suite and only runs post-merge; Python Artifacts is the full wheel matrix,
i.e. the release rehearsal). Optionally run just check-all locally on the
merge commit as a belt-and-braces gate.
git tag py-<version> <merge-sha>
git push origin py-<version>
The py-* tag triggers python-release.yml in full-release mode: all
platform wheels (pecos-rslib, pecos-rslib-llvm), quantum-pecos wheel + sdist,
abi3 wheel tests across Python versions, and a collect_artifacts bundle
pinned to the tagged commit. Wait for it to go green. Publish from the tag
run's bundle, not a branch run -- provenance stays tied to the immutable
ref.
gh run download <tag-run-id> -n pecos-distribution -D pecos-distribution
./scripts/publish-wheels.sh --dry-run -f pecos-distribution
(-f accepts the extracted directory gh produces, or the original zip.)
The dry run must show a real twine check ... PASSED per file (twine must be
installed, e.g. uv tool install twine).
./scripts/publish-wheels.sh -f pecos-distribution
Confirm each package at its prompt. Upload order matters and the script
enforces it: all packages are preflighted (complete set, consistent version,
only expected files) before anything uploads, dependencies go first
(pecos-rslib -> pecos-rslib-llvm -> quantum-pecos, which pins both at
exact versions), and any failure or declined prompt aborts the remaining
uploads rather than continuing. Publishing quantum-pecos alone (-p) is
blocked unless its pinned dependencies already exist on PyPI. Checks run
twine check --strict.
Credentials come from ~/.pypirc; a new package's first upload needs an
account-scoped token (project-scoped tokens cannot create projects).
Create a release on the tag (marked pre-release for dev versions), attach the bundle's files, and include:
- the headline change and any escape hatch / migration note
- an install snippet -- uv users need
--prerelease=allowfor dev-version transitive pins
gh release create py-<version> --prerelease --title "py-<version>" \
--notes-file <notes.md> --generate-notes <dist-files...>
Prove the published set resolves and runs, from a clean environment:
uv venv /tmp/verify && uv pip install --python /tmp/verify \
--no-cache --prerelease=allow quantum-pecos==<version>
/tmp/verify/bin/python -c "import pecos; print(pecos.__version__)"
(--no-cache matters: a resolver that looked while an upload was in flight
may have cached "not found".)