Compare commits

..
2 Commits
Author SHA1 Message Date
Victor Kuznetsov 09a4b20ac6 Release 0.37.1 with the Chroma tokenizer dependency 2026-09-02 10:03:03 -07:00
Victor KuznetsovandClaude Opus 5 9bb4e9cd54 Tell a pending ComfyUI upload apart from one that never happened
The check read `latest_version` on the node object, which reports only the
ACTIVE version. The registry reviews an upload before activating it, so for the
length of that review a correctly published version looks exactly like a release
that was never synced -- and 0.37.0 hit precisely that: the node repo bumped to
0.1.37 requiring >=0.37.0, the registry accepted it as
NodeVersionStatusPending, and the check called it missing.

Read the versions list instead and branch on the status: active passes, pending
passes while saying so in the summary rather than claiming the node is live, and
anything else fails. A missing version still fails hard, which is the case this
check exists for. Sort the matches by createdAt rather than trusting the
endpoint's current newest-first ordering.

Verified against the live registry in all three states: 0.37.0 pending, 0.36.0
active, 0.99.0 absent and erroring.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 18:00:53 -07:00
7 changed files with 77 additions and 32 deletions

No files matched your search

+43 -19
View File
@@ -106,28 +106,52 @@ jobs:
run: |
set -euo pipefail
echo "Checking the ComfyUI Registry node..."
# The registry publishes the node as JSON, including the dependency the
# published version declares. Read that directly instead of proxying
# through the sync workflow's run status: the node repo runs
# sync-library.yml on a daily schedule too, so a green nightly run says
# nothing about whether THIS release was ever synced.
node=$(curl -fsSL "https://api.comfy.org/nodes/remove-ai-watermarks")
echo "$node" | VERSION="$VERSION" python3 -c "
# Read the published versions directly. Do NOT proxy through the sync
# workflow's run status: the node repo runs that workflow on a daily
# schedule too, so a green nightly says nothing about THIS release.
#
# And read the versions list, not `latest_version` on the node object.
# The registry reviews a new upload before activating it, and
# `latest_version` reports only the active one -- so for the whole
# review window a correctly published version looks identical to one
# that was never uploaded. Those are opposite situations: one is a
# queue nobody here controls, the other is a broken release.
versions=$(curl -fsSL "https://api.comfy.org/nodes/remove-ai-watermarks/versions")
status=$(printf '%s' "$versions" | VERSION="$VERSION" python3 -c "
import json, os, sys
version = os.environ['VERSION']
latest = json.load(sys.stdin).get('latest_version') or {}
deps = latest.get('dependencies') or []
floor = f'>={version}'
if latest.get('status') != 'NodeVersionStatusActive':
sys.exit(f\"::error::ComfyUI node {latest.get('version')} is {latest.get('status')}, not active\")
if not any(floor in d for d in deps):
sys.exit(
f'::error::ComfyUI node {latest.get(\"version\")} does not require '
f'{version}; it declares {deps}'
)
print(f' ComfyUI: node {latest[\"version\"]} requires {version}')
"
echo "- ComfyUI: registry node requires $VERSION" >> "$GITHUB_STEP_SUMMARY"
matches = [
v for v in json.load(sys.stdin)
if any(floor in d for d in (v.get('dependencies') or []))
]
if not matches:
sys.exit(f'::error::No ComfyUI node version requires {version}; the sync never published one')
# Sort rather than trusting the response order: the endpoint happens to
# return newest-first today and nothing documents that it must.
newest = max(matches, key=lambda v: v.get('createdAt') or '')
print(f\"{newest.get('version')} {newest.get('status')}\")
")
node_version=${status%% *}
node_status=${status##* }
echo " ComfyUI: node $node_version requires $VERSION (status $node_status)"
case "$node_status" in
NodeVersionStatusActive)
echo "- ComfyUI: registry node $node_version requires $VERSION and is live" >> "$GITHUB_STEP_SUMMARY"
;;
NodeVersionStatusPending)
# Published and waiting on the registry's own review. Say exactly
# that in the summary rather than reporting it as live, and do not
# fail: an external moderation queue going red on every release
# trains everyone to ignore this workflow.
echo "::notice::ComfyUI node $node_version is published and awaiting registry review."
echo "- ComfyUI: registry node $node_version requires $VERSION, awaiting registry activation" >> "$GITHUB_STEP_SUMMARY"
;;
*)
echo "::error::ComfyUI node $node_version is $node_status"
exit 1
;;
esac
# -- HF Space -------------------------------------------------------------------
- name: Verify the HF Space serves this release
+6 -4
View File
@@ -77,9 +77,11 @@ uv tool install --force "remove-ai-watermarks[qwen-zimage]"
Both remaining profiles run a Z-Image face stage on the DiffSynth runtime, so
both need this extra. The third profile, `chroma-zimage`, uses diffusers'
ChromaImg2ImgPipeline instead of DiffSynth for its global stage but inherits
the same face stage, so it needs this extra too. It includes the `diffusion`
dependencies; `diffusion` on its own covers the torch and diffusers imports
but not the face stage, so it is not enough to run a removal.
the same face stage, so it needs this extra too. The extra also installs
SentencePiece for Chroma1-HD's binary `spiece.model`; without that backend,
Transformers cannot construct the Chroma tokenizer. It includes the `diffusion`
dependencies; `diffusion` on its own covers the torch and diffusers imports but
not the face stage or Chroma tokenizer, so it is not enough to run a removal.
**An NVIDIA GPU is required.** `qwen-zimage`, `sdxl-zimage` and `chroma-zimage`
are CUDA-only, and
@@ -117,7 +119,7 @@ application actually uses:
| `diffusion` | Torch and Diffusers runtime; video SynthID regeneration | `pixels`, Torch, Diffusers | Yes |
| `migan` | MI-GAN ONNX fill backend | `visible`, ONNX Runtime | Model download, no Torch |
| `lama` | big-LaMa ONNX fill backend | `visible`, ONNX Runtime | Model download, no Torch |
| `qwen-zimage` | Invisible image-watermark removal, all CUDA-only profiles | `diffusion`, DiffSynth | Yes |
| `qwen-zimage` | Invisible image-watermark removal, all CUDA-only profiles | `diffusion`, DiffSynth, SentencePiece | Yes |
| `text-restoration` | Opt-in verified profile-VAE glyph restoration | `qwen-zimage`, `lama` | Yes |
| `text-draft` | Draft OCR proposals for operator verification | PaddleOCR, PaddlePaddle | Model download, no Torch |
| `all` | Every production feature available on the active Python | All compatible rows above | Yes |
+14 -6
View File
@@ -137,12 +137,20 @@ After publication, the `verify-release.yml` workflow runs automatically once
`distribute.yml` completes and checks every surface below. It can also be
dispatched manually with a specific version. Every check fails the job; none of
them warns and continues, because a surface nobody can confirm is not a verified
surface. The ComfyUI check reads the published node from the registry's JSON API
(`https://api.comfy.org/nodes/remove-ai-watermarks`) and asserts the active
version declares this library release. It deliberately does NOT proxy through
the sync workflow's run status: the node repository also runs that workflow on a
daily schedule, so a green nightly run says nothing about whether this release
was synced. The full checklist for completeness:
surface, with one deliberate exception noted below. The ComfyUI check reads the
registry's JSON API and asserts that some published node version declares this
library release. It deliberately does NOT proxy through the sync workflow's run
status: the node repository also runs that workflow on a daily schedule, so a
green nightly run says nothing about whether this release was synced.
Read `/nodes/remove-ai-watermarks/versions`, not `latest_version` on the node
object. The registry reviews an upload before activating it, and
`latest_version` reports only the active version, so during that review window a
correctly published version is indistinguishable from one that was never
uploaded. A new version therefore sits in `NodeVersionStatusPending` for a while
after a release; the check reports that state in the run summary and passes,
because it is the registry's queue rather than an incomplete release. A missing
version, or any other status, fails. The full checklist for completeness:
- both wheel and source distribution exist on PyPI;
- the package version matches the tag;
+4 -1
View File
@@ -4,7 +4,7 @@
# on it, including the ComfyUI node package. The console script below carries
# the same weight, since users have it on PATH.
name = "remove-ai-watermarks"
version = "0.37.0"
version = "0.37.1"
description = "AI watermark remover for visible, invisible, and provenance marks in images and video"
readme = "README.md"
requires-python = ">=3.11,<3.15"
@@ -118,6 +118,9 @@ diffusion = [
qwen-zimage = [
"remove-ai-watermarks[diffusion]",
"diffsynth>=2.0.17,<3",
# Chroma1-HD ships a binary spiece.model. Without the matching backend,
# Transformers falls through to tiktoken, which cannot parse that file.
"sentencepiece>=0.2.2,<0.3",
"torchvision>=0.20.0",
]
# Opt-in verified-text reconstruction over qwen-zimage. LaMa removes the changed
+1 -1
View File
@@ -33,7 +33,7 @@ _os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error")
_warnings.filterwarnings("ignore", message=r".*ImageProcessorFast.*")
__version__ = "0.37.0"
__version__ = "0.37.1"
__all__ = [
"BatchSummary",
+4
View File
@@ -81,6 +81,10 @@ def test_classify_extra_owns_the_photo_heads() -> None:
assert {"torch", "transformers", "huggingface-hub", "tokenizers"} <= names
def test_qwen_zimage_extra_owns_every_profile_tokenizer_backend() -> None:
assert "sentencepiece" in _requirement_names("qwen-zimage")
def test_production_all_does_not_include_development_tools():
assert {
"pyright",
Generated
+5 -1
View File
@@ -3311,7 +3311,7 @@ wheels = [
[[package]]
name = "remove-ai-watermarks"
version = "0.37.0"
version = "0.37.1"
source = { editable = "." }
dependencies = [
{ name = "c2pa-python" },
@@ -3336,6 +3336,7 @@ all = [
{ name = "pillow-heif" },
{ name = "pywavelets" },
{ name = "safetensors" },
{ name = "sentencepiece" },
{ name = "tokenizers" },
{ name = "torch" },
{ name = "torchvision" },
@@ -3412,6 +3413,7 @@ qwen-zimage = [
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
{ name = "opencv-python-headless" },
{ name = "safetensors" },
{ name = "sentencepiece" },
{ name = "tokenizers" },
{ name = "torch" },
{ name = "torchvision" },
@@ -3432,6 +3434,7 @@ text-restoration = [
{ name = "onnxruntime" },
{ name = "opencv-python-headless" },
{ name = "safetensors" },
{ name = "sentencepiece" },
{ name = "tokenizers" },
{ name = "torch" },
{ name = "torchvision" },
@@ -3500,6 +3503,7 @@ requires-dist = [
{ name = "remove-ai-watermarks", extras = ["visible"], marker = "extra == 'video'" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.4.0" },
{ name = "safetensors", marker = "extra == 'diffusion'" },
{ name = "sentencepiece", marker = "extra == 'qwen-zimage'", specifier = ">=0.2.2,<0.3" },
{ name = "tokenizers", marker = "extra == 'classify'", specifier = ">=0.22,<0.23" },
{ name = "tokenizers", marker = "extra == 'diffusion'", specifier = ">=0.22,<0.23" },
{ name = "torch", marker = "extra == 'classify'", specifier = ">=2.0.0" },