Repository navigation
Template 0.1.25: publish the app's Python code on PyPI at each release (opt-in, trusted publishing) - #46
Merged
Conversation
… release, off until turned on - .github/workflows/publish_pypi.yml (new): at a published release, a build job and an upload job with PyPI trusted publishing (environment pypi, id-token: write, pypa/gh-action-pypi-publish pinned by SHA). Both jobs are skipped unless the repository variable PYPI_PUBLISH is "true" (PyPI) or "testpypi" (TestPyPI). - .tools/python/pypi_package.py (new): stops early with the reason (no package in src/, version not X.Y.Z or already on the index, another workflow that publishes, distribution.pip naming another package, placeholder left), writes the release version into setup.py of the checkout, builds sdist + wheel and checks them. - setup.py: dependencies from requirements.txt (lines setuptools would refuse are dropped, so the installer never breaks), README.md as description, the repository as Source link; MANIFEST.in ships requirements.txt. - bump_version.py: the release also writes its version into setup.py. - Migration 0.1.24 -> 0.1.25: surgical, idempotent, SKIPPED for customised files, keeps line endings; no publish_pypi.yml for a repository that already publishes to PyPI (NucleiSky). - Docs: PyPI section in external_code_upload.md, migration_0_1_25.md, troubleshooting entry. - Tests: test_template_sync_0_1_25.py (builds the sdist and wheel of an initialised copy, no upload); tests/_v0_1_25.py keeps the older frozen-resource tests comparing; tests.yml installs build + setuptools. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Template 0.1.25, based on
mainafter #45 (0.1.24). It makes the preprint's claim true: GitHub Actions publish an app's own Python code (src/<package>) as a package on PyPI. Publishing is off until the developer turns it on.Design (chosen 2026-11-12)
.github/workflows/publish_pypi.ymlin every app. It runs at a published release, the same moment as the installers, with the same version (the tag without thev).if: vars.PYPI_PUBLISH == 'true' || vars.PYPI_PUBLISH == 'testpypi'. An app that does not use it sees skipped jobs, never failures.pypi,id-token: writeon the upload job only, andpypa/gh-action-pypi-publishv1.14.3 pinned by SHA. There is no secret and no token fallback.PYPI_PUBLISH=testpypiuploads to TestPyPI, as a trial.buildhas no special permission and storesdist/.publishdownloads it and uploads it.publish_pypi.yml,pypi). On GitHub, add the repository variablePYPI_PUBLISH=true. To turn it off, delete the variable..tools/python/pypi_package.py(new)Stops before building, with an
::errorannotation, when:src/;/pypi/<name>/<version>/json);distribution.pipnames a different package thansetup.py;setup.pystill has a placeholder;pyproject.tomlgives another version than the release.Then:
setup.pyof the checkout, which is not committed. The tag points to the commit before the "Update version" commit, so the committed version can't be trusted.python -m build. The wheel is built from the sdist.Package fixes
setup.pyrequirements.txt, one per line, as written. Options, paths and URLs are left out, with a warning in the build log.pip install --no-deps --no-build-isolationwithrequirements.txtnext tosetup.py. Any line setuptools would refuse is dropped, so the newsetup.pycannot break an installation.README.mdis the long description. ASourceURL is added fromGITHUB_REPOSITORY.PYTHON_PROJ_NAME,VERSION_NUMBERandPYTHON_VERSIONkeep their exact lines, for the form,bump_constructor.py,merge_requirements.pyandcheck_placeholders.py. The form's template test (LC_TEMPLATE_DIR) passes against this branch.MANIFEST.in(new) addsrequirements.txtto the sdist.bump_version.pynow writes the release version intosetup.py.Name:
distribution.pipis a check, not a renameThe package is published under
setup.py'sname=. Whendistribution.pipis present, it must be the same name (PEP 503 normalised); otherwise the build stops. I chose this over silently renaming, because a rename could publish the wrong code under the App Centre's name. For example, VLab4Mic'ssrc/only holdsvlab4mic_lc_tools.Migration 0.1.24 → 0.1.25
Surgical and idempotent. It never deletes, never raises for a customised file, and keeps line endings.
publish_pypi.ymlandpypi_package.py. A file of the repository's own with the same name is kept (SKIPPED).publish_to_pypi.yml) does not getpublish_pypi.yml. The log says that nothing is needed to keep publishing its way, and how to switch.setup.pyis replaced only when it is exactly the earlier template file, with any name, version and Python series. In that caseMANIFEST.inis added too. A hand-editedsetup.pyis left alone.bump_version.py: three known blocks are changed.Checks
tests/test_template_sync_0_1_25.py(42 tests). They build the sdist and wheel of an initialised copy (--no-isolation --offline) and check the metadata: dependencies, description, version, Source URL. They also cover:setup.pypasses to setuptools withoutrequirements.txtorREADME.md(the installer case), and withoutpackaging;pypi_package.py;bump_version.pywith CRLF;sync.pychain.tests/_v0_1_25.pykeeps the frozen-resource tests comparing, and the pins table oftest_template_sync_0_1_18.pyhas the two new actions.tests.ymlnow installsbuild==1.3.0 setuptools==80.9.0, so the build test runs in CI instead of skipping.setup.py: CellTracksColab, LabChronicle, Demo, Stardist2D.setup.py: Playground, GuessTheCondition, CellPatcher, VLab4Mic, NucleiSky.pypi_package.py, except CellPatcher, which stops as intended: itssetup.pyneedsmorphobricks-*from GitHub URLs.Not verified
vars.*conditions and the trusted-publisher exchange were checked only by reading them. The first real check is an app release withPYPI_PUBLISH=testpypi..tools/docs/external_code_upload.mdare not updated by the migration (they are rendered per app). The migration log links to the template's docs.Decided
requirements.txtexactly as written. For Stardist2D, that istensorflow==2.20.0,numpy==2.1.3,jl-hidecode==0.0.1and so on: the versions the app is tested with. The cost is that the package can be hard to install next to other packages. Loosening==to>=was considered and not chosen.🤖 Generated with Claude Code