mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-10-14 21:40:47 +02:00
Repair image and metadata publication contracts, detection routing, research artifact validation, release verification, and the published agent skill. Refresh the dependency lock and reconcile documentation with verified behavior. Add regression coverage and record the completed audit and corrected VideoSeal measurements. Preserve the existing Accelerate and Lightning versions. Validation: Ruff and Pyright pass; 2358 tests pass with 3 skipped. The maintenance gate exits 2 on the documented Accelerate and Lightning security findings.
240 lines
8.9 KiB
YAML
240 lines
8.9 KiB
YAML
name: Distribute on release
|
|
|
|
# Fans a published GitHub Release out to the channels that need a nudge.
|
|
# PyPI is handled by publish.yml. This workflow event-drives the four channels
|
|
# that would otherwise be manual:
|
|
# - Homebrew tap: rewrite the formula's url + sha256 to the new sdist.
|
|
# - HF Space: factory-rebuild the version already pinned in the Space repo.
|
|
# - ComfyUI: sync, test, and publish the node against the exact library release.
|
|
# - ClawHub: publish the agent skill when its own version moved.
|
|
# All wait for the freshly published sdist to appear on PyPI first, since the
|
|
# Release event fires in parallel with publish.yml's upload.
|
|
|
|
on:
|
|
release:
|
|
types: [published]
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: "Version to distribute (no leading v), e.g. 0.10.3. Blank = latest on PyPI."
|
|
required: false
|
|
|
|
jobs:
|
|
resolve:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.v.outputs.version }}
|
|
url: ${{ steps.sdist.outputs.url }}
|
|
sha: ${{ steps.sdist.outputs.sha }}
|
|
steps:
|
|
- name: Resolve target version
|
|
id: v
|
|
run: |
|
|
set -euo pipefail
|
|
if [ "${{ github.event_name }}" = "release" ]; then
|
|
v="${GITHUB_REF_NAME#v}"
|
|
elif [ -n "${{ github.event.inputs.version }}" ]; then
|
|
v="${{ github.event.inputs.version }}"
|
|
else
|
|
v=$(curl -fsSL https://pypi.org/pypi/remove-ai-watermarks/json \
|
|
| python3 -c "import sys,json;print(json.load(sys.stdin)['info']['version'])")
|
|
fi
|
|
echo "version=$v" >> "$GITHUB_OUTPUT"
|
|
echo "Target version: $v"
|
|
|
|
- name: Record distributed release
|
|
env:
|
|
VERSION: ${{ steps.v.outputs.version }}
|
|
run: |
|
|
python3 - <<'PYTHON'
|
|
import json, os
|
|
from pathlib import Path
|
|
Path("release-identity.json").write_text(json.dumps({
|
|
"version": os.environ["VERSION"],
|
|
"run_id": os.environ["GITHUB_RUN_ID"],
|
|
"run_attempt": os.environ["GITHUB_RUN_ATTEMPT"],
|
|
}), encoding="utf-8")
|
|
PYTHON
|
|
|
|
- name: Upload distributed release identity
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: distributed-release-${{ github.run_attempt }}
|
|
path: release-identity.json
|
|
if-no-files-found: error
|
|
|
|
- name: Wait for the sdist on PyPI, capture url + sha256
|
|
id: sdist
|
|
run: |
|
|
set -euo pipefail
|
|
v="${{ steps.v.outputs.version }}"
|
|
for i in $(seq 1 30); do
|
|
if rel=$(curl -fsSL "https://pypi.org/pypi/remove-ai-watermarks/$v/json" 2>/dev/null); then
|
|
url=$(echo "$rel" | python3 -c "import sys,json;d=json.load(sys.stdin);print(next((u['url'] for u in d['urls'] if u['packagetype']=='sdist'),''))")
|
|
sha=$(echo "$rel" | python3 -c "import sys,json;d=json.load(sys.stdin);print(next((u['digests']['sha256'] for u in d['urls'] if u['packagetype']=='sdist'),''))")
|
|
if [ -n "$url" ] && [ -n "$sha" ]; then
|
|
echo "url=$url" >> "$GITHUB_OUTPUT"
|
|
echo "sha=$sha" >> "$GITHUB_OUTPUT"
|
|
echo "Found sdist for $v"
|
|
exit 0
|
|
fi
|
|
fi
|
|
echo "sdist for $v not on PyPI yet (attempt $i), waiting..."
|
|
sleep 20
|
|
done
|
|
echo "::error::sdist for $v did not appear on PyPI in time"
|
|
exit 1
|
|
|
|
homebrew:
|
|
needs: resolve
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout the tap
|
|
uses: actions/checkout@v7
|
|
with:
|
|
repository: wiltodelta/homebrew-tap
|
|
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
|
- name: Bump the formula
|
|
env:
|
|
URL: ${{ needs.resolve.outputs.url }}
|
|
SHA: ${{ needs.resolve.outputs.sha }}
|
|
VERSION: ${{ needs.resolve.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
F=Formula/remove-ai-watermarks.rb
|
|
sed -i -E "s|url \"[^\"]*\"|url \"$URL\"|" "$F"
|
|
sed -i -E "s|sha256 \"[^\"]*\"|sha256 \"$SHA\"|" "$F"
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
git add "$F"
|
|
git commit -m "remove-ai-watermarks $VERSION" || { echo "Formula already current"; exit 0; }
|
|
git push
|
|
|
|
hf-space:
|
|
needs: resolve
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Factory-rebuild the HuggingFace Space
|
|
env:
|
|
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
|
run: |
|
|
python3 -m pip install --quiet huggingface_hub
|
|
python3 - <<'PY'
|
|
import os
|
|
from huggingface_hub import HfApi
|
|
HfApi(token=os.environ["HF_TOKEN"]).restart_space(
|
|
"wiltodelta/remove-ai-watermarks", factory_reboot=True
|
|
)
|
|
print("HF Space factory rebuild triggered")
|
|
PY
|
|
|
|
clawhub:
|
|
needs: resolve
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Publish the agent skill when its version moved
|
|
env:
|
|
CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "${CLAWHUB_TOKEN:-}" ]; then
|
|
echo "CLAWHUB_TOKEN secret is not set; skipping ClawHub publish."
|
|
exit 0
|
|
fi
|
|
npm install --silent --no-fund --no-audit clawhub
|
|
skill_version=$(python3 - <<'PY'
|
|
import re
|
|
text = open("skills/remove-ai-watermarks/SKILL.md", encoding="utf-8").read()
|
|
m = re.search(r"(?m)^ version:\s*[\"']?([^\"'\s]+)", text)
|
|
assert m, "skill version missing"
|
|
print(m.group(1))
|
|
PY
|
|
)
|
|
./node_modules/.bin/clawhub login --token "$CLAWHUB_TOKEN" >/dev/null
|
|
published=$(./node_modules/.bin/clawhub inspect remove-ai-watermarks --file SKILL.md 2>/dev/null \
|
|
| python3 -c 'import re,sys; t=sys.stdin.read(); m=re.search(r"(?m)^ version:\s*[\"'"'"']?([^\"'"'"'\s]+)", t); print(m.group(1) if m else "")' || true)
|
|
if [ "$published" = "$skill_version" ]; then
|
|
echo "ClawHub already at $skill_version; nothing to publish."
|
|
exit 0
|
|
fi
|
|
echo "ClawHub at ${published:-<none>}; publishing skill $skill_version."
|
|
./node_modules/.bin/clawhub skill publish skills/remove-ai-watermarks \
|
|
--slug remove-ai-watermarks \
|
|
--name "Remove AI Watermarks" \
|
|
--version "$skill_version" \
|
|
--changelog "Agent skill sync for the ${{ github.event.release.name || github.ref_name }} release."
|
|
echo "Submitted; ClawHub runs security scans before the listing goes public."
|
|
|
|
comfyui:
|
|
needs: resolve
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Sync and publish the ComfyUI nodes
|
|
env:
|
|
GH_TOKEN: ${{ secrets.COMFYUI_RELEASE_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
repo=wiltodelta/ComfyUI-remove-ai-watermarks
|
|
workflow=sync-library.yml
|
|
dispatched_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
|
|
gh workflow run "$workflow" \
|
|
--repo "$repo" \
|
|
--ref main \
|
|
--raw-field library_version="${{ needs.resolve.outputs.version }}"
|
|
|
|
run_id=""
|
|
for attempt in $(seq 1 30); do
|
|
run_id=$(
|
|
gh run list \
|
|
--repo "$repo" \
|
|
--workflow "$workflow" \
|
|
--event workflow_dispatch \
|
|
--limit 10 \
|
|
--json databaseId,createdAt \
|
|
| python3 -c '
|
|
import json
|
|
import sys
|
|
|
|
dispatched_at = sys.argv[1]
|
|
runs = json.load(sys.stdin)
|
|
print(next(
|
|
(run["databaseId"] for run in runs if run["createdAt"] >= dispatched_at),
|
|
"",
|
|
))
|
|
' "$dispatched_at"
|
|
)
|
|
if [ -n "$run_id" ]; then
|
|
break
|
|
fi
|
|
echo "ComfyUI sync run not visible yet (attempt $attempt), waiting..."
|
|
sleep 2
|
|
done
|
|
|
|
if [ -z "$run_id" ]; then
|
|
echo "::error::The dispatched ComfyUI sync run did not appear"
|
|
exit 1
|
|
fi
|
|
|
|
for attempt in $(seq 1 180); do
|
|
read -r status conclusion <<< "$(
|
|
gh run view "$run_id" \
|
|
--repo "$repo" \
|
|
--json status,conclusion \
|
|
--jq '[.status, (.conclusion // "")] | @tsv'
|
|
)"
|
|
if [ "$status" = "completed" ]; then
|
|
if [ "$conclusion" = "success" ]; then
|
|
echo "ComfyUI sync run $run_id succeeded"
|
|
exit 0
|
|
fi
|
|
echo "::error::ComfyUI sync run $run_id completed with $conclusion"
|
|
exit 1
|
|
fi
|
|
echo "ComfyUI sync run $run_id is $status (attempt $attempt), waiting..."
|
|
sleep 5
|
|
done
|
|
echo "::error::ComfyUI sync run $run_id did not finish in time"
|
|
exit 1
|