From 58c20d5e763619f535b991ebcb2ff04d06e018ce Mon Sep 17 00:00:00 2001 From: Emile Cantin Date: Wed, 27 May 2020 21:10:49 -0400 Subject: [PATCH 1/8] Rework the update process This changes the update process so the Python code itself calls the unzip utility (instead of going through a script). The new code is also extracted to a temporary directory which is then moved into place after the current version has been modev away as a backup. I also added a lot of logging around this area, to have better visibility on what happens. --- ReleaseManager/releaseManager.py | 99 ++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 30 deletions(-) diff --git a/ReleaseManager/releaseManager.py b/ReleaseManager/releaseManager.py index 02440382..1e07b80e 100644 --- a/ReleaseManager/releaseManager.py +++ b/ReleaseManager/releaseManager.py @@ -9,7 +9,7 @@ from github import Github import wget import subprocess -from shutil import copyfile +import shutil class ReleaseManager(MakesmithInitFuncs): @@ -50,7 +50,7 @@ def checkForLatestPyRelease(self): tag_float = float(tag_name) eligible = False if not enableExperimental: - if not self.isExperimental(tag_name): + if not self.isExperimental(release): eligible = True else: eligible = True @@ -74,19 +74,16 @@ def checkForLatestPyRelease(self): except Exception as e: print("Error checking pyrelease: " + str(e)) - def isExperimental(self, tag): + def isExperimental(self, release): ''' - Deternmines if release is experimental. All even releases are stable, odd releases are experimental + Deternmines if release is experimental. Pre-Releases are experimental. :param tag: :return: ''' - if float(tag) <= 0.931: # all releases before now are 'stable' - return False - lastDigit = tag[-1] - if (int(lastDigit) % 2) == 0: # only even releases are 'stable' - return False - else: + if release.prerelease: return True + else: + return False def processAbsolutePath(self, path): index = path.find("main.py") @@ -106,36 +103,78 @@ def updatePyInstaller(self, bypassCheck = False): except: print("error cleaning download directory: ", filePath) print("---") + print("Downloading new WebControl release...") if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": filename = wget.download(self.data.pyInstallUpdateBrowserUrl, out=home + "\\.WebControl\\downloads") else: filename = wget.download(self.data.pyInstallUpdateBrowserUrl, out=home + "/.WebControl/downloads") - print(filename) + print("Successfully downloaded new release to:" + filename) if self.data.platform == "PYINSTALLER": lhome = os.path.join(self.data.platformHome) else: lhome = "." - if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": - path = lhome + "/tools/upgrade_webcontrol_win.bat" - copyfile(path, home + "/.WebControl/downloads/upgrade_webcontrol_win.bat") - path = lhome + "/tools/7za.exe" - copyfile(path, home + "/.WebControl/downloads/7za.exe") - self.data.pyInstallInstalledPath = self.data.pyInstallInstalledPath.replace('/', '\\') - program_name = home + "\\.WebControl\\downloads\\upgrade_webcontrol_win.bat" + print("Creating target version directory...") + target_dir = self.data.pyInstallInstalledPath + '_next' + if os.path.exists(target_dir): + print("New release directory already exists, removing it") + shutil.rmtree(target_dir) + os.mkdir(target_dir) + print("Creating target version directory DONE") + + print("Unzipping new release...") + if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": + command = [ + lhome + "/tools/7za.exe", + "x", + "-y", + filename, + "-o", + target_dir + ] + print(command) + subprocess.run(command) + else: + command = [ + "tar", + "-zxf", + filename, + "-C", + target_dir + ] + print(command) + subprocess.run(command) + print("Unzipping new release DONE") + + print("Upgrade script...") + print("Checking if it needs to be run for platform: " + self.data.pyInstallPlatform) + if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": + upgrade_script_path = target_dir + "\\tools\\upgrade_" + self.data.pyInstallPlatform + ".bat" + else: + upgrade_script_path = target_dir + "/tools/upgrade_" + self.data.pyInstallPlatform + ".sh" + if os.path.exists(upgrade_script_path): + print("Yes, running it") + self.make_executable(upgrade_script_path) + subprocess.run([upgrade_script_path]) else: - path = lhome + "/tools/upgrade_webcontrol.sh" - copyfile(path, home + "/.WebControl/downloads/upgrade_webcontrol.sh") - program_name = home + "/.WebControl/downloads/upgrade_webcontrol.sh" - self.make_executable(home + "/.WebControl/downloads/upgrade_webcontrol.sh") - tool_path = home + "\\.WebControl\\downloads\\7za.exe" - arguments = [filename, self.data.pyInstallInstalledPath, tool_path] - command = [program_name] - command.extend(arguments) - print("popening") - print(command) - subprocess.Popen(command) + print("No upgrade script needed.") + print("Upgrade script DONE") + + print("Backing up the current install...") + backup_path = self.data.pyInstallInstalledPath + '_old' + print("Backup location: " + backup_path) + if os.path.exists(backup_path): + print("Old backup found, removing it") + shutil.rmtree(backup_path) + os.rename(self.data.pyInstallInstalledPath, self.data.pyInstallInstalledPath + '_old') + print("Backing up the current install DONE") + + print("Moving the target version in place...") + os.rename(target_dir, self.data.pyInstallInstalledPath) + print("Moving the target version in place DONE") + + print("WebControl upgrade complete, shutting down to make way to the target version") return True return False @@ -165,7 +204,7 @@ def update(self, version): self.data.pyInstallUpdateBrowserUrl = asset.browser_download_url print(self.data.pyInstallUpdateBrowserUrl) return self.updatePyInstaller(True) - print("hmmm.. issue") + print("Couldn't find a suitable file for the current platform and target version: " + version) return False From 9c007ec74b73b2aff59acbce7d8fd879dda10d88 Mon Sep 17 00:00:00 2001 From: gb0101010101 <7369439+gb0101010101@users.noreply.github.com> Date: Sat, 30 May 2020 20:48:58 -0700 Subject: [PATCH 2/8] Add logic to compare pre-release and stable releases. Ensure releases have been fetched from github. Use regex to match: stable having only version number with/without 'v' prefix, pre-release having version number and date, with/without 'v' prefix using underscore and date format YYYY-MM-DD. Use full release.tag_name when performing update to ensure it uses the right release. --- .pydevproject | 14 +++ .settings/org.eclipse.core.resources.prefs | 12 ++ Actions/actions.py | 2 +- Background/messageProcessor.py | 2 +- DataStructures/data.py | 5 +- ReleaseManager/releaseManager.py | 129 ++++++++++++--------- WebPageProcessor/webPageProcessor.py | 38 +++--- templates/releases.html | 5 +- 8 files changed, 129 insertions(+), 78 deletions(-) create mode 100644 .pydevproject create mode 100644 .settings/org.eclipse.core.resources.prefs diff --git a/.pydevproject b/.pydevproject new file mode 100644 index 00000000..3f56be2d --- /dev/null +++ b/.pydevproject @@ -0,0 +1,14 @@ + + + + + + /${PROJECT_DIR_NAME} + + + + python interpreter + + WebControl Python3 + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..07b452b5 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +encoding//.venv/lib/python3.6/site-packages/flask/app.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/flask/ctx.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/flask/helpers.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/flask/templating.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/github/GitRelease.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/github/MainClass.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/github/PaginatedList.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/github/Repository.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/github/Requester.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/github/__init__.py=utf-8 +encoding//.venv/lib/python3.6/site-packages/werkzeug/local.py=utf-8 diff --git a/Actions/actions.py b/Actions/actions.py index b8f74993..6d80791e 100644 --- a/Actions/actions.py +++ b/Actions/actions.py @@ -1989,7 +1989,7 @@ def checkForLatestPyRelease(self): except: print("error parsing tagname") print(latest) - if latest>self.data.pyInstallCurrentVersion: + if latest>self.data.pyInstallCurrentVersionNumber: if latestRelease is not None: print(latestRelease.tag_name) assets = latestRelease.get_assets() diff --git a/Background/messageProcessor.py b/Background/messageProcessor.py index 6f30bec8..4e0d3347 100644 --- a/Background/messageProcessor.py +++ b/Background/messageProcessor.py @@ -18,7 +18,7 @@ def start(self): # check for available update file every hour. if time.time()-self.data.lastChecked > 60*60: self.data.lastChecked = time.time() - self.data.releaseManager.checkForLatestPyRelease() + self.data.releaseManager.checkLatestRelease(True) self.data.helpManager.checkForUpdatedHelp() # process messages while queue is not empty. Everything else is on hold until queue is cleared. while ( not self.data.message_queue.empty() ): # if there is new data to be read diff --git a/DataStructures/data.py b/DataStructures/data.py index 316b109f..79ac3c73 100644 --- a/DataStructures/data.py +++ b/DataStructures/data.py @@ -4,7 +4,7 @@ from DataStructures.uiQueue import UIQueue from config.config import Config import queue - +import datetime class Data: """ @@ -38,7 +38,8 @@ class Data: Version Updater ''' lastChecked = -1 - pyInstallCurrentVersion = 0.94 + pyInstallCurrentVersionNumber = 0.94 + pyInstallCurrentVersionDate = datetime.datetime(2020, 5, 21) pyInstallUpdateAvailable = False pyInstallUpdateBrowserUrl = "" pyInstallUpdateVersion = 0 diff --git a/ReleaseManager/releaseManager.py b/ReleaseManager/releaseManager.py index 1e07b80e..bf6bac2c 100644 --- a/ReleaseManager/releaseManager.py +++ b/ReleaseManager/releaseManager.py @@ -10,7 +10,7 @@ import wget import subprocess import shutil - +import datetime class ReleaseManager(MakesmithInitFuncs): @@ -20,48 +20,84 @@ def __init__(self): releases = None latestRelease = None - def getReleases(self): - tempReleases = [] - enableExperimental = self.data.config.getValue("WebControl Settings", "experimentalReleases") - for release in self.releases: - if not enableExperimental: - if not self.isExperimental(re.sub(r'[v]', r'', release.tag_name)): + def fetchReleases(self): + try: + print("Fetching GitHub releases.") + g = Github() + repo = g.get_repo("WebControlCNC/WebControl") + self.releases = repo.get_releases() + for release in self.releases: + release.tag_version = 0 + release.tag_date = None + + versionPattern = re.compile(r"^v?([0-9.]+)_?([0-9]{4}-[0-9]{2}-[0-9]{2})?$") + versionInfo = versionPattern.match(release.tag_name) + if (versionInfo): + release.tag_version = float(versionInfo.group(1)) + if (versionInfo.group(2)): + release.tag_date = datetime.datetime.strptime("%Y-%m-%d", versionInfo.group(2)) + else: + release.tag_date = release.published_at() + else: + # This release tage name is not valid. + print("Release tag invlaid: " + release.tag_name) + # For testing of temp release. + if (release.tag_name == "2020-05-26-2021"): + release.tag_version = 0.94 + release.tag_date = datetime.datetime(2020, 5, 26) + + except Exception as e: + print("Error fetching github releases: " + str(e)) + + def getValidReleases(self, refresh = False): + if (self.releases is None or refresh): + self.fetchReleases() + + try: + print("Getting valid releases.") + enablePreRelease = self.data.config.getValue("WebControl Settings", "experimentalReleases") + tempReleases = [] + for release in self.releases: + if release.prerelease: + if enablePreRelease: + # Add experimental releases is setting enabled. + tempReleases.append(release) + else: + # Add stable releases. tempReleases.append(release) - else: - tempReleases.append(release) - return tempReleases + return tempReleases + + except Exception as e: + print("Error getting valid releases: " + str(e)) + def getLatestValidRelease(self, refresh = False): + if (self.latestRelease == None or refresh): + self.checkLatestRelease() - def getLatestRelease(self): return self.latestRelease - def checkForLatestPyRelease(self): + def checkLatestRelease(self, refresh = False): if True: # self.data.platform=="PYINSTALLER": - print("Checking latest pyrelease.") try: - enableExperimental = self.data.config.getValue("WebControl Settings", "experimentalReleases") - g = Github() - repo = g.get_repo("WebControlCNC/WebControl") - self.releases = repo.get_releases() - latestVersionGithub = 0 + print("Checking latest valid release.") + validReleases = self.getValidReleases(refresh) self.latestRelease = None - for release in self.releases: - tag_name = re.sub(r'[v]', r'', release.tag_name) - tag_float = float(tag_name) - eligible = False - if not enableExperimental: - if not self.isExperimental(release): - eligible = True - else: - eligible = True - #print("tag:"+tag_name+", eligible:"+str(eligible)) - if eligible and tag_float > latestVersionGithub: - latestVersionGithub = tag_float + latestVersionNumber = 0 + latestVersionDate = None + + for release in validReleases: + if (release.tag_version > latestVersionNumber): + latestVersionNumber = release.tag_version + latestVersionDate = release.tag_date self.latestRelease = release + elif (release.tag_version == latestVersionNumber and release.tag_date > latestVersionDate): + latestVersionDate = release.tag_date + self.latestRelease = release + + print("Latest release: " + str(self.latestRelease.tag_name)) - print("Latest pyrelease: " + str(latestVersionGithub)) - if self.latestRelease is not None and latestVersionGithub > self.data.pyInstallCurrentVersion: - print("Latest release tag: " + self.latestRelease.tag_name) + if self.latestRelease is not None and latestVersionNumber >= self.data.pyInstallCurrentVersionNumber and latestVersionDate > self.data.pyInstallCurrentVersionDate: + print("Fetching Assets for release: " + self.latestRelease.tag_name) assets = self.latestRelease.get_assets() for asset in assets: if asset.name.find(self.data.pyInstallType) != -1 and asset.name.find(self.data.pyInstallPlatform) != -1: @@ -71,19 +107,9 @@ def checkForLatestPyRelease(self): self.data.pyInstallUpdateAvailable = True self.data.pyInstallUpdateBrowserUrl = asset.browser_download_url self.data.pyInstallUpdateVersion = self.latestRelease - except Exception as e: - print("Error checking pyrelease: " + str(e)) - def isExperimental(self, release): - ''' - Deternmines if release is experimental. Pre-Releases are experimental. - :param tag: - :return: - ''' - if release.prerelease: - return True - else: - return False + except Exception as e: + print("Error checking latest valid release: " + str(e)) def processAbsolutePath(self, path): index = path.find("main.py") @@ -187,15 +213,14 @@ def make_executable(self, path): os.chmod(path, mode) print("4") - - def update(self, version): + def update(self, releaseTagName): ''' - Need to clean this up. :param version: :return: ''' - for release in self.releases: - if release.tag_name == version: + validReleases = self.getValidReleases() + for release in validReleases: + if release.tag_name == releaseTagName: assets = release.get_assets() for asset in assets: if asset.name.find(self.data.pyInstallType) != -1 and asset.name.find(self.data.pyInstallPlatform) != -1: @@ -204,7 +229,7 @@ def update(self, version): self.data.pyInstallUpdateBrowserUrl = asset.browser_download_url print(self.data.pyInstallUpdateBrowserUrl) return self.updatePyInstaller(True) - print("Couldn't find a suitable file for the current platform and target version: " + version) - return False + print("Update failed for release: " + releaseTagName) + return False diff --git a/WebPageProcessor/webPageProcessor.py b/WebPageProcessor/webPageProcessor.py index 3892cd9c..5084ff52 100644 --- a/WebPageProcessor/webPageProcessor.py +++ b/WebPageProcessor/webPageProcessor.py @@ -511,7 +511,7 @@ def createWebPage(self, pageID, isMobile, args): ) return page, "Open Board", False, "medium", "content", "footerSubmit" elif pageID == "about": - version = self.data.pyInstallCurrentVersion + version = self.data.pyInstallCurrentVersionNumber if isMobile: pageName = "about.html" else: @@ -526,30 +526,26 @@ def createWebPage(self, pageID, isMobile, args): page = render_template(pageName, pageID="gettingStarted") return page, "Getting Started", False, "medium", "content", False elif pageID == "releases": - releases = self.data.releaseManager.getReleases() - latestRelease = self.data.releaseManager.getLatestRelease() - currentRelease = "v"+str(self.data.pyInstallCurrentVersion) - for release in releases: - tag_name = re.sub(r'[v]', r'', release.tag_name) + latestRelease = self.data.releaseManager.getLatestValidRelease() + releases = self.data.releaseManager.getValidReleases() + currentRelease = "v" + str(self.data.pyInstallCurrentVersionNumber) + if isMobile: - page = render_template( - "releases_mobile.html", - title="Update Manager", - releases=releases, - latestRelease=latestRelease, - currentRelease=currentRelease, - pageID="releases", - ) + file = "releases_mobile.html" else: - page = render_template( - "releases.html", - title="Update Manager", - releases=releases, - latestRelease=latestRelease, - currentRelease=currentRelease, - pageID="releases", + file = "releases.html", + + page = render_template( + file, + title = "Update Manager", + releases = releases, + latestRelease = latestRelease, + currentRelease = currentRelease, + pageID = "releases", ) + return page, "Update Manager", False, "medium", "content", False + elif pageID == "helpPages": helpPages = self.data.helpManager.getHelpPages() if isMobile: diff --git a/templates/releases.html b/templates/releases.html index c05f7147..ce2e8f33 100644 --- a/templates/releases.html +++ b/templates/releases.html @@ -6,7 +6,10 @@
  • -

    {{release.tag_name}}

    +

    {{release.tag_version}}

    + {% if release.tag_date %} +

    {{release.tag_date.strftime("%x")}}

    + {% endif %}

    {{release.title}}

    From b3a21164264887eca576ddd511aac7c9fe212324 Mon Sep 17 00:00:00 2001 From: gb0101010101 <7369439+gb0101010101@users.noreply.github.com> Date: Mon, 1 Jun 2020 01:24:53 -0700 Subject: [PATCH 3/8] Delete .pydevproject --- .pydevproject | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .pydevproject diff --git a/.pydevproject b/.pydevproject deleted file mode 100644 index 3f56be2d..00000000 --- a/.pydevproject +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - /${PROJECT_DIR_NAME} - - - - python interpreter - - WebControl Python3 - - From c6c4c6c768b638c7f1190b90d3cab69b21ef4f33 Mon Sep 17 00:00:00 2001 From: gb0101010101 <7369439+gb0101010101@users.noreply.github.com> Date: Mon, 1 Jun 2020 01:25:08 -0700 Subject: [PATCH 4/8] Delete org.eclipse.core.resources.prefs --- .settings/org.eclipse.core.resources.prefs | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .settings/org.eclipse.core.resources.prefs diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 07b452b5..00000000 --- a/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,12 +0,0 @@ -eclipse.preferences.version=1 -encoding//.venv/lib/python3.6/site-packages/flask/app.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/flask/ctx.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/flask/helpers.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/flask/templating.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/github/GitRelease.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/github/MainClass.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/github/PaginatedList.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/github/Repository.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/github/Requester.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/github/__init__.py=utf-8 -encoding//.venv/lib/python3.6/site-packages/werkzeug/local.py=utf-8 From 4bb4742ea8ea281317e6d8f38788826b255d9f36 Mon Sep 17 00:00:00 2001 From: gb0101010101 <7369439+gb0101010101@users.noreply.github.com> Date: Mon, 1 Jun 2020 12:10:29 -0700 Subject: [PATCH 5/8] Revert webbrowser.open_new_tab(). --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 023f9e2a..9f41250f 100644 --- a/main.py +++ b/main.py @@ -773,7 +773,7 @@ def isnumber(s): print("opening browser") webPortStr = str(webPortInt) - webbrowser.open_new_tab("//localhost:"+webPortStr) + webbrowser.open_new_tab("http://localhost:"+webPortStr) host_name = socket.gethostname() host_ip = socket.gethostbyname(host_name) app.data.hostAddress = host_ip + ":" + webPortStr From 8d1b70bcf5497637b88ac960c804af89a5099837 Mon Sep 17 00:00:00 2001 From: gb0101010101 <7369439+gb0101010101@users.noreply.github.com> Date: Wed, 3 Jun 2020 20:01:09 -0700 Subject: [PATCH 6/8] Only Shutdown if upgrade suceeds. --- Actions/actions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Actions/actions.py b/Actions/actions.py index 6d80791e..48ae84f0 100644 --- a/Actions/actions.py +++ b/Actions/actions.py @@ -130,7 +130,8 @@ def processAction(self, msg): elif msg["data"]["command"] == "update": if not self.data.releaseManager.update(msg["data"]["arg"]): self.data.ui_queue1.put("Alert", "Alert", "Error with updating webcontrol.") - return "Shutdown" + else: + return "Shutdown" elif msg["data"]["command"] == "cutTriangularCalibrationPattern": if not self.data.triangularCalibration.cutTriangularCalibrationPattern(): self.data.ui_queue1.put("Alert", "Alert", "Error with cutting triangular calibration pattern.") From 65e6cd2dc0fb13fd78aa40dd3292442e276b4409 Mon Sep 17 00:00:00 2001 From: gb0101010101 <7369439+gb0101010101@users.noreply.github.com> Date: Fri, 5 Jun 2020 17:52:07 -0700 Subject: [PATCH 7/8] Only shutdown on successfully update. --- Actions/actions.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Actions/actions.py b/Actions/actions.py index 48ae84f0..876b492e 100644 --- a/Actions/actions.py +++ b/Actions/actions.py @@ -128,9 +128,8 @@ def processAction(self, msg): else: self.data.ui_queue1.put("Alert", "Alert", "No GCode file loaded.") elif msg["data"]["command"] == "update": - if not self.data.releaseManager.update(msg["data"]["arg"]): - self.data.ui_queue1.put("Alert", "Alert", "Error with updating webcontrol.") - else: + # Errors handled during update so only reboot on success. + if self.data.releaseManager.update(msg["data"]["arg"]): return "Shutdown" elif msg["data"]["command"] == "cutTriangularCalibrationPattern": if not self.data.triangularCalibration.cutTriangularCalibrationPattern(): From 4c4b7b7b4b00a32c915e43b8769905d47d8bc533 Mon Sep 17 00:00:00 2001 From: gb0101010101 <7369439+gb0101010101@users.noreply.github.com> Date: Fri, 5 Jun 2020 18:00:10 -0700 Subject: [PATCH 8/8] Add Update progress modal. Modal uses SocketIO room 'modal' to isolate from main thread. Fix default dates for version comparision. --- ReleaseManager/releaseManager.py | 229 +++++++++++++++++-------------- main.py | 6 +- templates/releases.html | 132 ++++++++++++++---- 3 files changed, 235 insertions(+), 132 deletions(-) diff --git a/ReleaseManager/releaseManager.py b/ReleaseManager/releaseManager.py index bf6bac2c..78ae6902 100644 --- a/ReleaseManager/releaseManager.py +++ b/ReleaseManager/releaseManager.py @@ -11,6 +11,8 @@ import subprocess import shutil import datetime +import sys +from app import socketio class ReleaseManager(MakesmithInitFuncs): @@ -28,7 +30,7 @@ def fetchReleases(self): self.releases = repo.get_releases() for release in self.releases: release.tag_version = 0 - release.tag_date = None + release.tag_date = datetime.datetime(1, 1, 1, 0, 0) versionPattern = re.compile(r"^v?([0-9.]+)_?([0-9]{4}-[0-9]{2}-[0-9]{2})?$") versionInfo = versionPattern.match(release.tag_name) @@ -37,7 +39,7 @@ def fetchReleases(self): if (versionInfo.group(2)): release.tag_date = datetime.datetime.strptime("%Y-%m-%d", versionInfo.group(2)) else: - release.tag_date = release.published_at() + release.tag_date = release.published_at else: # This release tage name is not valid. print("Release tag invlaid: " + release.tag_name) @@ -83,7 +85,7 @@ def checkLatestRelease(self, refresh = False): validReleases = self.getValidReleases(refresh) self.latestRelease = None latestVersionNumber = 0 - latestVersionDate = None + latestVersionDate = datetime.datetime(1, 1, 1, 0, 0) for release in validReleases: if (release.tag_version > latestVersionNumber): @@ -116,93 +118,105 @@ def processAbsolutePath(self, path): self.data.pyInstallInstalledPath = path[0:index - 1] print(self.data.pyInstallInstalledPath) + def wgetBarCustom(self, current, total, width=80): + percentDone = int(current / total * 100) + data = { + "percent": percentDone, + "current" : current, + "total" : total, + } + socketio.emit("message", {"command": "upgradeDownload", "data": json.dumps(data), "dataFormat": "json"}, namespace="/MaslowCNC", room="modal") + print("Downloading: %d%% [%d / %d] bytes" % (percentDone, current, total)) + def updatePyInstaller(self, bypassCheck = False): - home = self.data.config.getHome() - if self.data.pyInstallUpdateAvailable == True or bypassCheck: - if not os.path.exists(home + "/.WebControl/downloads"): - print("creating downloads directory") - os.mkdir(home + "/.WebControl/downloads") - fileList = glob.glob(home + "/.WebControl/downloads/*.gz") - for filePath in fileList: - try: + try: + home = self.data.config.getHome() + + if self.data.pyInstallUpdateAvailable == True or bypassCheck: + if not os.path.exists(home + "/.WebControl/downloads"): + self.emitStatusUpdate("Creating downloads directory.") + os.mkdir(home + "/.WebControl/downloads", exists_ok=True) + + fileList = glob.glob(home + "/.WebControl/downloads/*.gz") + + for filePath in fileList: os.remove(filePath) - except: - print("error cleaning download directory: ", filePath) - print("---") - print("Downloading new WebControl release...") - if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": - filename = wget.download(self.data.pyInstallUpdateBrowserUrl, out=home + "\\.WebControl\\downloads") - else: - filename = wget.download(self.data.pyInstallUpdateBrowserUrl, out=home + "/.WebControl/downloads") - print("Successfully downloaded new release to:" + filename) - if self.data.platform == "PYINSTALLER": - lhome = os.path.join(self.data.platformHome) - else: - lhome = "." - - print("Creating target version directory...") - target_dir = self.data.pyInstallInstalledPath + '_next' - if os.path.exists(target_dir): - print("New release directory already exists, removing it") - shutil.rmtree(target_dir) - os.mkdir(target_dir) - print("Creating target version directory DONE") - - print("Unzipping new release...") - if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": - command = [ - lhome + "/tools/7za.exe", - "x", - "-y", - filename, - "-o", - target_dir - ] - print(command) - subprocess.run(command) - else: - command = [ - "tar", - "-zxf", - filename, - "-C", - target_dir - ] - print(command) - subprocess.run(command) - print("Unzipping new release DONE") - - print("Upgrade script...") - print("Checking if it needs to be run for platform: " + self.data.pyInstallPlatform) - if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": - upgrade_script_path = target_dir + "\\tools\\upgrade_" + self.data.pyInstallPlatform + ".bat" - else: - upgrade_script_path = target_dir + "/tools/upgrade_" + self.data.pyInstallPlatform + ".sh" - if os.path.exists(upgrade_script_path): - print("Yes, running it") - self.make_executable(upgrade_script_path) - subprocess.run([upgrade_script_path]) - else: - print("No upgrade script needed.") - print("Upgrade script DONE") - - print("Backing up the current install...") - backup_path = self.data.pyInstallInstalledPath + '_old' - print("Backup location: " + backup_path) - if os.path.exists(backup_path): - print("Old backup found, removing it") - shutil.rmtree(backup_path) - os.rename(self.data.pyInstallInstalledPath, self.data.pyInstallInstalledPath + '_old') - print("Backing up the current install DONE") - - print("Moving the target version in place...") - os.rename(target_dir, self.data.pyInstallInstalledPath) - print("Moving the target version in place DONE") - - print("WebControl upgrade complete, shutting down to make way to the target version") - return True - return False + self.emitStatusUpdate("Downloading new release.") + if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": + filename = wget.download(self.data.pyInstallUpdateBrowserUrl, out=home + "\\.WebControl\\downloads", bar = self.wgetBarCustom) + else: + filename = wget.download(self.data.pyInstallUpdateBrowserUrl, out=home + "/.WebControl/downloads", bar = self.wgetBarCustom) + self.emitStatusUpdate("Successfully downloaded new release to:" + filename) + + if self.data.platform == "PYINSTALLER": + lhome = os.path.join(self.data.platformHome) + else: + lhome = "." + + self.emitStatusUpdate("Creating target version directory.") + target_dir = self.data.pyInstallInstalledPath + '_next' + if os.path.exists(target_dir): + self.emitStatusUpdate("New release directory already exists, removing it.") + shutil.rmtree(target_dir) + os.mkdir(target_dir) + + self.emitStatusUpdate("Extracting new release.") + if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": + command = [ + lhome + "/tools/7za.exe", + "x", + "-y", + filename, + "-o", + target_dir + ] + print(command) + subprocess.run(command) + else: + command = [ + "tar", + "-zxf", + filename, + "-C", + target_dir + ] + print(command) + subprocess.run(command) + + self.emitStatusUpdate("Checking for upgrade script.") + if self.data.pyInstallPlatform == "win32" or self.data.pyInstallPlatform == "win64": + upgrade_script_path = target_dir + "\\tools\\upgrade_" + self.data.pyInstallPlatform + ".bat" + else: + upgrade_script_path = target_dir + "/tools/upgrade_" + self.data.pyInstallPlatform + ".sh" + if os.path.exists(upgrade_script_path): + self.emitStatusUpdate("Running upgrade script.") + self.make_executable(upgrade_script_path) + subprocess.run([upgrade_script_path]) + else: + self.emitStatusUpdate("Upgrade script not required.") + + self.emitStatusUpdate("Backing up the current install.") + backup_path = self.data.pyInstallInstalledPath + '_old' + print("Backup location: " + backup_path) + if os.path.exists(backup_path): + self.emitStatusUpdate("Old backup found, removing it.") + shutil.rmtree(backup_path) + os.rename(self.data.pyInstallInstalledPath, self.data.pyInstallInstalledPath + '_old') + print("Backing up the current install DONE") + + self.emitStatusUpdate("Moving the target version in place.") + os.rename(target_dir, self.data.pyInstallInstalledPath) + print("Moving the target version in place DONE") + + self.emitStatusUpdate("WebControl upgrade complete.") + socketio.emit("message", {"command": "upgradeSuccess", "data": "success", "dataFormat": "text"}, namespace="/MaslowCNC", room="modal") + # Restart application terminating current. + return True + + except Exception as e: + self.emitStatusUpdate("Error updating release: " + str(e)) + return False def make_executable(self, path): print("1") @@ -215,21 +229,32 @@ def make_executable(self, path): def update(self, releaseTagName): ''' - :param version: + :param releaseTagName: :return: ''' - validReleases = self.getValidReleases() - for release in validReleases: - if release.tag_name == releaseTagName: - assets = release.get_assets() - for asset in assets: - if asset.name.find(self.data.pyInstallType) != -1 and asset.name.find(self.data.pyInstallPlatform) != -1: - print(asset.name) - print(asset.url) - self.data.pyInstallUpdateBrowserUrl = asset.browser_download_url - print(self.data.pyInstallUpdateBrowserUrl) - return self.updatePyInstaller(True) - - print("Update failed for release: " + releaseTagName) - return False + try: + validReleases = self.getValidReleases() + releaseAssetsFound = False + for release in validReleases: + if release.tag_name == releaseTagName: + assets = release.get_assets() + for asset in assets: + if asset.name.find(self.data.pyInstallType) != -1 and asset.name.find(self.data.pyInstallPlatform) != -1: + print(asset.name) + print(asset.url) + self.data.pyInstallUpdateBrowserUrl = asset.browser_download_url + print(self.data.pyInstallUpdateBrowserUrl) + releaseAssetsFound = True + break + if releaseAssetsFound: + return self.updatePyInstaller(True) + else: + raise Exception("Assets not found for release: " + str(releaseTagName)) + + except Exception as e: + print("Update error: " + str(e)) + return False + def emitStatusUpdate(self, text): + print(text) + socketio.emit("message", {"command": "upgradeStatus", "data": text, "dataFormat": "text"}, namespace="/MaslowCNC", room="modal") diff --git a/main.py b/main.py index 9f41250f..9a65a1d3 100644 --- a/main.py +++ b/main.py @@ -14,7 +14,7 @@ import time import threading import json - +from flask_socketio import join_room from flask import Flask, jsonify, render_template, current_app, request, flash, Response, send_file, send_from_directory from flask_mobility.decorators import mobile_template from werkzeug import secure_filename @@ -737,6 +737,10 @@ def log_connect(): def log_disconnect(): app.data.console_queue.put("Client disconnected") +@socketio.on("join_room", namespace="/MaslowCNC") +def joinRoom(msg): + room = msg["data"]["room"] + join_room(room) @app.template_filter('isnumber') def isnumber(s): diff --git a/templates/releases.html b/templates/releases.html index ce2e8f33..73621600 100644 --- a/templates/releases.html +++ b/templates/releases.html @@ -1,36 +1,110 @@ {% block content %}
    -
      - {% for release in releases %} -
    • -
      -
      -

      {{release.tag_version}}

      - {% if release.tag_date %} -

      {{release.tag_date.strftime("%x")}}

      - {% endif %} -
      -
      -

      {{release.title}}

      -

      {{release.body|markdown}}

      -
      -
      - {% if release.tag_name == currentRelease %} - - {% elif release.tag_name == latestRelease.tag_name %} - - {% else %} - - {% endif %} -
      +
        + {% for release in releases %} +
      • +
        +
        +

        {{release.tag_version}}

        + {% if release.tag_date %} +

        {{release.tag_date.strftime("%x")}}

        + {% endif %}
        -
      • - {% endfor %} -
      +
      +

      {{release.title}}

      +

      {{release.body|markdown}}

      +
      +
      + {% if release.tag_name == currentRelease %} + + {% elif release.tag_name == latestRelease.tag_name %} + + {% else %} + + {% endif %} +
      +
      +
    • + {% endfor %} +
    +
    {% endblock %} - {% block javascript %} - -{% endblock %} \ No newline at end of file + +{% endblock %}