|
|
|
import os |
|
import sys |
|
import subprocess |
|
from functools import lru_cache |
|
import logging |
|
import gradio as gr |
|
import datetime |
|
|
|
|
|
|
|
|
|
python = sys.executable |
|
pip = os.environ.get('PIP', "pip") |
|
git = os.environ.get('GIT', "git") |
|
|
|
|
|
index_url = os.environ.get('INDEX_URL', "") |
|
|
|
|
|
default_command_live = True |
|
|
|
|
|
def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live) -> str: |
|
if desc is not None: |
|
print(desc) |
|
run_kwargs = { |
|
"args": command, |
|
"shell": True, |
|
"env": os.environ if custom_env is None else custom_env, |
|
"encoding": 'utf8', |
|
"errors": 'ignore', |
|
} |
|
|
|
if not live: |
|
run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE |
|
|
|
result = subprocess.run(**run_kwargs) |
|
if result.returncode != 0: |
|
error_bits = [ |
|
f"{errdesc or 'Error running command'}.", |
|
f"Command: {command}", |
|
f"Error code: {result.returncode}", |
|
] |
|
if result.stdout: |
|
error_bits.append(f"stdout: {result.stdout}") |
|
if result.stderr: |
|
error_bits.append(f"stderr: {result.stderr}") |
|
raise RuntimeError("\n".join(error_bits)) |
|
|
|
return (result.stdout or "") |
|
|
|
|
|
def run_pip(command, desc=None, pref=None, live=default_command_live): |
|
|
|
|
|
|
|
index_url_line = f' --index-url {index_url}' if index_url != '' else '' |
|
return run( |
|
f'"{python}" -m pip {command} --prefer-binary{index_url_line}', |
|
desc=f"{pref} Installing {desc}...", |
|
errdesc=f"Couldn't install {desc}", |
|
live=live |
|
) |
|
|
|
|
|
@lru_cache() |
|
def commit_hash(): |
|
try: |
|
return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip() |
|
except Exception: |
|
return "<none>" |
|
|
|
def commit_html(): |
|
commit = commit_hash() |
|
if commit != "<none>": |
|
short_commit = commit[0:7] |
|
commit_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/commit/{short_commit}">{short_commit}</a>' |
|
else: |
|
commit_info = "unknown \U0001F615" |
|
return commit_info |
|
|
|
@lru_cache() |
|
def tag_html(): |
|
try: |
|
latest_tag = run(f"{git} describe --tags --abbrev=0", live=False).strip() |
|
try: |
|
|
|
tag = run(f"{git} describe --tags --exact-match", live=False).strip() |
|
except Exception: |
|
tag = "<edited>" |
|
except Exception: |
|
tag = "<none>" |
|
|
|
if tag == "<none>": |
|
tag_info = "unknown \U0001F615" |
|
elif tag == "<edited>": |
|
tag_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/releases/tag/{latest_tag}">{latest_tag}</a><span style="font-size:smaller">*</span>' |
|
else: |
|
tag_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/releases/tag/{tag}">{tag}</a>' |
|
|
|
return tag_info |
|
|
|
def repo_tag_html(): |
|
commit_version = commit_html() |
|
tag_version = tag_html() |
|
return tag_version if tag_version != "unknown \U0001F615" else commit_version |
|
|
|
def versions_html(): |
|
python_version = ".".join([str(x) for x in sys.version_info[0:3]]) |
|
repo_version = repo_tag_html() |
|
return f""" |
|
Python: <span title="{sys.version}">{python_version}</span> |
|
• |
|
Gradio: {gr.__version__} |
|
• |
|
<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT">ChuanhuChat</a>: {repo_version} |
|
""" |
|
|
|
def version_time(): |
|
try: |
|
commit_time = subprocess.check_output(f"TZ=UTC {git} log -1 --format=%cd --date='format-local:%Y-%m-%dT%H:%M:%SZ'", shell=True, encoding='utf8').strip() |
|
|
|
except Exception: |
|
commit_time = "unknown" |
|
return commit_time |
|
|
|
|
|
|
|
def get_current_branch(): |
|
try: |
|
|
|
branch = subprocess.check_output([git, "rev-parse", "--abbrev-ref", "HEAD"], shell=False, encoding='utf8').strip() |
|
except Exception: |
|
branch = "<none>" |
|
return branch |
|
|
|
|
|
def get_latest_release(): |
|
try: |
|
import requests |
|
release = requests.get("https://api.github.com/repos/GaiZhenbiao/ChuanhuChatGPT/releases/latest").json() |
|
tag = release["tag_name"] |
|
release_note = release["body"] |
|
need_pip = release_note.find("requirements reinstall needed") != -1 |
|
except Exception: |
|
tag = "<none>" |
|
release_note = "" |
|
need_pip = False |
|
return {"tag": tag, "release_note": release_note, "need_pip": need_pip} |
|
|
|
def get_tag_commit_hash(tag): |
|
try: |
|
import requests |
|
tags = requests.get("https://api.github.com/repos/GaiZhenbiao/ChuanhuChatGPT/tags").json() |
|
commit_hash = [x["commit"]["sha"] for x in tags if x["name"] == tag][0] |
|
except Exception: |
|
commit_hash = "<none>" |
|
return commit_hash |
|
|
|
def repo_need_stash(): |
|
try: |
|
return subprocess.check_output([git, "diff-index", "--quiet", "HEAD", "--"], shell=False, encoding='utf8').strip() != "" |
|
except Exception: |
|
return True |
|
|
|
def background_update(): |
|
|
|
try: |
|
latest_release = get_latest_release() |
|
latest_release_tag = latest_release["tag"] |
|
latest_release_hash = get_tag_commit_hash(latest_release_tag) |
|
need_pip = latest_release["need_pip"] |
|
need_stash = repo_need_stash() |
|
|
|
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") |
|
current_branch = get_current_branch() |
|
updater_branch = f'tmp_{timestamp}' |
|
backup_branch = f'backup_{timestamp}' |
|
track_repo = "https://github.com/GaiZhenbiao/ChuanhuChatGPT.git" |
|
try: |
|
try: |
|
run(f"{git} fetch {track_repo}", desc="[Updater] Fetching from github...", live=False) |
|
except Exception: |
|
logging.error(f"Update failed in fetching, check your network connection") |
|
return "failed" |
|
|
|
run(f'{git} stash push --include-untracked -m "updater-{timestamp}"', |
|
desc=f'[Updater] Restoring you local changes on stash updater-{timestamp}', live=False) if need_stash else None |
|
|
|
run(f"{git} checkout -b {backup_branch}", live=False) |
|
run(f"{git} checkout -b {updater_branch}", live=False) |
|
run(f"{git} reset --hard FETCH_HEAD", live=False) |
|
run(f"{git} reset --hard {latest_release_hash}", desc=f'[Updater] Checking out {latest_release_tag}...', live=False) |
|
run(f"{git} checkout {current_branch}", live=False) |
|
|
|
try: |
|
run(f"{git} merge --no-edit {updater_branch} -q", desc=f"[Updater] Trying to apply latest update on version {latest_release_tag}...") |
|
except Exception: |
|
logging.error(f"Update failed in merging") |
|
try: |
|
run(f"{git} merge --abort", desc="[Updater] Conflict detected, canceling update...") |
|
run(f"{git} reset --hard {backup_branch}", live=False) |
|
run(f"{git} branch -D -f {updater_branch}", live=False) |
|
run(f"{git} branch -D -f {backup_branch}", live=False) |
|
run(f"{git} stash pop", live=False) if need_stash else None |
|
logging.error(f"Update failed, but your file was safely reset to the state before the update.") |
|
return "failed" |
|
except Exception as e: |
|
logging.error(f"!!!Update failed in resetting, try to reset your files manually. {e}") |
|
return "failed" |
|
|
|
if need_stash: |
|
try: |
|
run(f"{git} stash apply", desc="[Updater] Trying to restore your local modifications...", live=False) |
|
except Exception: |
|
run(f"{git} reset --hard {backup_branch}", desc="[Updater] Conflict detected, canceling update...", live=False) |
|
run(f"{git} branch -D -f {updater_branch}", live=False) |
|
run(f"{git} branch -D -f {backup_branch}", live=False) |
|
run(f"{git} stash pop", live=False) |
|
logging.error(f"Update failed in applying your local changes, but your file was safely reset to the state before the update.") |
|
return "failed" |
|
run(f"{git} stash drop", live=False) |
|
|
|
run(f"{git} branch -D -f {updater_branch}", live=False) |
|
run(f"{git} branch -D -f {backup_branch}", live=False) |
|
except Exception as e: |
|
logging.error(f"Update failed: {e}") |
|
return "failed" |
|
if need_pip: |
|
try: |
|
run_pip(f"install -r requirements.txt", pref="[Updater]", desc="requirements", live=False) |
|
except Exception: |
|
logging.error(f"Update failed in pip install") |
|
return "failed" |
|
return "success" |
|
except Exception as e: |
|
logging.error(f"Update failed: {e}") |
|
return "failed" |
|
|