Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish Node: Parse comfynode.toml and calls publisherNodeVersion api. #17

Merged
merged 2 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 5 additions & 21 deletions DEV_README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@

# Development Guide

This guide provides an overview of how to develop in this repository.

## General guide

1. Clone the repo, create and activate a conda env
1. Clone the repo, create and activate a conda env. Minimum Python version is 3.9.

2. Install the package to local

`pip install -e .`
`pip install -e .`

3. Test script running

`comfy --help`
`comfy --help`

4. Use pre commit hook

`pre-commit install`
`pre-commit install`

## Make changes to the code base

Expand All @@ -28,7 +27,7 @@ env and reinstalling the package (`pip install -e .`)

## Add New Command

- Register it under `comfy_cli/cmdline.py`
- Register it under `comfy_cli/cmdline.py`

If it's contains subcommand, create folder under comfy_cli/command/[new_command] and
add the following boilerplate
Expand Down Expand Up @@ -59,21 +58,6 @@ def remove(name: str):

```

## Code explainer:

- `comfy_cli/cmdline.py` is the entry point of the CLI
- `comfy_cli/command/` contains definition for some commands (e.g. `node`,
`model`, etc)
- `comfy_cli/config_manager.py` implements ConfigManager class that handles
comfy_cli configuration (config.ini) file reading and writing
- `comfy_cli/workspace_manager.py` implements WorkspaceManager class that
handles which ComfyUI workspace (path) and defines workspace comfy-lock.yaml
file that register the state fo the comfy workspace.
- `comfy_cli/env_checker.py` implements EnvChecker class that helps with python
env related variables
- `comfy_cli/tracking.py` handles opt-in anonymous telemetry data from users.


## Guide

- Use `typer` for all command args management
Expand Down
29 changes: 27 additions & 2 deletions comfy_cli/command/custom_nodes/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import uuid
from comfy_cli.config_manager import ConfigManager
from comfy_cli.workspace_manager import WorkspaceManager
from comfy_cli.registry import publish_node_version, extract_node_configuration

app = typer.Typer()
manager_app = typer.Typer()
Expand Down Expand Up @@ -141,7 +142,6 @@ def show(
str, "--mode", typer.Option(show_default=False, help="[remote|local|cache]")
] = None,
):

valid_commands = [
"installed",
"enabled",
Expand Down Expand Up @@ -183,7 +183,6 @@ def simple_show(
str, "--mode", typer.Option(show_default=False, help="[remote|local|cache]")
] = None,
):

valid_commands = [
"installed",
"enabled",
Expand Down Expand Up @@ -399,3 +398,29 @@ def fix(
raise typer.Exit(code=1)

execute_cm_cli(ctx, ["fix"] + args, channel, mode)


@app.command("publish", help="Publish node to registry")
@tracking.track_command("node")
def publish(
ctx: typer.Context,
):
"""
Publish a node with optional validation.
"""

# Perform some validation logic here
typer.echo("Validating node configuration...")
config = extract_node_configuration()

# Prompt for Personal Access Token
token = typer.prompt("Please enter your Personal Access Token", hide_input=True)

# Call API to fetch node version with the token in the body
response = publish_node_version(config, token)

if response.ok:
typer.echo("Node published successfully!")
else:
typer.echo(f"Failed to publish node: {response.text}", err=True)
raise typer.Exit(code=1)
2 changes: 2 additions & 0 deletions comfy_cli/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,5 @@ class OS(Enum):
# Referencing supported pt extension from ComfyUI
# https://github.com/comfyanonymous/ComfyUI/blob/a88b0ebc2d2f933c94e42aa689c42e836eedaf3c/folder_paths.py#L5
SUPPORTED_PT_EXTENSIONS = (".ckpt", ".pt", ".bin", ".pth", ".safetensors")

COMFY_REGISTRY_URL_ROOT = "http://localhost:8080"
7 changes: 7 additions & 0 deletions comfy_cli/registry/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .api import publish_node_version

# Import specific functions from the config_parser module
from .config_parser import extract_node_configuration
from .types import PyProjectConfig

__all__ = ["publish_node_version", "extract_node_configuration", "PyProjectConfig"]
42 changes: 42 additions & 0 deletions comfy_cli/registry/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import requests
import json
from comfy_cli import constants
from comfy_cli.registry.types import PyProjectConfig


def publish_node_version(node_config: PyProjectConfig, token: str):
"""
Publishes a new version of a node.

Args:
node_config (PyProjectConfig): The node configuration.
token (str): Personal access token for authentication.

Returns:
dict: JSON response from the API server.
"""
url = f"{constants.COMFY_REGISTRY_URL_ROOT}/publishers/{node_config.tool_comfy.publisher_id}/nodes/{node_config.project.name}/versions"
headers = {"Content-Type": "application/json"}
body = {
"personal_access_token": token,
"node": {
"id": node_config.project.name,
"description": node_config.project.description,
"name": node_config.tool_comfy.display_name,
"license": node_config.project.license,
"repository": node_config.project.urls.repository,
},
"node_version": {
"version": node_config.project.version,
"dependencies": node_config.project.dependencies,
},
}

response = requests.post(url, headers=headers, data=json.dumps(body))
# print the json of response
if response.status_code == 200:
return response.json()
else:
raise Exception(
f"Failed to publish node version: {response.status_code} {response.text}"
)
48 changes: 48 additions & 0 deletions comfy_cli/registry/config_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import os
from comfy_cli.registry.types import (
PyProjectConfig,
ProjectConfig,
URLs,
Model,
ComfyConfig,
)


def extract_node_configuration(
path: str = os.path.join(os.getcwd(), "comfynode.toml"),
) -> PyProjectConfig:
import tomlkit

with open(path, "r") as file:
data = tomlkit.load(file)

project_data = data.get("project", {})
urls_data = project_data.get("urls", {})
comfy_data = data.get("tool", {}).get("comfy", {})

project = ProjectConfig(
name=project_data.get("name", ""),
description=project_data.get("description", ""),
version=project_data.get("version", ""),
requires_python=project_data.get("requires-pyton", ""),
dependencies=project_data.get("dependencies", []),
license=project_data.get("license", ""),
urls=URLs(
homepage=urls_data.get("Homepage", ""),
documentation=urls_data.get("Documentation", ""),
repository=urls_data.get("Repository", ""),
issues=urls_data.get("Issues", ""),
),
)

comfy = ComfyConfig(
publisher_id=comfy_data.get("PublisherId", ""),
display_name=comfy_data.get("DisplayName", ""),
icon=comfy_data.get("Icon", ""),
models=[
Model(location=m["location"], model_url=m["model_url"])
for m in comfy_data.get("Models", [])
],
)

return PyProjectConfig(project=project, tool_comfy=comfy)
41 changes: 41 additions & 0 deletions comfy_cli/registry/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from dataclasses import dataclass, field
from typing import List


@dataclass
class URLs:
homepage: str = ""
documentation: str = ""
repository: str = ""
issues: str = ""


@dataclass
class Model:
location: str
model_url: str


@dataclass
class ComfyConfig:
publisher_id: str = ""
display_name: str = ""
icon: str = ""
models: List[Model] = field(default_factory=list)


@dataclass
class ProjectConfig:
name: str = ""
description: str = ""
version: str = "1.0.0"
requires_python: str = ">= 3.9"
dependencies: List[str] = field(default_factory=list)
license: str = ""
urls: URLs = URLs()


@dataclass
class PyProjectConfig:
project: ProjectConfig = ProjectConfig()
tool_comfy: ComfyConfig = ComfyConfig()
3 changes: 2 additions & 1 deletion requirement.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ GitPython
requests
pyyaml
typing-extensions
mixpanel
mixpanel
tomlkit
Loading