1
0
mirror of https://gitee.com/sui-feng-cb/AzurLaneAutoScript1 synced 2026-03-09 18:39:04 +08:00
AzurLaneAutoScript/module/webui/updater.py

294 lines
8.6 KiB
Python
Raw Normal View History

2022-01-10 22:14:25 +08:00
import builtins
2022-01-09 18:59:25 +08:00
import datetime
import subprocess
import threading
import time
2022-01-12 22:27:56 +08:00
from typing import Generator, Tuple
2022-01-09 18:59:25 +08:00
import requests
2022-01-10 22:14:25 +08:00
from deploy.installer import DeployConfig, ExecutionError, Installer
from deploy.utils import DEPLOY_CONFIG, cached_property
from module.base.retry import retry
2022-01-09 18:59:25 +08:00
from module.logger import logger
2022-01-10 13:23:22 +08:00
from module.webui.process_manager import AlasManager
from module.webui.utils import TaskHandler, get_next_time
2022-01-09 18:59:25 +08:00
class Config(DeployConfig):
def __init__(self, file=DEPLOY_CONFIG):
self.file = file
self.config = {}
self.read()
self.write()
2022-01-10 22:14:25 +08:00
class Updater(Config, Installer):
2022-01-11 02:36:45 +08:00
def __init__(self, file=DEPLOY_CONFIG):
super().__init__(file=file)
self.state = 0
self.event: threading.Event = None
2022-01-12 22:27:56 +08:00
@property
def delay(self):
self.read()
return int(self.config['CheckUpdateInterval'])*60
@property
def schedule_time(self):
self.read()
return datetime.time.fromisoformat(self.config['AutoRestartTime'])
2022-01-14 21:34:54 +08:00
@cached_property
def enabled(self):
return self.bool('EnableReload')
2022-01-09 18:59:25 +08:00
@cached_property
def repo(self):
return self.config['Repository']
@cached_property
def branch(self):
return self.config['Branch']
2022-01-12 22:27:56 +08:00
def execute_output(self, command) -> str:
command = command.replace(
r'\\', '/').replace('\\', '/').replace('\"', '"')
log = subprocess.run(command, capture_output=True,
text=True, encoding='utf8').stdout
return log
def get_commit(self, revision='', n=1, short_sha1=False) -> Tuple:
"""
Return:
(sha1, author, isotime, message,)
"""
ph = 'h' if short_sha1 else 'H'
log = self.execute_output(
f'{self.git} log {revision} --pretty=format:"%{ph}---%an---%ad---%s" --date=iso -{n}')
if not log:
return None, None, None, None
logs = log.split('\n')
logs = list(map(lambda log: tuple(log.split('---')), logs))
if n == 1:
return logs[0]
else:
return logs
2022-01-11 02:36:45 +08:00
def _check_update(self) -> bool:
2022-01-12 22:27:56 +08:00
self.state = 'checking'
source = 'origin'
for _ in range(3):
if self.execute(f'"{self.git}" fetch {source} {self.branch}', allow_failure=True):
break
else:
logger.warning("Git fetch failed")
return False
log = self.execute_output(
f'{self.git} log --not --remotes={source}/* -1 --oneline')
if log:
logger.info(
f"Cannot find local commit {log.split()[0]} in upstream, skip update")
return False
sha1, _, _, message = self.get_commit(f'..{source}/{self.branch}')
if sha1:
logger.info(f"New update avaliable")
logger.info(f"{sha1[:8]} - {message}")
return True
else:
logger.info(f"No update")
return False
def _check_update_(self) -> bool:
"""
Deprecated
"""
2022-01-11 02:36:45 +08:00
self.state = 'checking'
2022-01-09 18:59:25 +08:00
r = self.repo.split('/')
owner = r[3]
repo = r[4]
if 'gitee' in r[2]:
2022-01-10 00:42:40 +08:00
base = "https://gitee.com/api/v5/repos/"
2022-01-09 18:59:25 +08:00
headers = {}
2022-01-10 15:05:34 +08:00
token = self.config['ApiToken']
if token:
para = {"access_token": token}
2022-01-09 18:59:25 +08:00
else:
2022-01-10 00:42:40 +08:00
base = "https://api.github.com/repos/"
2022-01-09 18:59:25 +08:00
headers = {
'Accept': 'application/vnd.github.v3.sha'
}
2022-01-10 15:05:34 +08:00
para = {}
token = self.config['ApiToken']
if token:
headers['Authorization'] = 'token ' + token
2022-01-09 18:59:25 +08:00
try:
2022-01-10 00:42:40 +08:00
list_commit = requests.get(
2022-01-10 15:05:34 +08:00
base + f"{owner}/{repo}/branches/{self.branch}", headers=headers, params=para)
2022-01-09 18:59:25 +08:00
except Exception as e:
logger.exception(e)
logger.warning("Check update failed")
2022-01-11 02:36:45 +08:00
return 0
2022-01-09 18:59:25 +08:00
if list_commit.status_code != 200:
logger.warning(
f"Check update failed, code {list_commit.status_code}")
2022-01-11 02:36:45 +08:00
return 0
2022-01-09 18:59:25 +08:00
try:
2022-01-10 00:42:40 +08:00
sha = list_commit.json()['commit']['sha']
2022-01-09 18:59:25 +08:00
except Exception as e:
logger.exception(e)
logger.warning("Check update failed when parsing return json")
2022-01-11 02:36:45 +08:00
return 0
2022-01-09 18:59:25 +08:00
2022-01-12 22:27:56 +08:00
local_sha, _, _, _ = self._get_local_commit()
2022-01-09 18:59:25 +08:00
if sha == local_sha:
logger.info("No update")
2022-01-11 02:36:45 +08:00
return 0
2022-01-09 18:59:25 +08:00
2022-01-10 15:05:34 +08:00
try:
get_commit = requests.get(
base + f"{owner}/{repo}/commits/" + local_sha, headers=headers, params=para)
except Exception as e:
logger.exception(e)
logger.warning("Check update failed")
2022-01-11 02:36:45 +08:00
return 0
2022-01-10 15:05:34 +08:00
if get_commit.status_code != 200:
# for develops
logger.info(
f"Cannot find local commit {local_sha[:8]} in upstream, skip update")
2022-01-11 02:36:45 +08:00
return 0
2022-01-10 15:05:34 +08:00
2022-01-09 18:59:25 +08:00
logger.info(f"Update {sha[:8]} avaliable")
2022-01-11 02:36:45 +08:00
return 1
def check_update(self):
if self.state in (0, 'failed'):
self.state = updater._check_update()
2022-01-09 18:59:25 +08:00
@retry(ExecutionError, tries=3, delay=10)
2022-01-10 22:14:25 +08:00
def git_install(self):
return super().git_install()
2022-01-09 18:59:25 +08:00
@retry(ExecutionError, tries=3, delay=10)
2022-01-10 22:14:25 +08:00
def pip_install(self):
return super().pip_install()
2022-01-09 18:59:25 +08:00
2022-01-10 14:04:02 +08:00
def update(self):
2022-01-10 22:14:25 +08:00
logger.hr("Run update")
backup, builtins.print = builtins.print, logger.info
try:
self.git_install()
self.pip_install()
except ExecutionError:
logger.error("Update failed")
builtins.print = backup
return False
builtins.print = backup
return True
2022-01-09 18:59:25 +08:00
2022-01-11 02:36:45 +08:00
def run_update(self):
if self.state not in ('failed', 0, 1):
return
self._start_update()
def _start_update(self):
self.state = 'start'
instances = AlasManager.running_instances()
names = []
for alas in instances:
names.append(alas.config_name + '\n')
logger.info("Waiting all running alas finish.")
self._wait_update(instances, names)
def _wait_update(self, instances, names):
if self.state == 'cancel':
self.state = 1
self.state = 'wait'
self.event.set()
_instances = instances.copy()
while _instances:
for alas in _instances:
if not alas.alive:
_instances.remove(alas)
logger.info(f"Alas [{alas.config_name}] stopped")
logger.info(
f"Remains: {[alas.config_name for alas in _instances]}")
if self.state == 'cancel':
self.state = 1
2022-01-11 18:06:06 +08:00
self.event.clear()
2022-01-11 15:00:28 +08:00
AlasManager.start_alas(instances, self.event)
2022-01-11 02:36:45 +08:00
return
time.sleep(0.25)
self._run_update(instances, names)
def _run_update(self, instances, names):
self.state = 'run update'
logger.info("All alas stopped, start updating")
if updater.update():
self.state = 'reload'
with open('./config/reloadalas', mode='w') as f:
2022-01-11 02:36:45 +08:00
f.writelines(names)
from module.webui.app import clearup
self._trigger_reload(2)
2022-01-11 02:36:45 +08:00
clearup()
else:
self.state = 'failed'
logger.warning("Update failed")
self.event.clear()
2022-01-11 15:00:28 +08:00
AlasManager.start_alas(instances, self.event)
2022-01-11 02:36:45 +08:00
return False
2022-01-12 22:27:56 +08:00
2022-01-11 02:36:45 +08:00
@staticmethod
def _trigger_reload(delay=2):
def trigger():
with open('./config/reloadflag', mode='w'):
# app ended here and uvicorn will restart whole app
pass
timer = threading.Timer(delay, trigger)
timer.start()
2022-01-11 02:36:45 +08:00
def schedule_restart(self) -> Generator:
th: TaskHandler
th = yield
if self.schedule_time is None:
th.remove_current_task()
yield
th._task.delay = get_next_time(self.schedule_time)
2022-01-10 13:23:22 +08:00
yield
2022-01-11 02:36:45 +08:00
while True:
if self.state == 0:
self.state = updater._check_update()
if self.state != 1:
th._task.delay = get_next_time(self.schedule_time)
yield
continue
if not self.run_update():
self.state = 'failed'
th._task.delay = get_next_time(self.schedule_time)
2022-01-10 13:23:22 +08:00
yield
2022-01-11 02:36:45 +08:00
def cancel(self):
self.state = 'cancel'
2022-01-10 13:23:22 +08:00
2022-01-11 02:36:45 +08:00
updater = Updater()
2022-01-10 13:23:22 +08:00
2022-01-09 18:59:25 +08:00
if __name__ == '__main__':
pass
2022-01-10 22:14:25 +08:00
# if updater.check_update():
updater.update()