mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-06 14:08:36 +02:00
Automate conda recipe synchronization
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
name: Distribute on release
|
||||
|
||||
# Fans a published GitHub Release out to the channels that need a nudge.
|
||||
# PyPI is handled by publish.yml; conda-forge is handled by its autotick bot.
|
||||
# This workflow event-drives the two channels that would otherwise be manual:
|
||||
# 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 two 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.
|
||||
# Both wait for the freshly published sdist to appear on PyPI first, since the
|
||||
@@ -87,6 +88,34 @@ 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
|
||||
|
||||
@@ -43,6 +43,7 @@ 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.
|
||||
@@ -52,12 +53,11 @@ 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.
|
||||
|
||||
After PyPI publication, update `packaging/conda/recipe.yaml` to the released
|
||||
version and the SHA-256 of the published source distribution. Use the published
|
||||
artifact rather than a locally built archive as the hash source. Keep the
|
||||
recipe's runtime dependencies aligned with the core dependencies in
|
||||
`pyproject.toml`; document any conda-forge package that is unavailable and must
|
||||
be omitted.
|
||||
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 core
|
||||
dependencies in `pyproject.toml`, and document any conda-forge package that is
|
||||
unavailable and must be omitted.
|
||||
|
||||
## Source distribution boundary
|
||||
|
||||
@@ -77,8 +77,12 @@ The package uses hatchling through the unpinned `hatchling` build requirement in
|
||||
|
||||
The repository includes a conda recipe under `packaging/conda/recipe.yaml`.
|
||||
|
||||
The ComfyUI nodes are maintained and versioned separately from this package.
|
||||
A library release does not by itself publish a new ComfyUI node version.
|
||||
The ComfyUI nodes are maintained and versioned in their own repository. Its
|
||||
scheduled workflow detects a newer PyPI library release, updates the dependency
|
||||
floor, runs compatibility tests, bumps the node patch version, and publishes to
|
||||
the ComfyUI Registry only when those tests pass. The library release event does
|
||||
not publish the node package directly, so the registry update follows on that
|
||||
schedule rather than inside `distribute.yml`.
|
||||
|
||||
## Release verification
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
"""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()
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Tests for the release-time conda recipe synchronizer."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
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)
|
||||
Reference in New Issue
Block a user