Refactor environment and configuration management
- Updated the project structure to store configuration files (.env, models.yaml, extensions.yaml) in the project root instead of the user's home directory. - Enhanced the setup process to automatically copy example files to the project root on first run. - Implemented a migration function to transfer legacy configuration files from the user's home directory to the new project structure. - Revised documentation to reflect changes in file locations and setup instructions. - Improved code readability and maintainability by refactoring path management functions.
This commit is contained in:
+15
-10
@@ -1,4 +1,4 @@
|
||||
"""Load ~/.gpu-rent/.env. No secrets in git."""
|
||||
"""Load <project>/.env. No secrets in git."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -10,10 +10,13 @@ from dotenv import load_dotenv
|
||||
|
||||
from gpu_rent.errors import ConfigError
|
||||
from gpu_rent.paths import (
|
||||
app_root,
|
||||
default_ssh_key_path,
|
||||
detect_app_root,
|
||||
env_path,
|
||||
home_dir,
|
||||
extensions_manifest_path,
|
||||
migrate_legacy_if_needed,
|
||||
models_manifest_path,
|
||||
runtime_dir,
|
||||
)
|
||||
|
||||
|
||||
@@ -96,12 +99,14 @@ def _required(name: str) -> str:
|
||||
|
||||
|
||||
def load_config(*, require_auth: bool = True) -> Config:
|
||||
home_dir().mkdir(parents=True, exist_ok=True)
|
||||
root = app_root()
|
||||
runtime_dir().mkdir(parents=True, exist_ok=True)
|
||||
migrate_legacy_if_needed()
|
||||
|
||||
env_file = env_path()
|
||||
if env_file.is_file():
|
||||
load_dotenv(env_file, override=False)
|
||||
|
||||
app_root = detect_app_root()
|
||||
missing: list[str] = []
|
||||
required = (
|
||||
"OS_AUTH_URL",
|
||||
@@ -119,7 +124,7 @@ def load_config(*, require_auth: bool = True) -> Config:
|
||||
|
||||
if require_auth and missing:
|
||||
raise ConfigError(
|
||||
"В ~/.gpu-rent/.env не хватает: "
|
||||
f"В {env_file} не хватает: "
|
||||
+ ", ".join(missing)
|
||||
+ ". Как заполнить: docs/setup.md (сервисный пользователь, не X-Token)."
|
||||
)
|
||||
@@ -128,16 +133,16 @@ def load_config(*, require_auth: bool = True) -> Config:
|
||||
ssh_key = Path(key_override).expanduser() if key_override else default_ssh_key_path()
|
||||
|
||||
models_manifest = Path(
|
||||
(os.environ.get("MODELS_MANIFEST") or "").strip() or (home_dir() / "models.yaml")
|
||||
(os.environ.get("MODELS_MANIFEST") or "").strip() or str(models_manifest_path())
|
||||
).expanduser()
|
||||
extensions_manifest = Path(
|
||||
(os.environ.get("EXTENSIONS_MANIFEST") or "").strip()
|
||||
or (home_dir() / "extensions.yaml")
|
||||
or str(extensions_manifest_path())
|
||||
).expanduser()
|
||||
|
||||
def _dir(env_name: str, folder: str) -> Path:
|
||||
raw = (os.environ.get(env_name) or "").strip()
|
||||
return Path(raw).expanduser() if raw else (app_root / folder)
|
||||
return Path(raw).expanduser() if raw else (root / folder)
|
||||
|
||||
return Config(
|
||||
os_auth_url=values["OS_AUTH_URL"] or "https://cloud.api.selcloud.ru/identity/v3",
|
||||
@@ -162,7 +167,7 @@ def load_config(*, require_auth: bool = True) -> Config:
|
||||
local_wildcards_dir=_dir("LOCAL_WILDCARDS_DIR", "Wildcards"),
|
||||
local_workflows_dir=_dir("LOCAL_WORKFLOWS_DIR", "CustomWorkflows"),
|
||||
local_output_dir=_dir("LOCAL_OUTPUT_DIR", "Output"),
|
||||
app_root=app_root,
|
||||
app_root=root,
|
||||
autocomplete_enabled=_as_bool(os.environ.get("AUTOCOMPLETE_ENABLED"), True),
|
||||
autocomplete_github_repo=(
|
||||
os.environ.get("AUTOCOMPLETE_GITHUB_REPO") or "DominikDoom/a1111-sd-webui-tagcomplete"
|
||||
|
||||
@@ -27,7 +27,7 @@ from gpu_rent.os_client import (
|
||||
volume_quotas,
|
||||
)
|
||||
from gpu_rent.payload import folder_bytes as _folder_bytes, has_payload as _has_payload
|
||||
from gpu_rent.paths import env_path, home_dir
|
||||
from gpu_rent.paths import env_path, runtime_dir
|
||||
from gpu_rent.ssh_keys import key_ready
|
||||
from gpu_rent.state import load_state
|
||||
|
||||
@@ -42,7 +42,7 @@ class Check:
|
||||
|
||||
def run_doctor() -> list[Check]:
|
||||
checks: list[Check] = []
|
||||
home_dir().mkdir(parents=True, exist_ok=True)
|
||||
runtime_dir().mkdir(parents=True, exist_ok=True)
|
||||
|
||||
env_file = env_path()
|
||||
if env_file.is_file():
|
||||
|
||||
+79
-21
@@ -1,33 +1,91 @@
|
||||
"""Paths: ~/.gpu-rent, app tree, SSH key."""
|
||||
"""Paths: project root, local .gpu-rent runtime, SSH key."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def home_dir() -> Path:
|
||||
return Path.home() / ".gpu-rent"
|
||||
|
||||
|
||||
def env_path() -> Path:
|
||||
return home_dir() / ".env"
|
||||
|
||||
|
||||
def state_path() -> Path:
|
||||
return home_dir() / "state.json"
|
||||
|
||||
|
||||
def lock_path() -> Path:
|
||||
return home_dir() / "gpu-rent.lock"
|
||||
|
||||
|
||||
def default_ssh_key_path() -> Path:
|
||||
return home_dir() / "id_ed25519"
|
||||
|
||||
|
||||
def detect_app_root() -> Path:
|
||||
cwd = Path.cwd().resolve()
|
||||
for candidate in (cwd, *cwd.parents):
|
||||
if (candidate / "Models").is_dir() and (candidate / "docs").is_dir():
|
||||
return candidate
|
||||
return cwd
|
||||
|
||||
|
||||
def app_root() -> Path:
|
||||
return detect_app_root()
|
||||
|
||||
|
||||
def runtime_dir() -> Path:
|
||||
"""State, lock, SSH key — always inside the project (gitignored)."""
|
||||
return app_root() / ".gpu-rent"
|
||||
|
||||
|
||||
# Back-compat alias used across the package.
|
||||
def home_dir() -> Path:
|
||||
return runtime_dir()
|
||||
|
||||
|
||||
def env_path() -> Path:
|
||||
return app_root() / ".env"
|
||||
|
||||
|
||||
def models_manifest_path() -> Path:
|
||||
return app_root() / "models.yaml"
|
||||
|
||||
|
||||
def extensions_manifest_path() -> Path:
|
||||
return app_root() / "extensions.yaml"
|
||||
|
||||
|
||||
def state_path() -> Path:
|
||||
return runtime_dir() / "state.json"
|
||||
|
||||
|
||||
def lock_path() -> Path:
|
||||
return runtime_dir() / "gpu-rent.lock"
|
||||
|
||||
|
||||
def default_ssh_key_path() -> Path:
|
||||
return runtime_dir() / "id_ed25519"
|
||||
|
||||
|
||||
def legacy_user_dir() -> Path:
|
||||
"""Old location (~/.gpu-rent) — only for one-shot migrate."""
|
||||
return Path.home() / ".gpu-rent"
|
||||
|
||||
|
||||
def migrate_legacy_if_needed() -> list[str]:
|
||||
"""Copy ~/.gpu-rent leftovers into the project once. Returns log lines."""
|
||||
legacy = legacy_user_dir()
|
||||
if not legacy.is_dir():
|
||||
return []
|
||||
root = app_root()
|
||||
runtime = runtime_dir()
|
||||
notes: list[str] = []
|
||||
|
||||
pairs = (
|
||||
(legacy / ".env", env_path()),
|
||||
(legacy / "models.yaml", models_manifest_path()),
|
||||
(legacy / "extensions.yaml", extensions_manifest_path()),
|
||||
(legacy / "state.json", state_path()),
|
||||
(legacy / "id_ed25519", default_ssh_key_path()),
|
||||
(legacy / "id_ed25519.pub", default_ssh_key_path().with_suffix(".pub")),
|
||||
)
|
||||
for src, dst in pairs:
|
||||
if not src.is_file() or dst.is_file():
|
||||
continue
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(src, dst)
|
||||
notes.append(f"перенёс {src} → {dst}")
|
||||
if notes:
|
||||
runtime.mkdir(parents=True, exist_ok=True)
|
||||
marker = runtime / ".migrated-from-user-home"
|
||||
if not marker.is_file():
|
||||
marker.write_text(
|
||||
f"from {legacy}\n" + "\n".join(notes) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return notes
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Ed25519 key at ~/.gpu-rent/id_ed25519 (no passphrase)."""
|
||||
"""Ed25519 key at <project>/.gpu-rent/id_ed25519 (no passphrase)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from gpu_rent.paths import home_dir, state_path
|
||||
from gpu_rent.paths import runtime_dir, state_path
|
||||
|
||||
CURRENT_VERSION = 1
|
||||
|
||||
@@ -60,7 +60,7 @@ def load_state() -> SessionState:
|
||||
|
||||
|
||||
def save_state(state: SessionState) -> None:
|
||||
home_dir().mkdir(parents=True, exist_ok=True)
|
||||
runtime_dir().mkdir(parents=True, exist_ok=True)
|
||||
path = state_path()
|
||||
path.write_text(json.dumps(state.to_dict(), indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user