diff --git a/README.md b/README.md index 073b94c0..c0ee774d 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ To contribute with this project, have a look at [CONTRIBUTING.md](https://github - **python-requests** - **python-pip** - **python-pyqt5** +- **python-yaml** ##### Optional - **flatpak**: to be able to handle Flatpak applications diff --git a/bauh/gems/web/__init__.py b/bauh/gems/web/__init__.py index dea2326d..3d756e18 100644 --- a/bauh/gems/web/__init__.py +++ b/bauh/gems/web/__init__.py @@ -12,4 +12,4 @@ NATIVEFIER_BIN_PATH = '{}/.bin/nativefier'.format(NODE_MODULES_PATH) ELECTRON_PATH = '{}/.cache/electron'.format(HOME_PATH) ELECTRON_DOWNLOAD_URL = 'https://github.com/electron/electron/releases/download/v{version}/electron-v{version}-linux-{arch}.zip' ELECTRON_SHA256_URL = 'https://github.com/electron/electron/releases/download/v{version}/SHASUMS256.txt' -ELECTRON_VERSION = '0.5.12' +URL_ENVIRONMENT_SETTINGS = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/web/environment.yml' diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py index 2dbb6dc4..5326bd1c 100644 --- a/bauh/gems/web/controller.py +++ b/bauh/gems/web/controller.py @@ -30,7 +30,8 @@ class WebApplicationManager(SoftwareManager): def __init__(self, context: ApplicationContext): super(WebApplicationManager, self).__init__(context=context) self.http_client = context.http_client - self.node_updater = NodeUpdater(logger=context.logger, file_downloader=context.file_downloader, i18n=context.i18n) + self.node_updater = NodeUpdater(logger=context.logger, http_client=context.http_client, + file_downloader=context.file_downloader, i18n=context.i18n) self.enabled = True def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult: diff --git a/bauh/gems/web/environment.py b/bauh/gems/web/environment.py index 848b5b64..f3201f11 100644 --- a/bauh/gems/web/environment.py +++ b/bauh/gems/web/environment.py @@ -5,37 +5,41 @@ import tarfile import traceback from pathlib import Path +import requests +import yaml + from bauh.api.abstract.download import FileDownloader from bauh.api.abstract.handler import ProcessWatcher -from bauh.api.exception import NoInternetException +from bauh.api.http import HttpClient from bauh.commons import system +from bauh.commons.html import bold from bauh.commons.system import SimpleProcess, ProcessHandler, run_cmd from bauh.gems.web import BIN_PATH, NODE_DIR_PATH, NODE_BIN_PATH, NPM_BIN_PATH, NODE_MODULES_PATH, NATIVEFIER_BIN_PATH, \ - ELECTRON_PATH, ELECTRON_DOWNLOAD_URL, ELECTRON_SHA256_URL, ELECTRON_VERSION + ELECTRON_PATH, ELECTRON_DOWNLOAD_URL, ELECTRON_SHA256_URL, URL_ENVIRONMENT_SETTINGS from bauh.view.util.translation import I18n class NodeUpdater: - def __init__(self, logger: logging.Logger, file_downloader: FileDownloader, i18n: I18n): + def __init__(self, logger: logging.Logger, http_client: HttpClient, file_downloader: FileDownloader, i18n: I18n): self.logger = logger self.file_downloader = file_downloader self.i18n = i18n + self.http_client = http_client def _is_internet_available(self) -> bool: self.logger.info('Checking internet connection') # TODO return True - def _download_and_install(self, cloud_version: str, watcher: ProcessWatcher ) -> bool: - npm_dlink = 'https://nodejs.org/dist/v12.13.1/node-v12.13.1-linux-x64.tar.xz' - self.logger.info("Downloading NodeJS {}: {}".format(cloud_version, npm_dlink)) + def _download_and_install(self, version: str, version_url: str, watcher: ProcessWatcher) -> bool: + self.logger.info("Downloading NodeJS {}: {}".format(version, version_url)) - tarf_path = '{}/{}'.format(BIN_PATH, npm_dlink.split('/')[-1]) - downloaded = self.file_downloader.download(npm_dlink, watcher=watcher, output_path=tarf_path, cwd=BIN_PATH) + tarf_path = '{}/{}'.format(BIN_PATH, version_url.split('/')[-1]) + downloaded = self.file_downloader.download(version_url, watcher=watcher, output_path=tarf_path, cwd=BIN_PATH) if not downloaded: - self.logger.error("Could not download '{}'. Aborting...".format(npm_dlink)) + self.logger.error("Could not download '{}'. Aborting...".format(version_url)) return False else: try: @@ -56,29 +60,22 @@ class NodeUpdater: except: self.logger.error('Could not delete file {}'.format(tarf_path)) - def update_node(self, watcher: ProcessWatcher = None) -> bool: - if not self._is_internet_available(): - raise NoInternetException() - - self.logger.info('Checking Node version from bauh-files') - # TODO - cloud_version = 'v12.13.1' - + def update_node(self, version: str, version_url: str, watcher: ProcessWatcher = None) -> bool: Path(BIN_PATH).mkdir(parents=True, exist_ok=True) if not os.path.exists(NODE_DIR_PATH): - if not self._download_and_install(cloud_version, watcher): - return False + return self._download_and_install(version=version, version_url=version_url, watcher=watcher) else: installed_version = system.run_cmd('{} --version'.format(NODE_BIN_PATH)) if installed_version: installed_version = installed_version.strip() - self.logger.info('Node -> installed: {}. cloud: {}.'.format(installed_version, cloud_version)) + self.logger.info('Node -> installed: {}. cloud: {}.'.format(installed_version, version)) - if cloud_version != installed_version: - return self._download_and_install(cloud_version, watcher) + if version != installed_version: + self.logger.info("The NodeJs installed version is different from the Cloud.") + return self._download_and_install(version=version, version_url=version_url, watcher=watcher) else: self.logger.info("Node is already up to date") return True @@ -87,15 +84,18 @@ class NodeUpdater: self.logger.info("Removing {}".format(NODE_DIR_PATH)) try: shutil.rmtree(NODE_DIR_PATH) - return self._download_and_install(cloud_version, watcher) + return self._download_and_install(version=version, version_url=version_url, watcher=watcher) except: self.logger.error('Could not delete the dir {}'.format(NODE_DIR_PATH)) return False - def _install_nativefier(self, handler: ProcessHandler) -> bool: + def _install_nativefier(self, version: str, handler: ProcessHandler) -> bool: self.logger.info("Installing nativefier") - # TODO freeze specific version - proc = SimpleProcess([NPM_BIN_PATH, 'install', 'nativefier'], cwd=BIN_PATH) + + if handler and handler.watcher: + handler.watcher.change_substatus(self.i18n['web.environment.install'].format(bold('nativefier'))) + + proc = SimpleProcess([NPM_BIN_PATH, 'install', 'nativefier@{}'.format(version)], cwd=BIN_PATH) if handler: return handler.handle_simple(proc)[0] @@ -103,22 +103,17 @@ class NodeUpdater: proc.instance.wait() if self._is_nativefier_installed(): - self.logger.info("nativefier installed") + self.logger.info("nativefier {} installed".format(version)) return True else: - self.logger.error("Could not install nativefier") + self.logger.error("Could not install nativefier {}".format(version)) return False def _is_nativefier_installed(self) -> bool: - try: - return bool(system.run_cmd('{} --version'.format(NATIVEFIER_BIN_PATH))) - except: - self.logger.error("Could not determine the installed nativefier version") - traceback.print_exc() - return False + return os.path.exists(NATIVEFIER_BIN_PATH) - def install_nativefier(self, remove_modules: bool = False, handler: ProcessHandler = None) -> bool: - self.logger.info("Preparing to install nativefier") + def install_nativefier(self, version: str, remove_modules: bool = False, handler: ProcessHandler = None) -> bool: + self.logger.info("Preparing to install nativefier {}".format(version)) if remove_modules and os.path.exists(NODE_MODULES_PATH): self.logger.info('Removing old dir {}'.format(NODE_MODULES_PATH)) @@ -128,10 +123,10 @@ class NodeUpdater: self.logger.error('Could not remove dir {}. Aborting...'.format(NODE_MODULES_PATH)) return False - return self._install_nativefier(handler) + return self._install_nativefier(version=version, handler=handler) else: if not self._is_nativefier_installed(): - return self._install_nativefier(handler) + return self._install_nativefier(version=version, handler=handler) self.logger.info("Nativefier is already installed") return True @@ -140,18 +135,14 @@ class NodeUpdater: self.logger.info("Downloading Electron {}".format(version)) arch = 'ia32' if not is_x86_x64_arch else 'x64' - Path(ELECTRON_PATH).mkdir(parents=True, exist_ok=True) - electron_url = ELECTRON_DOWNLOAD_URL.format(arch=arch, version=version) electron_path = '{}/{}'.format(ELECTRON_PATH, electron_url.split('/')[-1]) - return self.file_downloader.download(file_url=electron_url, watcher=watcher, output_path=electron_path,cwd=ELECTRON_PATH) + return self.file_downloader.download(file_url=electron_url, watcher=watcher, output_path=electron_path, cwd=ELECTRON_PATH) def _download_electron_sha256(self, version: str, watcher: ProcessWatcher) -> bool: self.logger.info("Downloading Electron {} sha526".format(version)) - Path(ELECTRON_PATH).mkdir(parents=True, exist_ok=True) - sha256_url = ELECTRON_SHA256_URL.format(version=version) sha256_path = '{}/{}'.format(ELECTRON_PATH, sha256_url.split('/')[-1]) return self.file_downloader.download(file_url=sha256_url, watcher=watcher, output_path=sha256_path, cwd=ELECTRON_PATH) @@ -161,7 +152,9 @@ class NodeUpdater: if not os.path.exists(ELECTRON_PATH): self.logger.info("Electron is not installed") - self._download_electron(version=version, is_x86_x64_arch=is_x86_x64_arch, watcher=watcher) + Path(ELECTRON_PATH).mkdir(parents=True, exist_ok=True) + if self._download_electron(version=version, is_x86_x64_arch=is_x86_x64_arch, watcher=watcher): + return self._download_electron_sha256(version=version, watcher=watcher) else: files = run_cmd('ls', print_error=False, cwd=ELECTRON_PATH) @@ -191,20 +184,37 @@ class NodeUpdater: return False - def update_environment(self, is_x86_x64_arch: bool, handler: ProcessHandler = None): - if not self.update_node(handler.watcher if handler else None): - self.logger.warning('Could not update the environment') + def update_environment(self, is_x86_x64_arch: bool, handler: ProcessHandler = None) -> bool: + try: + res = self.http_client.get(URL_ENVIRONMENT_SETTINGS) + + if not res: + self.logger.warning('Could not retrieve the environments settings from the cloud') + return False + + try: + settings = yaml.safe_load(res.content) + except yaml.YAMLError: + self.logger.error('Could not parse environment settings: {}'.format(res.text)) + return False + + if not self.update_node(version=settings['nodejs']['version'], version_url=settings['nodejs']['url'], + watcher=handler.watcher if handler else None): + self.logger.warning('Could not install / update NodeJS') + return False + + if not self.install_nativefier(version=settings['nativefier']['version'], handler=handler): + self.logger.warning('Could not install / update nativefier') + return False + + res = self.install_electron(version=settings['electron']['version'], is_x86_x64_arch=is_x86_x64_arch, + watcher=handler.watcher if handler else None) + + if res: + self.logger.info('Environment updated') + else: + self.logger.warning('Could not update the environment') + + return res + except requests.exceptions.ConnectionError: return False - - if not self.install_nativefier(handler=handler): - self.logger.warning('Could not update the environment') - return False - - res = self.install_electron(version=ELECTRON_VERSION, is_x86_x64_arch=is_x86_x64_arch, watcher=handler.watcher if handler else None) - - if res: - self.logger.info('Environment updated') - else: - self.logger.warning('Could not update the environment') - - return res diff --git a/bauh/gems/web/resources/locale/en b/bauh/gems/web/resources/locale/en index 990b3f71..1cd028ca 100644 --- a/bauh/gems/web/resources/locale/en +++ b/bauh/gems/web/resources/locale/en @@ -1 +1,2 @@ -gem.web.info=It allows to install Web applications on your system \ No newline at end of file +gem.web.info=It allows to install Web applications on your system +web.environment.install=Installing {} \ No newline at end of file diff --git a/bauh/gems/web/resources/locale/pt b/bauh/gems/web/resources/locale/pt index 0a595ef1..96f15395 100644 --- a/bauh/gems/web/resources/locale/pt +++ b/bauh/gems/web/resources/locale/pt @@ -1 +1,2 @@ -gem.web.info=Permite instalar aplicações Web no seu sistema \ No newline at end of file +gem.web.info=Permite instalar aplicações Web no seu sistema +web.environment.nativefier=Instalando {} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index d7d761f6..9e4762fe 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pyqt5>=5.12 requests>=2.22 colorama>=0.4.1 +pyyaml>=5.2