diff --git a/application/api/v1/blueprints/architect.py b/application/api/v1/blueprints/architect.py index 3420411..fa9132d 100644 --- a/application/api/v1/blueprints/architect.py +++ b/application/api/v1/blueprints/architect.py @@ -1,4 +1,7 @@ -from flask import Blueprint, jsonify +import os +import yaml + +from flask import Blueprint, jsonify, current_app from application.common import logger from application.common.decorators import authorization_required @@ -34,6 +37,11 @@ def agent_info(): games = games_controller.get_all_games(add_server_status=True) games = games["items"] + version_file = os.path.join(current_app.static_folder, "version.yml") + + with open(version_file, "r") as file: + version_data = yaml.safe_load(file) + # TODO - Tech Debt: Update agent info page to get this info over websocket. Works for now # but does not scale. for game in games: @@ -55,5 +63,6 @@ def agent_info(): info: dict = platform_dict info.update({"games": games}) + info["version"] = version_data["version"] return jsonify(info) diff --git a/application/gui/widgets/about_widget.py b/application/gui/widgets/about_widget.py index fa070f6..65909b6 100644 --- a/application/gui/widgets/about_widget.py +++ b/application/gui/widgets/about_widget.py @@ -1,14 +1,18 @@ -from PyQt5.QtWidgets import ( - QWidget, - QLabel, - QVBoxLayout, -) +import requests + +from packaging.version import Version +from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QMessageBox, QPushButton from application.common.decorators import timeit from operator_client import Operator class AboutWidget(QWidget): + RELEASES_URL = "https://api.github.com/repos/agentsofthesystem/agent-smith/releases" + RELEASE_DL_LINK_FMT = ( + "https://github.com/agentsofthesystem/agent-smith/releases/tag/{tag}" + ) + @timeit def __init__( self, @@ -19,18 +23,71 @@ def __init__( self._layout = QVBoxLayout() self._client = client self._initialized = False + self._update_check_btn: QPushButton = QPushButton("Check for Update") + + self._latest_version = "" + self._current_version = "" + + self.q_label_1: QLabel = None + self.q_label_2: QLabel = None + self.q_label_3: QLabel = None self.init_ui() + def _check_for_update(self, is_initialize=False) -> None: + update_available = False + self._latest_version = self._get_latest_release() + self._current_version = self._client.app.get_app_version() + + if Version(self._current_version) < Version(self._latest_version): + update_available = True + + update_available_str = "Yes" if update_available else "No" + + self.q_label_1.setText(f"Software Version: {self._current_version}") + self.q_label_2.setText(f"Latest Version: {self._latest_version}") + self.q_label_3.setText(f"Update Available? {update_available_str}") + + if update_available and not is_initialize: + message = QMessageBox(self) + message.setWindowTitle("Update Download Link") + message.setText( + "

Update Available

" + f'

Download: {self.RELEASE_DL_LINK_FMT.format(tag=self._latest_version)}

' # noqa: E501 + ) + message.exec() + + def _get_latest_release(self) -> str: + latest_version = None + response = requests.get(self.RELEASES_URL) + + if response.status_code == 200: + output = response.json() + version_list = [x["tag_name"] for x in output] + + if len(version_list) > 0: + latest_version = version_list[0] + + return latest_version + def init_ui(self): v_layout = QVBoxLayout() - version = self._client.app.get_app_version() - q_label = QLabel(f"Software Version: {version}") - v_layout.addWidget(q_label) + self._update_check_btn.clicked.connect(lambda: self._check_for_update()) + + self.q_label_1 = QLabel(f"Software Version: {self._current_version}") + self.q_label_2 = QLabel(f"Latest Version: {self._latest_version}") + self.q_label_3 = QLabel("Update Available? ") + + v_layout.addWidget(self.q_label_1) + v_layout.addWidget(self.q_label_2) + v_layout.addWidget(self.q_label_3) + v_layout.addWidget(self._update_check_btn) self._layout.addLayout(v_layout) + self._check_for_update(is_initialize=True) + self.setFocus() self.setLayout(self._layout) self.show() diff --git a/application/static/version.yml b/application/static/version.yml index 250a854..e5a3c6e 100644 --- a/application/static/version.yml +++ b/application/static/version.yml @@ -1 +1 @@ -version: 0.0.0 +version: v0.0.0 diff --git a/requirements.txt b/requirements.txt index a353022..8aeedf4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ flask==3.0.0 Flask-SQLAlchemy==3.1.1 gunicorn oauthlib +packaging psutil pydocstyle pyinstaller