Skip to content

Commit d9d4248

Browse files
authored
Added: publish all files in includes field in pyproject.toml (#270)
1 parent 6d35be1 commit d9d4248

5 files changed

Lines changed: 118 additions & 25 deletions

File tree

comfy_cli/command/custom_nodes/command.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,13 @@ def publish(
765765
signed_url = response.signedUrl
766766
zip_filename = NODE_ZIP_FILENAME
767767
typer.echo("Creating zip file...")
768-
zip_files(zip_filename)
768+
769+
includes = config.tool_comfy.includes if config and config.tool_comfy else []
770+
771+
if includes:
772+
typer.echo(f"Including additional directories: {', '.join(includes)}")
773+
774+
zip_files(zip_filename, includes=includes)
769775

770776
# Upload the zip file to the signed URL
771777
typer.echo("Uploading zip file...")
@@ -917,7 +923,13 @@ def pack():
917923
raise typer.Exit(code=1)
918924

919925
zip_filename = NODE_ZIP_FILENAME
920-
zip_files(zip_filename)
926+
includes = config.tool_comfy.includes if config and config.tool_comfy else []
927+
928+
if includes:
929+
typer.echo(f"Including additional directories: {', '.join(includes)}")
930+
931+
zip_files(zip_filename, includes=includes)
932+
921933
typer.echo(f"Created zip file: {NODE_ZIP_FILENAME}")
922934
logging.info("Node has been packed successfully.")
923935

comfy_cli/file_utils.py

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,39 +88,61 @@ def download_file(url: str, local_filepath: pathlib.Path, headers: Optional[dict
8888
raise DownloadException(f"Failed to download file.\n{status_reason}")
8989

9090

91-
def zip_files(zip_filename):
91+
def zip_files(zip_filename, includes=None):
9292
"""
93-
Zip all files in the current directory that are tracked by git.
93+
Zip all files in the current directory that are tracked by git,
94+
plus any additional directories specified in includes.
9495
"""
96+
includes = includes or []
97+
included_paths = set()
98+
git_files = []
99+
95100
try:
96-
# Get list of git-tracked files using git ls-files
97101
import subprocess
98102

99103
git_files = subprocess.check_output(["git", "ls-files"], text=True).splitlines()
100-
# Zip only git-tracked files
101-
with zipfile.ZipFile(zip_filename, "w", zipfile.ZIP_DEFLATED) as zipf:
104+
except (subprocess.SubprocessError, FileNotFoundError):
105+
print("Warning: Not in a git repository or git not installed. Zipping all files.")
106+
107+
# Zip only git-tracked files
108+
with zipfile.ZipFile(zip_filename, "w", zipfile.ZIP_DEFLATED) as zipf:
109+
if git_files:
102110
for file_path in git_files:
103111
if zip_filename in file_path:
104112
continue
105113
if os.path.exists(file_path):
106114
zipf.write(file_path)
115+
included_paths.add(file_path)
107116
else:
108117
print(f"File not found. Not including in zip: {file_path}")
109-
return
110-
except (subprocess.SubprocessError, FileNotFoundError):
111-
print("Warning: Not in a git repository or git not installed. Zipping all files.")
112-
113-
with zipfile.ZipFile(zip_filename, "w", zipfile.ZIP_DEFLATED) as zipf:
114-
for root, dirs, files in os.walk("."):
115-
if ".git" in dirs:
116-
dirs.remove(".git")
117-
for file in files:
118-
file_path = os.path.join(root, file)
119-
# Skip zipping the zip file itself
120-
if zip_filename in file_path:
121-
continue
122-
relative_path = os.path.relpath(file_path, start=".")
123-
zipf.write(file_path, relative_path)
118+
else:
119+
for root, dirs, files in os.walk("."):
120+
if ".git" in dirs:
121+
dirs.remove(".git")
122+
for file in files:
123+
file_path = os.path.join(root, file)
124+
# Skip zipping the zip file itself
125+
if zip_filename in file_path:
126+
continue
127+
relative_path = os.path.relpath(file_path, start=".")
128+
zipf.write(file_path, relative_path)
129+
included_paths.add(file_path)
130+
131+
for include_dir in includes:
132+
include_dir = include_dir.lstrip("/")
133+
if not os.path.exists(include_dir):
134+
print(f"Warning: Included directory '{include_dir}' does not exist, creating empty directory")
135+
zipf.writestr(f"{include_dir}/", "")
136+
continue
137+
138+
for root, dirs, files in os.walk(include_dir):
139+
for file in files:
140+
file_path = os.path.join(root, file)
141+
if zip_filename in file_path or file_path in included_paths:
142+
continue
143+
relative_path = os.path.relpath(file_path, start=".")
144+
zipf.write(file_path, relative_path)
145+
included_paths.add(file_path)
124146

125147

126148
def upload_file_to_signed_url(signed_url: str, file_path: str):

comfy_cli/registry/config_parser.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def create_comfynode_config():
4242
comfy["PublisherId"] = ""
4343
comfy["DisplayName"] = "ComfyUI-AIT"
4444
comfy["Icon"] = ""
45+
comfy["includes"] = tomlkit.array()
4546

4647
tool.add("comfy", comfy)
4748
document.add("tool", tool)
@@ -196,6 +197,7 @@ def extract_node_configuration(
196197
display_name=comfy_data.get("DisplayName", ""),
197198
icon=comfy_data.get("Icon", ""),
198199
models=[Model(location=m["location"], model_url=m["model_url"]) for m in comfy_data.get("Models", [])],
200+
includes=comfy_data.get("includes", []),
199201
)
200202

201203
return PyProjectConfig(project=project, tool_comfy=comfy)

comfy_cli/registry/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class ComfyConfig:
5151
display_name: str = ""
5252
icon: str = ""
5353
models: List[Model] = field(default_factory=list)
54+
includes: List[str] = field(default_factory=list)
5455

5556

5657
@dataclass

tests/comfy_cli/command/nodes/test_publish.py

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,29 @@
77
runner = CliRunner()
88

99

10+
def create_mock_config(includes_list=None):
11+
if includes_list is None:
12+
includes_list = []
13+
14+
mock_pyproject_config = MagicMock()
15+
16+
mock_tool_comfy_section = MagicMock()
17+
mock_tool_comfy_section.name = "test-node"
18+
mock_tool_comfy_section.version = "0.1.0"
19+
mock_tool_comfy_section.description = "A test node."
20+
mock_tool_comfy_section.author = "Test Author"
21+
mock_tool_comfy_section.license = "MIT"
22+
mock_tool_comfy_section.tags = ["test"]
23+
mock_tool_comfy_section.repository = "http://example.com/repo"
24+
mock_tool_comfy_section.homepage = "http://example.com/home"
25+
mock_tool_comfy_section.documentation = "http://example.com/docs"
26+
mock_tool_comfy_section.includes = includes_list
27+
28+
mock_pyproject_config.tool_comfy = mock_tool_comfy_section
29+
30+
return mock_pyproject_config
31+
32+
1033
def test_publish_fails_on_security_violations():
1134
# Mock subprocess.run to simulate security violations
1235
mock_result = MagicMock()
@@ -40,7 +63,8 @@ def test_publish_continues_on_no_security_violations():
4063
patch("comfy_cli.command.custom_nodes.command.upload_file_to_signed_url") as mock_upload,
4164
):
4265
# Setup the mocks
43-
mock_extract.return_value = {"name": "test-node"}
66+
mock_extract.return_value = create_mock_config()
67+
4468
mock_prompt.return_value = "test-token"
4569
mock_publish.return_value = MagicMock(signedUrl="https://test.url")
4670

@@ -76,7 +100,8 @@ def test_publish_with_token_option():
76100
patch("comfy_cli.command.custom_nodes.command.upload_file_to_signed_url") as mock_upload,
77101
):
78102
# Setup the mocks
79-
mock_extract.return_value = {"name": "test-node"}
103+
mock_extract.return_value = create_mock_config()
104+
80105
mock_publish.return_value = MagicMock(signedUrl="https://test.url")
81106

82107
# Run the publish command with token
@@ -104,7 +129,8 @@ def test_publish_exits_on_upload_failure():
104129
patch("comfy_cli.command.custom_nodes.command.upload_file_to_signed_url") as mock_upload,
105130
):
106131
# Setup the mocks
107-
mock_extract.return_value = {"name": "test-node"}
132+
mock_extract.return_value = create_mock_config()
133+
108134
mock_publish.return_value = MagicMock(signedUrl="https://test.url")
109135
mock_upload.side_effect = Exception("Upload failed with status code: 403")
110136

@@ -117,3 +143,33 @@ def test_publish_exits_on_upload_failure():
117143
assert mock_publish.called
118144
assert mock_zip.called
119145
assert mock_upload.called
146+
147+
148+
def test_publish_with_includes_parameter():
149+
# Mock subprocess.run to simulate no violations
150+
mock_result = MagicMock()
151+
mock_result.returncode = 0
152+
mock_result.stdout = ""
153+
154+
with (
155+
patch("subprocess.run", return_value=mock_result),
156+
patch("comfy_cli.command.custom_nodes.command.extract_node_configuration") as mock_extract,
157+
patch("comfy_cli.command.custom_nodes.command.registry_api.publish_node_version") as mock_publish,
158+
patch("comfy_cli.command.custom_nodes.command.zip_files") as mock_zip,
159+
patch("comfy_cli.command.custom_nodes.command.upload_file_to_signed_url") as mock_upload,
160+
):
161+
includes = ["/js", "/dist"]
162+
163+
# Setup the mocks
164+
mock_extract.return_value = create_mock_config(includes)
165+
166+
mock_publish.return_value = MagicMock(signedUrl="https://test.url")
167+
168+
# Run the publish command with token
169+
_result = runner.invoke(app, ["publish", "--token", "test-token"])
170+
171+
# Verify the publish flow worked with provided token
172+
assert mock_extract.called
173+
assert mock_publish.called
174+
assert mock_zip.called
175+
assert mock_upload.called

0 commit comments

Comments
 (0)