Keldos commited on
Commit
bde4648
1 Parent(s): 58a0a81

feat: 加入完整的版本更新功能

Browse files
ChuanhuChatbot.py CHANGED
@@ -13,6 +13,7 @@ from modules.utils import *
13
  from modules.presets import *
14
  from modules.overwrites import *
15
  from modules.webui import *
 
16
  from modules.models.models import get_model
17
 
18
  logging.getLogger("httpx").setLevel(logging.WARNING)
@@ -42,7 +43,7 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
42
  with gr.Row(elem_id="float-display"):
43
  user_info = gr.Markdown(value="getting user info...", elem_id="user-info")
44
  update_info = gr.HTML(get_html("update.html").format(
45
- current_version=repo_html(),
46
  version_time=version_time(),
47
  cancel_btn=i18n("取消"),
48
  update_btn=i18n("更新"),
 
13
  from modules.presets import *
14
  from modules.overwrites import *
15
  from modules.webui import *
16
+ from modules.repo import *
17
  from modules.models.models import get_model
18
 
19
  logging.getLogger("httpx").setLevel(logging.WARNING)
 
43
  with gr.Row(elem_id="float-display"):
44
  user_info = gr.Markdown(value="getting user info...", elem_id="user-info")
45
  update_info = gr.HTML(get_html("update.html").format(
46
+ current_version=repo_tag_html(),
47
  version_time=version_time(),
48
  cancel_btn=i18n("取消"),
49
  update_btn=i18n("更新"),
modules/repo.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding:utf-8 -*-
2
+ import os
3
+ import sys
4
+ import subprocess
5
+ from functools import lru_cache
6
+ import logging
7
+ import gradio as gr
8
+ import datetime
9
+
10
+ # This file is mainly used to describe repo version info, execute the git command, python pip command, shell command, etc.
11
+ # Part of the code in this file is referenced from stable-diffusion-webui/modules/launch_utils.py
12
+
13
+ python = sys.executable
14
+ pip = os.environ.get('PIP', "pip")
15
+ git = os.environ.get('GIT', "git")
16
+
17
+ # Pypi index url
18
+ index_url = os.environ.get('INDEX_URL', "")
19
+
20
+ # Whether to default to printing command output
21
+ default_command_live = True
22
+
23
+
24
+ def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live) -> str:
25
+ if desc is not None:
26
+ print(desc)
27
+ run_kwargs = {
28
+ "args": command,
29
+ "shell": True,
30
+ "env": os.environ if custom_env is None else custom_env,
31
+ "encoding": 'utf8',
32
+ "errors": 'ignore',
33
+ }
34
+
35
+ if not live:
36
+ run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE
37
+
38
+ result = subprocess.run(**run_kwargs)
39
+ if result.returncode != 0:
40
+ error_bits = [
41
+ f"{errdesc or 'Error running command'}.",
42
+ f"Command: {command}",
43
+ f"Error code: {result.returncode}",
44
+ ]
45
+ if result.stdout:
46
+ error_bits.append(f"stdout: {result.stdout}")
47
+ if result.stderr:
48
+ error_bits.append(f"stderr: {result.stderr}")
49
+ raise RuntimeError("\n".join(error_bits))
50
+
51
+ return (result.stdout or "")
52
+
53
+
54
+ def run_pip(command, desc=None, live=default_command_live):
55
+ # if args.skip_install:
56
+ # return
57
+
58
+ index_url_line = f' --index-url {index_url}' if index_url != '' else ''
59
+ return run(
60
+ f'"{python}" -m pip {command} --prefer-binary{index_url_line}',
61
+ desc=f"Installing {desc}...",
62
+ errdesc=f"Couldn't install {desc}",
63
+ live=live
64
+ )
65
+
66
+
67
+ @lru_cache()
68
+ def commit_hash():
69
+ try:
70
+ return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()
71
+ except Exception:
72
+ return "<none>"
73
+
74
+ def commit_html():
75
+ commit = commit_hash()
76
+ if commit != "<none>":
77
+ short_commit = commit[0:7]
78
+ commit_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/commit/{short_commit}">{short_commit}</a>'
79
+ else:
80
+ commit_info = "unknown \U0001F615"
81
+ return commit_info
82
+
83
+ @lru_cache()
84
+ def tag_html():
85
+ try:
86
+ latest_tag = run(f"{git} describe --tags --abbrev=0", live=False).strip()
87
+ try:
88
+ # tag = subprocess.check_output([git, "describe", "--tags", "--exact-match"], shell=False, encoding='utf8').strip()
89
+ tag = run(f"{git} describe --tags --exact-match", live=False).strip()
90
+ except Exception:
91
+ tag = "<edited>"
92
+ except Exception:
93
+ tag = "<none>"
94
+
95
+ if tag == "<none>":
96
+ tag_info = "unknown \U0001F615"
97
+ elif tag == "<edited>":
98
+ 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>'
99
+ else:
100
+ tag_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/releases/tag/{tag}">{tag}</a>'
101
+
102
+ return tag_info
103
+
104
+ def repo_tag_html():
105
+ commit_version = commit_html()
106
+ tag_version = tag_html()
107
+ return tag_version if tag_version != "unknown \U0001F615" else commit_version
108
+
109
+ def versions_html():
110
+ python_version = ".".join([str(x) for x in sys.version_info[0:3]])
111
+ repo_version = repo_tag_html()
112
+ return f"""
113
+ Python: <span title="{sys.version}">{python_version}</span>
114
+  • 
115
+ Gradio: {gr.__version__}
116
+  • 
117
+ <a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT">ChuanhuChat</a>: {repo_version}
118
+ """
119
+
120
+ def version_time():
121
+ try:
122
+ 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()
123
+ # commit_time = run(f"TZ=UTC {git} log -1 --format=%cd --date='format-local:%Y-%m-%dT%H:%M:%SZ'").strip()
124
+ except Exception:
125
+ commit_time = "unknown"
126
+ return commit_time
127
+
128
+
129
+
130
+ def get_current_branch():
131
+ try:
132
+ # branch = run(f"{git} rev-parse --abbrev-ref HEAD").strip()
133
+ branch = subprocess.check_output([git, "rev-parse", "--abbrev-ref", "HEAD"], shell=False, encoding='utf8').strip()
134
+ except Exception:
135
+ branch = "<none>"
136
+ return branch
137
+
138
+
139
+ def get_latest_release():
140
+ try:
141
+ import requests
142
+ release = requests.get("https://api.github.com/repos/GaiZhenbiao/ChuanhuChatGPT/releases/latest").json()
143
+ tag = release["tag_name"]
144
+ release_note = release["body"]
145
+ need_pip = release_note.find("requirements reinstall needed") != -1
146
+ except Exception:
147
+ tag = "<none>"
148
+ release_note = ""
149
+ need_pip = False
150
+ return {"tag": tag, "release_note": release_note, "need_pip": need_pip}
151
+
152
+ def get_tag_commit_hash(tag):
153
+ try:
154
+ import requests
155
+ tags = requests.get("https://api.github.com/repos/GaiZhenbiao/ChuanhuChatGPT/tags").json()
156
+ commit_hash = [x["commit"]["sha"] for x in tags if x["name"] == tag][0]
157
+ except Exception:
158
+ commit_hash = "<none>"
159
+ return commit_hash
160
+
161
+ def background_update():
162
+ # {git} fetch --all && ({git} pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f || ({git} stash && {git} pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f && {git} stash pop)) && {pip} install -r requirements.txt")
163
+ try:
164
+ latest_release = get_latest_release()
165
+ latest_release_tag = latest_release["tag"]
166
+ latest_release_hash = get_tag_commit_hash(latest_release_tag)
167
+ need_pip = latest_release["need_pip"]
168
+
169
+ current_branch = get_current_branch()
170
+ updater_branch = f'tmp_{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}'
171
+ track_repo = "https://github.com/GaiZhenbiao/ChuanhuChatGPT.git"
172
+ try:
173
+ try:
174
+ run(f"{git} fetch {track_repo}", desc="Fetching from github...", live=False)
175
+ except Exception:
176
+ logging.error(f"Update failed in fetching")
177
+ return "failed"
178
+
179
+ run(f'{git} stash save -a "updater-tmp"')
180
+
181
+ run(f"{git} checkout -b {updater_branch}", live=False)
182
+ run(f"{git} reset --hard FETCH_HEAD", live=False)
183
+ run(f"{git} reset --hard {latest_release_hash}", desc=f'Checking out {latest_release_tag}...')
184
+ run(f"{git} checkout {current_branch}", live=False)
185
+
186
+ try:
187
+ run(f"{git} merge {updater_branch} -q", desc="Trying to apply latest update...")
188
+ except Exception:
189
+ logging.error(f"Update failed in merging")
190
+ try:
191
+ run(f"{git} merge --abort", desc="Canceling update...")
192
+ run(f"{git} reset --hard {current_branch}", live=False)
193
+ run(f"{git} stash pop", live=False)
194
+ run(f"{git} branch -D -f {updater_branch}", live=False)
195
+ logging.error(f"Update failed, but your file was safely reset to the state before the update.")
196
+ return "failed"
197
+ except Exception as e:
198
+ logging.error(f"!!!Update failed in resetting, try to reset your files manually.")
199
+ return "failed"
200
+
201
+ run(f"{git} stash pop", live=False)
202
+ run(f"{git} branch -D -f {updater_branch}", live=False)
203
+ except Exception as e:
204
+ logging.error(f"Update failed: {e}")
205
+ return "failed"
206
+ if need_pip:
207
+ try:
208
+ run_pip(f"install -r requirements.txt", "requirements")
209
+ except Exception:
210
+ logging.error(f"Update failed in pip install")
211
+ return "failed"
212
+ return "success"
213
+ except Exception as e:
214
+ logging.error(f"Update failed: {e}")
215
+ return "failed"
modules/utils.py CHANGED
@@ -539,88 +539,16 @@ def transfer_input(inputs):
539
  )
540
 
541
 
542
-
543
- def run(command, desc=None, errdesc=None, custom_env=None, live=False):
544
- if desc is not None:
545
- print(desc)
546
- if live:
547
- result = subprocess.run(command, shell=True, env=os.environ if custom_env is None else custom_env)
548
- if result.returncode != 0:
549
- raise RuntimeError(f"""{errdesc or 'Error running command'}.
550
- Command: {command}
551
- Error code: {result.returncode}""")
552
-
553
- return ""
554
- result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env=os.environ if custom_env is None else custom_env)
555
- if result.returncode != 0:
556
- message = f"""{errdesc or 'Error running command'}.
557
- Command: {command}
558
- Error code: {result.returncode}
559
- stdout: {result.stdout.decode(encoding="utf8", errors="ignore") if len(result.stdout)>0 else '<empty>'}
560
- stderr: {result.stderr.decode(encoding="utf8", errors="ignore") if len(result.stderr)>0 else '<empty>'}
561
- """
562
- raise RuntimeError(message)
563
- return result.stdout.decode(encoding="utf8", errors="ignore")
564
-
565
- def commit_html():
566
- git = os.environ.get('GIT', "git")
567
- try:
568
- commit_hash = run(f"{git} rev-parse HEAD").strip()
569
- except Exception:
570
- commit_hash = "<none>"
571
- if commit_hash != "<none>":
572
- short_commit = commit_hash[0:7]
573
- commit_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/commit/{short_commit}">{short_commit}</a>'
574
- else:
575
- commit_info = "unknown \U0001F615"
576
- return commit_info
577
-
578
- def tag_html():
579
- git = os.environ.get('GIT', "git")
580
- try:
581
- tag = run(f"{git} describe --tags --exact-match").strip()
582
- except Exception:
583
- tag = "<none>"
584
- if tag != "<none>":
585
- tag_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/releases/tag/{tag}">{tag}</a>'
586
- else:
587
- tag_info = "unknown \U0001F615"
588
- return tag_info
589
-
590
- def repo_html():
591
- commit_version = commit_html()
592
- tag_version = tag_html()
593
- return tag_version if tag_version != "unknown \U0001F615" else commit_version
594
-
595
- def versions_html():
596
- python_version = ".".join([str(x) for x in sys.version_info[0:3]])
597
- repo_version = repo_html()
598
- return f"""
599
- Python: <span title="{sys.version}">{python_version}</span>
600
-  • 
601
- Gradio: {gr.__version__}
602
-  • 
603
- <a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT">ChuanhuChat</a>: {repo_version}
604
- """
605
-
606
- def version_time():
607
- git = os.environ.get('GIT', "git")
608
- try:
609
- commit_time = run(f"TZ=UTC {git} log -1 --format=%cd --date='format-local:%Y-%m-%dT%H:%M:%SZ'").strip()
610
- except Exception:
611
- commit_time = "unknown"
612
- return commit_time
613
-
614
  def update_chuanhu():
615
- git = os.environ.get('GIT', "git")
616
- pip = os.environ.get('PIP', "pip")
617
- try:
618
- run(f"{git} fetch --all && ({git} pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f || ({git} stash && {git} pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f && {git} stash pop)) && {pip} install -r requirements.txt")
619
- logging.info("Successfully updated")
 
620
  status = '<span id="update-status" class="hideK">success</span>'
621
  return gr.Markdown.update(value=i18n("更新成功,请重启本程序")+status)
622
- except Exception:
623
- logging.info("Failed to update")
624
  status = '<span id="update-status" class="hideK">failure</span>'
625
  return gr.Markdown.update(value=i18n("更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)")+status)
626
 
 
539
  )
540
 
541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
  def update_chuanhu():
543
+ from .repo import background_update
544
+
545
+ print("Trying to update...")
546
+ update_status = background_update()
547
+ if update_status == "success":
548
+ print("Successfully updated, restart needed")
549
  status = '<span id="update-status" class="hideK">success</span>'
550
  return gr.Markdown.update(value=i18n("更新成功,请重启本程序")+status)
551
+ else:
 
552
  status = '<span id="update-status" class="hideK">failure</span>'
553
  return gr.Markdown.update(value=i18n("更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)")+status)
554
 
web_assets/javascript/updater.js CHANGED
@@ -51,10 +51,13 @@ var releaseNoteElement = document.getElementById('release-note-content');
51
  var updatingInfoElement = document.getElementById('updating-info');
52
  async function updateLatestVersion() {
53
  const currentVersionElement = document.getElementById('current-version');
 
 
 
54
  const latestVersionElement = document.getElementById('latest-version-title');
55
  releaseNoteElement = document.getElementById('release-note-content');
56
  updatingInfoElement = document.getElementById('updating-info');
57
- const currentVersion = currentVersionElement.textContent;
58
  const versionTime = document.getElementById('version-time').innerText;
59
  const localVersionTime = versionTime !== "unknown" ? (new Date(versionTime)).getTime() : 0;
60
  updateInfoGotten = true; //无论成功与否都只执行一次,否则容易api超限...
@@ -65,18 +68,31 @@ async function updateLatestVersion() {
65
  releaseNoteElement.innerHTML = marked.parse(releaseNote, {mangle: false, headerIds: false});
66
  }
67
  const latestVersion = data.tag_name;
68
- const latestVersionTime = (new Date(data.created_at)).getTime();
69
- if (latestVersionTime) {
70
- if (localVersionTime < latestVersionTime) {
 
71
  latestVersionElement.textContent = latestVersion;
72
  console.log(`New version ${latestVersion} found!`);
73
- if (!isInIframe) {openUpdateToast();}
74
- } else {
75
- noUpdate();
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
77
- currentTime = new Date().getTime();
78
- localStorage.setItem('lastCheckTime', currentTime);
79
  }
 
 
80
  } catch (error) {
81
  console.error(error);
82
  }
@@ -124,18 +140,22 @@ function manualCheckUpdate() {
124
  currentTime = new Date().getTime();
125
  localStorage.setItem('lastCheckTime', currentTime);
126
  }
127
- function noUpdate() {
128
  localStorage.setItem('isLatestVersion', 'true');
129
  isLatestVersion = true;
130
- noUpdateHtml();
131
  }
132
- function noUpdateHtml() {
133
  const versionInfoElement = document.getElementById('version-info-title');
134
  const gotoUpdateBtn = document.getElementById('goto-update-btn');
135
  const closeUpdateBtn = document.getElementById('close-update-btn');
136
  const releaseNoteWrap = document.getElementById('release-note-wrap');
137
  releaseNoteWrap.style.setProperty('display', 'none');
138
- versionInfoElement.textContent = i18n(usingLatest_i18n)
 
 
 
 
139
  gotoUpdateBtn.classList.add('hideK');
140
  closeUpdateBtn.classList.remove('hideK');
141
  }
 
51
  var updatingInfoElement = document.getElementById('updating-info');
52
  async function updateLatestVersion() {
53
  const currentVersionElement = document.getElementById('current-version');
54
+ const reVersion = /<a[^>]*>([^<]*)<\/a>/g;
55
+ const versionMatch = reVersion.exec(currentVersionElement.innerHTML);
56
+ const currentVersion = versionMatch ? versionMatch[1] : null;
57
  const latestVersionElement = document.getElementById('latest-version-title');
58
  releaseNoteElement = document.getElementById('release-note-content');
59
  updatingInfoElement = document.getElementById('updating-info');
60
+
61
  const versionTime = document.getElementById('version-time').innerText;
62
  const localVersionTime = versionTime !== "unknown" ? (new Date(versionTime)).getTime() : 0;
63
  updateInfoGotten = true; //无论成功与否都只执行一次,否则容易api超限...
 
68
  releaseNoteElement.innerHTML = marked.parse(releaseNote, {mangle: false, headerIds: false});
69
  }
70
  const latestVersion = data.tag_name;
71
+ if (currentVersion) {
72
+ if (latestVersion <= currentVersion) {
73
+ noUpdate();
74
+ } else {
75
  latestVersionElement.textContent = latestVersion;
76
  console.log(`New version ${latestVersion} found!`);
77
+ if (!isInIframe) openUpdateToast();
78
+ }
79
+ } else { //如果当前版本号获取失败,使用时间比较
80
+ const latestVersionTime = (new Date(data.created_at)).getTime();
81
+ if (latestVersionTime) {
82
+ if (localVersionTime == 0) {
83
+ latestVersionElement.textContent = "Local version check failed. But latest revision is " + latestVersion;
84
+ console.log(`New version ${latestVersion} found!`);
85
+ } else if (localVersionTime < latestVersionTime) {
86
+ latestVersionElement.textContent = "Local version check failed, it seems to be a local rivision. But latest revision is " + latestVersion;
87
+ console.log(`New version ${latestVersion} found!`);
88
+ // if (!isInIframe) openUpdateToast();
89
+ } else {
90
+ noUpdate("Local version check failed, it seems to be a local rivision. But your revision is newer than the latest release.");
91
+ }
92
  }
 
 
93
  }
94
+ currentTime = new Date().getTime();
95
+ localStorage.setItem('lastCheckTime', currentTime);
96
  } catch (error) {
97
  console.error(error);
98
  }
 
140
  currentTime = new Date().getTime();
141
  localStorage.setItem('lastCheckTime', currentTime);
142
  }
143
+ function noUpdate(message="") {
144
  localStorage.setItem('isLatestVersion', 'true');
145
  isLatestVersion = true;
146
+ noUpdateHtml(message);
147
  }
148
+ function noUpdateHtml(message="") {
149
  const versionInfoElement = document.getElementById('version-info-title');
150
  const gotoUpdateBtn = document.getElementById('goto-update-btn');
151
  const closeUpdateBtn = document.getElementById('close-update-btn');
152
  const releaseNoteWrap = document.getElementById('release-note-wrap');
153
  releaseNoteWrap.style.setProperty('display', 'none');
154
+ if (message === "") {
155
+ versionInfoElement.textContent = i18n(usingLatest_i18n)
156
+ } else {
157
+ versionInfoElement.textContent = message;
158
+ }
159
  gotoUpdateBtn.classList.add('hideK');
160
  closeUpdateBtn.classList.remove('hideK');
161
  }