From ac5fed425ac3befb80925427ff3a1eb718c9cc01 Mon Sep 17 00:00:00 2001 From: akierson Date: Tue, 10 Nov 2026 17:11:15 -0700 Subject: [PATCH] Add release tagging to the publish workflow; add changelog and release ADR The publish workflow now skips an already-tagged version and tags v on the published commit. Same setup as ComfyUI_image_manager (ADR 0001 here). Adds a test for the release configuration. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/publish_actions.yml | 27 +++++- CHANGELOG.md | 16 ++++ ...01-release-tagging-and-registry-publish.md | 18 ++++ tests/pytest.ini | 2 + tests/test_release_config.py | 90 +++++++++++++++++++ 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md create mode 100644 docs/adr/0001-release-tagging-and-registry-publish.md create mode 100644 tests/pytest.ini create mode 100644 tests/test_release_config.py diff --git a/.github/workflows/publish_actions.yml b/.github/workflows/publish_actions.yml index a8719ae..0bbc0d0 100644 --- a/.github/workflows/publish_actions.yml +++ b/.github/workflows/publish_actions.yml @@ -8,17 +8,42 @@ on: paths: - "pyproject.toml" +# contents: write lets the tag step push v (ADR 0023) +permissions: + contents: write + jobs: publish-node: name: Publish Custom Node to registry runs-on: ubuntu-latest # if this is a forked repository. Skipping the workflow. - if: github.event.repository.fork == false + if: github.event.repository.fork == false steps: - name: Check out code uses: actions/checkout@v4 + with: + fetch-tags: true + - name: Skip if this version is already released + id: guard + run: | + VERSION=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + if git rev-parse -q --verify "refs/tags/v$VERSION" >/dev/null; then + echo "Tag v$VERSION already exists; skipping publish." + echo "released=true" >> "$GITHUB_OUTPUT" + else + echo "released=false" >> "$GITHUB_OUTPUT" + fi - name: Publish Custom Node + if: steps.guard.outputs.released == 'false' uses: Comfy-Org/publish-node-action@main with: ## Add your own personal access token to your Github Repository secrets and reference it here. personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} + - name: Tag the published commit + if: steps.guard.outputs.released == 'false' + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -a "v${{ steps.guard.outputs.version }}" -m "Release v${{ steps.guard.outputs.version }}" + git push origin "v${{ steps.guard.outputs.version }}" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bf30290 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +All notable changes to this project are recorded here. Versions follow [semantic versioning](https://semver.org); see `docs/adr/0001-release-tagging-and-registry-publish.md`. + +## [0.0.2] + +Published to the Comfy Registry on 2024-10-20. + +### Added +- Text nodes: Tidy Tags and Prompt Truncate. + +### Fixed + +### Changed + +### Breaking diff --git a/docs/adr/0001-release-tagging-and-registry-publish.md b/docs/adr/0001-release-tagging-and-registry-publish.md new file mode 100644 index 0000000..c981272 --- /dev/null +++ b/docs/adr/0001-release-tagging-and-registry-publish.md @@ -0,0 +1,18 @@ +# ADR 0001: Releases publish on a version bump and are tagged by CI + +## Status +Accepted + +## Context +ComfyUI-textnodes is registered with the Comfy Registry under the publisher named in `[tool.comfy]` of `pyproject.toml`. Its releases had no git tags, so nothing linked a published version to a commit. This repo adopts the same release setup as `ComfyUI_image_manager` (see its `docs/adr/0023-release-tagging-and-registry-publish.md` for the full reasoning). + +## Decision +- `pyproject.toml` holds the only version. Pushing a version change to `main` publishes through `.github/workflows/publish_actions.yml`. +- The workflow skips publishing when tag `v` already exists, and after a successful publish it tags `v` on the published commit. An unrelated `pyproject.toml` edit therefore cannot republish a released version. +- Versions are semantic with a `v` prefix. Patch for bug-fix-only changes, minor for new features, major for breaking changes; while on `0.x` a breaking change bumps minor. +- `CHANGELOG.md` gets one section per release (Added, Fixed, Changed, Breaking). +- Versions published before this ADR were tagged afterwards: `v0.0.2` at c67e5e3. Those tags mark the commit whose `pyproject.toml` first set the version, which is the best available evidence of what was published, not a record of the exact commit the Registry received. + +## Consequences +- Pushing a `pyproject.toml` version bump publishes. Test before pushing. +- Releases are not yet proposed automatically for this repo. diff --git a/tests/pytest.ini b/tests/pytest.ini new file mode 100644 index 0000000..7b001b9 --- /dev/null +++ b/tests/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = --import-mode=importlib diff --git a/tests/test_release_config.py b/tests/test_release_config.py new file mode 100644 index 0000000..30bac8d --- /dev/null +++ b/tests/test_release_config.py @@ -0,0 +1,90 @@ +"""Release/publish configuration (ADR 0001): version source, changelog, publish workflow.""" +import re +import tomllib +from pathlib import Path + +import yaml + +ROOT = Path(__file__).parent.parent +SEMVER = re.compile(r"^\d+\.\d+\.\d+$") +WORKFLOW = next(iter(sorted((ROOT / ".github" / "workflows").glob("publish*.yml"))), ROOT / ".github" / "workflows" / "publish_action.yml") + + +def _pyproject(): + return tomllib.loads((ROOT / "pyproject.toml").read_text()) + + +def _workflow(): + return yaml.safe_load(WORKFLOW.read_text()) + + +def _triggers(wf): + # PyYAML (YAML 1.1) parses the bare key `on` as boolean True + return wf.get("on", wf.get(True)) + + +def _steps(wf): + return wf["jobs"]["publish-node"]["steps"] + + +def _step_index(steps, needle): + for i, s in enumerate(steps): + if needle in (s.get("uses", "") + s.get("run", "") + s.get("name", "")): + return i + raise AssertionError(f"no step matching {needle!r}") + + +def test_pyproject_version_is_semver(): + assert SEMVER.match(_pyproject()["project"]["version"]) + + +def test_changelog_has_a_version_section_with_standard_headings(): + text = (ROOT / "CHANGELOG.md").read_text() + assert re.search(r"^## \[\d+\.\d+\.\d+\]", text, re.M) + for heading in ("Added", "Fixed", "Changed", "Breaking"): + assert re.search(rf"^### {heading}\b", text, re.M), heading + + +def test_exactly_one_publish_workflow(): + assert len(list((ROOT / ".github" / "workflows").glob("publish*.yml"))) == 1 + + +def test_workflow_triggers(): + on = _triggers(_workflow()) + assert "workflow_dispatch" in on + push = on["push"] + assert set(push["branches"]) == {"main", "master"} + assert push["paths"] == ["pyproject.toml"] + + +def test_workflow_skips_forks(): + job = _workflow()["jobs"]["publish-node"] + assert job["if"].replace(" ", "") == "github.event.repository.fork==false" + + +def test_workflow_can_write_contents_for_tagging(): + wf = _workflow() + perms = wf.get("permissions") or wf["jobs"]["publish-node"].get("permissions") + assert perms["contents"] == "write" + + +def test_publish_step_uses_registry_secret(): + steps = _steps(_workflow()) + publish = steps[_step_index(steps, "Comfy-Org/publish-node-action")] + assert publish["with"]["personal_access_token"] == "${{ secrets.REGISTRY_ACCESS_TOKEN }}" + + +def test_tag_guard_runs_before_publish_and_gates_it(): + steps = _steps(_workflow()) + guard = _step_index(steps, "git rev-parse") + publish = _step_index(steps, "Comfy-Org/publish-node-action") + assert guard < publish + assert "steps." in steps[publish]["if"] # publish is conditional on the guard's output + + +def test_tag_step_runs_after_publish(): + steps = _steps(_workflow()) + publish = _step_index(steps, "Comfy-Org/publish-node-action") + tag = _step_index(steps, "git tag") + assert tag > publish + assert "git push origin" in steps[tag]["run"]