-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
43 changed files
with
3,181 additions
and
445 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,4 +31,4 @@ yarn-error.log* | |
# Sentry Config File | ||
.sentryclirc | ||
|
||
package-lock.json | ||
package-lock.json |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,64 @@ | ||
# Setup a server ready to accept component requests | ||
# Use ubuntu user, not root | ||
# Use a venv for ComfyUI | ||
# Builder stage for SSH key | ||
FROM pytorch/pytorch:latest as start | ||
|
||
FROM pytorch/pytorch:latest AS start | ||
RUN apt update && apt-get install -y \ | ||
git git-lfs rsync nginx wget curl nano ffmpeg libsm6 libxext6 \ | ||
cron sudo ssh zstd jq build-essential cmake ninja-build \ | ||
gcc g++ openssh-client aria2 \ | ||
&& apt-get clean \ | ||
&& rm -rf /var/lib/apt/lists/* | ||
|
||
# ARG DEBIAN_FRONTEND=noninteractive PIP_PREFER_BINARY=1 | ||
|
||
RUN apt update && apt-get install -y git rsync nginx wget nano ffmpeg libsm6 libxext6 cron sudo ssh && apt-get clean | ||
|
||
ARG GITACCESSKEY | ||
FROM start AS middle | ||
|
||
RUN useradd -m -d /home/ubuntu -s /bin/bash ubuntu | ||
RUN usermod -aG sudo ubuntu | ||
RUN mkdir -p /home/ubuntu/.ssh && touch /home/ubuntu/.ssh/authorized_keys | ||
RUN echo ${GITACCESSKEY} >> /home/ubuntu/.ssh/authorized_keys | ||
RUN chown -R ubuntu:ubuntu /home/ubuntu/.ssh | ||
ENV ROOT=/workspace | ||
ENV PATH="${ROOT}/.local/bin:${PATH}" | ||
ENV CONFIG_DIR=${ROOT}/config | ||
ENV COMFY_DIR=${ROOT}/ComfyUI | ||
|
||
RUN mkdir -p /etc/ssh/sshd_config.d | ||
RUN touch /etc/ssh/sshd_config.d/ubuntu.conf | ||
RUN echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config.d/ubuntu.conf | ||
RUN echo "PasswordAuthentication no" >> /etc/ssh/sshd_config.d/ubuntu.conf | ||
WORKDIR ${ROOT} | ||
|
||
RUN service ssh restart | ||
RUN sudo cp /etc/sudoers /etc/sudoers.bak | ||
RUN echo 'ubuntu ALL=(ALL:ALL) NOPASSWD: ALL' >> /etc/sudoers | ||
RUN git clone https://github.com/comfyanonymous/ComfyUI.git ${COMFY_DIR} && \ | ||
cd ${COMFY_DIR} && \ | ||
pip install --upgrade pip && \ | ||
pip install -r requirements.txt && \ | ||
pip install huggingface_hub[hf_transfer] && \ | ||
pip install tqdm | ||
|
||
FROM start AS middle | ||
FROM middle AS end | ||
|
||
RUN su - ubuntu | ||
COPY config/ ${CONFIG_DIR}/ | ||
|
||
RUN mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo ${GITACCESSKEY} >> ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519 | ||
# Debug: List config files | ||
RUN find ${CONFIG_DIR} -name "config.json" | ||
|
||
ENV ROOT=/comfyui-launcher | ||
# Set default config type | ||
ARG CONFIG_TYPE | ||
|
||
RUN eval "$(ssh-agent -s)" && ssh-add /root/.ssh/id_ed25519 && ssh-keyscan github.com > ~/.ssh/githubKey && ssh-keygen -lf ~/.ssh/githubKey && cat ~/.ssh/githubKey >> ~/.ssh/known_hosts | ||
# Copy type-specific config files | ||
COPY config/${CONFIG_TYPE} ${CONFIG_DIR}/ | ||
|
||
WORKDIR ${ROOT} | ||
# Copy init.d script | ||
COPY scripts/comfyui /etc/init.d/comfyui | ||
RUN chmod +x /etc/init.d/comfyui && \ | ||
update-rc.d comfyui defaults | ||
|
||
# COPY ./build.sh /scripts/build.sh | ||
# Copy startup script | ||
COPY scripts/start.sh /start.sh | ||
RUN chmod +x /start.sh | ||
|
||
# RUN /scripts/build.sh | ||
COPY scripts ${ROOT}/scripts | ||
|
||
COPY ./nodes.sh /nodes.sh | ||
RUN /nodes.sh | ||
RUN rm -rf ${ROOT}/scripts/start.sh && rm -rf ${ROOT}/scripts/comfyui | ||
|
||
FROM middle AS end | ||
|
||
COPY --from=scripts . /scripts/ | ||
RUN chmod +x /scripts/*.sh | ||
# RUN usermod -aG crontab ubuntu | ||
# Create cron pid directory with correct permissions | ||
RUN mkdir -p /var/run/cron \ | ||
&& touch /var/run/cron/crond.pid \ | ||
&& chmod 644 /var/run/cron/crond.pid | ||
RUN sed -i 's/touch $PIDFILE/# touch $PIDFILE/g' /etc/init.d/cron | ||
|
||
RUN mv /nodes.sh /scripts/nodes.sh | ||
RUN cd ${ROOT} && git-lfs install | ||
|
||
COPY ./models.sh /scripts/models.sh | ||
|
||
CMD ["/scripts/start.sh"] | ||
# Start services and application | ||
CMD ["/start.sh"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
#!/usr/bin/env python3 | ||
import json | ||
import os | ||
import hashlib | ||
import shutil | ||
from typing import Dict, List | ||
|
||
def read_json_config(config_path: str) -> Dict: | ||
with open(config_path, 'r') as f: | ||
return json.load(f) | ||
|
||
def generate_download_stage(model: Dict, index: int) -> str: | ||
stage_name = f"download_{index}" | ||
path = model['path'] | ||
name = model.get('name', f'model_{index}') | ||
|
||
# If path ends with /, append the name to create full path | ||
if path.endswith('/'): | ||
path_dir = path.rstrip('/') | ||
full_path = os.path.join(path_dir, name) | ||
else: | ||
full_path = path | ||
path_dir = os.path.dirname(path) | ||
|
||
# Prepend ComfyUI to the path | ||
comfy_path = os.path.join("ComfyUI", full_path) | ||
comfy_dir = os.path.dirname(comfy_path) | ||
|
||
return f"""RUN echo "Downloading {name}..." | ||
RUN mkdir -p ${{ROOT}}/{comfy_dir} | ||
RUN wget -O "${{ROOT}}/{comfy_path}" "{model['url']}" | ||
""", comfy_path | ||
|
||
def generate_node_stage(node: Dict, index: int) -> str: | ||
"""Generate a Dockerfile stage for installing a node.""" | ||
name = node.get('name', f'node_{index}') | ||
url = node.get('url', '') | ||
commit = node.get('commit', None) | ||
|
||
# Extract repo name from URL | ||
repo_name = url.split('/')[-1].replace('.git', '') | ||
|
||
# Build the clone commands only | ||
commands = [f"""FROM middle-comfy-install AS node_{index} | ||
RUN echo "Installing node: {name}..." | ||
RUN mkdir -p ${{ROOT}}/ComfyUI/custom_nodes && \\ | ||
cd ${{ROOT}}/ComfyUI/custom_nodes && \\ | ||
git clone {url}"""] | ||
|
||
if commit: | ||
commands.append(f'RUN cd ${{ROOT}}/ComfyUI/custom_nodes/{repo_name} && git checkout {commit}') | ||
|
||
return "\n".join(commands), repo_name | ||
|
||
def generate_dockerfile_content(models_config: str, nodes_config: str = None) -> str: | ||
# First, generate the startup script | ||
script_content = ["#!/bin/bash", ""] | ||
|
||
# Add model download function | ||
script_content.extend([ | ||
"check_and_download_models() {", | ||
' echo "Checking and downloading models if needed..."', | ||
"" | ||
]) | ||
|
||
# Add model download commands | ||
if models_config: | ||
models = read_json_config(models_config).get('models', []) | ||
for model in models: | ||
url = model.get('url', '') | ||
path = model.get('path', '') | ||
name = model.get('name', '') | ||
|
||
full_path = os.path.join("${ROOT}/ComfyUI", path) | ||
script_content.extend([ | ||
f' if [ ! -f "{full_path}/{name}" ]; then', | ||
f' echo "Downloading {name}..."', | ||
f' mkdir -p "{full_path}"', | ||
f' wget -q --header="Authorization: Bearer $HF_TOKEN" "{url}" -O "{full_path}/{name}"', | ||
' fi', | ||
'' | ||
]) | ||
|
||
# Add node installation function | ||
script_content.extend([ | ||
"}", | ||
"", | ||
"install_nodes() {", | ||
' cd ${ROOT}/ComfyUI/custom_nodes', | ||
"" | ||
]) | ||
|
||
# Add node installation commands | ||
if nodes_config: | ||
nodes = read_json_config(nodes_config).get('nodes', []) | ||
for node in nodes: | ||
url = node.get('url', '') | ||
name = node.get('name', '') | ||
commit = node.get('commit', None) | ||
repo_name = url.split('/')[-1].replace('.git', '') | ||
|
||
script_content.extend([ | ||
f' if [ ! -d "{repo_name}" ]; then', | ||
f' echo "Installing node: {name}"', | ||
f' git clone {url}', | ||
]) | ||
|
||
if commit: | ||
script_content.append(f' cd {repo_name} && git checkout {commit} && cd ..') | ||
|
||
script_content.extend([ | ||
f' if [ -f "{repo_name}/requirements.txt" ]; then', | ||
f' pip install -r "{repo_name}/requirements.txt"', | ||
' fi', | ||
' fi', | ||
'' | ||
]) | ||
|
||
# Add script execution | ||
script_content.extend([ | ||
"}", | ||
"", | ||
"# Run setup functions", | ||
"check_and_download_models", | ||
"install_nodes", | ||
"", | ||
"# Start ComfyUI", | ||
"cd ${ROOT}/ComfyUI", | ||
"python main.py" | ||
]) | ||
|
||
# Write the startup script to a file | ||
script_path = os.path.join(os.path.dirname(os.path.dirname(models_config)), "scripts") | ||
os.makedirs(script_path, exist_ok=True) | ||
with open(os.path.join(script_path, "start.sh"), "w") as f: | ||
f.write("\n".join(script_content)) | ||
|
||
# Generate the Dockerfile content | ||
content = [] | ||
content.append("# [GENERATED_CONTENT]\n") | ||
|
||
# Setup environment variables | ||
content.append("# Setup environment variables") | ||
content.append('ARG HF_TOKEN') | ||
content.append('ARG OPENAI_API_KEY') | ||
content.append('ENV HF_TOKEN=$HF_TOKEN') | ||
content.append('ENV OPENAI_API_KEY=$OPENAI_API_KEY') | ||
content.append('ENV ROOT=/home/ubuntu') | ||
content.append('ENV PATH="${ROOT}/.local/bin:${PATH}"\n') | ||
|
||
# Create directories for volumes | ||
content.append("# Create directories for volumes") | ||
content.append("""RUN mkdir -p ${ROOT}/ComfyUI/models && \\ | ||
mkdir -p ${ROOT}/ComfyUI/custom_nodes && \\ | ||
chown -R ubuntu:ubuntu ${ROOT}/ComfyUI""") | ||
|
||
# Copy and setup the startup script | ||
content.extend([ | ||
"", | ||
"# Copy startup script", | ||
"COPY scripts/start.sh /start.sh", | ||
"RUN chmod +x /start.sh", | ||
"", | ||
"# Set working directory", | ||
"WORKDIR ${ROOT}/ComfyUI", | ||
"", | ||
"# Start the application", | ||
'CMD ["/start.sh"]', | ||
"", | ||
"# [END_GENERATED_CONTENT]" | ||
]) | ||
|
||
return "\n".join(content) | ||
|
||
def merge_with_dockerfile(dockerfile_path: str, generated_content: str, marker: str = "# [GENERATED_CONTENT]") -> None: | ||
# Create a new Dockerfile in the current directory | ||
current_dir = os.getcwd() | ||
new_dockerfile = os.path.join(current_dir, "Dockerfile") | ||
|
||
# Copy the original Dockerfile | ||
shutil.copy2(dockerfile_path, new_dockerfile) | ||
|
||
# Read the contents of the new Dockerfile | ||
with open(new_dockerfile, 'r') as f: | ||
content = f.read() | ||
|
||
# Split content at marker and reconstruct | ||
if marker in content: | ||
parts = content.split(marker) | ||
if len(parts) >= 2: | ||
# Construct new content with marker, generated content, and final stages | ||
new_content = parts[0] + marker + "\n" + generated_content.split(marker)[1].split("# [END_GENERATED_CONTENT]")[0] + "\n\n" + "# [END_GENERATED_CONTENT]" + "\n\n" + parts[1].split("# [END_GENERATED_CONTENT]")[1] | ||
|
||
# Write back to the new Dockerfile | ||
with open(new_dockerfile, 'w') as f: | ||
f.write(new_content) | ||
|
||
print(f"Generated new Dockerfile at: {new_dockerfile}") | ||
else: | ||
print(f"Error: Marker found but content format unexpected") | ||
else: | ||
print(f"Error: Marker '{marker}' not found in Dockerfile") | ||
|
||
if __name__ == "__main__": | ||
import sys | ||
import os | ||
|
||
if len(sys.argv) < 3: | ||
print("Usage: generate_dockerfile.py <dockerfile_path> <models_config_path> [nodes_config_path]") | ||
sys.exit(1) | ||
|
||
dockerfile_path = sys.argv[1] | ||
models_config_path = sys.argv[2] | ||
nodes_config_path = sys.argv[3] if len(sys.argv) > 3 else None | ||
|
||
if not os.path.exists(dockerfile_path): | ||
print(f"Dockerfile not found: {dockerfile_path}") | ||
sys.exit(1) | ||
|
||
if not os.path.exists(models_config_path): | ||
print(f"Models config not found: {models_config_path}") | ||
sys.exit(1) | ||
|
||
if nodes_config_path and not os.path.exists(nodes_config_path): | ||
print(f"Nodes config not found: {nodes_config_path}") | ||
sys.exit(1) | ||
|
||
content = generate_dockerfile_content(models_config_path, nodes_config_path) | ||
merge_with_dockerfile(dockerfile_path, content) |
Oops, something went wrong.