[web] installing, removing and reading

This commit is contained in:
Vinícius Moreira
2019-12-11 18:49:43 -03:00
parent 49a3f12047
commit 3e46822e5e
15 changed files with 261 additions and 62 deletions

View File

@@ -3,3 +3,4 @@ from pathlib import Path
HOME_PATH = Path.home()
CACHE_PATH = '{}/.cache/bauh'.format(HOME_PATH)
CONFIG_PATH = '{}/.config/bauh'.format(HOME_PATH)
DESKTOP_ENTRIES_DIR = '{}/.local/share/applications'.format(HOME_PATH)

View File

@@ -1,9 +1,10 @@
import os
from bauh.api.constants import HOME_PATH
from bauh.api.constants import HOME_PATH, DESKTOP_ENTRIES_DIR
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
BIN_PATH = '{}/.local/share/bauh/web'.format(HOME_PATH)
INSTALLED_PATH = '{}/installed'.format(BIN_PATH)
NODE_DIR_PATH = '{}/node'.format(BIN_PATH)
NODE_BIN_PATH = '{}/bin/node'.format(NODE_DIR_PATH)
NPM_BIN_PATH = '{}/bin/npm'.format(NODE_DIR_PATH)
@@ -13,3 +14,4 @@ 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'
URL_ENVIRONMENT_SETTINGS = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/web/environment.yml'
DESKTOP_ENTRY_PATH_PATTERN = DESKTOP_ENTRIES_DIR + '/bauh.web.{name}.desktop'

View File

@@ -1,4 +1,10 @@
import glob
import json
import os
import shutil
import subprocess
import traceback
from pathlib import Path
from threading import Thread
from typing import List, Type, Set
@@ -8,8 +14,10 @@ from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import SoftwarePackage, PackageAction, PackageSuggestion, PackageUpdate, PackageHistory
from bauh.api.abstract.view import MessageType
from bauh.commons import system
from bauh.commons.html import bold
from bauh.commons.system import ProcessHandler
from bauh.gems.web import INSTALLED_PATH, nativefier, DESKTOP_ENTRY_PATH_PATTERN
from bauh.gems.web.environment import EnvironmentUpdater
from bauh.gems.web.model import WebApplication
@@ -37,6 +45,8 @@ class WebApplicationManager(SoftwareManager):
self.enabled = True
self.i18n = context.i18n
self.env_updater = env_updater
self.env_settings = {}
self.logger = context.logger
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult:
res = SearchResult([], [], 0)
@@ -56,7 +66,13 @@ class WebApplicationManager(SoftwareManager):
icon_tag = soup.head.find('link', attrs={"rel": "icon"})
icon_url = icon_tag.get('href') if icon_tag else None
res.new = [WebApplication(url=words, name=name, description=desc, icon_url=icon_url)]
app = WebApplication(url=words, name=name, description=desc, icon_url=icon_url)
if self.env_settings.get('electron') and self.env_settings['electron'].get('version'):
app.version = self.env_settings['electron']['version']
app.latest_version = app.version
res.new = [app]
res.total = 1
else:
# TODO
@@ -65,8 +81,15 @@ class WebApplicationManager(SoftwareManager):
return res
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = True) -> SearchResult:
# TODO
return SearchResult([], [], 0)
res = SearchResult([], [], 0)
if os.path.exists(INSTALLED_PATH):
for data_path in glob.glob('{}/*/*data.json'.format(INSTALLED_PATH)):
with open(data_path, 'r') as f:
res.installed.append(WebApplication(installed=True, **json.loads(f.read())))
res.total += 1
return res
def downgrade(self, pkg: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool:
pass
@@ -74,14 +97,47 @@ class WebApplicationManager(SoftwareManager):
def update(self, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher) -> bool:
pass
def uninstall(self, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher) -> bool:
pass
def uninstall(self, pkg: WebApplication, root_password: str, watcher: ProcessWatcher) -> bool:
self.logger.info("Checking if {} installation directory {} exists".format(pkg.name, pkg.installation_dir))
if not os.path.exists(pkg.installation_dir):
watcher.show_message(title=self.i18n['error'],
body=self.i18n['web.uninstall.error.install_dir.not_found'].format(bold(pkg.installation_dir)),
type_=MessageType.ERROR)
return False
self.logger.info("Removing {} installation directory {}".format(pkg.name, pkg.installation_dir))
try:
shutil.rmtree(pkg.installation_dir)
except:
watcher.show_message(title=self.i18n['error'],
body=self.i18n['web.uninstall.error.remove'].format(bold(pkg.installation_dir)),
type_=MessageType.ERROR)
traceback.print_exc()
return False
self.logger.info("Checking if {} desktop entry file {} exists".format(pkg.name, pkg.desktop_entry))
if os.path.exists(pkg.desktop_entry):
try:
os.remove(pkg.desktop_entry)
except:
watcher.show_message(title=self.i18n['error'],
body=self.i18n['web.uninstall.error.remove'].format(bold(pkg.desktop_entry)),
type_=MessageType.ERROR)
traceback.print_exc()
return False
return True
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
return {WebApplication}
def get_info(self, pkg: SoftwarePackage) -> dict:
pass
def get_info(self, pkg: WebApplication) -> dict:
if pkg.installed:
info = {'{}_{}'.format(idx + 1, att): getattr(pkg, att) for idx, att in enumerate(('url', 'description', 'version', 'installation_dir', 'desktop_entry'))}
info['6_exec_file'] = pkg.get_exec_path()
info['7_icon_path'] = pkg.get_disk_icon_path()
return info
def get_history(self, pkg: SoftwarePackage) -> PackageHistory:
pass
@@ -97,8 +153,78 @@ class WebApplicationManager(SoftwareManager):
watcher.show_message(title=self.i18n['error'], body=self.i18n['web.env.error'].format(bold(pkg.name)), type_=MessageType.ERROR)
return False
Path(INSTALLED_PATH).mkdir(parents=True, exist_ok=True)
pkg_name = pkg.name.lower()
app_name_id, app_dir = pkg_name, '{}/{}'.format(INSTALLED_PATH, pkg_name)
counter = 1
while True:
if not os.path.exists(app_dir):
break
else:
app_name_id += str(counter)
app_dir = '{}/{}'.format(INSTALLED_PATH, app_name_id)
counter += 1
installed = handler.handle_simple(nativefier.install(url=pkg.url, name=pkg_name, output_dir=app_dir,
electron_version=self.env_settings['electron']['version'], cwd=INSTALLED_PATH))
if not installed:
msg = '{}.{}.'.format(self.i18n['wen.install.error'].format(bold(pkg.name)),
self.i18n['web.install.nativefier.error.unknown'].format(bold(self.i18n['details'].capitalize())))
watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.ERROR)
return False
inner_dir = os.listdir(app_dir)
if not inner_dir:
msg = '{}.{}.'.format(self.i18n['wen.install.error'].format(bold(pkg.name)),
self.i18n['web.install.nativefier.error.inner_dir'].format(bold(app_dir)))
watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.ERROR)
return False
# bringing the inner app folder to the 'installed' folder level:
inner_dir = '{}/{}'.format(app_dir, inner_dir[0])
temp_dir = '{}/tmp_{}'.format(INSTALLED_PATH, app_name_id)
os.rename(inner_dir, temp_dir)
shutil.rmtree(app_dir)
os.rename(temp_dir, app_dir)
pkg.installation_dir = app_dir
version_path = '{}/version'.format(app_dir)
if os.path.exists(version_path):
with open(version_path, 'r') as f:
pkg.version = f.read().strip()
pkg.latest_version = pkg.version
desktop_entry = DESKTOP_ENTRY_PATH_PATTERN.format(name=app_name_id)
entry_content = self._gen_desktop_entry_content(pkg)
# TODO check if the desktop entry exists
with open(desktop_entry, 'w+') as f:
f.write(entry_content)
pkg.desktop_entry = desktop_entry
return True
def _gen_desktop_entry_content(self, pkg: WebApplication) -> str:
return """
[Desktop Entry]
Type=Application
Name={name} ( web )
Categories=Applications;
Comment={desc}
Icon={icon}
Exec={exec_path}
""".format(name=pkg.name, exec_path=pkg.get_exec_path(),
desc=pkg.description or pkg.url, icon=pkg.get_disk_icon_path())
def is_enabled(self) -> bool:
return self.enabled
@@ -112,7 +238,13 @@ class WebApplicationManager(SoftwareManager):
return False
def _update_environment(self, handler: ProcessHandler = None) -> bool:
return self.node_updater.update_environment(self.context.is_system_x86_64(), handler=handler)
updated_settings = self.node_updater.update_environment(self.context.is_system_x86_64(), handler=handler)
if updated_settings is not None:
self.env_settings = updated_settings
return True
return False
def prepare(self):
if bool(int(os.getenv('BAUH_WEB_UPDATE_NODE', 1))):
@@ -134,8 +266,8 @@ class WebApplicationManager(SoftwareManager):
def is_default_enabled(self) -> bool:
return True
def launch(self, pkg: SoftwarePackage):
pass
def launch(self, pkg: WebApplication):
subprocess.Popen(pkg.get_exec_path())
def get_screenshots(self, pkg: SoftwarePackage) -> List[str]:
pass

View File

@@ -163,12 +163,11 @@ class EnvironmentUpdater:
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)
files = os.listdir(ELECTRON_PATH)
if files:
file_list = files.split('\n')
file_name = ELECTRON_DOWNLOAD_URL.format(version=version, arch='x64' if is_x86_x64_arch else 'ia32').split('/')[-1]
electron_file = [f for f in file_list if f == file_name]
electron_file = [f for f in files if f == file_name]
if electron_file:
self.logger.info("Electron {} already downloaded".format(version))
@@ -177,7 +176,7 @@ class EnvironmentUpdater:
return False
file_name = ELECTRON_SHA256_URL.split('/')[-1]
sha256_file = [f for f in file_list if f == file_name]
sha256_file = [f for f in files if f == file_name]
if sha256_file:
self.logger.info("Electron {} sha256 already downloaded".format(version))
@@ -191,28 +190,37 @@ class EnvironmentUpdater:
return False
def update_environment(self, is_x86_x64_arch: bool, handler: ProcessHandler = None) -> bool:
def get_environment(self) -> dict:
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
return
try:
settings = yaml.safe_load(res.content)
return yaml.safe_load(res.content)
except yaml.YAMLError:
self.logger.error('Could not parse environment settings: {}'.format(res.text))
return False
return
except requests.exceptions.ConnectionError:
return
def update_environment(self, is_x86_x64_arch: bool, handler: ProcessHandler = None) -> dict:
settings = self.get_environment()
if settings is None:
return
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
return
if not self.install_nativefier(version=settings['nativefier']['version'], handler=handler):
self.logger.warning('Could not install / update nativefier')
return False
return
res = self.install_electron(version=settings['electron']['version'], is_x86_x64_arch=is_x86_x64_arch,
watcher=handler.watcher if handler else None)
@@ -222,6 +230,4 @@ class EnvironmentUpdater:
else:
self.logger.warning('Could not update the environment')
return res
except requests.exceptions.ConnectionError:
return False
return settings

View File

@@ -6,19 +6,31 @@ from bauh.gems.web import ROOT_DIR
class WebApplication(SoftwarePackage):
def __init__(self, url: str, name: str, description: str, icon_url: str):
super(WebApplication, self).__init__(id=url, name=name, description=description, icon_url=icon_url)
def __init__(self, url: str = None, name: str = None, description: str = None, icon_url: str = None,
installation_dir: str = None, desktop_entry: str = None, installed: bool = False, version: str = None):
super(WebApplication, self).__init__(id=url, name=name, description=description,
icon_url=icon_url, installed=installed, version=version)
self.url = url
self.installation_dir = installation_dir
self.desktop_entry = desktop_entry
def has_history(self):
return False
def has_info(self):
return False
return self.installed
@staticmethod
def _get_cached_attrs() -> tuple:
return 'name', 'version', 'url', 'description', 'icon_url', 'installation_dir', 'desktop_entry'
def can_be_downgraded(self):
return False
def get_exec_path(self) -> str:
if self.installation_dir:
return '{}/{}'.format(self.installation_dir, self.installation_dir.split('/')[-1])
def get_type(self):
return 'web'
@@ -32,22 +44,32 @@ class WebApplication(SoftwarePackage):
return True
def supports_disk_cache(self):
return self.installed and self.is_application()
return self.installed
def get_disk_cache_path(self):
return CACHE_PATH + '/' + self.get_type()
return self.installation_dir
def get_data_to_cache(self) -> dict:
"""
:return: the application data that should be cached in disk / memory for quick access
"""
pass
data = {}
for attr in self._get_cached_attrs():
if hasattr(self, attr):
val = getattr(self, attr)
if val is not None:
data[attr] = val
return data
def fill_cached_data(self, data: dict):
pass
for attr in self._get_cached_attrs():
val = data.get(attr)
if val and hasattr(self, attr):
setattr(self, attr, val)
def can_be_run(self) -> bool:
return self.installed
return self.installed and self.installation_dir
def is_trustable(self) -> bool:
return False

View File

@@ -0,0 +1,6 @@
from bauh.commons.system import SimpleProcess
from bauh.gems.web import NATIVEFIER_BIN_PATH
def install(url: str, name: str, output_dir: str, electron_version: str, cwd: str) -> SimpleProcess:
return SimpleProcess([NATIVEFIER_BIN_PATH, url, '--name', name, '-e', electron_version, output_dir], cwd=cwd)

View File

@@ -1,5 +1,17 @@
gem.web.info=It allows to install Web applications on your system
web.environment.install=Installing {}
web.waiting.env_updater=Updating environment
web.env.checking=Checking the Web application installation environment
web.env.error=It seems there are issues with the Web application installation environment. It wil not be possible to install {}.
web.env.checking=Checking the Web installation environment
web.env.error=It seems there are issues with the Web installation environment. It wil not be possible to install {}.
web.install.error=An error has happened during the {} installation
web.install.nativefier.error.unknown=Have a look at the {} to identify the reason
web.install.nativefier.error.inner_dir=The app installation directory was not found in {}
web.uninstall.error.install_dir.not_found=The installation directory {} was not found
web.uninstall.error.remove_dir=It was not possible to remove {}
web.info.1_url=URL
web.info.2_description=description
web.info.3_version=version
web.info.4_installation_dir=installation dir
web.info.5_desktop_entry=shortcut
web.info.6_exec_file=executable
web.info.7_icon_path=icon

View File

@@ -1,5 +1,17 @@
gem.web.info=Permite instalar aplicações Web no seu sistema
web.environment.nativefier=Instalando {}
web.waiting.env_updater=Atualizando ambiente
web.env.checking=Verificando o ambiente de instalação para aplicativos Web
web.env.error=Parece haver problemas com o ambiente de instalação para aplicativos Web. Não sera possível instalar {}.
web.env.checking=Verificando o ambiente de instalação Web
web.env.error=Parce que existem problemas com o ambiente de instalação Web. Não será possível instalar {}.
wen.install.error=Ocorreu um erro durante a instalação de {}
web.install.nativefier.error.unknown=Dê uma olhada em {} para identificar o motivo
web.install.nativefier.error.inner_dir=O diretório de instalação do aplicativo não foi encontrado em {}
web.uninstall.error.install_dir.not_found=O diretório de instalação {} não foi encontrado
web.uninstall.error.remove=Não foi possível remover {}
web.info.1_url=URL
web.info.2_description=descrição
web.info.3_version=versão
web.info.4_installation_dir=diretório de instalação
web.info.5_desktop_entry=atalho
web.info.6_exec_file=executável
web.info.7_icon_path=ícone

View File

@@ -202,3 +202,4 @@ mirror=mirall
emulator=emulador
do_not.install=no instal·leu
repository=dipòsit
details=detalls

View File

@@ -156,3 +156,4 @@ publisher.verified=Verifiziert
emulator=emulator
do_not.install=nicht installieren
repository=repository
details=details

View File

@@ -157,3 +157,4 @@ mirror=mirror
emulator=emulator
do_not.install=don't install
repository=repository
details=details

View File

@@ -201,3 +201,4 @@ mirror=espejo
emulator=emulador
do_not.install=no instalar
repository=repositorio
details=detalles

View File

@@ -157,3 +157,4 @@ mirror=specchio
emulator=emulatore
do_not.install=non installare
repository=deposito
details=dettagli

View File

@@ -204,3 +204,4 @@ mirror=espelho
emulator=emulador
do_not.install=não instalar
repository=repositório
details=detalhes

View File

@@ -3,11 +3,11 @@ import os
from setuptools import setup, find_packages
DESCRIPTION = (
"Graphical interface to manage Flatpak, Snap, AppImage and AUR packages"
"Graphical interface to manage Flatpak, Snap, AppImage, AUR and Web applications"
)
AUTHOR = "Vinicius Moreira"
AUTHOR_EMAIL = "vinicius_fmoreira@hotmail.com"
AUTHOR = "bauh developers"
AUTHOR_EMAIL = "bauh4linux@gmail.com"
NAME = 'bauh'
URL = "https://github.com/vinifmor/" + NAME