Require Manager clone gate before Registry publish
This commit is contained in:
@@ -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
@@ -2,11 +2,16 @@
|
|||||||
|
|
||||||
All notable changes to this project are documented here.
|
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
|
## 1.0.2 - 2026-08-26
|
||||||
|
|
||||||
- Add a credential-free ComfyUI Manager clone gate and tag-triggered workflow
|
- Add a credential-free ComfyUI Manager clone gate so Registry releases fail
|
||||||
so Registry releases fail when their declared public repository is missing,
|
when their declared public repository is missing, inaccessible, stale, or
|
||||||
inaccessible, stale, or inconsistent with the release commit and version.
|
inconsistent with the release commit and version.
|
||||||
|
|
||||||
## 1.0.1 - 2026-08-26
|
## 1.0.1 - 2026-08-26
|
||||||
|
|
||||||
|
|||||||
@@ -57,8 +57,19 @@ python3 tools/verify_manager_install.py --source .
|
|||||||
The gate reads the public repository from `pyproject.toml`, disables ambient
|
The gate reads the public repository from `pyproject.toml`, disables ambient
|
||||||
Git credentials, performs Manager's recursive clone into a clean
|
Git credentials, performs Manager's recursive clone into a clean
|
||||||
`custom_nodes` directory, verifies the release commit and version tag, and
|
`custom_nodes` directory, verifies the release commit and version tag, and
|
||||||
compiles every shipped Python and JavaScript source from the clone. The Gitea
|
compiles every shipped Python and JavaScript source from the clone.
|
||||||
tag workflow runs the same command before a release is considered installable.
|
|
||||||
|
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
|
## License
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "etk-ltxv-timeline-editor"
|
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."
|
description = "A visual timeline editor for LTXV image guides, prompts, strengths, and frame positions in ComfyUI."
|
||||||
license = { file = "LICENSE" }
|
license = { file = "LICENSE" }
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
@@ -20,3 +20,6 @@ Documentation = "https://git.hoppingmadgames.com/hmg-comfy/ComfyUI_ETK_LTXV_Time
|
|||||||
PublisherId = "hmg"
|
PublisherId = "hmg"
|
||||||
DisplayName = "ETK LTXV Timeline Image Editor"
|
DisplayName = "ETK LTXV Timeline Image Editor"
|
||||||
requires-comfyui = ">=0.33.0"
|
requires-comfyui = ">=0.33.0"
|
||||||
|
|
||||||
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
"tools/__init__.py" = ["N999"]
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Release tooling for the standalone timeline node pack."""
|
||||||
Executable
+68
@@ -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())
|
||||||
Reference in New Issue
Block a user