def test_replace_desktop_entry_exec_command__only_one_exec_field_no_line_jump_in_the_end(self): desktop_entry = """ Name=MyApp Icon=MyApp Exec=myapp %f""" res = replace_desktop_entry_exec_command(desktop_entry=desktop_entry, appname='myapp', file_path='/path/to/myapp.appimage') expected = """ Name=MyApp Icon=MyApp Exec="/path/to/myapp.appimage" %f""" self.assertEqual(expected, res)
def test_replace_desktop_entry_exec_command__only_one_exec_field_with_spaces_and_params(self): desktop_entry = """ Name=MyApp Icon=MyApp Exec = myapp %f --a """ res = replace_desktop_entry_exec_command(desktop_entry=desktop_entry, appname='myapp', file_path='/path/to/myapp.appimage') expected = """ Name=MyApp Icon=MyApp Exec ="/path/to/myapp.appimage" %f --a """ self.assertEqual(expected, res)
def test_replace_desktop_entry_exec_command__evvar_with_same_app_name(self): desktop_entry = """ Name=MyApp Icon=MyApp Exec=MYAPP=123 myapp """ res = replace_desktop_entry_exec_command(desktop_entry=desktop_entry, appname='myapp', file_path='/path/to/myapp.appimage') expected = """ Name=MyApp Icon=MyApp Exec=MYAPP=123 "/path/to/myapp.appimage" """ self.assertEqual(expected, res)
def test_replace_desktop_entry_exec_command__param_with_the_same_app_name(self): desktop_entry = """ Name=MyApp Icon=MyApp Exec=myapp %f --myapp """ res = replace_desktop_entry_exec_command(desktop_entry=desktop_entry, appname='myapp', file_path='/path/to/myapp.appimage') expected = """ Name=MyApp Icon=MyApp Exec="/path/to/myapp.appimage" %f --myapp """ self.assertEqual(expected, res)
def test_replace_desktop_entry_exec_command__exec_and_tryexec_fields_with_envvars_and_params(self): desktop_entry = """ Name=MyApp Icon=MyApp TryExec=__MY_VAR=1 myapp %f Exec=NEW_VAR=abc myapp --a Terminal=false """ res = replace_desktop_entry_exec_command(desktop_entry=desktop_entry, appname='myapp', file_path='/path/to/myapp.appimage') expected = """ Name=MyApp Icon=MyApp TryExec=__MY_VAR=1 "/path/to/myapp.appimage" %f Exec=NEW_VAR=abc "/path/to/myapp.appimage" --a Terminal=false """ self.assertEqual(expected, res)
def test_replace_desktop_entry_exec_command__it_should_replace_the_command_by_the_file_path_if_the_appname_is_not_present(self): desktop_entry = """ [Desktop Entry] Name=GameHub GenericName=GameHub Comment=All your games in one place Categories=Game;Amusement; Keywords=Game;Hub;Steam;GOG;Humble;HumbleBundle; Exec=com.github.tkashkin.gamehub X-GNOME-Gettext-Domain=com.github.tkashkin.gamehub Icon=/gamehub-0/logo.svg Terminal=false Type=Application X-AppImage-Version=bionic-0.16.0-83-dev-0ca783e """ res = replace_desktop_entry_exec_command(desktop_entry=desktop_entry, appname='gamehub', file_path='/path/to/gamehub.appimage') expected = """ [Desktop Entry] Name=GameHub GenericName=GameHub Comment=All your games in one place Categories=Game;Amusement; Keywords=Game;Hub;Steam;GOG;Humble;HumbleBundle; Exec="/path/to/gamehub.appimage" X-GNOME-Gettext-Domain=com.github.tkashkin.gamehub Icon=/gamehub-0/logo.svg Terminal=false Type=Application X-AppImage-Version=bionic-0.16.0-83-dev-0ca783e """ self.assertEqual(expected, res)
def test_replace_desktop_entry_exec_command__rpcs3(self): desktop_entry = """ [Desktop Entry] Type=Application Name=RPCS3 GenericName=PlayStation 3 Emulator Comment=An open-source PlayStation 3 emulator/debugger written in C++. Icon=rpcs3 TryExec=rpcs3 Exec=rpcs3 %f Terminal=false Categories=Game;Emulator; Keywords=PS3;Playstation; """ res = replace_desktop_entry_exec_command(desktop_entry=desktop_entry, appname='rpcs3', file_path='/path/to/rpcs3.appimage') expected = """ [Desktop Entry] Type=Application Name=RPCS3 GenericName=PlayStation 3 Emulator Comment=An open-source PlayStation 3 emulator/debugger written in C++. Icon=rpcs3 TryExec="/path/to/rpcs3.appimage" Exec="/path/to/rpcs3.appimage" %f Terminal=false Categories=Game;Emulator; Keywords=PS3;Playstation; """ self.assertEqual(expected, res)
def _install(self, pkg: AppImage, watcher: ProcessWatcher, pre_downloaded_file: Optional[Tuple[str, str]] = None) \ -> TransactionResult: handler = ProcessHandler(watcher) out_dir = f'{INSTALLATION_DIR}/{pkg.get_clean_name()}' counter = 0 while True: if os.path.exists(out_dir): self.logger.info(f"Installation dir '{out_dir}' already exists. Generating a different one") out_dir += f'-{counter}' counter += 1 else: break Path(out_dir).mkdir(parents=True, exist_ok=True) pkg.install_dir = out_dir if pkg.imported: downloaded, file_name = True, pkg.local_file_path.split('/')[-1] install_file_path = out_dir + '/' + file_name try: moved, output = handler.handle_simple(SimpleProcess(['mv', pkg.local_file_path, install_file_path])) except: output = '' self.logger.error(f"Could not rename file '{pkg.local_file_path}' as '{install_file_path}'") moved = False if not moved: watcher.show_message(title=self.i18n['error'].capitalize(), body=self.i18n['appimage.install.imported.rename_error'].format(bold(pkg.local_file_path.split('/')[-1]), bold(output)), type_=MessageType.ERROR) return TransactionResult.fail() else: download_data = pre_downloaded_file if pre_downloaded_file else self._download(pkg, watcher) if not download_data: return TransactionResult.fail() file_name, download_path = download_data[0], download_data[1] install_file_path = f'{out_dir}/{file_name}' try: shutil.move(download_path, install_file_path) except OSError: watcher.show_message(title=self.i18n['error'], body=self.i18n['error.mvfile'].formmat(src=bold(download_path), dest=bold(install_file_path))) return TransactionResult.fail() watcher.change_substatus(self.i18n['appimage.install.permission'].format(bold(file_name))) permission_given = handler.handle(SystemProcess(new_subprocess(['chmod', 'a+x', install_file_path]))) if permission_given: watcher.change_substatus(self.i18n['appimage.install.extract'].format(bold(file_name))) try: res, output = handler.handle_simple( SimpleProcess([install_file_path, '--appimage-extract'], cwd=out_dir)) if 'Error: Failed to register AppImage in AppImageLauncherFS' in output: watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.install.appimagelauncher.error'].format( appimgl=bold('AppImageLauncher'), app=bold(pkg.name)), type_=MessageType.ERROR) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() except: watcher.show_message(title=self.i18n['error'], body=traceback.format_exc(), type_=MessageType.ERROR) traceback.print_exc() handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() watcher.change_substatus(self.i18n['appimage.install.desktop_entry']) extracted_folder = f'{out_dir}/squashfs-root' if os.path.exists(extracted_folder): desktop_entry = self._find_desktop_file(extracted_folder) with open(f'{extracted_folder}/{desktop_entry}') as f: de_content = f.read() if de_content: de_content = replace_desktop_entry_exec_command(desktop_entry=de_content, appname=pkg.name, file_path=install_file_path) extracted_icon = self._find_icon_file(extracted_folder) if extracted_icon: icon_path = out_dir + '/logo.' + extracted_icon.split('/')[-1].split('.')[-1] shutil.copy(extracted_icon, icon_path) if de_content: de_content = RE_DESKTOP_ICON.sub(f'Icon={icon_path}\n', de_content) pkg.icon_path = icon_path if not de_content: de_content = pkg.to_desktop_entry() Path(DESKTOP_ENTRIES_DIR).mkdir(parents=True, exist_ok=True) with open(self._gen_desktop_entry_path(pkg), 'w+') as f: f.write(de_content) try: shutil.rmtree(extracted_folder) except: traceback.print_exc() SymlinksVerifier.create_symlink(app=pkg, file_path=install_file_path, logger=self.logger, watcher=watcher) return TransactionResult(success=True, installed=[pkg], removed=[]) else: watcher.show_message(title=self.i18n['error'], body=f'Could extract content from {bold(file_name)}', type_=MessageType.ERROR) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail()
def install(self, pkg: AppImage, root_password: str, disk_loader: Optional[DiskCacheLoader], watcher: ProcessWatcher) -> TransactionResult: handler = ProcessHandler(watcher) out_dir = INSTALLATION_PATH + pkg.get_clean_name() counter = 0 while True: if os.path.exists(out_dir): self.logger.info( "Installation dir '{}' already exists. Generating a different one" .format(out_dir)) out_dir += '-{}'.format(counter) counter += 1 else: break Path(out_dir).mkdir(parents=True, exist_ok=True) pkg.install_dir = out_dir if pkg.imported: downloaded, file_name = True, pkg.local_file_path.split('/')[-1] file_path = out_dir + '/' + file_name try: moved, output = handler.handle_simple( SimpleProcess(['mv', pkg.local_file_path, file_path])) except: self.logger.error("Could not rename file '' as '{}'".format( pkg.local_file_path, file_path)) moved = False if not moved: watcher.show_message( title=self.i18n['error'].capitalize(), body=self.i18n['appimage.install.imported.rename_error']. format(bold(pkg.local_file_path.split('/')[-1]), bold(output)), type_=MessageType.ERROR) return TransactionResult.fail() else: appimage_url = pkg.url_download_latest_version if pkg.update else pkg.url_download file_name = appimage_url.split('/')[-1] pkg.version = pkg.latest_version pkg.url_download = appimage_url file_path = out_dir + '/' + file_name downloaded = self.file_downloader.download( file_url=pkg.url_download, watcher=watcher, output_path=file_path, cwd=str(Path.home())) if downloaded: watcher.change_substatus( self.i18n['appimage.install.permission'].format( bold(file_name))) permission_given = handler.handle( SystemProcess(new_subprocess(['chmod', 'a+x', file_path]))) if permission_given: watcher.change_substatus( self.i18n['appimage.install.extract'].format( bold(file_name))) try: res, output = handler.handle_simple( SimpleProcess([file_path, '--appimage-extract'], cwd=out_dir)) if 'Error: Failed to register AppImage in AppImageLauncherFS' in output: watcher.show_message( title=self.i18n['error'], body=self. i18n['appimage.install.appimagelauncher.error']. format(appimgl=bold('AppImageLauncher'), app=bold(pkg.name)), type_=MessageType.ERROR) handler.handle( SystemProcess( new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() except: watcher.show_message(title=self.i18n['error'], body=traceback.format_exc(), type_=MessageType.ERROR) traceback.print_exc() handler.handle( SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() watcher.change_substatus( self.i18n['appimage.install.desktop_entry']) extracted_folder = '{}/{}'.format(out_dir, 'squashfs-root') if os.path.exists(extracted_folder): desktop_entry = self._find_desktop_file(extracted_folder) with open('{}/{}'.format(extracted_folder, desktop_entry)) as f: de_content = f.read() de_content = replace_desktop_entry_exec_command( desktop_entry=de_content, appname=pkg.name, file_path=file_path) extracted_icon = self._find_icon_file(extracted_folder) if extracted_icon: icon_path = out_dir + '/logo.' + extracted_icon.split( '/')[-1].split('.')[-1] shutil.copy(extracted_icon, icon_path) de_content = RE_DESKTOP_ICON.sub( 'Icon={}\n'.format(icon_path), de_content) pkg.icon_path = icon_path Path(DESKTOP_ENTRIES_PATH).mkdir(parents=True, exist_ok=True) with open(self._gen_desktop_entry_path(pkg), 'w+') as f: f.write(de_content) try: shutil.rmtree(extracted_folder) except: traceback.print_exc() SymlinksVerifier.create_symlink(app=pkg, file_path=file_path, logger=self.logger, watcher=watcher) return TransactionResult(success=True, installed=[pkg], removed=[]) else: watcher.show_message( title=self.i18n['error'], body='Could extract content from {}'.format( bold(file_name)), type_=MessageType.ERROR) else: watcher.show_message( title=self.i18n['error'], body=self.i18n['appimage.install.download.error'].format( bold(pkg.url_download)), type_=MessageType.ERROR) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail()