1 Commits

Author SHA1 Message Date
Hopping Mad Games d0f5416624 Require Manager clone gate before Registry publish 2026-08-26 10:52:07 -06:00
7 changed files with 152 additions and 31 deletions
-25
View File
@@ -1,25 +0,0 @@
name: Verify Manager release install
on:
push:
tags:
- "v*"
jobs:
verify-manager-install:
runs-on: ubuntu-latest
steps:
- name: Check out release tag
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Clone and validate the declared public repository
run: python tools/verify_manager_install.py --source .
+8 -3
View File
@@ -2,11 +2,16 @@
All notable changes to this project are documented here.
## 1.0.3 - 2026-08-26
- Route Registry publication through the Manager installation gate and keep
the API key out of process arguments and environment variables.
## 1.0.2 - 2026-08-26
- Add a credential-free ComfyUI Manager clone gate and tag-triggered workflow
so Registry releases fail when their declared public repository is missing,
inaccessible, stale, or inconsistent with the release commit and version.
- Add a credential-free ComfyUI Manager clone gate so Registry releases fail
when their declared public repository is missing, inaccessible, stale, or
inconsistent with the release commit and version.
## 1.0.1 - 2026-08-26
+13 -2
View File
@@ -57,8 +57,19 @@ python3 tools/verify_manager_install.py --source .
The gate reads the public repository from `pyproject.toml`, disables ambient
Git credentials, performs Manager's recursive clone into a clean
`custom_nodes` directory, verifies the release commit and version tag, and
compiles every shipped Python and JavaScript source from the clone. The Gitea
tag workflow runs the same command before a release is considered installable.
compiles every shipped Python and JavaScript source from the clone.
Registry publishing is supported only through the gated release command:
```bash
python3 -m tools.publish_registry_release \
--changelog "Describe this release"
```
It runs the Manager installation gate and Registry validation before invoking
publication. The API key is read interactively and sent to `comfy` over
standard input; it is never placed in process arguments or environment
variables. Direct `comfy node publish` calls bypass the required release gate.
## License
+4 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "etk-ltxv-timeline-editor"
version = "1.0.2"
version = "1.0.3"
description = "A visual timeline editor for LTXV image guides, prompts, strengths, and frame positions in ComfyUI."
license = { file = "LICENSE" }
requires-python = ">=3.10"
@@ -20,3 +20,6 @@ Documentation = "https://git.hoppingmadgames.com/hmg-comfy/ComfyUI_ETK_LTXV_Time
PublisherId = "hmg"
DisplayName = "ETK LTXV Timeline Image Editor"
requires-comfyui = ">=0.33.0"
[tool.ruff.lint.per-file-ignores]
"tools/__init__.py" = ["N999"]
+58
View File
@@ -0,0 +1,58 @@
from pathlib import Path
import pytest
from tools import publish_registry_release
def test_publish_stops_before_registry_when_manager_gate_fails(monkeypatch, tmp_path):
calls = []
def fail_gate(source):
raise RuntimeError("public clone failed")
monkeypatch.setattr(publish_registry_release, "verify_manager_install", fail_gate)
monkeypatch.setattr(
publish_registry_release.subprocess,
"run",
lambda *args, **kwargs: calls.append((args, kwargs)),
)
with pytest.raises(RuntimeError, match="public clone failed"):
publish_registry_release.publish_release(
tmp_path,
comfy="comfy",
token="private-token",
changelog="release",
)
assert calls == []
def test_publish_keeps_token_out_of_arguments_and_environment(monkeypatch, tmp_path):
calls = []
monkeypatch.setattr(
publish_registry_release,
"verify_manager_install",
lambda source: ("1.0.3", "https://example.test/owner/repo", "abc123"),
)
def record_run(command, **kwargs):
calls.append((command, kwargs))
monkeypatch.setattr(publish_registry_release.subprocess, "run", record_run)
publish_registry_release.publish_release(
Path(tmp_path),
comfy="comfy",
token="private-token",
changelog="release notes",
)
assert [command for command, _ in calls] == [
["comfy", "node", "validate"],
["comfy", "node", "publish"],
]
publish_kwargs = calls[1][1]
assert publish_kwargs["input"] == "private-token\n"
assert "private-token" not in repr(calls[1][0])
assert "private-token" not in repr(publish_kwargs["env"])
assert publish_kwargs["env"]["COMFY_NODE_CHANGELOG"] == "release notes"
+1
View File
@@ -0,0 +1 @@
"""Release tooling for the standalone timeline node pack."""
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
"""Publish only after the public Manager installation path is verified."""
from __future__ import annotations
import argparse
import getpass
import os
import shutil
import subprocess
import sys
from pathlib import Path
from tools.verify_manager_install import verify_manager_install
def publish_release(
source: Path,
*,
comfy: str,
token: str,
changelog: str,
) -> None:
verify_manager_install(source)
subprocess.run([comfy, "node", "validate"], cwd=source, check=True)
env = dict(os.environ)
env["COMFY_NODE_CHANGELOG"] = changelog
subprocess.run(
[comfy, "node", "publish"],
cwd=source,
env=env,
input=f"{token}\n",
text=True,
check=True,
)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
"--source",
type=Path,
default=Path(__file__).resolve().parents[1],
help="tagged release repository",
)
parser.add_argument("--changelog", required=True)
parser.add_argument("--comfy", default=shutil.which("comfy") or "comfy")
args = parser.parse_args()
token = getpass.getpass("Comfy Registry API key: ").strip()
if not token:
print("REGISTRY RELEASE FAILED: API key is empty", file=sys.stderr)
return 1
try:
publish_release(
args.source.resolve(),
comfy=args.comfy,
token=token,
changelog=args.changelog,
)
except (OSError, subprocess.CalledProcessError, RuntimeError) as exc:
print(f"REGISTRY RELEASE FAILED: {exc}", file=sys.stderr)
return 1
print("REGISTRY RELEASE PASSED THE MANAGER INSTALL GATE AND WAS UPLOADED")
return 0
if __name__ == "__main__":
raise SystemExit(main())