Remove conda distribution support

This commit is contained in:
Victor Kuznetsov
2026-08-08 21:18:10 -07:00
parent 9b656513f1
commit f9beef365f
5 changed files with 2 additions and 210 deletions
+2 -31
View File
@@ -1,9 +1,8 @@
name: Distribute on release
# Fans a published GitHub Release out to the channels that need a nudge.
# PyPI is handled by publish.yml; the conda-forge channel is handled by its
# autotick bot. This workflow updates the repository recipe and event-drives
# the three channels that would otherwise be manual:
# PyPI is handled by publish.yml. This workflow event-drives the three channels
# that would otherwise be manual:
# - Homebrew tap: rewrite the formula's url + sha256 to the new sdist.
# - HF Space: factory-rebuild so it reinstalls the latest sdist from PyPI.
# - ComfyUI: sync, test, and publish the node against the exact library release.
@@ -89,34 +88,6 @@ jobs:
git commit -m "remove-ai-watermarks $VERSION" || { echo "Formula already current"; exit 0; }
git push
conda-recipe:
needs: resolve
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout main
uses: actions/checkout@v7
with:
ref: main
- name: Bump the repository conda recipe
env:
SHA: ${{ needs.resolve.outputs.sha }}
VERSION: ${{ needs.resolve.outputs.version }}
run: |
set -euo pipefail
python scripts/sync_conda_recipe.py --version "$VERSION" --sha256 "$SHA"
git diff --check
if git diff --quiet -- packaging/conda/recipe.yaml; then
echo "Conda recipe already current"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add packaging/conda/recipe.yaml
git commit -m "Sync conda recipe with v$VERSION"
git push
hf-space:
needs: resolve
runs-on: ubuntu-latest
-15
View File
@@ -43,25 +43,14 @@ PyPI API token from the repository.
waits for the matching source distribution to appear on PyPI, then:
- updates the Homebrew tap formula URL and SHA-256;
- updates the repository conda recipe version and source-distribution SHA-256;
- triggers a factory rebuild of the Hugging Face Space.
The workflow can also be started manually with an optional version input.
Conda-forge updates are outside this workflow.
If a distribution job fails because a repository or Hugging Face credential is
invalid, rotate the corresponding GitHub secret and rerun the failed job. A
manual Homebrew formula update is the fallback when its automation is blocked.
The conda job uses the published artifact rather than a locally built archive
as the hash source and commits the resulting recipe change to `main`. Runtime
dependency mapping remains review-controlled: keep it aligned with the default
metadata dependencies in `pyproject.toml`, do not copy optional pixel extras
into the default recipe, and document any conda-forge package that is
unavailable and must be omitted.
The optional `video` extra carries PyAV with Python-version-specific bounds; it
does not belong in the default metadata-focused conda recipe.
## Source distribution boundary
The wheel includes the package under `src/`.
@@ -83,8 +72,6 @@ The package uses hatchling through the unpinned `hatchling` build requirement in
## Other channels
The repository includes a conda recipe under `packaging/conda/recipe.yaml`.
The ComfyUI nodes are maintained and versioned in their own repository. After
the matching source distribution appears on PyPI, `distribute.yml` dispatches
that repository's sync workflow with the exact library version and waits for it
@@ -112,7 +99,5 @@ After publication, verify:
- the package version matches the tag;
- the Homebrew formula points to the new source distribution;
- the distribution workflow completed successfully;
- the repository's conda recipe matches the published version and source
distribution;
- the ComfyUI Registry node requires the new library version;
- a clean install can run `remove-ai-watermarks --version`.
-62
View File
@@ -1,62 +0,0 @@
schema_version: 1
context:
version: "0.26.1"
python_min: "3.10"
package:
name: remove-ai-watermarks
version: ${{ version }}
source:
url: https://pypi.org/packages/source/r/remove-ai-watermarks/remove_ai_watermarks-${{ version }}.tar.gz
sha256: 4557e10fa2856451df896d87e0158d0232ea5cad90e2c4f2d1359766ac18873b
build:
noarch: python
number: 0
script: python -m pip install . -vv --no-deps --no-build-isolation
requirements:
host:
- python ${{ python_min }}.*
- pip
- hatchling
run:
- python >=${{ python_min }}
- pillow >=10.0.0
- piexif >=1.1.3
- click >=8.0.0
- python-dotenv >=1.0.0
# c2pa-python is a default PyPI dependency but is not packaged on conda-forge.
# The guarded import falls back to the built-in C2PA byte scanner when it is
# absent. Add it here once a c2pa-python feedstock exists.
tests:
- python:
imports:
- remove_ai_watermarks
- remove_ai_watermarks.identify
- remove_ai_watermarks.metadata
python_version: ${{ python_min }}.*
# pip_check defaults to true; disable it explicitly. `pip check` fails on
# the conda-forge piexif `py_2` build, whose stale 2019 metadata pip reads
# as "piexif 1.1.3 is not supported on this platform" -- even though the
# package installs, imports, and works. The imports test verifies loading.
pip_check: false
about:
homepage: https://github.com/wiltodelta/remove-ai-watermarks
summary: Inspect and strip AI provenance metadata from media
description: |
Inspect and strip AI-provenance metadata (C2PA, EXIF, IPTC, and PNG text
chunks) from images and supported video containers. Optional pip extras add
visible watermark removal, video processing, SynthID diffusion removal, and
additional invisible-watermark detectors.
license: Apache-2.0
license_file: LICENSE
repository: https://github.com/wiltodelta/remove-ai-watermarks
extra:
recipe-maintainers:
- wiltodelta
-53
View File
@@ -1,53 +0,0 @@
"""Update the conda recipe to a published package version and sdist hash."""
from __future__ import annotations
import argparse
import logging
import re
from pathlib import Path
log = logging.getLogger(__name__)
_VERSION_LINE = re.compile(r'^ version: "[^"]+"$', re.MULTILINE)
_SHA256_LINE = re.compile(r"^ sha256: [0-9a-f]{64}$", re.MULTILINE)
def update_recipe(text: str, *, version: str, sha256: str) -> str:
"""Return recipe text with exactly one context version and source hash updated."""
if not version or any(char.isspace() for char in version):
raise ValueError("version must be a non-empty value without whitespace")
if re.fullmatch(r"[0-9a-f]{64}", sha256) is None:
raise ValueError("sha256 must be 64 lowercase hexadecimal characters")
updated, version_count = _VERSION_LINE.subn(f' version: "{version}"', text)
updated, sha_count = _SHA256_LINE.subn(f" sha256: {sha256}", updated)
if version_count != 1 or sha_count != 1:
raise ValueError(
"expected exactly one context version and one source sha256 "
f"(found version={version_count}, sha256={sha_count})"
)
return updated
def main() -> None:
"""Update the selected recipe in place."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--version", required=True)
parser.add_argument("--sha256", required=True)
parser.add_argument(
"--recipe",
type=Path,
default=Path("packaging/conda/recipe.yaml"),
)
args = parser.parse_args()
original = args.recipe.read_text()
updated = update_recipe(original, version=args.version, sha256=args.sha256)
args.recipe.write_text(updated)
log.info("Updated %s to version %s", args.recipe, args.version)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
main()
-49
View File
@@ -1,49 +0,0 @@
"""Tests for the release-time conda recipe synchronizer."""
from __future__ import annotations
from pathlib import Path
import pytest
from scripts.sync_conda_recipe import update_recipe
_OLD_SHA = "a" * 64
_NEW_SHA = "b" * 64
_RECIPE = f"""\
schema_version: 1
context:
version: "1.2.3"
source:
sha256: {_OLD_SHA}
"""
def test_update_recipe_replaces_version_and_hash() -> None:
updated = update_recipe(_RECIPE, version="2.0.0", sha256=_NEW_SHA)
assert ' version: "2.0.0"' in updated
assert f" sha256: {_NEW_SHA}" in updated
assert "1.2.3" not in updated
assert _OLD_SHA not in updated
@pytest.mark.parametrize("sha256", ["", "A" * 64, "f" * 63, "not-a-hash"])
def test_update_recipe_rejects_invalid_hash(sha256: str) -> None:
with pytest.raises(ValueError, match="sha256"):
update_recipe(_RECIPE, version="2.0.0", sha256=sha256)
def test_update_recipe_rejects_ambiguous_recipe() -> None:
duplicate = _RECIPE + f"\nsource_two:\n sha256: {_OLD_SHA}\n"
with pytest.raises(ValueError, match="exactly one"):
update_recipe(duplicate, version="2.0.0", sha256=_NEW_SHA)
def test_repository_recipe_stays_metadata_only() -> None:
recipe = Path("packaging/conda/recipe.yaml").read_text(encoding="utf-8")
assert " - av >=16\n" not in recipe