- Updated CLI documentation to reflect the new handling of `CIVITAI_API_TOKEN`, which is now automatically passed to SwarmUI user settings during startup. - Improved the `render_access_panel` function to include additional warnings for idle-killer failures and stack errors, enhancing user feedback. - Introduced a new function `seed_swarmui_api_keys` to manage API key injection into SwarmUI, ensuring seamless integration with the Model Downloader. - Enhanced GPU environment verification logic to include fail-fast checks for critical components like CUDA, improving error handling and user notifications. - Updated tests to validate the new API key handling and access panel behavior, ensuring robustness in the integration process.
98 lines
3.1 KiB
Python
98 lines
3.1 KiB
Python
"""SFTP push/pull of local app folders. Never delete remote extras."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
from pathlib import Path
|
|
|
|
from gpu_rent.config import Config
|
|
from gpu_rent.payload import has_payload, iter_payload_files, model_push_set, sha256_file
|
|
from gpu_rent.ssh_ops import (
|
|
get_file_on,
|
|
open_ssh,
|
|
put_file_on,
|
|
remote_sha256_on,
|
|
run_ssh_on,
|
|
)
|
|
|
|
Log = Callable[[str], None]
|
|
|
|
|
|
def push_tree(
|
|
cfg: Config,
|
|
host: str,
|
|
local_root: Path,
|
|
remote_root: str,
|
|
log: Log,
|
|
*,
|
|
models: bool,
|
|
) -> int:
|
|
if not has_payload(local_root):
|
|
log(f"push {local_root.name}: пусто — skip")
|
|
return 0
|
|
files = model_push_set(local_root) if models else iter_payload_files(local_root)
|
|
file_list = list(files)
|
|
total = len(file_list)
|
|
sent = 0
|
|
skipped = 0
|
|
client = open_ssh(cfg, host)
|
|
try:
|
|
for i, path in enumerate(file_list, start=1):
|
|
rel = path.relative_to(local_root).as_posix()
|
|
remote = f"{remote_root.rstrip('/')}/{rel}"
|
|
local_hash = sha256_file(path)
|
|
remote_hash = remote_sha256_on(client, remote)
|
|
if remote_hash and remote_hash.lower() == local_hash.lower():
|
|
skipped += 1
|
|
continue
|
|
log(f"push [{i}/{total}] {rel}")
|
|
put_file_on(client, path, remote)
|
|
sent += 1
|
|
finally:
|
|
client.close()
|
|
if sent == 0:
|
|
log(f"push {local_root.name}: всё уже на VM ({total} файл(ов), skip={skipped})")
|
|
else:
|
|
log(f"push {local_root.name}: {sent}/{total} отправлено (skip={skipped})")
|
|
return sent
|
|
|
|
|
|
def pull_tree(cfg: Config, host: str, remote_root: str, local_root: Path, log: Log) -> int:
|
|
client = open_ssh(cfg, host)
|
|
try:
|
|
listing = run_ssh_on(
|
|
client,
|
|
f"find {remote_root} -type f 2>/dev/null | sed 's|^{remote_root}/||'",
|
|
check=False,
|
|
timeout=120,
|
|
)
|
|
names = [line.strip() for line in listing.splitlines() if line.strip()]
|
|
work = [
|
|
rel
|
|
for rel in names
|
|
if not (
|
|
rel.endswith("/.gitkeep")
|
|
or rel.rsplit("/", 1)[-1] in {".gitkeep", "README.md"}
|
|
)
|
|
]
|
|
total = len(work)
|
|
pulled = 0
|
|
skipped = 0
|
|
for i, rel in enumerate(work, start=1):
|
|
remote = f"{remote_root.rstrip('/')}/{rel}"
|
|
local = local_root / rel
|
|
remote_hash = remote_sha256_on(client, remote)
|
|
if local.is_file() and remote_hash and sha256_file(local).lower() == remote_hash.lower():
|
|
skipped += 1
|
|
continue
|
|
log(f"pull [{i}/{total}] {rel}")
|
|
get_file_on(client, remote, local)
|
|
pulled += 1
|
|
finally:
|
|
client.close()
|
|
if pulled:
|
|
log(f"pull Output: {pulled}/{total} (skip={skipped})")
|
|
else:
|
|
log(f"pull Output: нечего забирать ({total} файл(ов), skip={skipped})")
|
|
return pulled
|