Files
LuqP2-ImageMetaHub-ComfyUI-…/docs/registry-compatibility-audit.md
LuqP2andClaude Sonnet 5 e9d210ee45 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>
2026-08-07 20:39:25 -03:00

8.6 KiB

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.

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

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, 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 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, 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, 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) — 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 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 (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 e metadata_utils_impl.py:1091, 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, 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)

[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