ComfyUI-Manager/docs/dev/TEST-environment-setup.md
Dr.Lt.Data f042d73b72
Some checks are pending
Publish to PyPI / build-and-publish (push) Waiting to run
Python Linting / Run Ruff (push) Waiting to run
feat(deps): add unified dependency resolver using uv pip compile (#2589)
* feat(deps): add unified dependency resolver using uv pip compile

- Add UnifiedDepResolver module with 7 FRs: collect, compile, install pipeline
- Integrate startup batch resolution in prestartup_script.py (module scope)
- Skip per-node pip install in execute_install_script() when unified mode active
- Add use_unified_resolver config flag following use_uv pattern
- Input sanitization: reject -r, -e, --find-links, @ file://, path separators
- Handle --index-url/--extra-index-url separation with credential redaction
- Fallback to per-node pip on resolver failure or uv unavailability
- Add 98 unit tests across 20 test classes
- Add PRD and Design docs with cm_global integration marked as DEFERRED

* fix(deps): reset use_unified_resolver flag on startup fallback

When the unified resolver fails at startup (compile error, install
error, uv unavailable, or generic exception), the runtime flag was
not being reset to False. This caused subsequent runtime installs
to incorrectly defer pip dependencies instead of falling back to
per-node pip install.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(deps): add manual test cases for unified dependency resolver

Add environment setup guide and 16 test cases covering:
- Normal batch resolution (TC-1), disabled state (TC-2)
- Fallback paths: uv unavailable (TC-3), compile fail (TC-4),
  install fail (TC-5), generic exception (TC-16)
- install.py preservation (TC-6), runtime defer (TC-13)
- Input sanitization: dangerous patterns (TC-7), path separators
  (TC-8), index-url separation (TC-9), credential redaction (TC-10)
- Disabled pack exclusion (TC-11), no-deps path (TC-12)
- Both unified resolver guard paths (TC-14), post-fallback (TC-15)

Includes API reference, traceability matrix, and out-of-scope items.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(deps): prevent read_config() from overriding resolver fallback state

read_config() in manager_core.py unconditionally re-read
use_unified_resolver from config.ini, undoing the False set by
prestartup_script.py on resolver fallback. This caused runtime
installs to still defer deps even after a startup batch failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(deps): support multiple index URLs per line and optimize downgrade check

- Rewrite _split_index_url() to handle multiple --index-url /
  --extra-index-url options on a single requirements.txt line using
  regex-based parsing instead of single split.
- Cache installed_packages snapshot in collect_requirements() to avoid
  repeated subprocess calls during downgrade blacklist checks.
- Add unit tests for multi-URL lines and bare --index-url edge case.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(deps): add E2E scripts and update test documentation

Add automated E2E test scripts for unified dependency resolver:
- setup_e2e_env.sh: idempotent environment setup (clone ComfyUI,
  create venv, install deps, symlink Manager, write config.ini)
- start_comfyui.sh: foreground-blocking launcher using
  tail -f | grep -q readiness detection
- stop_comfyui.sh: graceful SIGTERM → SIGKILL shutdown

Update test documentation reflecting E2E testing findings:
- TEST-environment-setup.md: add automated script usage, document
  caveats (PYTHONPATH, config.ini path, Manager v4 /v2/ prefix,
  Blocked by policy, bash ((var++)) trap, git+https:// rejection)
- TEST-unified-dep-resolver.md: add TC-17 (restart dependency
  detection), TC-18 (real node pack integration), Validated
  Behaviors section, normalize API port to 8199

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(deps): harden input sanitization, expand test coverage, bump version

Security:
- Add _INLINE_DANGEROUS_OPTIONS regex to catch pip options after package
  names (--find-links, --constraint, --requirement, --editable, --trusted-host,
  --global-option, --install-option and short forms)
- Stage index URLs in pending_urls, commit only after full line validation
  to prevent URL injection from rejected lines

Tests:
- Add 50 new tests: inline sanitization, false-positive guards, parse
  helpers (_parse_conflicts, _parse_install_output), exception paths
  (91 → 141 total, all pass)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(cli): add uv-compile command and --uv-compile flag for batch dependency resolution

Add two CLI entry points for the unified dependency resolver:

- `cm_cli uv-compile`: standalone batch resolution of all installed
  node pack dependencies via uv pip compile
- `cm_cli install --uv-compile`: skip per-node pip, batch-resolve all
  deps after install completes (mutually exclusive with --no-deps)

Both use a shared `_run_unified_resolve()` helper that passes real
cm_global values (pip_blacklist, pip_overrides, pip_downgrade_blacklist)
and guarantees PIPFixer.fix_broken() runs via try/finally.

Update DESIGN, PRD, and TEST docs for consistency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 06:51:53 +09:00

6.5 KiB

Test Environment Setup

Procedures for setting up a ComfyUI environment with ComfyUI-Manager installed for functional testing.

Three shell scripts in tests/e2e/scripts/ automate the entire lifecycle:

# 1. Setup: clone ComfyUI, create venv, install deps, symlink Manager
E2E_ROOT=/tmp/e2e_test MANAGER_ROOT=/path/to/comfyui-manager-draft4 \
    bash tests/e2e/scripts/setup_e2e_env.sh

# 2. Start: launches ComfyUI in background, blocks until ready
E2E_ROOT=/tmp/e2e_test bash tests/e2e/scripts/start_comfyui.sh

# 3. Stop: graceful SIGTERM → SIGKILL shutdown
E2E_ROOT=/tmp/e2e_test bash tests/e2e/scripts/stop_comfyui.sh

# 4. Cleanup
rm -rf /tmp/e2e_test

Script Details

Script Purpose Input Output
setup_e2e_env.sh Full environment setup (8 steps) E2E_ROOT, MANAGER_ROOT, COMFYUI_BRANCH (default: master), PYTHON (default: python3) E2E_ROOT=<path> on last line
start_comfyui.sh Foreground-blocking launcher E2E_ROOT, PORT (default: 8199), TIMEOUT (default: 120s) COMFYUI_PID=<pid> PORT=<port>
stop_comfyui.sh Graceful shutdown E2E_ROOT, PORT (default: 8199)

Idempotent: setup_e2e_env.sh checks for a .e2e_setup_complete marker file and skips setup if the environment already exists.

Blocking mechanism: start_comfyui.sh uses tail -n +1 -f | grep -q -m1 'To see the GUI' to block until ComfyUI is ready. No polling loop needed.


Prerequisites

  • Python 3.9+
  • Git
  • uv (install via pip install uv or standalone)

Manual Setup (Reference)

For understanding or debugging, the manual steps are documented below. The automated scripts execute these same steps.

1. ComfyUI Clone

COMFY_ROOT=$(mktemp -d)/ComfyUI
git clone https://github.com/comfyanonymous/ComfyUI.git "$COMFY_ROOT"
cd "$COMFY_ROOT"

2. Virtual Environment

cd "$COMFY_ROOT"
uv venv .venv
source .venv/bin/activate    # Linux/macOS
# .venv\Scripts\activate     # Windows

3. ComfyUI Dependencies

# GPU (CUDA)
uv pip install -r requirements.txt --extra-index-url https://download.pytorch.org/whl/cu121

# CPU only (lightweight, for functional testing)
uv pip install -r requirements.txt --extra-index-url https://download.pytorch.org/whl/cpu

4. ComfyUI-Manager Install (Development)

# MANAGER_ROOT = comfyui-manager-draft4 repository root
MANAGER_ROOT=/path/to/comfyui-manager-draft4

# Editable install from current source
uv pip install -e "$MANAGER_ROOT"

Note

: Editable mode (-e) reflects code changes without reinstalling. For production-like testing, use uv pip install "$MANAGER_ROOT" (non-editable).

ln -s "$MANAGER_ROOT" "$COMFY_ROOT/custom_nodes/ComfyUI-Manager"

6. Write config.ini

mkdir -p "$COMFY_ROOT/user/__manager"
cat > "$COMFY_ROOT/user/__manager/config.ini" << 'EOF'
[default]
use_uv = true
use_unified_resolver = true
EOF

Important

: The config path is $COMFY_ROOT/user/__manager/config.ini, resolved by folder_paths.get_system_user_directory("manager"). It is NOT inside the symlinked Manager directory.

7. HOME Isolation

export HOME=/tmp/e2e_home
mkdir -p "$HOME/.config" "$HOME/.local/share"

8. ComfyUI Launch

cd "$COMFY_ROOT"
PYTHONUNBUFFERED=1 python main.py --enable-manager --cpu --port 8199
Flag Purpose
--enable-manager Enable ComfyUI-Manager (disabled by default)
--cpu Run without GPU (for functional testing)
--port 8199 Use non-default port to avoid conflicts
--enable-manager-legacy-ui Enable legacy UI (optional)
--listen Allow remote connections (optional)

Key Directories

Directory Path Description
ComfyUI root $COMFY_ROOT/ ComfyUI installation root
Manager data $COMFY_ROOT/user/__manager/ Manager config, startup scripts, snapshots
Config file $COMFY_ROOT/user/__manager/config.ini Manager settings (use_uv, use_unified_resolver, etc.)
custom_nodes $COMFY_ROOT/custom_nodes/ Installed node packs

The Manager data path is resolved via folder_paths.get_system_user_directory("manager"). Printed at startup: ** ComfyUI-Manager config path: <path>/config.ini

Startup Sequence

When Manager loads successfully, the following log lines appear:

[PRE] ComfyUI-Manager          # prestartup_script.py executed
[START] ComfyUI-Manager         # manager_server.py loaded

The Blocked by policy message for Manager in custom_nodes is expectedshould_be_disabled() in comfyui_manager/__init__.py prevents legacy double-loading when Manager is already pip-installed.


Caveats & Known Issues

PYTHONPATH for comfy imports

ComfyUI's comfy package is a local package inside the ComfyUI directory — it is NOT pip-installed. Any code that imports from comfy (including comfyui_manager.__init__) requires PYTHONPATH to include the ComfyUI directory:

PYTHONPATH="$COMFY_ROOT" python -c "import comfy"
PYTHONPATH="$COMFY_ROOT" python -c "import comfyui_manager"

The automated scripts handle this via PYTHONPATH in verification checks and the ComfyUI process inherits it implicitly by running from the ComfyUI directory.

config.ini path

The config file must be at $COMFY_ROOT/user/__manager/config.ini, NOT inside the Manager symlink directory. This is resolved by folder_paths.get_system_user_directory("manager") at prestartup_script.py:65-73.

Manager v4 endpoint prefix

All Manager endpoints use the /v2/ prefix (e.g., /v2/manager/queue/status, /v2/snapshot/get_current). Paths without the prefix will return 404.

Blocked by policy is expected

When Manager detects that it's loaded as a custom_node but is already pip-installed, it prints Blocked by policy and skips legacy loading. This is intentional behavior in comfyui_manager/__init__.py:39-51.

Bash ((var++)) trap

Under set -e, ((0++)) evaluates the pre-increment value (0), and (( 0 )) returns exit code 1, killing the script. Use var=$((var + 1)) instead.

git+https:// URLs in requirements.txt

Some node packs (e.g., Impact Pack's SAM2 dependency) use git+https://github.com/... URLs. The unified resolver correctly rejects these with "rejected path separator" — they must be installed separately.


Cleanup

deactivate
rm -rf "$COMFY_ROOT"