diff --git a/overlays/default.nix b/overlays/default.nix index c264017..12e8f42 100644 --- a/overlays/default.nix +++ b/overlays/default.nix @@ -5,6 +5,7 @@ in { inherit (packages) audiveris + comfyui example-a example-b pyzotero diff --git a/pkgs/comfyui/package.nix b/pkgs/comfyui/package.nix new file mode 100644 index 0000000..d54d4d7 --- /dev/null +++ b/pkgs/comfyui/package.nix @@ -0,0 +1,137 @@ +{ + lib, + callPackage, + cudaPackages_13, + fetchFromGitHub, + makeBinaryWrapper, + python3, + stdenvNoCC, + withManager ? false, +}: + +let + # Same as nixpkgs comfyui, but with cudaSupport=true so torch builds with + # CUDA (the nixpkgs version only sets cudaPackages, not cudaSupport, so torch + # builds CPU-only and comfyui crashes with "Torch not compiled with CUDA"). + python = python3.override { + self = python; + packageOverrides = final: prev: { + torch = prev.torch.override { + cudaSupport = true; + cudaPackages = cudaPackages_13; + }; + triton = prev.triton.override { + cudaSupport = true; + cudaPackages = cudaPackages_13; + }; + }; + }; + + appDependencies = + ps: + with ps; + [ + aiohttp + alembic + av + blake3 + comfy-aimdo + comfy-angle + comfy-kitchen + comfyui-embedded-docs + comfyui-frontend-package + comfyui-workflow-templates + einops + filelock + kornia + numpy + pillow + psutil + pydantic + pydantic-settings + pyopengl + pyyaml + requests + safetensors + scipy + sentencepiece + simpleeval + spandrel + sqlalchemy + tokenizers + torch + torchaudio + torchsde + torchvision + tqdm + transformers + yarl + ] + ++ lib.optionals withManager [ + ps.comfyui-manager + ]; + + pythonEnv = python.withPackages appDependencies; +in +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "comfyui"; + version = "0.30.1"; + + strictDeps = true; + __structuredAttrs = true; + + src = fetchFromGitHub { + owner = "Comfy-Org"; + repo = "ComfyUI"; + tag = "v${finalAttrs.version}"; + hash = "sha256-z8bBKnW2iF8WXI3Ju6Ei+8YpOWqmbUgYiErKiVBBwEk="; + }; + + nativeBuildInputs = [ makeBinaryWrapper ]; + + patches = [ ./use-writable-runtime-paths.patch ]; + + installPhase = '' + runHook preInstall + + mkdir -p $out/share/comfyui $out/bin + cp -r . $out/share/comfyui + + makeBinaryWrapper ${lib.getExe pythonEnv} $out/bin/comfyui \ + --add-flag "$out/share/comfyui/main.py" \ + --unset NIX_PYTHONPATH \ + --unset PYTHONPATH + + runHook postInstall + ''; + + doInstallCheck = true; + installCheckPhase = '' + runHook preInstallCheck + + PYTHONPATH=${pythonEnv}/${python.sitePackages} ${lib.getExe python.pkgs.pip} install --break-system-packages --dry-run --no-index -r requirements.txt + "$out"/bin/comfyui --help + export XDG_DATA_HOME="$(mktemp -d)" + "$out"/bin/comfyui --cpu --quick-test-for-ci + + runHook postInstallCheck + ''; + + passthru = { + inherit python pythonEnv; + } + // lib.optionalAttrs (!withManager) { + tests.withManager = callPackage ./package.nix { + withManager = true; + }; + }; + + meta = { + description = "Modular diffusion model GUI, API, and backend with graph and nodes interface (CUDA-enabled)"; + homepage = "https://github.com/Comfy-Org/ComfyUI"; + changelog = "https://github.com/Comfy-Org/ComfyUI/releases/tag/v${finalAttrs.version}"; + license = lib.licenses.gpl3Only; + mainProgram = "comfyui"; + platforms = lib.platforms.linux; + }; +}) diff --git a/pkgs/comfyui/use-writable-runtime-paths.patch b/pkgs/comfyui/use-writable-runtime-paths.patch new file mode 100644 index 0000000..f264e7e --- /dev/null +++ b/pkgs/comfyui/use-writable-runtime-paths.patch @@ -0,0 +1,64 @@ +diff --git a/comfy/cli_args.py b/comfy/cli_args.py +index 4bef096..30f8920 100644 +--- a/comfy/cli_args.py ++++ b/comfy/cli_args.py +@@ -42,7 +42,15 @@ parser.add_argument("--tls-certfile", type=str, help="Path to TLS (SSL) certific + parser.add_argument("--enable-cors-header", type=str, default=None, metavar="ORIGIN", nargs="?", const="*", help="Enable CORS (Cross-Origin Resource Sharing) with optional origin or allow all with default '*'.") + parser.add_argument("--max-upload-size", type=float, default=100, help="Set the maximum upload size in MB.") + +-parser.add_argument("--base-directory", type=str, default=None, help="Set the ComfyUI base directory for models, custom_nodes, input, output, temp, and user directories.") ++xdg_data_home = os.environ.get("XDG_DATA_HOME") ++if xdg_data_home is None: ++ home = os.environ.get("HOME") ++ if home is None: ++ xdg_data_home = "/var/lib" ++ else: ++ xdg_data_home = os.path.join(home, ".local", "share") ++comfyui_data_home = os.path.join(xdg_data_home, "comfyui") ++parser.add_argument("--base-directory", type=str, default=comfyui_data_home, help="Set the ComfyUI base directory for models, custom_nodes, input, output, temp, and user directories.") + parser.add_argument("--extra-model-paths-config", type=str, default=None, metavar="PATH", nargs='+', action='append', help="Load one or more extra_model_paths.yaml files.") + parser.add_argument("--output-directory", type=str, default=None, help="Set the ComfyUI output directory. Overrides --base-directory.") + parser.add_argument("--temp-directory", type=str, default=None, help="Set the ComfyUI temp directory (default is in the ComfyUI directory). Overrides --base-directory.") +@@ -235,9 +243,7 @@ parser.add_argument( + help="Set the base URL for the ComfyUI API. (default: https://api.comfy.org)", + ) + +-database_default_path = os.path.abspath( +- os.path.join(os.path.dirname(__file__), "..", "user", "comfyui.db") +-) ++database_default_path = os.path.join(comfyui_data_home, "user", "comfyui.db") + parser.add_argument("--database-url", type=str, default=f"sqlite:///{database_default_path}", help="Specify the database URL, e.g. for an in-memory database you can use 'sqlite:///:memory:'.") + parser.add_argument("--enable-assets", action="store_true", help="Enable the assets system (API routes, database synchronization, and background scanning).") + parser.add_argument("--enable-asset-hashing", action="store_true", help="Compute blake3 content hashes when scanning assets. Hashing enables future asset-portability features (deduplication, cross-machine model resolution) but adds startup cost and per-output cost on large models directories. Off by default; enable to opt in.") +diff --git a/folder_paths.py b/folder_paths.py +index 7304e1b..32a66c9 100644 +--- a/folder_paths.py ++++ b/folder_paths.py +@@ -38,7 +38,8 @@ folder_names_and_paths["upscale_models"] = ([os.path.join(models_dir, "upscale_m + + folder_names_and_paths["latent_upscale_models"] = ([os.path.join(models_dir, "latent_upscale_models")], supported_pt_extensions) + +-folder_names_and_paths["custom_nodes"] = ([os.path.join(base_path, "custom_nodes")], set()) ++custom_nodes_directory = os.path.join(base_path, "custom_nodes") ++folder_names_and_paths["custom_nodes"] = ([custom_nodes_directory], set()) + + folder_names_and_paths["hypernetworks"] = ([os.path.join(models_dir, "hypernetworks")], supported_pt_extensions) + +@@ -107,11 +108,12 @@ def map_legacy(folder_name: str) -> str: + "clip": "text_encoders"} + return legacy.get(folder_name, folder_name) + +-if not os.path.exists(input_directory): +- try: +- os.makedirs(input_directory) +- except: +- logging.error("Failed to create input directory") ++for default_directory in (input_directory, custom_nodes_directory): ++ if not os.path.exists(default_directory): ++ try: ++ os.makedirs(default_directory) ++ except: ++ logging.error(f"Failed to create {default_directory}") + + def set_output_directory(output_dir: str) -> None: + global output_directory diff --git a/pkgs/default.nix b/pkgs/default.nix index a72de11..783db2a 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -4,6 +4,7 @@ let self = { audiveris = pkgs.callPackage ./audiveris { }; + comfyui = pkgs.callPackage ./comfyui { }; dolos = pkgs.callPackage ./dolos { nodejs = pkgs.nodejs_22; }; eqc = pkgs.callPackage ./eqc { }; example-a = pkgs.callPackage ./example-a { };