This commit is contained in:
+120
-13
@@ -1,30 +1,137 @@
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
from rich.console import Console
|
||||
from typing import Dict, Any
|
||||
import json
|
||||
import traceback
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
import typer
|
||||
from typing import Any, Dict, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, field_validator
|
||||
from rich.console import Console
|
||||
|
||||
from utils.tools import CONFIGS
|
||||
|
||||
|
||||
class ToolsConfig(BaseModel):
|
||||
apktool_jar_url: str
|
||||
apktool_wrapper_url: str
|
||||
|
||||
@field_validator("apktool_jar_url", "apktool_wrapper_url")
|
||||
@classmethod
|
||||
def validate_url(cls, v: str) -> str:
|
||||
if not v.startswith(("http://", "https://")):
|
||||
raise ValueError("URL должен начинаться с http:// или https://")
|
||||
return v
|
||||
|
||||
|
||||
class SigningConfig(BaseModel):
|
||||
keystore: Path = Field(default=Path("keystore.jks"))
|
||||
keystore_pass_file: Path = Field(default=Path("keystore.pass"))
|
||||
v1_signing: bool = False
|
||||
v2_signing: bool = True
|
||||
v3_signing: bool = True
|
||||
|
||||
|
||||
class BuildConfig(BaseModel):
|
||||
verbose: bool = False
|
||||
force: bool = False
|
||||
clean_after_build: bool = True
|
||||
|
||||
|
||||
class Config(BaseModel):
|
||||
tools: ToolsConfig
|
||||
base: Dict[str, Any]
|
||||
|
||||
|
||||
class PatchConfig(BaseModel):
|
||||
enabled: bool = Field(True, description="Включить или отключить патч")
|
||||
signing: SigningConfig = Field(default_factory=SigningConfig)
|
||||
build: BuildConfig = Field(default_factory=BuildConfig)
|
||||
base: Dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
def load_config(console: Console) -> Config:
|
||||
try:
|
||||
return Config.model_validate_json(Path("config.json").read_text())
|
||||
except FileNotFoundError:
|
||||
"""Загружает и валидирует конфигурацию"""
|
||||
config_path = Path("config.json")
|
||||
|
||||
if not config_path.exists():
|
||||
console.print("[red]Файл config.json не найден")
|
||||
raise typer.Exit(1)
|
||||
|
||||
try:
|
||||
return Config.model_validate_json(config_path.read_text())
|
||||
except ValidationError as e:
|
||||
console.print("[red]Ошибка валидации config.json:", e)
|
||||
console.print(f"[red]Ошибка валидации config.json:\n{e}")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
class PatchTemplate(BaseModel, ABC):
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True, validate_default=True)
|
||||
|
||||
enabled: bool = Field(default=True, description="Включить или отключить патч")
|
||||
priority: int = Field(default=0, description="Приоритет применения патча")
|
||||
|
||||
_name: str = PrivateAttr()
|
||||
_applied: bool = PrivateAttr(default=False)
|
||||
_console: Console | None = PrivateAttr(default=None)
|
||||
|
||||
def __init__(self, name: str, console: Console, **data):
|
||||
loaded_data = self._load_config_static(name, console)
|
||||
|
||||
merged_data = {**loaded_data, **data}
|
||||
|
||||
valid_fields = set(self.model_fields.keys())
|
||||
filtered_data = {k: v for k, v in merged_data.items() if k in valid_fields}
|
||||
|
||||
super().__init__(**filtered_data)
|
||||
self._name = name
|
||||
self._console = console
|
||||
self._applied = False
|
||||
|
||||
@staticmethod
|
||||
def _load_config_static(name: str, console: Console | None) -> Dict[str, Any]:
|
||||
"""Загружает конфигурацию из файла (статический метод)"""
|
||||
config_path = CONFIGS / f"{name}.json"
|
||||
try:
|
||||
if config_path.exists():
|
||||
return json.loads(config_path.read_text())
|
||||
except Exception as e:
|
||||
if console:
|
||||
console.print(
|
||||
f"[red]Ошибка при загрузке конфигурации патча {name}: {e}"
|
||||
)
|
||||
console.print(f"[yellow]Используются значения по умолчанию")
|
||||
return {}
|
||||
|
||||
def save_config(self) -> None:
|
||||
"""Сохраняет конфигурацию в файл"""
|
||||
config_path = CONFIGS / f"{self._name}.json"
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
config_path.write_text(self.model_dump_json(indent=2))
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def applied(self) -> bool:
|
||||
return self._applied
|
||||
|
||||
@applied.setter
|
||||
def applied(self, value: bool) -> None:
|
||||
self._applied = value
|
||||
|
||||
@property
|
||||
def console(self) -> Console | None:
|
||||
return self._console
|
||||
|
||||
@abstractmethod
|
||||
def apply(self, base: Dict[str, Any]) -> Any:
|
||||
raise NotImplementedError(
|
||||
"Попытка применения шаблона патча, а не его реализации"
|
||||
)
|
||||
|
||||
def safe_apply(self, base: Dict[str, Any]) -> bool:
|
||||
"""Безопасно применяет патч с обработкой ошибок"""
|
||||
try:
|
||||
self._applied = self.apply(base)
|
||||
return self._applied
|
||||
except Exception as e:
|
||||
if self._console:
|
||||
self._console.print(f"[red]Ошибка в патче {self._name}: {e}")
|
||||
if base.get("verbose"):
|
||||
self._console.print_exception()
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user