Enhance Settings.fds management and installation flag handling
- Introduced `comfy_on_disk` and `sync_is_installed` functions to manage the `IsInstalled` flag based on the presence of ComfyUI, ensuring accurate installation state reporting. - Updated `install_swarm_comfy.py` to clear the `IsInstalled` flag when necessary, allowing the installation process to proceed without conflicts. - Improved logic to prevent setting `IsInstalled` to true on first boot or empty data volumes, addressing issues with the InstallConfirmWS process. - Enhanced diagnostics and logging for installation state changes, providing clearer feedback during the installation process.
This commit is contained in:
+115
-23
@@ -287,41 +287,85 @@ import sys
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def comfy_on_disk() -> bool:
|
||||||
|
"""True only after first Comfy install — not on empty data volume."""
|
||||||
|
override = (os.environ.get("GPU_RENT_COMFY_PRESENT") or "").strip().lower()
|
||||||
|
if override in ("1", "true", "yes"):
|
||||||
|
return True
|
||||||
|
if override in ("0", "false", "no"):
|
||||||
|
return False
|
||||||
|
for cand in (
|
||||||
|
Path("/mnt/swarm_data/dlbackend/ComfyUI/venv/bin/python"),
|
||||||
|
Path("/mnt/swarm_data/dlbackend/ComfyUI/main.py"),
|
||||||
|
Path("/opt/swarmui/dlbackend/ComfyUI/venv/bin/python"),
|
||||||
|
Path("/opt/swarmui/dlbackend/ComfyUI/main.py"),
|
||||||
|
):
|
||||||
|
if cand.is_file():
|
||||||
|
return True
|
||||||
|
backends = Path("/mnt/swarm_data/Data/Backends.fds")
|
||||||
|
try:
|
||||||
|
if backends.is_file() and "StartScript" in backends.read_text(
|
||||||
|
encoding="utf-8", errors="replace"
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def sync_is_installed(text: str) -> tuple[str, str | None]:
|
||||||
|
"""Set IsInstalled true only when Comfy exists; else clear leftover true."""
|
||||||
|
if comfy_on_disk():
|
||||||
|
if re.search(r"(?im)^\s*IsInstalled:\s*true\s*$", text):
|
||||||
|
return text, None
|
||||||
|
installed_block = (
|
||||||
|
"IsInstalled: true\n"
|
||||||
|
f"InstallDate: {time.strftime('%Y-%m-%d')}\n"
|
||||||
|
"InstallVersion: gpu-rent\n"
|
||||||
|
)
|
||||||
|
if re.search(r"(?im)^\s*IsInstalled:\s*", text):
|
||||||
|
text = re.sub(r"(?im)^(\s*IsInstalled:\s*).*$", r"\1true", text, count=1)
|
||||||
|
else:
|
||||||
|
text = installed_block + text
|
||||||
|
return text, "patched IsInstalled: true (Comfy on disk)"
|
||||||
|
if re.search(r"(?im)^\s*IsInstalled:\s*true\s*$", text):
|
||||||
|
text = re.sub(r"(?im)^(\s*IsInstalled:\s*).*$", r"\1false", text, count=1)
|
||||||
|
return text, "cleared IsInstalled: false (нет Comfy — первый InstallConfirmWS)"
|
||||||
|
return text, None
|
||||||
|
|
||||||
|
|
||||||
p = Path(os.environ.get("GPU_RENT_SETTINGS_FDS") or "/mnt/swarm_data/Data/Settings.fds")
|
p = Path(os.environ.get("GPU_RENT_SETTINGS_FDS") or "/mnt/swarm_data/Data/Settings.fds")
|
||||||
fname = (os.environ.get("GPU_RENT_AUTOCOMPLETE_FILE") or "").strip()
|
fname = (os.environ.get("GPU_RENT_AUTOCOMPLETE_FILE") or "").strip()
|
||||||
if not fname:
|
if not fname:
|
||||||
print("no GPU_RENT_AUTOCOMPLETE_FILE", file=sys.stderr)
|
print("no GPU_RENT_AUTOCOMPLETE_FILE", file=sys.stderr)
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
installed_block = (
|
ac_block = (
|
||||||
|
"DefaultUser:\n"
|
||||||
|
" AutoComplete:\n"
|
||||||
|
f" Source: {fname}\n"
|
||||||
|
" EscapeParens: true\n"
|
||||||
|
)
|
||||||
|
installed_prefix = (
|
||||||
"IsInstalled: true\n"
|
"IsInstalled: true\n"
|
||||||
f"InstallDate: {time.strftime('%Y-%m-%d')}\n"
|
f"InstallDate: {time.strftime('%Y-%m-%d')}\n"
|
||||||
"InstallVersion: gpu-rent\n"
|
"InstallVersion: gpu-rent\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
block = (
|
|
||||||
installed_block
|
|
||||||
+ "DefaultUser:\n"
|
|
||||||
" AutoComplete:\n"
|
|
||||||
f" Source: {fname}\n"
|
|
||||||
" EscapeParens: true\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
if not p.is_file():
|
if not p.is_file():
|
||||||
p.parent.mkdir(parents=True, exist_ok=True)
|
p.parent.mkdir(parents=True, exist_ok=True)
|
||||||
p.write_text(block, encoding="utf-8")
|
prefix = installed_prefix if comfy_on_disk() else ""
|
||||||
print(f"created Settings.fds IsInstalled+AutoComplete.Source={fname}")
|
p.write_text(prefix + ac_block, encoding="utf-8")
|
||||||
|
extra = "+IsInstalled" if prefix else "без IsInstalled (первый Comfy install)"
|
||||||
|
print(f"created Settings.fds AutoComplete.Source={fname} {extra}")
|
||||||
raise SystemExit(0)
|
raise SystemExit(0)
|
||||||
|
|
||||||
text = p.read_text(encoding="utf-8", errors="replace")
|
text = p.read_text(encoding="utf-8", errors="replace")
|
||||||
# Never leave a Settings.fds that sends the UI to /Install.
|
text, note = sync_is_installed(text)
|
||||||
if not re.search(r"(?im)^\s*IsInstalled:\s*true\s*$", text):
|
if note:
|
||||||
if re.search(r"(?im)^\s*IsInstalled:\s*", text):
|
|
||||||
text = re.sub(r"(?im)^(\s*IsInstalled:\s*).*$", r"\1true", text, count=1)
|
|
||||||
else:
|
|
||||||
text = installed_block + text
|
|
||||||
p.write_text(text, encoding="utf-8")
|
p.write_text(text, encoding="utf-8")
|
||||||
print("patched IsInstalled: true (was missing/false)")
|
print(note)
|
||||||
text = p.read_text(encoding="utf-8", errors="replace")
|
text = p.read_text(encoding="utf-8", errors="replace")
|
||||||
|
|
||||||
if re.search(rf"^\s*Source:\s*{re.escape(fname)}\s*$", text, re.M):
|
if re.search(rf"^\s*Source:\s*{re.escape(fname)}\s*$", text, re.M):
|
||||||
@@ -356,14 +400,19 @@ if re.search(r"^DefaultUser:\s*$", text, re.M):
|
|||||||
p.write_text(new, encoding="utf-8")
|
p.write_text(new, encoding="utf-8")
|
||||||
print(f"inserted AutoComplete under DefaultUser Source={fname}")
|
print(f"inserted AutoComplete under DefaultUser Source={fname}")
|
||||||
else:
|
else:
|
||||||
p.write_text(text.rstrip() + "\n\n" + block, encoding="utf-8")
|
p.write_text(text.rstrip() + "\n\n" + ac_block, encoding="utf-8")
|
||||||
print(f"appended DefaultUser.AutoComplete Source={fname}")
|
print(f"appended DefaultUser.AutoComplete Source={fname}")
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
_ENSURE_INSTALLED_PY = r'''
|
_ENSURE_INSTALLED_PY = r'''
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Ensure Settings.fds has IsInstalled: true so UI skips /Install wizard."""
|
"""Sync Settings.fds IsInstalled with whether Comfy actually exists.
|
||||||
|
|
||||||
|
Warm re-up: backends/venv present → IsInstalled true (UI skips /Install).
|
||||||
|
First boot / empty dlbackend: do NOT set true — InstallConfirmWS refuses
|
||||||
|
with "Server is already installed!" and never clones ComfyUI.
|
||||||
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -372,6 +421,31 @@ import sys
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def comfy_on_disk() -> bool:
|
||||||
|
override = (os.environ.get("GPU_RENT_COMFY_PRESENT") or "").strip().lower()
|
||||||
|
if override in ("1", "true", "yes"):
|
||||||
|
return True
|
||||||
|
if override in ("0", "false", "no"):
|
||||||
|
return False
|
||||||
|
Path("/mnt/swarm_data/dlbackend/ComfyUI/venv/bin/python"),
|
||||||
|
Path("/mnt/swarm_data/dlbackend/ComfyUI/main.py"),
|
||||||
|
Path("/opt/swarmui/dlbackend/ComfyUI/venv/bin/python"),
|
||||||
|
Path("/opt/swarmui/dlbackend/ComfyUI/main.py"),
|
||||||
|
):
|
||||||
|
if cand.is_file():
|
||||||
|
return True
|
||||||
|
backends = Path("/mnt/swarm_data/Data/Backends.fds")
|
||||||
|
try:
|
||||||
|
if backends.is_file() and "StartScript" in backends.read_text(
|
||||||
|
encoding="utf-8", errors="replace"
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
paths = []
|
paths = []
|
||||||
env = (os.environ.get("GPU_RENT_SETTINGS_FDS") or "").strip()
|
env = (os.environ.get("GPU_RENT_SETTINGS_FDS") or "").strip()
|
||||||
if env:
|
if env:
|
||||||
@@ -392,6 +466,7 @@ for p in paths:
|
|||||||
seen.add(key)
|
seen.add(key)
|
||||||
uniq.append(p)
|
uniq.append(p)
|
||||||
|
|
||||||
|
present = comfy_on_disk()
|
||||||
changed = False
|
changed = False
|
||||||
for p in uniq:
|
for p in uniq:
|
||||||
try:
|
try:
|
||||||
@@ -408,7 +483,10 @@ for p in uniq:
|
|||||||
if same and changed:
|
if same and changed:
|
||||||
continue
|
continue
|
||||||
if not p.is_file():
|
if not p.is_file():
|
||||||
# Only create on data volume path
|
# Don't invent IsInstalled=true on empty first boot.
|
||||||
|
if not present:
|
||||||
|
print(f"skip create {p}: Comfy ещё нет")
|
||||||
|
continue
|
||||||
if "/mnt/swarm_data/" not in str(p):
|
if "/mnt/swarm_data/" not in str(p):
|
||||||
continue
|
continue
|
||||||
p.parent.mkdir(parents=True, exist_ok=True)
|
p.parent.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -424,6 +502,17 @@ for p in uniq:
|
|||||||
changed = True
|
changed = True
|
||||||
continue
|
continue
|
||||||
text = p.read_text(encoding="utf-8", errors="replace")
|
text = p.read_text(encoding="utf-8", errors="replace")
|
||||||
|
if not present:
|
||||||
|
if re.search(r"(?im)^\s*IsInstalled:\s*true\s*$", text):
|
||||||
|
text = re.sub(
|
||||||
|
r"(?im)^(\s*IsInstalled:\s*).*$", r"\1false", text, count=1
|
||||||
|
)
|
||||||
|
p.write_text(text, encoding="utf-8")
|
||||||
|
print(f"cleared {p}: IsInstalled false (нет Comfy — первый install)")
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
print(f"ok {p}: IsInstalled not true, Comfy отсутствует")
|
||||||
|
continue
|
||||||
if re.search(r"(?im)^\s*IsInstalled:\s*true\s*$", text):
|
if re.search(r"(?im)^\s*IsInstalled:\s*true\s*$", text):
|
||||||
print(f"ok {p}: IsInstalled true")
|
print(f"ok {p}: IsInstalled true")
|
||||||
continue
|
continue
|
||||||
@@ -475,7 +564,10 @@ def _merge_autocomplete_into_settings(
|
|||||||
|
|
||||||
|
|
||||||
def ensure_settings_is_installed(cfg: Config, host: str, log: Log) -> bool:
|
def ensure_settings_is_installed(cfg: Config, host: str, log: Log) -> bool:
|
||||||
"""Make sure Settings.fds has IsInstalled:true (UI /Install wizard).
|
"""Sync Settings.fds IsInstalled with Comfy on disk.
|
||||||
|
|
||||||
|
Warm: backends/venv exist → true (skip UI /Install). First boot / empty
|
||||||
|
dlbackend → leave false/missing so InstallConfirmWS can clone Comfy.
|
||||||
|
|
||||||
Returns True if the file was created/patched (caller may need SwarmUI restart).
|
Returns True if the file was created/patched (caller may need SwarmUI restart).
|
||||||
"""
|
"""
|
||||||
@@ -936,7 +1028,7 @@ def provision_vm(
|
|||||||
)
|
)
|
||||||
if cfg.pull_output:
|
if cfg.pull_output:
|
||||||
pull_tree(cfg, host, f"{DATA}/Output", cfg.local_output_dir, log)
|
pull_tree(cfg, host, f"{DATA}/Output", cfg.local_output_dir, log)
|
||||||
# Avoid /Install wizard when backends already exist but Settings lack flag.
|
# Warm: IsInstalled true if Comfy exists. First boot: keep false.
|
||||||
if ensure_settings_is_installed(cfg, host, log):
|
if ensure_settings_is_installed(cfg, host, log):
|
||||||
restart = True
|
restart = True
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
"""Headless SwarmUI first-install: ComfyUI backend via InstallConfirmWS.
|
"""Headless SwarmUI first-install: ComfyUI backend via InstallConfirmWS.
|
||||||
|
|
||||||
Runs on the VM (stdlib only). Idempotent: skips when backends exist or
|
Runs on the VM (stdlib only). Idempotent: skips when backends exist or
|
||||||
dlbackend/ComfyUI venv is already present and IsInstalled.
|
dlbackend/ComfyUI venv is already present.
|
||||||
|
|
||||||
|
InstallConfirmWS refuses with ``Server is already installed!`` if
|
||||||
|
Settings.fds has IsInstalled:true — even when Comfy was never cloned.
|
||||||
|
First boot and leftover flags must clear that bit before the WS call.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -778,6 +782,35 @@ def settings_is_installed() -> bool | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def clear_settings_installed_flag() -> bool:
|
||||||
|
"""Set IsInstalled:false so InstallConfirmWS is allowed to run."""
|
||||||
|
if not SETTINGS.is_file():
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
text = SETTINGS.read_text(encoding="utf-8", errors="replace")
|
||||||
|
except OSError as exc:
|
||||||
|
print(f"WARN Settings read: {exc}", flush=True)
|
||||||
|
return False
|
||||||
|
if re.search(r"(?im)^\s*IsInstalled:\s*false\s*$", text):
|
||||||
|
return False
|
||||||
|
if re.search(r"(?im)^\s*IsInstalled:\s*", text):
|
||||||
|
text = re.sub(r"(?im)^(\s*IsInstalled:\s*).*$", r"\1false", text, count=1)
|
||||||
|
else:
|
||||||
|
text = "IsInstalled: false\n" + text
|
||||||
|
SETTINGS.write_text(text, encoding="utf-8")
|
||||||
|
print(
|
||||||
|
"patched Settings.fds IsInstalled=false (InstallConfirmWS)",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_first_install() -> None:
|
||||||
|
"""Clear leftover IsInstalled and reload Swarm so the installer WS works."""
|
||||||
|
if clear_settings_installed_flag():
|
||||||
|
restart_swarmui_local()
|
||||||
|
|
||||||
|
|
||||||
def ensure_settings_installed_flag() -> bool:
|
def ensure_settings_installed_flag() -> bool:
|
||||||
"""Write IsInstalled:true if missing — UI otherwise stays on /Install."""
|
"""Write IsInstalled:true if missing — UI otherwise stays on /Install."""
|
||||||
SETTINGS.parent.mkdir(parents=True, exist_ok=True)
|
SETTINGS.parent.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -1086,14 +1119,16 @@ def main() -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
installed = settings_is_installed()
|
installed = settings_is_installed()
|
||||||
if installed is True and not comfy_venv_ok() and bstat == "empty":
|
# Leftover IsInstalled=true (autocomplete/warm patch, failed previous up)
|
||||||
|
# makes InstallConfirmWS return "Server is already installed!" and skip clone.
|
||||||
|
if installed is True and not comfy_venv_ok() and need_recover:
|
||||||
print(
|
print(
|
||||||
"WARN: Settings IsInstalled=true but backends empty and no Comfy venv. "
|
"IsInstalled=true, backends empty, нет Comfy venv — "
|
||||||
"Open SwarmUI → Server → Backends and add ComfyUI Self-Starting, "
|
"сбрасываю флаг и ставлю Comfy через InstallConfirmWS",
|
||||||
"or delete Data/Settings.fds IsInstalled and re-run up."
|
flush=True,
|
||||||
)
|
)
|
||||||
run_diagnostics()
|
prepare_first_install()
|
||||||
return 1
|
installed = settings_is_installed()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
installed is True
|
installed is True
|
||||||
@@ -1138,6 +1173,19 @@ def main() -> int:
|
|||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
|
sid = get_session(time.time() + 120)
|
||||||
|
try:
|
||||||
|
run_install(sid)
|
||||||
|
except SystemExit as exc:
|
||||||
|
msg = str(exc).lower()
|
||||||
|
if "already installed" not in msg:
|
||||||
|
raise
|
||||||
|
print(
|
||||||
|
"InstallConfirmWS: Server is already installed — "
|
||||||
|
"сброс IsInstalled и повтор",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
prepare_first_install()
|
||||||
sid = get_session(time.time() + 120)
|
sid = get_session(time.time() + 120)
|
||||||
run_install(sid)
|
run_install(sid)
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
Reference in New Issue
Block a user