[improvement][appimage] new initialization task that checks if the installed AppImage files have symlinks associated with

This commit is contained in:
Vinicius Moreira
2020-06-16 18:51:13 -03:00
parent 20d6f0fa35
commit c66bd4d900
13 changed files with 147 additions and 68 deletions

View File

@@ -4,10 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.9.6] 2020 ## [0.9.6] 2020-06
### Improvements ### Improvements
- AppImage - AppImage
- creating a symlink for the installed applications at **~.local/bin** (the link will have the same name of the application. if the link already exists, it will be named as 'app_name-appimage') [#122](https://github.com/vinifmor/bauh/issues/122) - creating a symlink for the installed applications at **~.local/bin** (the link will have the same name of the application. if the link already exists, it will be named as 'app_name-appimage') [#122](https://github.com/vinifmor/bauh/issues/122)
- new initialization task that checks if the installed AppImage files have symlinks associated with (it creates new symlinks if they don't)
<p align="center">
<img src="https://raw.githubusercontent.com/vinifmor/bauh/staging/pictures/releases/0.9.6/appim_symlinks.png">
</p>
- able to update AppImages with continuous releases - able to update AppImages with continuous releases
- UI - UI
- not performing a full table refresh after installing and uninstalling packages - not performing a full table refresh after installing and uninstalling packages

View File

@@ -29,10 +29,10 @@ from bauh.commons.config import save_config
from bauh.commons.html import bold from bauh.commons.html import bold
from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler, run_cmd, SimpleProcess from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler, run_cmd, SimpleProcess
from bauh.gems.appimage import query, INSTALLATION_PATH, LOCAL_PATH, SUGGESTIONS_FILE, CONFIG_FILE, ROOT_DIR, \ from bauh.gems.appimage import query, INSTALLATION_PATH, LOCAL_PATH, SUGGESTIONS_FILE, CONFIG_FILE, ROOT_DIR, \
CONFIG_DIR, UPDATES_IGNORED_FILE, SYMLINKS_DIR CONFIG_DIR, UPDATES_IGNORED_FILE, util
from bauh.gems.appimage.config import read_config from bauh.gems.appimage.config import read_config
from bauh.gems.appimage.model import AppImage from bauh.gems.appimage.model import AppImage
from bauh.gems.appimage.worker import DatabaseUpdater from bauh.gems.appimage.worker import DatabaseUpdater, SymlinksVerifier
DB_APPS_PATH = '{}/{}'.format(str(Path.home()), '.local/share/bauh/appimage/apps.db') DB_APPS_PATH = '{}/{}'.format(str(Path.home()), '.local/share/bauh/appimage/apps.db')
DB_RELEASES_PATH = '{}/{}'.format(str(Path.home()), '.local/share/bauh/appimage/releases.db') DB_RELEASES_PATH = '{}/{}'.format(str(Path.home()), '.local/share/bauh/appimage/releases.db')
@@ -50,7 +50,7 @@ class AppImageManager(SoftwareManager):
super(AppImageManager, self).__init__(context=context) super(AppImageManager, self).__init__(context=context)
self.i18n = context.i18n self.i18n = context.i18n
self.api_cache = context.cache_factory.new() self.api_cache = context.cache_factory.new()
context.disk_loader_factory.map(AppImageManager, self.api_cache) context.disk_loader_factory.map(AppImage, self.api_cache)
self.enabled = True self.enabled = True
self.http_client = context.http_client self.http_client = context.http_client
self.logger = context.logger self.logger = context.logger
@@ -413,12 +413,6 @@ class AppImageManager(SoftwareManager):
if f.endswith('.desktop'): if f.endswith('.desktop'):
return f return f
def _find_appimage_file(self, folder: str) -> str:
for r, d, files in os.walk(folder):
for f in files:
if f.lower().endswith('.appimage'):
return '{}/{}'.format(folder, f)
def _find_icon_file(self, folder: str) -> str: def _find_icon_file(self, folder: str) -> str:
for r, d, files in os.walk(folder): for r, d, files in os.walk(folder):
for f in files: for f in files:
@@ -524,8 +518,7 @@ class AppImageManager(SoftwareManager):
except: except:
traceback.print_exc() traceback.print_exc()
self._add_symlink(pkg, file_path, watcher) SymlinksVerifier.create_symlink(app=pkg, file_path=file_path, logger=self.logger, watcher=watcher)
return TransactionResult(success=True, installed=[pkg], removed=[]) return TransactionResult(success=True, installed=[pkg], removed=[])
else: else:
watcher.show_message(title=self.i18n['error'], watcher.show_message(title=self.i18n['error'],
@@ -539,59 +532,6 @@ class AppImageManager(SoftwareManager):
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir])))
return TransactionResult.fail() return TransactionResult.fail()
def _add_symlink(self, app: AppImage, file_path: str, watcher: ProcessWatcher = None):
possible_names = (app.name.lower(), '{}-appimage'.format(app.name.lower()))
if os.path.exists(SYMLINKS_DIR) and not os.path.isdir(SYMLINKS_DIR):
self.logger.warning("'{}' is not a directory. It will not be possible to create a symlink for '{}'".format(SYMLINKS_DIR, app.name))
return
available_system_dirs = (SYMLINKS_DIR, *(l for l in ('/usr/bin', '/usr/local/bin') if os.path.isdir(l)))
# checking if the link already exists:
available_name = None
for name in possible_names:
available_name = name
for sysdir in available_system_dirs:
if os.path.exists('{}/{}'.format(sysdir, name)):
available_name = None
break
if available_name:
break
if not available_name:
msg = "It was not possible to create a symlink for '{}' because the names {} are already available on the system".format(app.name,
possible_names)
self.logger.warning(msg)
if watcher:
watcher.print('[warning] {}'.format(msg))
else:
try:
Path(SYMLINKS_DIR).mkdir(parents=True, exist_ok=True)
except:
self.logger.error("Could not create symlink directory '{}'".format(SYMLINKS_DIR))
return
symlink_path = '{}/{}'.format(SYMLINKS_DIR, available_name)
try:
os.symlink(src=file_path, dst=symlink_path)
app.symlink = symlink_path
msg = "symlink successfully created at {}".format(symlink_path)
self.logger.info(msg)
if watcher:
watcher.print(msg)
except:
msg = "Could not create the symlink '{}'".format(symlink_path)
self.logger.error(msg)
if watcher:
watcher.print('[error] {}'.format(msg))
def _gen_desktop_entry_path(self, app: AppImage) -> str: def _gen_desktop_entry_path(self, app: AppImage) -> str:
return '{}/bauh_appimage_{}.desktop'.format(DESKTOP_ENTRIES_PATH, app.name.lower()) return '{}/bauh_appimage_{}.desktop'.format(DESKTOP_ENTRIES_PATH, app.name.lower())
@@ -624,6 +564,9 @@ class AppImageManager(SoftwareManager):
elif internet_available: elif internet_available:
updater.download_databases() # only once updater.download_databases() # only once
symlink_check = SymlinksVerifier(taskman=task_manager, i18n=self.i18n, logger=self.logger)
symlink_check.start()
def list_updates(self, internet_available: bool) -> List[PackageUpdate]: def list_updates(self, internet_available: bool) -> List[PackageUpdate]:
res = self.read_installed(disk_loader=None, internet_available=internet_available) res = self.read_installed(disk_loader=None, internet_available=internet_available)
@@ -692,7 +635,7 @@ class AppImageManager(SoftwareManager):
def launch(self, pkg: AppImage): def launch(self, pkg: AppImage):
installation_dir = pkg.get_disk_cache_path() installation_dir = pkg.get_disk_cache_path()
if os.path.exists(installation_dir): if os.path.exists(installation_dir):
appimag_path = self._find_appimage_file(installation_dir) appimag_path = util.find_appimage_file(installation_dir)
if appimag_path: if appimag_path:
subprocess.Popen([appimag_path]) subprocess.Popen([appimag_path])

View File

@@ -32,6 +32,7 @@ appimage.install.extract=Sestà extraient el contingut de {}
appimage.install.imported.rename_error=It was not possible to move the file {} to {} appimage.install.imported.rename_error=It was not possible to move the file {} to {}
appimage.install.permission=Sestà concedint el permís dexecució a {} appimage.install.permission=Sestà concedint el permís dexecució a {}
appimage.task.db_update=Actualització de bases de dades appimage.task.db_update=Actualització de bases de dades
appimage.task.symlink_check=Checking symlinks
appimage.uninstall.error.remove_folder=No sha pogut suprimir el directori dinstal·lació de laplicació {} appimage.uninstall.error.remove_folder=No sha pogut suprimir el directori dinstal·lació de laplicació {}
gem.appimage.info=Aplicacions publicades a https://appimage.github.io/ gem.appimage.info=Aplicacions publicades a https://appimage.github.io/
gem.appimage.label=AppImage gem.appimage.label=AppImage

View File

@@ -32,6 +32,7 @@ appimage.install.extract=Entpacke Inhalt von {}
appimage.install.imported.rename_error=It was not possible to move the file {} to {} appimage.install.imported.rename_error=It was not possible to move the file {} to {}
appimage.install.permission=Ausführberechtigungen für {} appimage.install.permission=Ausführberechtigungen für {}
appimage.task.db_update=Updating databases appimage.task.db_update=Updating databases
appimage.task.symlink_check=Checking symlinks
appimage.uninstall.error.remove_folder=Anwendungordner {} konnte nicht entfernt werden appimage.uninstall.error.remove_folder=Anwendungordner {} konnte nicht entfernt werden
gem.appimage.info=Anwendungen veröffentlicht auf https://appimage.github.io/ gem.appimage.info=Anwendungen veröffentlicht auf https://appimage.github.io/
gem.appimage.label=AppImage gem.appimage.label=AppImage

View File

@@ -32,6 +32,7 @@ appimage.install.extract=Extracting the content from {}
appimage.install.imported.rename_error=It was not possible to move the file {} to {} appimage.install.imported.rename_error=It was not possible to move the file {} to {}
appimage.install.permission=Giving execution permission to {} appimage.install.permission=Giving execution permission to {}
appimage.task.db_update=Updating databases appimage.task.db_update=Updating databases
appimage.task.symlink_check=Checking symlinks
appimage.uninstall.error.remove_folder=Could not remove the application installation directory {} appimage.uninstall.error.remove_folder=Could not remove the application installation directory {}
gem.appimage.info=Applications published at https://appimage.github.io/ gem.appimage.info=Applications published at https://appimage.github.io/
gem.appimage.label=AppImage gem.appimage.label=AppImage

View File

@@ -32,6 +32,7 @@ appimage.install.extract=Extrayendo el contenido de {}
appimage.install.imported.rename_error=No fue posible mover el archivo {} a {} appimage.install.imported.rename_error=No fue posible mover el archivo {} a {}
appimage.install.permission=Concediendo permiso de ejecución a {} appimage.install.permission=Concediendo permiso de ejecución a {}
appimage.task.db_update=Actualizando base de datos appimage.task.db_update=Actualizando base de datos
appimage.task.symlink_check=Verificando links simbólicos
appimage.uninstall.error.remove_folder=No se pudo eliminar el directorio de instalación de la aplicación {} appimage.uninstall.error.remove_folder=No se pudo eliminar el directorio de instalación de la aplicación {}
gem.appimage.info=Aplicativos publicados en https://appimage.github.io/ gem.appimage.info=Aplicativos publicados en https://appimage.github.io/
gem.appimage.label=AppImage gem.appimage.label=AppImage

View File

@@ -32,6 +32,7 @@ appimage.install.extract=Estrarre il contenuto da {}
appimage.install.imported.rename_error=It was not possible to move the file {} to {} appimage.install.imported.rename_error=It was not possible to move the file {} to {}
appimage.install.permission=Gdare il permesso di esecuzione a {} appimage.install.permission=Gdare il permesso di esecuzione a {}
appimage.task.db_update=Aggiornamento dei database appimage.task.db_update=Aggiornamento dei database
appimage.task.symlink_check=Checking symlinks
appimage.uninstall.error.remove_folder=Impossibile rimuovere la directory di installazione dell'applicazione {} appimage.uninstall.error.remove_folder=Impossibile rimuovere la directory di installazione dell'applicazione {}
gem.appimage.info=Applicazioni pubblicate su https://appimage.github.io/ gem.appimage.info=Applicazioni pubblicate su https://appimage.github.io/
gem.appimage.label=AppImage gem.appimage.label=AppImage

View File

@@ -32,6 +32,7 @@ appimage.install.extract=Extraindo o conteúdo de {}
appimage.install.imported.rename_error=Não foi possível mover o arquivo {} para {} appimage.install.imported.rename_error=Não foi possível mover o arquivo {} para {}
appimage.install.permission=Concedendo permissão de execução para {} appimage.install.permission=Concedendo permissão de execução para {}
appimage.task.db_update=Atualizando base de dados appimage.task.db_update=Atualizando base de dados
appimage.task.symlink_check=Verificando links simbólicos
appimage.uninstall.error.remove_folder=Não foi possível remover o diretóri ode instalação do aplicativo {} appimage.uninstall.error.remove_folder=Não foi possível remover o diretóri ode instalação do aplicativo {}
gem.appimage.info=Aplicativos publicados em https://appimage.github.io/ gem.appimage.info=Aplicativos publicados em https://appimage.github.io/
gem.appimage.label=AppImage gem.appimage.label=AppImage

View File

@@ -32,6 +32,7 @@ appimage.install.extract=Извлечь содержимое из {}
appimage.install.imported.rename_error=Не удалось переместить файл {} в {} appimage.install.imported.rename_error=Не удалось переместить файл {} в {}
appimage.install.permission=Установить права на выполнение для {} appimage.install.permission=Установить права на выполнение для {}
appimage.task.db_update=Обновление базы данных appimage.task.db_update=Обновление базы данных
appimage.task.symlink_check=Checking symlinks
appimage.uninstall.error.remove_folder=Не удалось удалить каталог приложения {} appimage.uninstall.error.remove_folder=Не удалось удалить каталог приложения {}
gem.appimage.info=Приложения, опубликованные на https://appimage.github.io/ gem.appimage.info=Приложения, опубликованные на https://appimage.github.io/
gem.appimage.label=AppImage gem.appimage.label=AppImage

View File

@@ -32,6 +32,7 @@ appimage.install.extract={} 'den içerik ayıklanıyor
appimage.install.imported.rename_error={} dosyasını {} klasörüne taşımak mümkün değildi appimage.install.imported.rename_error={} dosyasını {} klasörüne taşımak mümkün değildi
appimage.install.permission={} için yürütme izni veriliyor appimage.install.permission={} için yürütme izni veriliyor
appimage.task.db_update=Veritabanları güncelleniyor appimage.task.db_update=Veritabanları güncelleniyor
appimage.task.symlink_check=Checking symlinks
appimage.uninstall.error.remove_folder={} uygulama kurulum dizini kaldırılamadı appimage.uninstall.error.remove_folder={} uygulama kurulum dizini kaldırılamadı
gem.appimage.info=https://appimage.github.io/ adresinde yayınlanan uygulamalar gem.appimage.info=https://appimage.github.io/ adresinde yayınlanan uygulamalar
gem.appimage.label=AppImage gem.appimage.label=AppImage

View File

@@ -0,0 +1,8 @@
import os
def find_appimage_file(folder: str) -> str:
for r, d, files in os.walk(folder):
for f in files:
if f.lower().endswith('.appimage'):
return '{}/{}'.format(folder, f)

View File

@@ -1,4 +1,5 @@
import glob import glob
import json
import logging import logging
import os import os
import tarfile import tarfile
@@ -9,10 +10,11 @@ from threading import Thread
import requests import requests
from bauh.api.abstract.handler import TaskManager from bauh.api.abstract.handler import TaskManager, ProcessWatcher
from bauh.api.http import HttpClient from bauh.api.http import HttpClient
from bauh.commons import internet from bauh.commons import internet
from bauh.gems.appimage import LOCAL_PATH, get_icon_path from bauh.gems.appimage import LOCAL_PATH, get_icon_path, INSTALLATION_PATH, SYMLINKS_DIR, util
from bauh.gems.appimage.model import AppImage
from bauh.view.util.translation import I18n from bauh.view.util.translation import I18n
@@ -102,3 +104,117 @@ class DatabaseUpdater(Thread):
self.download_databases() self.download_databases()
self.logger.info('Sleeping') self.logger.info('Sleeping')
time.sleep(self.sleep) time.sleep(self.sleep)
class SymlinksVerifier(Thread):
def __init__(self, taskman: TaskManager, i18n: I18n, logger: logging.Logger):
super(SymlinksVerifier, self).__init__(daemon=True)
self.taskman = taskman
self.i18n = i18n
self.logger = logger
self.task_id = 'appim_symlink_check'
@staticmethod
def create_symlink(app: AppImage, file_path: str, logger: logging.Logger, watcher: ProcessWatcher = None):
logger.info("Creating a symlink for '{}'".format(app.name))
possible_names = (app.name.lower(), '{}-appimage'.format(app.name.lower()))
if os.path.exists(SYMLINKS_DIR) and not os.path.isdir(SYMLINKS_DIR):
logger.warning("'{}' is not a directory. It will not be possible to create a symlink for '{}'".format(SYMLINKS_DIR, app.name))
return
available_system_dirs = (SYMLINKS_DIR, *(l for l in ('/usr/bin', '/usr/local/bin') if os.path.isdir(l)))
# checking if the link already exists:
available_name = None
for name in possible_names:
available_name = name
for sysdir in available_system_dirs:
if os.path.exists('{}/{}'.format(sysdir, name)):
available_name = None
break
if available_name:
break
if not available_name:
msg = "It was not possible to create a symlink for '{}' because the names {} are already available on the system".format(app.name,
possible_names)
logger.warning(msg)
if watcher:
watcher.print('[warning] {}'.format(msg))
else:
try:
Path(SYMLINKS_DIR).mkdir(parents=True, exist_ok=True)
except:
logger.error("Could not create symlink directory '{}'".format(SYMLINKS_DIR))
return
symlink_path = '{}/{}'.format(SYMLINKS_DIR, available_name)
try:
os.symlink(src=file_path, dst=symlink_path)
app.symlink = symlink_path
msg = "symlink successfully created at {}".format(symlink_path)
logger.info(msg)
if watcher:
watcher.print(msg)
except:
msg = "Could not create the symlink '{}'".format(symlink_path)
logger.error(msg)
if watcher:
watcher.print('[error] {}'.format(msg))
def run(self):
self.taskman.register_task(self.task_id, self.i18n['appimage.task.symlink_check'], get_icon_path())
if os.path.exists(INSTALLATION_PATH):
installed_files = glob.glob('{}/*/*.json'.format(INSTALLATION_PATH))
if installed_files:
self.logger.info("Checking installed AppImage files with no symlinks created")
progress_per_file = (1/len(installed_files)) * 100
total_progress = 0
for json_file in installed_files:
with open(json_file) as f:
try:
data = json.loads(f.read())
except:
self.logger.warning("Could not parse data from '{}'".format(json_file))
data = None
if data and not data.get('symlink'):
app = AppImage(**data, i18n=self.i18n)
file_path = util.find_appimage_file(app.install_dir)
if file_path:
self.create_symlink(app, file_path, self.logger)
data['symlink'] = app.symlink
# caching
try:
with open(json_file, 'w+') as f:
f.write(json.dumps(data))
except:
self.logger.warning("Could not update cached data on '{}'".format(json_file))
traceback.print_exc()
else:
self.logger.warning("No AppImage file found on installation dir '{}'".format(file_path))
total_progress += progress_per_file
self.taskman.update_progress(self.task_id, total_progress, '')
self.taskman.update_progress(self.task_id, 100, '')
self.taskman.finish_task(self.task_id)
return
self.logger.info("No AppImage applications found. Aborting")
self.taskman.update_progress(self.task_id, 100, '')
self.taskman.finish_task(self.task_id)

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB