2023-05-14 07:48:34 +00:00
|
|
|
import copy
|
2023-09-09 10:36:14 +00:00
|
|
|
import os
|
2023-05-14 07:48:34 +00:00
|
|
|
import subprocess
|
2024-04-22 15:47:37 +00:00
|
|
|
import sys
|
2023-05-14 07:48:34 +00:00
|
|
|
from typing import Optional, Union
|
|
|
|
|
|
|
|
from deploy.Windows.logger import logger
|
2023-09-09 10:36:14 +00:00
|
|
|
from deploy.Windows.utils import DEPLOY_CONFIG, DEPLOY_TEMPLATE, cached_property, poor_yaml_read, poor_yaml_write
|
2023-05-14 07:48:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ExecutionError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class ConfigModel:
|
|
|
|
# Git
|
|
|
|
Repository: str = "https://github.com/LmeSzinc/AzurLaneAutoScript"
|
|
|
|
Branch: str = "master"
|
|
|
|
GitExecutable: str = "./toolkit/Git/mingw64/bin/git.exe"
|
|
|
|
GitProxy: Optional[str] = None
|
|
|
|
SSLVerify: bool = False
|
|
|
|
AutoUpdate: bool = True
|
|
|
|
KeepLocalChanges: bool = False
|
|
|
|
|
|
|
|
# Python
|
|
|
|
PythonExecutable: str = "./toolkit/python.exe"
|
|
|
|
PypiMirror: Optional[str] = None
|
|
|
|
InstallDependencies: bool = True
|
|
|
|
RequirementsFile: str = "requirements.txt"
|
|
|
|
|
|
|
|
# Adb
|
|
|
|
AdbExecutable: str = "./toolkit/Lib/site-packages/adbutils/binaries/adb.exe"
|
|
|
|
ReplaceAdb: bool = True
|
|
|
|
AutoConnect: bool = True
|
|
|
|
InstallUiautomator2: bool = True
|
|
|
|
|
|
|
|
# Ocr
|
|
|
|
UseOcrServer: bool = False
|
|
|
|
StartOcrServer: bool = False
|
|
|
|
OcrServerPort: int = 22268
|
|
|
|
OcrClientAddress: str = "127.0.0.1:22268"
|
|
|
|
|
|
|
|
# Update
|
|
|
|
EnableReload: bool = True
|
|
|
|
CheckUpdateInterval: int = 5
|
|
|
|
AutoRestartTime: str = "03:50"
|
|
|
|
|
|
|
|
# Misc
|
|
|
|
DiscordRichPresence: bool = False
|
|
|
|
|
|
|
|
# Remote Access
|
|
|
|
EnableRemoteAccess: bool = False
|
|
|
|
SSHUser: Optional[str] = None
|
|
|
|
SSHServer: Optional[str] = None
|
|
|
|
SSHExecutable: Optional[str] = None
|
|
|
|
|
|
|
|
# Webui
|
|
|
|
WebuiHost: str = "0.0.0.0"
|
2023-09-09 18:10:57 +00:00
|
|
|
WebuiPort: int = 22367
|
2023-05-14 07:48:34 +00:00
|
|
|
Language: str = "en-US"
|
|
|
|
Theme: str = "default"
|
|
|
|
DpiScaling: bool = True
|
|
|
|
Password: Optional[str] = None
|
|
|
|
CDN: Union[str, bool] = False
|
|
|
|
Run: Optional[str] = None
|
2023-09-09 18:10:57 +00:00
|
|
|
AppAsarUpdate: bool = True
|
|
|
|
NoSandbox: bool = True
|
|
|
|
|
|
|
|
# Dynamic
|
|
|
|
GitOverCdn: bool = False
|
2023-05-14 07:48:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
class DeployConfig(ConfigModel):
|
|
|
|
def __init__(self, file=DEPLOY_CONFIG):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
file (str): User deploy config.
|
|
|
|
"""
|
|
|
|
self.file = file
|
|
|
|
self.config = {}
|
|
|
|
self.config_template = {}
|
|
|
|
self.read()
|
2023-09-09 10:36:14 +00:00
|
|
|
|
2023-05-14 07:48:34 +00:00
|
|
|
self.write()
|
|
|
|
self.show_config()
|
|
|
|
|
|
|
|
def show_config(self):
|
|
|
|
logger.hr("Show deploy config", 1)
|
|
|
|
for k, v in self.config.items():
|
|
|
|
if k in ("Password", "SSHUser"):
|
|
|
|
continue
|
|
|
|
if self.config_template[k] == v:
|
|
|
|
continue
|
|
|
|
logger.info(f"{k}: {v}")
|
|
|
|
|
|
|
|
logger.info(f"Rest of the configs are the same as default")
|
|
|
|
|
|
|
|
def read(self):
|
|
|
|
self.config = poor_yaml_read(DEPLOY_TEMPLATE)
|
|
|
|
self.config_template = copy.deepcopy(self.config)
|
|
|
|
self.config.update(poor_yaml_read(self.file))
|
|
|
|
|
|
|
|
for key, value in self.config.items():
|
|
|
|
if hasattr(self, key):
|
|
|
|
super().__setattr__(key, value)
|
|
|
|
|
2024-04-22 15:32:53 +00:00
|
|
|
self.config_redirect()
|
|
|
|
|
2023-05-14 07:48:34 +00:00
|
|
|
def write(self):
|
|
|
|
poor_yaml_write(self.config, self.file)
|
|
|
|
|
2024-04-22 15:32:53 +00:00
|
|
|
def config_redirect(self):
|
|
|
|
"""
|
|
|
|
Redirect deploy config, must be called after each `read()`
|
|
|
|
"""
|
|
|
|
# Bypass webui.config.DeployConfig.__setattr__()
|
|
|
|
# Don't write these into deploy.yaml
|
|
|
|
super().__setattr__('GitOverCdn', self.Repository in ['cn'])
|
|
|
|
if self.Repository in ['global', 'cn']:
|
|
|
|
super().__setattr__('Repository', 'https://github.com/LmeSzinc/StarRailCopilot')
|
|
|
|
|
2023-05-14 07:48:34 +00:00
|
|
|
def filepath(self, path):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
path (str):
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
str: Absolute filepath.
|
|
|
|
"""
|
2023-09-10 06:52:37 +00:00
|
|
|
if os.path.isabs(path):
|
|
|
|
return path
|
|
|
|
|
2023-05-14 07:48:34 +00:00
|
|
|
return (
|
|
|
|
os.path.abspath(os.path.join(self.root_filepath, path))
|
|
|
|
.replace(r"\\", "/")
|
|
|
|
.replace("\\", "/")
|
|
|
|
)
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def root_filepath(self):
|
|
|
|
return (
|
|
|
|
os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
|
|
|
|
.replace(r"\\", "/")
|
|
|
|
.replace("\\", "/")
|
|
|
|
)
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def adb(self) -> str:
|
|
|
|
exe = self.filepath(self.AdbExecutable)
|
|
|
|
if os.path.exists(exe):
|
|
|
|
return exe
|
|
|
|
|
2024-04-22 15:47:37 +00:00
|
|
|
logger.warning(f'AdbExecutable: {exe} does not exist, use `adb` instead')
|
2023-05-14 07:48:34 +00:00
|
|
|
return 'adb'
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def git(self) -> str:
|
|
|
|
exe = self.filepath(self.GitExecutable)
|
|
|
|
if os.path.exists(exe):
|
|
|
|
return exe
|
|
|
|
|
2024-04-22 15:47:37 +00:00
|
|
|
logger.warning(f'GitExecutable: {exe} does not exist, use `git` instead')
|
2023-05-14 07:48:34 +00:00
|
|
|
return 'git'
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def python(self) -> str:
|
2024-04-22 15:47:37 +00:00
|
|
|
exe = self.filepath(self.PythonExecutable)
|
|
|
|
if os.path.exists(exe):
|
|
|
|
return exe
|
|
|
|
|
|
|
|
current = sys.executable.replace("\\", "/")
|
|
|
|
logger.warning(f'PythonExecutable: {exe} does not exist, use current python instead: {current}')
|
|
|
|
return current
|
2023-05-14 07:48:34 +00:00
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def requirements_file(self) -> str:
|
|
|
|
if self.RequirementsFile == 'requirements.txt':
|
|
|
|
return 'requirements.txt'
|
|
|
|
else:
|
|
|
|
return self.filepath(self.RequirementsFile)
|
|
|
|
|
|
|
|
def execute(self, command, allow_failure=False, output=True):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
command (str):
|
|
|
|
allow_failure (bool):
|
|
|
|
output(bool):
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: If success.
|
|
|
|
Terminate installation if failed to execute and not allow_failure.
|
|
|
|
"""
|
|
|
|
command = command.replace(r"\\", "/").replace("\\", "/").replace('"', '"')
|
|
|
|
if not output:
|
|
|
|
command = command + ' >nul 2>nul'
|
|
|
|
logger.info(command)
|
|
|
|
error_code = os.system(command)
|
|
|
|
if error_code:
|
|
|
|
if allow_failure:
|
|
|
|
logger.info(f"[ allowed failure ], error_code: {error_code}")
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
logger.info(f"[ failure ], error_code: {error_code}")
|
|
|
|
self.show_error(command)
|
|
|
|
raise ExecutionError
|
|
|
|
else:
|
|
|
|
logger.info(f"[ success ]")
|
|
|
|
return True
|
|
|
|
|
|
|
|
def subprocess_execute(self, cmd, timeout=10):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
cmd (list[str]):
|
|
|
|
timeout:
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
str:
|
|
|
|
"""
|
|
|
|
logger.info(' '.join(cmd))
|
|
|
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
|
|
|
|
try:
|
|
|
|
stdout, stderr = process.communicate(timeout=timeout)
|
|
|
|
process.kill()
|
|
|
|
except subprocess.TimeoutExpired:
|
|
|
|
process.kill()
|
|
|
|
stdout, stderr = process.communicate()
|
|
|
|
logger.info(f'TimeoutExpired, stdout={stdout}, stderr={stderr}')
|
|
|
|
return stdout.decode()
|
|
|
|
|
|
|
|
def show_error(self, command=None):
|
|
|
|
logger.hr("Update failed", 0)
|
|
|
|
self.show_config()
|
|
|
|
logger.info("")
|
|
|
|
logger.info(f"Last command: {command}")
|
|
|
|
logger.info(
|
|
|
|
"Please check your deploy settings in config/deploy.yaml "
|
|
|
|
"and re-open Alas.exe"
|
|
|
|
)
|
|
|
|
logger.info("Take the screenshot of entire window if you need help")
|