feat: Add proper --base-directory support for custom data locations#7
feat: Add proper --base-directory support for custom data locations#7jamesbrink merged 7 commits intomainfrom
Conversation
- Parse --base-directory in bash config before setting up paths - Update Python persistence module to respect user-specified base directory - Keep app code and venv fixed in ~/.config/comfy-ui/ (managed by nix) - Allow all data (models, input, output, custom_nodes) to use custom location - Update README with --base-directory documentation and examples This enables storing all ComfyUI data on a separate drive (e.g., ZFS volume) while keeping the app installation in a fixed location. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review: Add proper --base-directory supportThank you for this PR! This is a well-architected feature that cleanly separates app code from user data. Here's my detailed review: ✅ Strengths
🐛 Potential Issues1. Argument Parsing Duplication and Race Condition (scripts/config.sh:38-57)The bash argument parsing logic is duplicated (lines 38-47 and 49-57), which could lead to maintenance issues. More importantly, there's a potential race condition:
If a user provides Recommendation: Consolidate into a single loop: BASE_DIR="$HOME/.config/comfy-ui"
skip_next=false
for i in "${!_args[@]}"; do
if [[ "$skip_next" == "true" ]]; then
skip_next=false
continue
fi
case "${_args[$i]}" in
--base-directory=*)
BASE_DIR="${_args[$i]#*=}"
BASE_DIR="${BASE_DIR/#\~\/$HOME}"
;;
--base-directory)
if [[ $((i+1)) -lt ${#_args[@]} ]]; then
BASE_DIR="${_args[$((i+1))]}"
BASE_DIR="${BASE_DIR/#\~\/$HOME}"
skip_next=true
fi
;;
esac
done2. Incomplete Argument Filtering (scripts/config.sh:112-133)The Recommendation: Add handling in parse_arguments() {
ARGS=()
skip_next=false
for arg in "$@"; do
if [[ "$skip_next" == "true" ]]; then
skip_next=false
continue
fi
case "$arg" in
--open)
OPEN_BROWSER=true
;;
--port=*)
COMFY_PORT="${arg#*=}"
;;
--base-directory=*)
# Already handled in config, skip
;;
--base-directory)
# Skip this and next arg
skip_next=true
;;
--debug)
export LOG_LEVEL=$DEBUG
;;
--verbose)
export LOG_LEVEL=$DEBUG
;;
*)
ARGS+=("$arg")
;;
esac
done
}3. Environment Variable Inconsistency (src/persistence/persistence.py:210-226)The Python code reads
This works, but the logic could be cleaner. Since bash already parses the argument and sets Recommendation: Either:
4. Missing Validation (both files)Neither bash nor Python validates that:
Recommendation: Add basic validation in config.sh: # After parsing BASE_DIR
if [[ ! -d "$(dirname "$BASE_DIR")" ]]; then
echo "ERROR: Parent directory of BASE_DIR does not exist: $(dirname "$BASE_DIR")"
exit 1
fi5. Path Expansion Edge Cases (scripts/config.sh:45, 54)The tilde expansion
Recommendation: Use BASE_DIR=$(eval echo "$BASE_DIR") # Handles all tilde formsOr simply: BASE_DIR="${BASE_DIR/#\~/$HOME}" # Without the slash requirement🔒 Security Considerations
⚡ Performance ConsiderationsNo significant performance concerns. The argument parsing happens once at startup and is negligible. 🧪 Test CoverageMissing tests: There are no automated tests for this feature. Consider adding:
📝 Code StyleGood:
Minor issues:
💡 Suggestions
✅ SummaryThis is a solid feature with good architecture. The main issues are:
The current implementation will work but could have edge case issues with argument parsing. I recommend addressing issues #1 and #2 before merging. Overall assessment: Approve with requested changes ✅ Review generated with Claude Code |
- Consolidate argument parsing into single loop with skip_next flag - Filter --base-directory from ARGS passed to ComfyUI - Add validation for base directory parent existence - Add comment explaining dual parsing in bash and Python - Update CLAUDE.md with command line options and clarified data structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review: Add --base-directory SupportOverviewThis PR successfully adds ✅ Strengths
🔍 Code Quality Issues1. Inconsistent Argument Parsing Logic (Medium Priority)Location: The argument parsing logic is duplicated in two places with slightly different implementations:
Issue: Both handle Recommendation: Consider extracting the base-directory parsing into a dedicated function called from both places: # Extract base directory from arguments
extract_base_directory() {
local result="$HOME/.config/comfy-ui"
local skip_next=false
for i in "${!@}"; do
# ... parsing logic
done
echo "$result"
}
BASE_DIR=$(extract_base_directory "$@")2. Argument Parsing Edge Case (Low Priority)Location: The Python parsing doesn't handle the Current code: if "--base-directory" in sys.argv:
idx = sys.argv.index("--base-directory")
if idx + 1 < len(sys.argv):
base_dir = os.path.expanduser(sys.argv[idx + 1])Issue: If a user passes Recommendation: Add support for both forms: base_dir = None
for i, arg in enumerate(sys.argv):
if arg == "--base-directory" and i + 1 < len(sys.argv):
base_dir = os.path.expanduser(sys.argv[i + 1])
break
elif arg.startswith("--base-directory="):
base_dir = os.path.expanduser(arg.split("=", 1)[1])
break3. Error Handling Gap (Medium Priority)Location: The validation only checks if the parent directory exists, but doesn't verify write permissions. Issue: Users might get cryptic errors later when the script tries to create directories. Recommendation: Add permission check: if [[ ! -d "$(dirname "$BASE_DIR")" ]]; then
echo "ERROR: Parent directory of BASE_DIR does not exist: $(dirname "$BASE_DIR")" >&2
echo "Please create it first or specify a valid path with --base-directory" >&2
exit 1
elif [[ ! -w "$(dirname "$BASE_DIR")" ]]; then
echo "ERROR: No write permission for parent directory: $(dirname "$BASE_DIR")" >&2
exit 1
fi🛡️ Security Considerations
No security vulnerabilities identified. ⚡ Performance Considerations
Overall performance impact: Minimal 🧪 Test CoverageMajor Gap: No automated tests for the new functionality. Recommendations:
Test Plan Status: The PR includes a manual test plan but no automated tests. This is acceptable for the initial implementation but should be addressed in a follow-up. 📝 DocumentationStrengths:
Minor Issue: The README example shows Recommendation: Add a note: **Note**: Paths with spaces should be quoted: `--base-directory "/path/with spaces"`🎯 Overall AssessmentCode Quality: 8/10
Architecture: 9/10
Completeness: 7/10
📋 RecommendationsBefore Merge:
Nice to Have:
Follow-up PRs:
✅ ApprovalDespite the minor issues noted above, this PR:
Recommendation: Approve with suggestions for follow-up improvements. The identified issues are minor and don't block the core functionality. They can be addressed in follow-up PRs or before merge at the maintainer's discretion. Review generated by Claude Code |
- Python now handles both --base-directory VALUE and --base-directory=VALUE - Add write permission validation for parent directory - Document quoting paths with spaces in README 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review: Add proper --base-directory supportThank you for this PR! This is a well-structured enhancement that cleanly separates app code from user data. Overall, the implementation is solid, but I've identified several issues that should be addressed before merging. Critical Issues1. Argument Parsing Race Condition (scripts/config.sh:39-61, persistence.py:216-226)The same
Problem: If bash and Python disagree on parsing (edge cases with quotes, spaces, etc.), they'll use different directories. Recommendation:
Example fix for persistence.py:210-232: # Use COMFY_USER_DIR set by bash, which already parsed --base-directory
base_dir = os.environ.get(
"COMFY_USER_DIR", os.path.join(os.path.expanduser("~"), ".config", "comfy-ui")
)
logger.info("Using persistent directory: %s", base_dir)The comment on lines 211-214 acknowledges the duplication but doesn't justify why it's necessary. If bash sets 2. Parent Directory Validation Timing (scripts/config.sh:63-73)The validation happens before the script sources logger.sh, so error messages bypass the logging system. Current behavior: echo "ERROR: Parent directory of BASE_DIR does not exist: $_parent_dir" >&2Issue: Inconsistent with the project's logging conventions (see CLAUDE.md: "Use the logging functions from logger.sh instead of direct echo statements") Recommendation:
3. Unhandled Edge Case: Relative Paths (scripts/config.sh:48-55, persistence.py:219-225)The tilde expansion is good, but relative paths are not converted to absolute paths. Example: nix run -- --base-directory ./dataThis will work initially but could cause issues if the working directory changes or in symlink resolution. Recommendation: Use BASE_DIR="$(realpath -m "${_args[]#*=}")"Note: Security Concerns4. Path Traversal via User Input (scripts/config.sh:47-58)User-supplied paths are accepted without sanitization. While directory creation is safe, symlinks could be created pointing outside expected boundaries. Attack scenario: nix run -- --base-directory "../../../../etc"Mitigation:
5. sys.argv Injection in Python (persistence.py:290-291)The code appends to sys.argv if if "--base-directory" not in sys.argv:
sys.argv.extend(["--base-directory", base_dir])Issues:
Recommendation: Remove this entirely. If ComfyUI needs the argument, pass it through properly. If not, use environment variables. Code Quality Issues6. ShellCheck Warnings (scripts/config.sh:120, 122)These are existing issues, not introduced by this PR, but worth noting. These arrays should be exported or the warnings suppressed with comments. 7. Inconsistent Variable Scope (scripts/config.sh:131)local skip_next=falseThis uses Recommendation: Be consistent - either use 8. Error Handling Gap (scripts/config.sh:53-57)The code checks if nix run -- --base-directory --openThis would set Recommendation: Validate that the next argument doesn't start with if [[ $((i+1)) -lt ${#_args[@]} ]] && [[ ! "${_args[$((i+1))]}" =~ ^-- ]]; thenDocumentation & Testing9. Missing Test Plan ItemsThe PR description includes a test plan, but it's not checked off. Before merging:
10. Documentation Quality✅ Good: README examples are clear and practical Performance Considerations11. Redundant Path ExpansionBoth bash and Python expand tildes independently. Minor overhead, but unnecessary. Positive Aspects✅ Clean separation of app code ( SummaryRecommendation: Request changes before merging. Priority fixes:
Non-blocking improvements:
The core architecture is sound, but the implementation has several edge cases that could cause subtle bugs. Once the critical issues are addressed, this will be a solid enhancement. Let me know if you'd like help with any of these fixes! |
…curity Changes: - Remove duplicate argument parsing from Python persistence.py - Python now trusts COMFY_USER_DIR env var set by bash - Eliminates potential inconsistency between bash/Python parsing - Remove sys.argv modification anti-pattern from persistence.py - Refactor config.sh to use proper function for argument parsing - Consistent use of local variables - Validates next argument isn't a flag (--base-directory --open) - Converts relative paths to absolute with realpath - Add path traversal security validation - Restricts paths to home directory and common data mounts - Prevents attacks like --base-directory "../../../../etc" - Fix ShellCheck warnings for unused arrays (SC2034) - Document why echo is used before logger.sh is sourced 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review: SupportThank you for this PR! This is a well-thought-out feature that enables users to store ComfyUI data on separate drives. I've reviewed the changes and have several observations across code quality, security, potential bugs, and testing. ✅ Strengths1. Clean Architecture
2. Documentation
3. Security Considerations
🔴 Critical Issues1. Race Condition in Argument Parsing (config.sh:82-86)Issue: The # Current code
BASE_DIR="$HOME/.config/comfy-ui"
_parsed_base_dir="$(_parse_base_directory "$@")"
if [[ -n "$_parsed_base_dir" ]]; then
BASE_DIR="$_parsed_base_dir"
fiProblem: When Impact: The Fix: Pass arguments explicitly when sourcing: # In launcher.sh
source "$SCRIPT_DIR/config.sh" "$@"
# Or parse arguments before sourcing config.sh
parse_base_dir_early "$@"
source "$SCRIPT_DIR/config.sh"2. Symlink Creation Before Base Directory ValidationIssue: The directory creation in Impact: If the race condition above causes Test: Run 🟡 High Priority Issues3. Path Validation Too Restrictive (config.sh:90-104)Issue: The validation only allows paths starting with specific prefixes, which may block legitimate use cases. local allowed_prefixes=("$home_dir" "/tmp" "/var/tmp" "/data" "/mnt" "/media" "/run/media")Problems:
Recommendation: _validate_base_dir() {
local dir="$1"
local blocked_prefixes=("/etc" "/bin" "/sbin" "/usr" "/lib" "/lib64" "/boot" "/sys" "/proc" "/dev")
for prefix in "${blocked_prefixes[@]}"; do
if [[ "$dir" == "$prefix"* ]]; then
echo "ERROR: --base-directory cannot be in system directory: $prefix" >&2
exit 1
fi
done
# Require absolute path
if [[ "$dir" \!= /* ]]; then
echo "ERROR: --base-directory must be an absolute path" >&2
exit 1
fi
}4. Missing Error Handling in Path Expansion (config.sh:76)Issue: realpath -m "$raw_path" 2>/dev/null || echo "$raw_path"Problems:
Recommendation: # Expand tilde and resolve path
expanded_path="${raw_path/#\~//home/runner}"
if command -v realpath &>/dev/null; then
realpath -m "$expanded_path" 2>/dev/null || {
echo "ERROR: Invalid path: $raw_path" >&2
exit 1
}
else
# Fallback: at least make it absolute
cd -P "$(dirname "$expanded_path")" 2>/dev/null && echo "$PWD/$(basename "$expanded_path")" || {
echo "ERROR: Cannot resolve path: $raw_path" >&2
exit 1
}
fi5. Inconsistent Argument Handling (config.sh:175-208)Issue: The Current flow:
Result: The filtering logic is dead code, and BASE_DIR is never set from command-line arguments. 🟠 Medium Priority Issues6. Python Environment Variable Trust (persistence.py:213-215)Issue: The Python code blindly trusts base_dir = os.environ.get(
"COMFY_USER_DIR", os.path.join(os.path.expanduser("~"), ".config", "comfy-ui")
)Problems:
Recommendation: base_dir = os.environ.get(
"COMFY_USER_DIR", os.path.join(os.path.expanduser("~"), ".config", "comfy-ui")
)
# Validate path
if not os.path.isabs(base_dir):
logger.error("base_dir must be an absolute path: %s", base_dir)
raise ValueError(f"Invalid base_dir: {base_dir}")
# Basic security check
blocked_dirs = ["/etc", "/bin", "/sbin", "/usr", "/lib", "/boot", "/sys", "/proc", "/dev"]
if any(base_dir.startswith(d) for d in blocked_dirs):
logger.error("base_dir cannot be in system directory: %s", base_dir)
raise ValueError(f"Unsafe base_dir: {base_dir}")7. Quotes in Path Handling (config.sh:74-76)Edge case: User provides # Current code
raw_path="${raw_path/#\~//home/runner}"
realpath -m "$raw_path" 2>/dev/null || echo "$raw_path"Problem: If the user literally includes quotes in the argument (e.g., via improper escaping), they'll become part of the path. Test case: ./launcher.sh --base-directory '"/tmp/test"' # Quotes become part of pathRecommendation: Strip surrounding quotes: # Remove surrounding quotes if present
raw_path=${raw_path%\"}
raw_path=${raw_path#\"}
raw_path=${raw_path%\'}
raw_path=${raw_path#\'}8. Missing COMFY_APP_DIR Export (config.sh:213-227)Issue: export_config() {
export COMFY_VERSION COMFY_PORT BASE_DIR CODE_DIR COMFY_VENV
# COMFY_APP_DIR is missing\!Impact: Python falls back to heuristic detection (lines 221-233), which works but is fragile. Fix: export COMFY_VERSION COMFY_PORT BASE_DIR CODE_DIR COMFY_VENV COMFY_APP_DIR
# ... set COMFY_APP_DIR before export
COMFY_APP_DIR="$CODE_DIR"🟢 Low Priority / Suggestions9. Shellcheck WarningsThe code has some shellcheck disables: # shellcheck disable=SC2034 # Used in install.sh
BASE_PACKAGES=(pyyaml pillow numpy requests)While the disables are correct, consider whether these variables could be used in the same file to avoid needing disables. 10. Test CoverageMissing:
Recommendation: #\!/usr/bin/env bash
# test_base_directory.sh
set -euo pipefail
# Test 1: Default location
tmpdir="$(mktemp -d)"
export HOME="$tmpdir"
./launcher.sh --help # Ensure script runs
[[ -d "$tmpdir/.config/comfy-ui" ]] || { echo "FAIL: default directory not created"; exit 1; }
# Test 2: Custom location
custom_dir="$tmpdir/custom-ai"
./launcher.sh --base-directory "$custom_dir" --help
[[ -d "$custom_dir" ]] || { echo "FAIL: custom directory not created"; exit 1; }
# Test 3: Path with spaces
space_dir="$tmpdir/path with spaces"
./launcher.sh --base-directory "$space_dir" --help
[[ -d "$space_dir" ]] || { echo "FAIL: space directory not created"; exit 1; }
echo "All tests passed"
rm -rf "$tmpdir"11. Documentation: COMFY_USER_DIR DeprecationThe README removes ### Deprecated
- `COMFY_USER_DIR`: Use `--base-directory` instead. This environment variable is still supported for backwards compatibility but may be removed in a future release.🔧 Performance ConsiderationsThe changes are minimal and don't introduce performance concerns. The argument parsing is O(n) in arguments, which is fine for CLI parsing. 🔒 Security SummaryGood:
Needs improvement:
Recommendation: Add symlink resolution and validation: # Resolve symlinks in parent directory
_parent_dir="$(dirname "$BASE_DIR")"
if [[ -L "$_parent_dir" ]]; then
_parent_dir="$(readlink -f "$_parent_dir")"
BASE_DIR="$_parent_dir/$(basename "$BASE_DIR")"
fi📋 Summary
Recommendation: Fix the critical race condition (#1 and #2) before merging. The current implementation likely doesn't work as intended due to argument parsing happening after 🧪 Testing ChecklistBefore merging, please verify:
Great work on this feature! The architecture is solid, but the argument parsing needs to be debugged to ensure it actually works at runtime. Happy to provide more detailed feedback on any of these points. 🚀 |
Code Review:
|
…rity Critical fixes: - Pass "$@" when sourcing config.sh to fix argument parsing race condition - BASE_DIR was always using default because $@ was empty when sourced - Now launcher.sh passes arguments: source "$SCRIPT_DIR/config.sh" "$@" Security improvements: - Change path validation from allowlist to blocklist approach - Allows custom mount points (/storage, /ai-models, etc.) - Blocks dangerous system directories (/etc, /bin, /usr, etc.) - Add symlink attack protection - Resolves symlinks in parent directory before validation - Re-validates resolved path to prevent symlink-to-system-dir attacks - Add Python-side path validation (defense in depth) - Validates absolute path requirement - Blocks system directories even if bash validation bypassed Code quality: - Export COMFY_APP_DIR in export_config() for Python persistence module - Improve realpath fallback with command existence check - Handle quoted paths edge case (strip surrounding quotes) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review: Add proper --base-directory support for custom data locationsI've reviewed PR #7 and overall this is a solid implementation with good security practices. Below is my detailed feedback: ✅ Strengths
🐛 Potential Issues1. Argument Parsing Duplication (Minor)Location: The Suggestion: Consider consolidating or adding a comment explaining why both are needed (early parsing for path setup vs. filtering for ComfyUI args). 2. Missing
|
Addresses Claude review feedback: - Add /lib32 to both bash and Python blocklists - Ensure Python blocklist matches bash exactly (add /root, /lib64) - Add comment noting blocklists must stay in sync 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review: --base-directory SupportThank you for this well-thought-out PR! This is a solid implementation that cleanly separates application code from user data. I've reviewed the changes across all commits and have some feedback organized by category. 🎯 Overall AssessmentStrengths:
Areas for Improvement:
🔒 Security Concerns1. Symlink Resolution Logic Issue (scripts/config.sh:132-136)Severity: Medium The symlink resolution only checks the parent directory, but doesn't handle the case where the final base directory itself is a symlink that could point to a system directory. # Current code only checks parent
if [[ -L "$_parent_dir" ]]; then
_resolved_parent="$(readlink -f "$_parent_dir" 2>/dev/null || echo "$_parent_dir")"
_validate_base_dir "$_resolved_parent/$(basename "$BASE_DIR")"Issue: If user does Recommendation: # Resolve the full BASE_DIR path after parent check
if [[ -L "$BASE_DIR" ]]; then
_resolved_base="$(readlink -f "$BASE_DIR" 2>/dev/null || echo "$BASE_DIR")"
_validate_base_dir "$_resolved_base"
BASE_DIR="$_resolved_base"
fi2. TOCTOU Race Condition (scripts/config.sh:139-146)Severity: Low There's a Time-of-Check-Time-of-Use gap between validation and actual directory creation/use. Recommendation: This is acceptable for a launcher script, but consider adding a comment explaining the limitation and why it's acceptable in this context. 🐛 Potential Bugs1. Quote Stripping Logic is Fragile (scripts/config.sh:73-77)Severity: Low The quote stripping doesn't handle mixed quotes or escaped quotes correctly: raw_path="${raw_path%\""}"
raw_path="${raw_path#\""}"
raw_path="${raw_path%\'"}"
raw_path="${raw_path#\'"}"Issue: Paths like Recommendation: Either remove this quote-stripping (bash's argument parsing already handles quotes), or improve it: # Only strip if surrounded by matching quotes
if [[ "$raw_path" =~ ^"\(.+\)"$ ]] || [[ "$raw_path" =~ ^'(.+)'$ ]]; then
raw_path="${BASH_REMATCH[1]}"
fi2. Inconsistent Error Handling in realpath Fallback (scripts/config.sh:83-93)Severity: Low The fallback path construction uses Recommendation: # Use HOME as base for relative paths since most users expect that
if [[ "$raw_path" == /* ]]; then
echo "$raw_path"
else
echo "$HOME/$raw_path"
fi3. Missing Validation for Empty Path (scripts/config.sh:99-103)Severity: Low If Recommendation: Add explicit check: _parsed_base_dir="$(_parse_base_directory "$@")"
if [[ -n "$_parsed_base_dir" ]]; then
BASE_DIR="$_parsed_base_dir"
elif [[ "$*" =~ --base-directory ]]; then
# User provided flag but parsing failed - this is an error
echo "ERROR: Failed to parse --base-directory argument" >&2
exit 1
fi📊 Code Quality1. Duplicate Validation Logic (scripts/config.sh & src/persistence/persistence.py)Severity: Medium The blocklist is duplicated between bash and Python with a comment to keep them in sync. This is error-prone. Recommendation:
2. Function Naming Inconsistency (scripts/config.sh)Issue: Mix of private ( Recommendation: Apply naming convention consistently:
3. Magic Number in Array Index Check (scripts/config.sh:60)Minor if [[ $((i+1)) -lt ${#args[@]} ]] && [[ \! "${args[$((i+1))]}" =~ ^-- ]]; thenRecommendation: Extract to variable for clarity: local next_idx=$((i+1))
if [[ $next_idx -lt ${#args[@]} ]] && [[ \! "${args[$next_idx]}" =~ ^-- ]]; then
raw_path="${args[$next_idx]}"4. Logging Before Logger Available (scripts/config.sh:38-39)Good Practice The comment explaining why ⚡ Performance ConsiderationsNo significant performance issues identified. The argument parsing is O(n) which is appropriate for command-line args. Minor suggestion: The validation could short-circuit early: for prefix in "${blocked_prefixes[@]}"; do
if [[ "$dir" == "$prefix" || "$dir" == "$prefix"/* ]]; then
echo "ERROR: --base-directory cannot be in system directory: $prefix" >&2
exit 1 # Already exits, good ✓
fi
done🧪 Test CoverageCritical Gap: No automated tests for this security-sensitive feature. Recommendations:
Example test structure: #\!/usr/bin/env bats
@test "--base-directory rejects /etc" {
run source config.sh --base-directory /etc
[ "$status" -eq 1 ]
[[ "$output" =~ "cannot be in system directory" ]]
}
@test "--base-directory accepts custom mount" {
run source config.sh --base-directory /mnt/ai-storage
[ "$status" -eq 0 ]
}📝 DocumentationStrengths:
Suggestions:
🔍 Architecture ReviewExcellent design decisions:
Minor concern: 🎓 Style & Best PracticesAdheres well to CLAUDE.md guidelines:
Minor deviations:
🚀 Recommendations SummaryMust Fix (before merge):
Should Fix (high priority): Nice to Have (can be follow-up): ✅ ConclusionThis is a well-engineered feature with good security awareness and clean architecture. The separation of concerns is excellent, and the implementation is mostly solid. The main concerns are:
With the symlink fix and basic tests added, this would be ready to merge. The code quality is high, documentation is good, and the feature solves a real user need (storing data on separate drives). Suggested merge strategy: Fix critical security issue (#1), add basic blocklist validation test (#3), then merge. Address other items in follow-up PR. Great work overall! 🎉 |
…tion
Fixes the two "Must Fix" items from Claude review:
1. Fix symlink resolution to check final BASE_DIR, not just parent
- If user provides --base-directory /home/user/link -> /etc,
now properly resolves and validates the target
- Prevents symlink-based bypasses of security validation
2. Add validation for empty/failed parse when flag is present
- If user provides --base-directory but parsing returns empty,
now properly errors instead of silently using default
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review for PR #7: Add --base-directory supportThank you for this well-implemented feature! This is a solid implementation that properly separates app code from user data. Below is my detailed review: ✅ Strengths1. Excellent Security Implementation
2. Good Architecture
3. Code Quality
🔍 Issues & RecommendationsCritical Issues1. Potential Argument Parsing Confusion (scripts/config.sh:220-239)The for arg in "$@"; do
if [[ "$skip_next" == "true" ]]; then
skip_next=false
continue # Skips the value after --base-directory
fi
case "$arg" in
--base-directory)
skip_next=true
;;Why it works: When Issue: This is hard to follow. The Recommendation: Restructure for clarity: parse_arguments() {
ARGS=()
local i=0
while [[ $i -lt $# ]]; do
case "${!i}" in
--base-directory=*)
# Already handled in config
;;
--base-directory)
((i++)) # Skip next arg
;;
--open|--debug|--verbose)
# Handle flags...
;;
--port=*)
COMFY_PORT="${arg#*=}"
;;
*)
ARGS+=("${!i}")
;;
esac
((i++))
done
}Or at minimum, add a comment explaining the skip_next pattern. High Priority2. Inconsistent Error Handling (scripts/config.sh:85, 91)The fallback in realpath -m "$raw_path" 2>/dev/null || echo "$raw_path"Issue: If Recommendation: Log the failure or validate the path more thoroughly: if command -v realpath &>/dev/null; then
local resolved
if resolved=$(realpath -m "$raw_path" 2>&1); then
echo "$resolved"
else
echo "WARNING: Failed to resolve path: $raw_path (using as-is)" >&2
echo "$raw_path"
fi
fi3. Missing Validation: Empty String PathNeither bash nor Python validates against empty string paths after processing. Add to if [[ -z "$dir" ]]; then
echo "ERROR: --base-directory path cannot be empty" >&2
exit 1
fiAdd to Python (persistence.py:217): if not base_dir or not base_dir.strip():
logger.error("base_dir cannot be empty")
raise ValueError("base_dir is empty")Medium Priority4. TOCTOU (Time-of-Check-Time-of-Use) Consideration (scripts/config.sh:145-149)The symlink resolution has a theoretical race condition: if [[ -L "$_parent_dir" ]]; then
_resolved_parent="$(readlink -f "$_parent_dir" 2>/dev/null || echo "$_parent_dir")"
_validate_base_dir "$_resolved_parent/$(basename "$BASE_DIR")"
_parent_dir="$_resolved_parent"
fiIssue: An attacker could change the symlink between readlink and validation. Recommendation: This is mostly theoretical (user would race against themselves), but document this limitation or use file descriptors: # Add comment explaining the limitation
# Note: TOCTOU race condition exists but is not exploitable in single-user context5. Python Type Annotations (src/persistence/persistence.py:213)The code uses base_dir = os.environ.get(
"COMFY_USER_DIR", os.path.join(os.path.expanduser("~"), ".config", "comfy-ui")
)Recommendation: This is actually fine because of the default value, but for clarity: base_dir: str = os.environ.get(
"COMFY_USER_DIR", os.path.join(os.path.expanduser("~"), ".config", "comfy-ui")
)6. Documentation: Quoting ExamplesREADME.md mentions quoting paths with spaces, but more examples would help: # Add to README:
# Paths with spaces (any of these work):
nix run -- --base-directory="/path/with spaces"
nix run -- --base-directory /path/with\ spaces
nix run -- '--base-directory=/path/with spaces'Low Priority / Nice to Have7. Blocklist vs Allowlist Trade-offsCurrent approach uses blocklist which is good for flexibility but could miss edge cases. Current (good for NixOS):
Alternative (more restrictive, maybe too much):
Recommendation: Keep current approach, it's the right balance. 8. Enhanced Logging for Security EventsWhen validation fails, consider logging: _validate_base_dir() {
local dir="$1"
# ... existing checks ...
for prefix in "${blocked_prefixes[@]}"; do
if [[ "$dir" == "$prefix" || "$dir" == "$prefix"/* ]]; then
logger -t comfyui-nix "Blocked dangerous base-directory: $dir" 2>/dev/null || true
echo "ERROR: --base-directory cannot be in system directory: $prefix" >&2
exit 1
fi
done
}9. ShellCheck ValidationRun 🧪 Testing RecommendationsThe test plan in the PR description is good, but consider adding: Edge Cases:
Security Tests:
Integration Tests:
Compatibility Tests:
📊 Performance Considerations
Recommendation: Performance is good. Network filesystem users might see slight delays during startup, but this is acceptable. 🔒 Security AssessmentOverall: Strong ✅ The implementation demonstrates excellent security awareness:
No critical security vulnerabilities identified. 📝 Documentation Quality
Suggested addition to README: ### Troubleshooting --base-directory
**Error: "Parent directory does not exist"**
- Create the parent directory first: `mkdir -p ~/AI`
**Error: "Cannot be in system directory"**
- Don't use system paths like `/etc`, `/usr`, `/bin`
- Use user-writable locations: `~/AI`, `/mnt/data`, `/opt/comfy-data`
**Path not recognized:**
- Use absolute paths: `/home/user/AI` instead of `./AI`
- Quote paths with spaces: `--base-directory="/path/with spaces"`✨ SummaryThis is a well-implemented feature with strong attention to security and architecture. The code quality is high and follows project conventions. Recommendation: Approve with minor improvementsShould fix before merge:
Nice to have: No blocking issues identified. The current implementation is production-ready. 📋 Detailed File Reviewscripts/config.sh
src/persistence/persistence.py
CLAUDE.md & README.md
Great work on this PR! The feature is architecturally sound and will be very useful for users wanting to store data on separate drives. 🎉 Estimated review time: ~30 minutes |
Summary
--base-directoryin bash config before setting up paths~/.config/comfy-ui/(managed by nix)--base-directorydocumentation and examplesThis enables storing all ComfyUI data on a separate drive (e.g., ZFS volume) while keeping the app installation in a fixed location.
Architecture
Fixed locations (always in
~/.config/comfy-ui/):app/- ComfyUI source codevenv/- Python virtual environmentUser-configurable via
--base-directory:models/- all model filesinput/- input filesoutput/- generated outputsuser/- workflows and settingscustom_nodes/- extensionsTest plan
--base-directory)--base-directory ~/AIand verify models download to correct location🤖 Generated with Claude Code