chore(ci): harden Registry publish workflow permissions + compatibility audit
Publish workflow (.github/workflows/publish.yml) already existed and was already push-to-main-only / workflow_dispatch with no pull_request_target. Hardened further: - permissions: issues: write -> contents: read (the action never uses GITHUB_TOKEN at all; checkout only needs read access, nothing else) - drop the unused 'master' branch trigger (repo only has main) - add a concurrency group so overlapping publish runs can't race - document, inline, why pull_request_target must never be added here and how the REGISTRY_ACCESS_TOKEN secret is handled (never echoed/logged, passed only as the action's declared input) Also adds docs/registry-compatibility-audit.md: a code-only audit (no README, no template comments) answering the four compatibility questions for pyproject.toml (min Python version, OS support, GPU/CUDA usage, ComfyUI min-version API), each with file:line evidence, plus a proposed pyproject.toml block for manual review. pyproject.toml itself is NOT touched by this PR. No secret is configured or requested by this change, and nothing is published. Node runtime behavior is unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,28 +1,50 @@
|
||||
name: Publish to Comfy registry
|
||||
|
||||
# Publishes this node to the ComfyUI Registry using Comfy-Org/publish-node-action.
|
||||
#
|
||||
# Security notes:
|
||||
# - Trigger is intentionally push-to-main (paths: pyproject.toml) or manual
|
||||
# workflow_dispatch ONLY. Never pull_request_target: that trigger runs with
|
||||
# this repo's secrets against a PR head from a fork, which would hand
|
||||
# REGISTRY_ACCESS_TOKEN to any third-party PR author. Do not add it.
|
||||
# - REGISTRY_ACCESS_TOKEN is passed only as the action's declared input; it is
|
||||
# never echoed, printed, or exported to a shell step. GitHub Actions masks
|
||||
# the literal secret value in all logs regardless.
|
||||
# - GITHUB_TOKEN permissions are minimal: read-only, and only what
|
||||
# actions/checkout needs to fetch the repo. The publish step itself does not
|
||||
# use GITHUB_TOKEN at all — it authenticates to the Registry solely with
|
||||
# REGISTRY_ACCESS_TOKEN.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths:
|
||||
- "pyproject.toml"
|
||||
workflow_dispatch: {}
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: publish-to-registry
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
publish-node:
|
||||
name: Publish Custom Node to registry
|
||||
runs-on: ubuntu-latest
|
||||
# Belt-and-suspenders: even if this workflow file were ever present on a
|
||||
# fork, publishing only ever runs in the canonical repo.
|
||||
if: ${{ github.repository_owner == 'LuqP2' }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Publish Custom Node
|
||||
uses: Comfy-Org/publish-node-action@v1
|
||||
with:
|
||||
## Add your own personal access token to your Github Repository secrets and reference it here.
|
||||
# Configure this once in Settings -> Secrets and variables -> Actions.
|
||||
# Never place the token value directly in this file.
|
||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
# Auditoria de compatibilidade — `pyproject.toml` / ComfyUI Registry
|
||||
|
||||
Investigação do **código** (não do README, não do template comentado) para decidir
|
||||
o que é seguro declarar em `classifiers`, `requires-python`, `Icon`/`Banner` e
|
||||
`requires-comfyui`, conforme
|
||||
[docs.comfy.org/registry/specifications](https://docs.comfy.org/registry/specifications).
|
||||
|
||||
Este documento é só o relatório. O `pyproject.toml` **não foi alterado** —
|
||||
a proposta está no bloco de código no final, para aplicação manual.
|
||||
|
||||
---
|
||||
|
||||
## 1. Menor versão de Python que o código realmente aceita
|
||||
|
||||
**Resposta: Python ≥ 3.10.**
|
||||
|
||||
Evidência: [`metahub_input_node.py:69`](../metahub_input_node.py#L69)
|
||||
|
||||
```python
|
||||
def mask_to_tensor(mask_path: Path | None, width: int, height: int) -> torch.Tensor:
|
||||
```
|
||||
|
||||
O arquivo não tem `from __future__ import annotations` (confirmado por busca em
|
||||
todo o repo), então essa anotação é avaliada **em tempo de definição da função**,
|
||||
não como string. `Path | None` usando o operador `|` em tipos reais é
|
||||
[PEP 604](https://peps.python.org/pep-0604/), introduzido no Python 3.10 — em
|
||||
3.9 ou anterior isso levanta `TypeError: unsupported operand type(s) for |`
|
||||
na hora de importar o módulo, ou seja, o node inteiro falha ao carregar.
|
||||
|
||||
Constraint mais fraca, mesma direção: [`metahub_input_node.py:46`](../metahub_input_node.py#L46)
|
||||
usa `-> tuple[dict, str]` (PEP 585, genéricos nativos em minúsculo), que exige
|
||||
apenas 3.9+ — já coberto pelo piso de 3.10 acima.
|
||||
|
||||
Não há nenhum outro uso de sintaxe mais nova (sem `match/case`, sem `:=`,
|
||||
sem `except*`, sem `str.removeprefix/removesuffix`, sem `tomllib`) em nenhum
|
||||
outro arquivo do pacote.
|
||||
|
||||
---
|
||||
|
||||
## 2. Windows / Linux / macOS — funciona igual? Existe caminho platform-dependent?
|
||||
|
||||
**Resposta: sim, funciona igual nos três. Há só um ponto assimétrico, não-crítico.**
|
||||
|
||||
- Todo I/O de arquivo passa por `pathlib.Path` (não há concatenação manual de
|
||||
string com `\` ou `/`, não há `os.path.join` platform-fráigl misturado com
|
||||
literais). Ex.: [`metahub_input_node.py:20-43`](../metahub_input_node.py#L20-L43),
|
||||
[`metadata_utils_impl.py`](../metadata_utils_impl.py) inteiro.
|
||||
- Busca em todo o repo por `sys.platform`, `os.name`, `platform.system`,
|
||||
`ctypes`, `winreg`, `msvcrt`, `os.startfile`, chamadas POSIX-only
|
||||
(`os.geteuid`, `fcntl`, `pwd`/`grp`) e `subprocess(..., shell=True)`:
|
||||
**zero ocorrências** em qualquer arquivo `.py` do pacote.
|
||||
- Único ponto assimétrico: [`video_metadata_utils.py:42-51`](../video_metadata_utils.py#L42-L51),
|
||||
a lista de fallback de localização do `ffmpeg` (`find_ffmpeg_binary`) só tem
|
||||
caminhos hard-coded do Windows (`C:/ffmpeg/bin/ffmpeg.exe` etc.) depois de já
|
||||
ter tentado `FFMPEG_PATH` e `shutil.which('ffmpeg')` — que são
|
||||
cross-platform e cobrem o caso comum em Linux/macOS (ffmpeg instalado via
|
||||
apt/brew fica no PATH). Se não encontrar em nenhum dos dois, o código levanta
|
||||
um `RuntimeError` com instrução clara ([`video_metadata_utils.py:317-322`](../video_metadata_utils.py#L317-L322))
|
||||
— não trava, não corrompe nada, só falha de forma explícita e igual nas
|
||||
três plataformas quando o ffmpeg realmente não está instalado.
|
||||
|
||||
Não há evidência de comportamento *diferente* ou incorreto em nenhuma
|
||||
plataforma — só uma heurística de descoberta de binário um pouco mais fraca
|
||||
fora do Windows. Isso sustenta o classifier `OS Independent`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Toca em GPU/CUDA/torch, ou é I/O + hashing puro?
|
||||
|
||||
**Resposta: usa `torch`, mas nunca como dependência de GPU/CUDA. Nenhuma chamada
|
||||
de device placement em lugar nenhum do repo (busca por `.cuda(`, `.to(device`,
|
||||
`.half()`, `torch.device` — zero ocorrências).**
|
||||
|
||||
Dois usos, ambos read-only / opcionais:
|
||||
|
||||
1. **Telemetria de GPU, defensiva** — [`metadata_utils_impl.py:196-256`](../metadata_utils_impl.py#L196-L256)
|
||||
`collect_gpu_metrics()`: faz `import torch` dentro de um `try/except
|
||||
ImportError`, depois só *lê* `torch.cuda.is_available()`,
|
||||
`torch.cuda.get_device_name(0)`, `torch.cuda.max_memory_allocated(0)`, e
|
||||
equivalente para `torch.backends.mps`. Se CUDA/MPS não existir, cai para
|
||||
`"CPU (CUDA not available)"` sem erro. Nunca aloca nada na GPU, nunca move
|
||||
um tensor para lá — só introspecciona o que já está rodando.
|
||||
2. **Criação de tensor CPU para o contrato de tipos do ComfyUI** —
|
||||
[`metahub_input_node.py:61-76`](../metahub_input_node.py#L61-L76)
|
||||
(`image_to_tensor`, `mask_to_tensor`): usa `torch.from_numpy(array)[None,]`
|
||||
para produzir os tipos `IMAGE`/`MASK` que o ComfyUI espera. Isso roda em
|
||||
CPU; não há `.to()`/`.cuda()` em seguida.
|
||||
|
||||
Fora isso, o node é I/O (leitura/escrita de PNG/JPEG/WebP/GLB, subprocess para
|
||||
ffmpeg) + hashing (SHA256 de modelos/LoRAs) + string/JSON building. Ele
|
||||
funciona sem problema em instalações ComfyUI CPU-only.
|
||||
|
||||
**Conclusão prática:** declarar `Environment :: GPU :: NVIDIA CUDA` (ou
|
||||
ROCm/Metal) seria uma claim falsa — o node não exige GPU nenhuma para
|
||||
funcionar, e isso só polui o filtro de busca de quem procura nodes que
|
||||
realmente *precisam* de CUDA. Nenhum classifier de acelerador deve ser
|
||||
adicionado.
|
||||
|
||||
---
|
||||
|
||||
## 4. Existe API do ComfyUI para estabelecer uma versão mínima verificável?
|
||||
|
||||
**Resposta: não, o código não usa nenhuma.**
|
||||
|
||||
Os dois únicos pontos de contato com o runtime do ComfyUI além dos hidden
|
||||
inputs padrão (`PROMPT`, `EXTRA_PNGINFO`, `UNIQUE_ID` — parte estável do
|
||||
contrato de custom nodes desde as primeiras versões) são:
|
||||
|
||||
- `import folder_paths` — [`metadata_utils_impl.py:59`](../metadata_utils_impl.py#L59)
|
||||
e [`metadata_utils_impl.py:1091`](../metadata_utils_impl.py#L1091), ambos
|
||||
dentro de `try/except`, usados só para localizar pastas de modelos / diretório
|
||||
de output. Se falhar, cai para caminhos relativos default.
|
||||
- `import comfy` / `comfy.__version__` — [`metadata_utils_impl.py:297-299`](../metadata_utils_impl.py#L297-L299),
|
||||
também dentro de `try/except`, usado só para *reportar* a versão do ComfyUI
|
||||
nos metadados salvos — nunca comparado contra um piso mínimo.
|
||||
|
||||
Nenhum desses é usado como *gate* (não há `if comfy_version < X: raise` nem
|
||||
equivalente), e nenhuma API nova/versionada é chamada — nada que dependa de
|
||||
uma versão específica do ComfyUI para existir.
|
||||
|
||||
**Conclusão prática:** não há piso de versão do ComfyUI provável a partir do
|
||||
código. Preencher `requires-comfyui` seria um chute. Se o mantenedor já
|
||||
validou manualmente uma versão mínima em teste real, isso é um fato externo
|
||||
ao código-fonte (não deriva de auditoria) e pode ser adicionado depois, com
|
||||
essa evidência explícita.
|
||||
|
||||
---
|
||||
|
||||
## Princípio aplicado
|
||||
|
||||
Cada campo abaixo só foi preenchido onde havia evidência de código
|
||||
(arquivo:linha) sustentando um limite real. Onde não havia prova, o campo foi
|
||||
**omitido**, não preenchido "por via das dúvidas".
|
||||
|
||||
## `pyproject.toml` proposto (não aplicado — para revisão)
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "imagemetahub-comfyui-save"
|
||||
description = "Official companion node for Image MetaHub. Saves A1111/Civitai-compatible parameters plus extended Image MetaHub metadata (workflow JSON, SHA256 model hashes, VRAM peak/total, GPU device, generation time, and steps/sec) for reproducibility and benchmarking."
|
||||
version = "1.1.9"
|
||||
license = {file = "LICENSE"}
|
||||
requires-python = ">=3.10"
|
||||
classifiers = [
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
|
||||
dependencies = []
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/LuqP2/ImageMetaHub-ComfyUI-Save"
|
||||
# Used by Comfy Registry https://registry.comfy.org
|
||||
Documentation = "https://github.com/LuqP2/ImageMetaHub-ComfyUI-Save/wiki"
|
||||
"Bug Tracker" = "https://github.com/LuqP2/ImageMetaHub-ComfyUI-Save/issues"
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "image-metahub"
|
||||
DisplayName = "ImageMetaHub-ComfyUI-Save"
|
||||
includes = []
|
||||
# Icon and Banner omitted: no square icon (<=400x400) or 21:9 banner asset
|
||||
# exists yet. Add them (with real URLs) when they do — an empty string is
|
||||
# not the same as omitting the field.
|
||||
# "requires-comfyui" omitted: no code path checks or requires a specific
|
||||
# ComfyUI version (see docs/registry-compatibility-audit.md, Q4). Add it only
|
||||
# once a floor version has actually been tested, not as a guess.
|
||||
```
|
||||
|
||||
**Diffs em relação ao atual, e por quê:**
|
||||
|
||||
| Campo | Atual | Proposto | Motivo |
|
||||
|---|---|---|---|
|
||||
| `requires-python` | ausente | `">=3.10"` | `metahub_input_node.py:69` usa `Path \| None` sem `from __future__ import annotations` |
|
||||
| `classifiers` | bloco inteiro comentado | só `OS Independent` | nenhum caminho platform-gated encontrado; nenhum classifier de GPU porque o node não exige GPU |
|
||||
| `Icon` | `""` | omitido | string vazia ≠ campo omitido; não existe asset ainda |
|
||||
| `requires-comfyui` | comentado | continua omitido | nenhuma API de versão mínima é usada no código |
|
||||
Reference in New Issue
Block a user