Skip to content

Latest commit

 

History

History
203 lines (163 loc) · 10.1 KB

RELEASING.md

File metadata and controls

203 lines (163 loc) · 10.1 KB

Note that this file is going to be AI Generated. If you're working on this, have your agent do the job.

Releasing pele

Release tested commits from main using matching version tags and GitHub Releases. Publish the source distribution and repaired wheels to PyPI. Wheels support CPython 3.11–3.14 on Linux x86_64 (manylinux_2_28) and macOS 14+ on Intel and Apple Silicon. Windows is not supported. Conda-forge packages follow recipe review.

For 0.1.1, all twelve wheels are live on PyPI. The tested source archive is attached to the GitHub release. PyPI rejected the archive because pele-0.1.1.tar.gz was previously uploaded and deleted; deleted filenames cannot be reused. The publication job therefore reports failure after successfully uploading the wheels. Use a new version for the next PyPI release.

Prepare a version

Keep the version identical in pyproject.toml, meson.build, and conda-recipe/recipe.yaml. The initial version is 0.1.1.

Run both the Tests and Release artifacts workflows successfully on the release commit. Release artifacts builds the source archive once, then builds and tests all 12 wheels from that archive on Linux and macOS with Python 3.11–3.14, including generated GUI forms. Python versions run in separate jobs on standard runners. Wheel jobs build pinned native dependencies, including macOS OpenMP. macOS jobs reuse completed dependency builds only when the source recipe, architecture, compilers, SDK, OS and deployment target match. macOS jobs install SHA-verified Sonoma GCC bottles to keep the bundled Fortran runtimes compatible with macOS 14. Jobs repair the wheels with auditwheel/delocate, then run installed-package tests with native-library search paths cleared. The tests reject libraries loaded from the build prefix or Homebrew. All wheel jobs must pass before publication. Distribution builds use native=false; use -Dnative=true only for local CPU-specific builds.

The native wheel dependency builder is ci/build-wheel-deps.sh; its source URLs and SHA256 values are pinned. LICENSE-THIRD-PARTY records redistributed-library notices. Before changing dependencies, verify their licenses, source availability, and ABI/macOS deployment requirements. Delocate must retain its deployment-target checks; auditwheel must successfully repair to the declared manylinux policy.

To validate locally, first install C/C++/Fortran compilers, SUNDIALS, Eigen, LAPACK headers, and OpenMP, then run:

pip install build twine
python -m build -Csetup-args=-Dnative=false -Csetup-args=-Dlammps=disabled
python -m twine check --strict dist/pele-0.1.1.tar.gz

The default python -m build builds a source archive and then builds its wheel from that archive. Keep the checkout committed: Meson creates the source archive from Git's committed files. The compiled LAMMPS integration is excluded from the generic distribution; the Python LAMMPS fallback remains available through the lammps extra.

For a local Linux conda build outside conda-forge, supply its standard-library variants explicitly (conda-forge injects these during staging):

rattler-build build -r conda-recipe/recipe.yaml -c conda-forge \
  --variant c_stdlib=sysroot --variant c_stdlib_version=2.17

Deployment gotchas

Floating point arithmetic differs across platforms

A fixed random seed does not guarantee identical numerical results across platforms. CPU instructions, compilers and math libraries can round intermediate operations differently. Non-convex optimization can amplify differences in the last bits and reach different stationary points, even when the initial coordinates and random-generator state agree exactly.

For example, the tested macOS ARM NumPy 2.5.4 wheel for CPython 3.14 uses a fused multiply-add to compute x1*x1 + x2*x2 in its legacy Gaussian conversion. Fusing the multiply and add rounds that combined operation once; separate operations round the product first. Replaying this arithmetic reproduced all 54 recorded macOS Gaussian values, including six that differed from Linux despite identical generator states. NumPy's conversion source shows the expression involved.

The seeded LJ18 investigation also recorded a difference of one unit in the last place (1 ULP) in the first native minimization step. The resulting macOS saddle had exactly one unstable physical mode and a force RMS of 8.08e-5, satisfying the 1e-4 stopping tolerance. Its transverse curvature search nevertheless exhausted 100 iterations with an RMS of 1.96e-5, above its separate 1e-5 tolerance. At the same fixed coordinates, that search converged after 103 iterations when given a larger budget. A small iteration cap can therefore reject a valid candidate on one platform while passing on another. The trace on PR #183 reproduced this ARM result; its Intel macOS wheel trace found the same cause, with the fixed-coordinate searches needing up to 117 iterations.

A later Intel macOS Python 3.12 trajectory trace reused one wheel across two runners and repeated the original seeded accounting fixture. All 44 starting configurations agreed bit for bit, but four searches failed: three after 21 genuine positive-curvature decisions, and one after exhausting 100 outer iterations with two unstable modes and a large force RMS. None of the 1,064 recorded primary snapshots misclassified the curvature sign, and evaluation accounting remained exact. In one positive-curvature failure, independent replay confirmed a fresh force RMS of 4.85e-4, above 1e-4, and 48 positive projected physical curvatures, with the lowest +0.10566. These were legitimate failed searches, unlike the valid saddle rejected by the 100-iteration cap. The trace reproduces the abort pattern; the earlier CI failure's exact coordinates were not recorded. Accounting tests therefore start near the existing known saddle, with a physical perturbation that still requires refinement, and retain their success and exact-count assertions. A loosely quenched random configuration does not guarantee a successful global transition-state search within a finite iteration budget.

Configure publishing accounts

Create accounts on PyPI and TestPyPI. Configure a pending Trusted Publisher on each service with these values:

Field PyPI TestPyPI
Project name pele pele
GitHub owner pele-python pele-python
Repository pele pele
Workflow filename release.yml release.yml
Environment pypi testpypi

Create the corresponding GitHub environments under Settings → Environments if they do not exist. Trusted Publishing uses GitHub's identity token; no PyPI API key is needed. A pending publisher does not reserve a package name until its first upload.

Test the upload

On GitHub, open Actions → Release artifacts → Run workflow. Select main and check the TestPyPI input. Publishing begins only after every validation job passes. A dispatch with the input unchecked validates artifacts without publishing.

Test a binary installation in a fresh supported environment:

pip install --only-binary=:all: --no-deps --index-url https://test.pypi.org/simple/ \
  pele==0.1.1

Install the runtime dependencies listed in pyproject.toml before using --no-deps. Use a new version for each revised TestPyPI upload; distribution filenames cannot be replaced.

Publish the release

Fetch main from pele-python/pele into a clean release checkout. Once the workflows pass and publishing accounts are configured, create and push an annotated tag:

git tag -a v0.1.1 -m 'pele 0.1.1'
git push https://github.com/pele-python/pele.git v0.1.1

Create a GitHub Release for v0.1.1 and publish it. The Release artifacts workflow requires the tag to match the package version, validates the distribution, and publishes the source archive and all repaired wheels to PyPI. Creating a tag alone does not upload a package.

Submit the conda-forge recipe

The recipe in this repository uses a local source path for development builds. For 0.1.1, use the tested GitHub release archive instead of a PyPI source URL:

source:
  url: https://github.com/pele-python/pele/releases/download/v0.1.1/pele-0.1.1.tar.gz
  sha256: 53cc1f0b32371551817044b21efcfa004f044fa47b64d0e7594b27adda97a3de

Conda-forge currently has SQLAlchemy 1.4 builds through Python 3.13. For the initial conda recipe, require python >=3.11,<3.14 and skip other Python variants. PyPI wheels support Python 3.14 using SQLAlchemy's Python fallback.

For future versions, use the PyPI source archive once its upload succeeds. After the PyPI source archive is published, find its exact URL and SHA256 at https://pypi.org/pypi/pele/<version>/json (the urls entry whose packagetype is sdist). Replace the recipe's entire source block with:

source:
  url: <the published sdist URL>
  sha256: <its SHA256 digest>

Copy the resulting recipe to recipes/pele/recipe.yaml in a fork of conda-forge/staged-recipes, and submit a pull request. Keep spraharsh under recipe-maintainers. The source URL and checksum refer to the same tested distribution used for PyPI. Conda-forge review and feedstock builds are required before conda install -c conda-forge pele becomes available.

For subsequent releases, publish a new version and update the version, source checksum, and dependencies in conda-forge/pele-feedstock. Start a maintenance branch only when an older supported release needs fixes alongside newer work.

PyPI Trusted Publishing and conda-forge submission instructions describe the account and review steps.