Skip to content
Open
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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#91](https://github.com/utensils/comfyui-nix/issues/91))

### Changed
- Linux now runs on Python 3.13 (from 3.12),
the version upstream ComfyUI recommends and ships in its portable builds.
macOS stays on 3.12 because the pinned PyTorch 2.5.1 has no Python 3.13 wheel for macOS.
Packages that ComfyUI-Manager installed into `<data-dir>/.venv/lib/python3.12` are not visible to Python 3.13,
so the launcher prints a note asking to reinstall them.
- PyTorch wheel pins in `nix/versions.nix` are now a URL template plus one hash per interpreter tag,
so moving to another Python only requires adding hashes.
The new `wheel-pins` check fails evaluation when a pin lacks the hash for the selected Python.
- Updated the bundled PyAV wheel `17.0.0` -> `17.0.1`,
because `17.0.0` hangs imageio's fork regression test on Python 3.13.
- Reduced the Python package overrides that Linux no longer needs on nixpkgs' default Python.
The overrides for open-clip-torch, torchmetrics, inline-snapshot, backrefs, jupyter-server, ultralytics, mss and filterpy
now apply to macOS only,
so on Linux those packages come from the binary cache or build with their test suites enabled.
The timm and torchsde overrides now apply only where PyTorch comes from a pre-built wheel.
The albumentations and bitsandbytes overrides and the `allowBrokenPredicate` for open-clip-torch are removed,
since nixpkgs dropped albumentations, already builds bitsandbytes with ninja,
and no longer marks open-clip-torch as broken.
- Updated ComfyUI to upstream `v0.37.0` (from `v0.34.0`). Updated its pinned
dependencies: frontend `1.49.6` -> `1.52.7`, workflow templates `0.11.48`
-> `0.11.66` (core `0.3.322` -> `0.3.357`, JSON `0.1.57` -> `0.1.92`,
Expand Down
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- `nixfmt`: Verify Nix formatting (nixfmt-rfc-style)
- `shellcheck`: Lint shell scripts in `scripts/`
- `package`: Full build of the default package
- `wheel-pins`: Every interpreter-specific wheel pin in `nix/versions.nix` has a hash for the selected Python (evaluation only)
- **Manual linting in dev shell** (`nix develop`):
- `ruff check src/` — lint Python (or `ruff check src/path/to/file.py` for a single file)
- `ruff format src/` — format Python (or `ruff format src/path/to/file.py`)
Expand All @@ -57,7 +58,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- Vendored wheels (spandrel, frontend, docs, etc.) also pinned in `nix/versions.nix`
- Template input files: auto-generated in `nix/template-inputs.nix`
- Update with: `./scripts/update-template-inputs.sh && git add nix/template-inputs.nix`
- Python version: 3.12
- Python version: 3.13 on Linux, 3.12 on macOS (torch 2.5.1 has no cp313 macOS wheel) — selected in `nix/python.nix`
- PyTorch: macOS uses pre-built wheels (2.5.1, pinned to work around MPS bugs on macOS 26); CUDA uses pre-built wheels from pytorch.org (cu130); ROCm uses pre-built wheels from pytorch.org (rocm7.1); Intel XPU uses pre-built wheels from pytorch.org (xpu, oneAPI/SYCL — no IPEX); Linux CPU uses nixpkgs

## Project Architecture
Expand Down Expand Up @@ -91,7 +92,7 @@ Each module branches on this value to select platform-specific PyTorch wheels, r

**XPU-specific notes** (Intel oneAPI / SYCL):

- Wheel channel: `https://download.pytorch.org/whl/xpu` — `torch 2.10.0+xpu`, `torchvision 0.25.0+xpu`, `torchaudio 2.10.0+xpu` (cp312, Linux x86_64 only)
- Wheel channel: `https://download.pytorch.org/whl/xpu` — `torch 2.10.0+xpu`, `torchvision 0.25.0+xpu`, `torchaudio 2.10.0+xpu` (cp313, Linux x86_64 only)
- No IPEX required — ComfyUI uses `torch.xpu.is_available()` auto-detection
- Host must provide Level Zero loader + Intel compute-runtime at runtime. The launcher prefers `/run/opengl-driver/lib` (NixOS `hardware.graphics.extraPackages`) and falls back to Nix-bundled `pkgs.level-zero` + `pkgs.intel-compute-runtime` + `pkgs.ocl-icd` for non-NixOS Linux
- Runtime env vars set by launcher: `SYCL_CACHE_PERSISTENT=1`, `SYCL_CACHE_DIR=<data-dir>/.cache/libsycl_cache`. Opt-in: `COMFY_ENABLE_XPU_FP64_EMULATION=1` enables `OverrideDefaultFP64Settings` + `IGC_EnableDPEmulation` for iGPUs without native FP64
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![NixOS](https://img.shields.io/badge/NixOS-unstable-blue.svg?logo=nixos)](https://nixos.org)

A slightly opinionated, pure Nix flake for [ComfyUI] with Python 3.12 and curated custom nodes. Supports macOS (Apple Silicon) and Linux.
A slightly opinionated, pure Nix flake for [ComfyUI] with Python 3.13 (3.12 on macOS) and curated custom nodes. Supports macOS (Apple Silicon) and Linux.

<p align="center">
<img src="comfyui-demo.gif" alt="ComfyUI Demo" />
Expand Down Expand Up @@ -645,7 +645,7 @@ nix run .#buildDockerXpu # Intel XPU
## Development

```bash
nix develop # Dev shell with Python 3.12, ruff, pyright
nix develop # Dev shell with Python 3.13, ruff, pyright
nix flake check # Run all checks (build, lint, type-check, nixfmt)
nix run .#update # Check for ComfyUI updates
```
Expand Down
7 changes: 2 additions & 5 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
description = "A Nix flake for ComfyUI with Python 3.12";
description = "A Nix flake for ComfyUI with Python 3.13";

nixConfig = {
extra-substituters = [
Expand Down Expand Up @@ -82,14 +82,12 @@
system = "x86_64-linux";
config = {
allowUnfree = true;
allowBrokenPredicate = pkg: (pkg.pname or "") == "open-clip-torch";
};
};
pkgsLinuxArm64 = import nixpkgs {
system = "aarch64-linux";
config = {
allowUnfree = true;
allowBrokenPredicate = pkg: (pkg.pname or "") == "open-clip-torch";
};
};

Expand All @@ -106,7 +104,7 @@

mkPython =
pkgs: gpuSupport:
pkgs.python312.override {
(import ./nix/python.nix { inherit pkgs; }).override {
packageOverrides = pythonOverridesFor pkgs gpuSupport "rocm71";
};

Expand Down Expand Up @@ -194,7 +192,6 @@
inherit system;
config = {
allowUnfree = true;
allowBrokenPredicate = pkg: (pkg.pname or "") == "open-clip-torch";
# Keep the x86_64-only ROCm development shell evaluable on aarch64-linux.
allowUnsupportedSystem = system == "aarch64-linux";
};
Expand Down
31 changes: 30 additions & 1 deletion nix/checks.nix
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ let
touch $out
'';
mssRuntimeDeps =
assert !pythonPackages.mss.doInstallCheck;
# The override that disables mss' tests only applies on Darwin.
assert pkgs.stdenv.hostPlatform.isDarwin -> !pythonPackages.mss.doInstallCheck;
pythonPackages.mss;
# Every backend's torch comes from a pre-built wheel whose metadata names
# distributions nixpkgs supplies outside PyPI. Those requirements are dropped
Expand Down Expand Up @@ -276,6 +277,28 @@ let
runtimeDepsChecks = pkgs.lib.mapAttrs' (
name: package: pkgs.lib.nameValuePair "${name}-runtime-deps" package
) runtimeDepsPackages;
# Interpreter-specific wheel pins carry one hash per interpreter tag.
# Most backends are never built in a single check run,
# so find the pins that lack a hash for the selected Python by evaluation alone.
basePython = pythonFor "none";
pythonTag = "cp" + pkgs.lib.replaceStrings [ "." ] [ "" ] basePython.pythonVersion;
versions = import ./versions.nix;
wheelChannels =
if pkgs.stdenv.hostPlatform.isDarwin then
{ inherit (versions.pytorchWheels) darwinArm64; }
else
removeAttrs versions.pytorchWheels [ "darwinArm64" ]
// {
xpuRuntime = { inherit (versions.xpuRuntime) triton-xpu; };
};
wheelPinsMissingHash = pkgs.lib.concatLists (
pkgs.lib.mapAttrsToList (
channel: wheels:
pkgs.lib.mapAttrsToList (name: _: "${channel}.${name}") (
pkgs.lib.filterAttrs (_: spec: !(spec.hashes ? ${pythonTag})) wheels
)
) wheelChannels
);
in
{
package = packages.default;
Expand Down Expand Up @@ -351,6 +374,12 @@ in
}
// runtimeDepsChecks
// torchRuntimeDepsChecks
// {
wheel-pins =
assert pkgs.lib.assertMsg (wheelPinsMissingHash == [ ])
"nix/versions.nix: no ${pythonTag} hash pinned for ${pkgs.lib.concatStringsSep ", " wheelPinsMissingHash}";
pkgs.runCommand "wheel-pins" { } "touch $out";
}
//
pkgs.lib.optionalAttrs
(
Expand Down
42 changes: 29 additions & 13 deletions nix/packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ let
];
};

python = pkgs.python312.override { packageOverrides = pythonOverrides; };
python = (import ./python.nix { inherit pkgs; }).override { packageOverrides = pythonOverrides; };

vendored = import ./vendored-packages.nix { inherit pkgs python versions; };

Expand Down Expand Up @@ -656,25 +656,41 @@ let
# This allows both pip and uv to install packages to a writable location
# while keeping the Nix store read-only
VENV_DIR="$BASE_DIR/.venv"
SITE_PACKAGES="$VENV_DIR/lib/python3.12/site-packages"
SITE_PACKAGES="$VENV_DIR/lib/python${python.pythonVersion}/site-packages"
mkdir -p "$SITE_PACKAGES"
mkdir -p "$VENV_DIR/bin"

# Create pyvenv.cfg if it doesn't exist (required for PEP 405 compliance)
if [[ ! -e "$VENV_DIR/pyvenv.cfg" ]]; then
cat > "$VENV_DIR/pyvenv.cfg" << PYVENV
# Packages are installed per Python minor version,
# so anything the Manager installed under a previous interpreter is no longer on sys.path.
# Say so instead of letting nodes fail with a bare ImportError.
for old_site in "$VENV_DIR"/lib/python3.*/site-packages; do
if [[ -d "$old_site" && "$old_site" != "$SITE_PACKAGES" ]] \
&& [[ -n "$(find "$old_site" -mindepth 1 -print -quit 2>/dev/null)" ]]; then
echo "Note: $old_site was populated by an older Python and is ignored by Python ${python.pythonVersion}."
echo " Reinstall the affected custom node dependencies via ComfyUI-Manager, then remove that directory."
fi
done

# Write pyvenv.cfg (required for PEP 405 compliance).
# Rewritten on every start because it names the interpreter's store path and version,
# both of which change whenever the flake is updated.
cat > "$VENV_DIR/pyvenv.cfg" << PYVENV
home = ${pythonRuntime}/bin
include-system-site-packages = true
version = 3.12.9
version = ${python.version}
PYVENV
fi

# Symlink Python executable into venv bin (some tools expect this)
if [[ ! -e "$VENV_DIR/bin/python" ]]; then
ln -sf "${pythonRuntime}/bin/python" "$VENV_DIR/bin/python"
ln -sf "${pythonRuntime}/bin/python3" "$VENV_DIR/bin/python3"
ln -sf "${pythonRuntime}/bin/python3.12" "$VENV_DIR/bin/python3.12"
fi
# Symlink Python executable into venv bin (some tools expect this).
# Refreshed on every start for the same reason as pyvenv.cfg,
# and links left behind by another Python minor version are dropped.
for old_link in "$VENV_DIR"/bin/python3.*; do
if [[ -L "$old_link" && "$old_link" != "$VENV_DIR/bin/python${python.pythonVersion}" ]]; then
rm -f "$old_link"
fi
done
ln -sfn "${pythonRuntime}/bin/python" "$VENV_DIR/bin/python"
ln -sfn "${pythonRuntime}/bin/python3" "$VENV_DIR/bin/python3"
ln -sfn "${pythonRuntime}/bin/python${python.pythonVersion}" "$VENV_DIR/bin/python${python.pythonVersion}"

# Set VIRTUAL_ENV so uv installs to our mutable venv instead of Nix store
export VIRTUAL_ENV="$VENV_DIR"
Expand Down
Loading
Loading