Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.8 KiB
Release and distribution
Relocated verbatim from
CLAUDE.mdon 2026-06-11 to keep the always-loaded context small. Long single-line entries were reformatted into paragraphs; no content was changed or summarized.
Release flow and every distribution channel (PyPI, Homebrew tap, conda-forge,
ComfyUI Registry, HF Space), plus sdist/build-backend history. The CI summary
stays in CLAUDE.md; read this before cutting a release.
publish.yml stays release-only and now verifies the release tag matches the pyproject.toml version (fails the build on a mismatch) before building, then uploads via uv publish (PyPI trusted publishing over OIDC, no token — replaced the pypa/gh-action-pypi-publish action so the upload no longer depends on that action's bundled twine accepting the Metadata-Version; the id-token: write permission + pypi environment + workflow filename are unchanged, so PyPI's trusted-publisher entry still matches).
Release flow: bump the version in pyproject.toml + src/remove_ai_watermarks/__init__.py + uv.lock (the project's own [[package]] entry — find it with grep -n 'name = "remove-ai-watermarks"' uv.lock, the version = line right below it, ~line 2246), commit chore(release): vX.Y.Z, git tag -a vX.Y.Z -m vX.Y.Z (annotated — git tag without -m errors here), push main + the tag, then gh release create vX.Y.Z — PyPI publish triggers on the GitHub Release published event, NOT on the tag push, so the tag alone does not publish.
After the PyPI sdist is live, bump the Homebrew formula in the separate public tap repo wiltodelta/homebrew-tap (Formula/remove-ai-watermarks.rb): update url to the new sdist URL (from https://pypi.org/pypi/remove-ai-watermarks/<version>/json, the sdist entry's url) and sha256 to its hash, commit + push there — otherwise brew install wiltodelta/tap/remove-ai-watermarks keeps installing the old version. The formula is a core-only venv that pip-installs the sdist (no vendored resources, so pip pulls the binary numpy/opencv wheels per platform at install time); only those two lines change per release.
This is now AUTOMATED: the main repo's .github/workflows/distribute.yml fires on the GitHub Release published event, waits for the sdist to appear on PyPI (poll loop, the Release event races publish.yml's upload), rewrites the formula's url+sha256, and pushes to the tap using the HOMEBREW_TAP_TOKEN repo secret (a fine-grained PAT with Contents:write on homebrew-tap). The SAME workflow also factory-rebuilds the HF Space (HfApi.restart_space(..., factory_reboot=True), HF_TOKEN secret) so the Space reinstalls the new sdist (it pins remove-ai-watermarks>=... and only re-resolves on a rebuild). The manual Homebrew steps above are the fallback / what the workflow automates — a normal release needs no Homebrew or HF action.
Other distribution channels: (1) conda-forge — recipe source of truth committed at packaging/conda/recipe.yaml (v1 recipe.yaml, noarch core-only: pillow/piexif/numpy/py-opencv/click/python-dotenv); the initial submission is conda-forge/staged-recipes PR #33674 (went green only after pip_check: false in the python test — rattler-build's pip check defaults to ON and fails on the ancient conda-forge piexif py_2 build's stale metadata with "piexif 1.1.3 is not supported on this platform", though the package installs/imports/works; keep it disabled). Once that merges and the remove-ai-watermarks-feedstock exists, the regro-cf-autotick-bot auto-opens a version-bump PR on the feedstock when each new PyPI sdist is detected — just review + merge it (hand-edit only if run-deps changed; keep packaging/conda/recipe.yaml in sync as the reference copy). (2) ComfyUI Registry — the node package is a SEPARATE repo wiltodelta/ComfyUI-remove-ai-watermarks with its OWN pyproject.toml version (independent of the library version). Publish a new node version by bumping that version in the node repo's pyproject.toml and pushing to main — the node repo's .github/workflows/publish.yml (Comfy-Org/publish-node-action@main, triggered on a push that touches pyproject.toml, secret COMFY_REGISTRY_TOKEN) auto-publishes it; comfy node publish --token <registry-PAT> is the manual/local fallback. It is NOT auto-published on a library release (the node has its own version), so only bump it when the node code or its remove-ai-watermarks>= dependency floor changes.
Sdist must exclude data/ ([tool.hatch.build.targets.sdist] exclude = ["/data"]): hatchling's default sdist bundles all VCS-tracked files, so the committed data/ test corpora (the multi-hundred-MB synthid_corpus images + the visible-mark captures) pushed the 0.8.0 sdist past PyPI's per-project file-size limit (400 "File too large") — the wheel uploaded but the sdist was rejected, so 0.8.0 shipped wheel-only and 0.8.1 carried the fix. The wheel only ships src/ (via [tool.hatch.build.targets.wheel] packages), so it was never affected.
A failed PyPI upload of one artifact still leaves the other live and you cannot re-upload the same version — fix the build and cut the next patch.
Build backend is unpinned hatchling ([build-system] requires) since 2026-06-09. History: it was pinned <1.31 because hatchling 1.30.0 made Metadata-Version 2.5 (PEP 794) the default and the twine bundled in pypa/gh-action-pypi-publish@release/v1 rejected it ("'2.5' is not a valid Metadata-Version"), which failed the v0.8.3 PyPI upload on 2026-06-01; hatchling 1.30.1 reverted the default to 2.4. After the workflow moved to uv publish (whose uploader accepts 2.5) the pin was belt-and-suspenders only, and once v0.9.0 + v0.10.0 both published wheel+sdist through that path (verified on PyPI) it was dropped. If a future hatchling flips the default to 2.5 again and some consumer chokes, re-pin with a dated comment.