def change_channel(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher) -> bool: if not internet.is_available(): raise NoInternetException() try: channel = self._request_channel_installation( pkg=pkg, snap_config=None, snapd_client=SnapdClient(self.logger), watcher=watcher, exclude_current=True) if not channel: watcher.show_message( title=self.i18n['snap.action.channel.label'], body=self.i18n['snap.action.channel.error.no_channel']) return False return ProcessHandler(watcher).handle_simple( snap.refresh_and_stream(app_name=pkg.name, root_password=root_password, channel=channel))[0] except: return False
def downgrade(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool: handler = ProcessHandler(watcher) pkg.commit = flatpak.get_commit(pkg.id, pkg.branch, pkg.installation) watcher.change_progress(10) watcher.change_substatus(self.i18n['flatpak.downgrade.commits']) commits = flatpak.get_app_commits(pkg.ref, pkg.origin, pkg.installation, handler) if commits is None: return False commit_idx = commits.index(pkg.commit) # downgrade is not possible if the app current commit in the first one: if commit_idx == len(commits) - 1: watcher.show_message( self.i18n['flatpak.downgrade.impossible.title'], self.i18n['flatpak.downgrade.impossible.body'], MessageType.WARNING) return False commit = commits[commit_idx + 1] watcher.change_substatus(self.i18n['flatpak.downgrade.reverting']) watcher.change_progress(50) success = handler.handle( SystemProcess( subproc=flatpak.downgrade(pkg.ref, commit, pkg.installation, root_password), success_phrases=['Changes complete.', 'Updates complete.'], wrong_error_phrase='Warning')) watcher.change_progress(100) return success
def downgrade(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool: if not self._make_exports_dir(watcher): return False watcher.change_progress(10) watcher.change_substatus(self.i18n['flatpak.downgrade.commits']) history = self.get_history(pkg, full_commit_str=True) # downgrade is not possible if the app current commit in the first one: if history.pkg_status_idx == len(history.history) - 1: watcher.show_message( self.i18n['flatpak.downgrade.impossible.title'], self.i18n['flatpak.downgrade.impossible.body'].format( bold(pkg.name)), MessageType.ERROR) return False commit = history.history[history.pkg_status_idx + 1]['commit'] watcher.change_substatus(self.i18n['flatpak.downgrade.reverting']) watcher.change_progress(50) success, _ = ProcessHandler(watcher).handle_simple( flatpak.downgrade(pkg.ref, commit, pkg.installation, root_password)) watcher.change_progress(100) return success
def show_dep_not_installed(watcher: ProcessWatcher, pkgname: str, depname: str, i18n: I18n): watcher.show_message( title=i18n['error'], body=i18n['arch.install.dependency.install.error'].format( bold(depname), bold(pkgname)), type_=MessageType.ERROR)
def uninstall(self, pkg: AppImage, root_password: Optional[str], watcher: ProcessWatcher, disk_loader: DiskCacheLoader = None) -> TransactionResult: if os.path.exists(pkg.get_disk_cache_path()): handler = ProcessHandler(watcher) if not handler.handle(SystemProcess(new_subprocess(['rm', '-rf', pkg.get_disk_cache_path()]))): watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.uninstall.error.remove_folder'].format(bold(pkg.get_disk_cache_path()))) return TransactionResult.fail() de_path = self._gen_desktop_entry_path(pkg) if os.path.exists(de_path): os.remove(de_path) self.revert_ignored_update(pkg) if pkg.symlink and os.path.islink(pkg.symlink): self.logger.info(f"Removing symlink '{pkg.symlink}'") try: os.remove(pkg.symlink) self.logger.info(f"symlink '{pkg.symlink}' successfully removed") except: msg = f"could not remove symlink '{pkg.symlink}'" self.logger.error(msg) if watcher: watcher.print(f"[error] {msg}") self._add_self_latest_version(pkg) # only for self installation return TransactionResult(success=True, installed=None, removed=[pkg])
def install(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool: config = read_config() install_level = config['installation_level'] if install_level is not None: self.logger.info("Default Flaptak installation level defined: {}".format(install_level)) if install_level not in ('user', 'system'): watcher.show_message(title=self.i18n['error'].capitalize(), body=self.i18n['flatpak.install.bad_install_level.body'].format(field=bold('installation_level'), file=bold(CONFIG_FILE)), type_=MessageType.ERROR) return False pkg.installation = install_level else: user_level = watcher.request_confirmation(title=self.i18n['flatpak.install.install_level.title'], body=self.i18n['flatpak.install.install_level.body'].format(bold(pkg.name)), confirmation_label=self.i18n['no'].capitalize(), deny_label=self.i18n['yes'].capitalize()) pkg.installation = 'user' if user_level else 'system' remotes = flatpak.list_remotes() handler = ProcessHandler(watcher) if pkg.installation == 'user' and not remotes['user']: handler.handle_simple(flatpak.set_default_remotes('user')) elif pkg.installation == 'system' and not remotes['system']: if user.is_root(): handler.handle_simple(flatpak.set_default_remotes('system')) else: user_password, valid = watcher.request_root_password() if not valid: watcher.print('Operation aborted') return False else: if not handler.handle_simple(flatpak.set_default_remotes('system', user_password)): watcher.show_message(title=self.i18n['error'].capitalize(), body=self.i18n['flatpak.remotes.system_flathub.error'], type_=MessageType.ERROR) watcher.print("Operation cancelled") return False res = handler.handle(SystemProcess(subproc=flatpak.install(str(pkg.id), pkg.origin, pkg.installation), wrong_error_phrase='Warning')) if res: try: fields = flatpak.get_fields(str(pkg.id), pkg.branch, ['Ref', 'Branch']) if fields: pkg.ref = fields[0] pkg.branch = fields[1] except: traceback.print_exc() return res
def show_deps_not_installed(watcher: ProcessWatcher, pkgname: str, depnames: Iterable[str], i18n: I18n): deps = ', '.join((bold(d) for d in depnames)) watcher.show_message( title=i18n['error'].capitalize(), body=i18n['arch.install.dependency.install.error'].format( deps, bold(pkgname)), type_=MessageType.ERROR)
def show_dep_not_found(depname: str, i18n: I18n, watcher: ProcessWatcher): body = '<p>{}</p><p>{}</p><p></p><p>{}</p>'.format( i18n['arch.install.dep_not_found.body.l1'].format(bold(depname)), i18n['arch.install.dep_not_found.body.l2'], i18n['arch.install.dep_not_found.body.l3']) watcher.show_message( title=i18n['arch.install.dep_not_found.title'].capitalize(), body=body, type_=MessageType.ERROR)
def update(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool: if not self.uninstall(pkg, root_password, watcher): watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.error.uninstall_current_version'], type_=MessageType.ERROR) return False if self.install(pkg, root_password, watcher): self.cache_to_disk(pkg, None, False) return True return False
def uninstall(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool: if os.path.exists(pkg.get_disk_cache_path()): handler = ProcessHandler(watcher) if not handler.handle(SystemProcess(new_subprocess(['rm', '-rf', pkg.get_disk_cache_path()]))): watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.uninstall.error.remove_folder'].format(bold(pkg.get_disk_cache_path()))) return False de_path = self._gen_desktop_entry_path(pkg) if os.path.exists(de_path): os.remove(de_path) return True
def download_electron_sha256(self, version: str, url: str, widevine: bool, watcher: ProcessWatcher) -> bool: self.logger.info("Downloading Electron {} sha526".format(version)) sha256_path = self._get_electron_file_path(url=url, relative=False) if not self.http_client.exists(url): self.logger.warning("The file {} seems not to exist".format(url)) watcher.show_message(title=self.i18n['message.file.not_exist'], body=self.i18n['message.file.not_exist.body'].format(bold(url)), type_=MessageType.ERROR) return False return self.file_downloader.download(file_url=url, watcher=watcher, output_path=sha256_path, cwd=ELECTRON_PATH)
def show_dep_not_found(depname: str, i18n: I18n, watcher: ProcessWatcher, dependent: Optional[str] = None): source = f" {bold('(' + dependent + ')')}" if dependent else '' body = f"<p>{i18n['arch.install.dep_not_found.body.l1'].format(dep=bold(depname), source=source)}</p>" \ f"<p><{i18n['arch.install.dep_not_found.body.l2']}</p>" \ f"<p>{i18n['arch.install.dep_not_found.body.l3']}</p>" watcher.show_message( title=i18n['arch.install.dep_not_found.title'].capitalize(), body=body, type_=MessageType.ERROR)
def downgrade(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool: versions = self.get_history(pkg) if len(versions.history) == 1: watcher.show_message( title=self.i18n['appimage.downgrade.impossible.title'], body=self.i18n['appimage.downgrade.impossible.body'].format( bold(pkg.name)), type_=MessageType.ERROR) return False elif versions.pkg_status_idx == -1: watcher.show_message( title=self.i18n['appimage.downgrade.impossible.title'], body=self.i18n['appimage.downgrade.unknown_version.body']. format(bold(pkg.name)), type_=MessageType.ERROR) return False elif versions.pkg_status_idx == len(versions.history) - 1: watcher.show_message( title=self.i18n['appimage.downgrade.impossible.title'], body=self.i18n['appimage.downgrade.first_version'].format( bold(pkg.name)), type_=MessageType.ERROR) return False else: if self.uninstall(pkg, root_password, watcher): old_release = versions.history[versions.pkg_status_idx + 1] pkg.version = old_release['0_version'] pkg.latest_version = pkg.version pkg.url_download = old_release['2_url_download'] if self.install(pkg, root_password, watcher): self.cache_to_disk(pkg, None, False) return True else: watcher.show_message( title=self.i18n['error'], body=self.i18n['appimage.downgrade.install_version']. format(bold(pkg.version), bold(pkg.url_download)), type_=MessageType.ERROR) else: watcher.show_message( title=self.i18n['error'], body=self.i18n['appimage.error.uninstall_current_version']. format(bold(pkg.name)), type_=MessageType.ERROR) return False
def launch_timeshift(self, root_password: str, watcher: ProcessWatcher): if self._is_timeshift_launcher_available(): try: Popen(['timeshift-launcher'], stderr=STDOUT) return True except: traceback.print_exc() watcher.show_message(title=self.i18n["error"].capitalize(), body=self.i18n['action.backups.tool_error'].format(bold('Timeshift')), type_=MessageType.ERROR) return False else: watcher.show_message(title=self.i18n["error"].capitalize(), body=self.i18n['action.backups.tool_error'].format(bold('Timeshift')), type_=MessageType.ERROR) return False
def download_electron(self, version: str, url: str, watcher: ProcessWatcher) -> bool: Path(ELECTRON_PATH).mkdir(parents=True, exist_ok=True) self.logger.info("Downloading Electron {}".format(version)) electron_path = '{}/{}'.format(ELECTRON_PATH, url.split('/')[-1]) if not self.http_client.exists(url): self.logger.warning("The file {} seems not to exist".format(url)) watcher.show_message( title=self.i18n['message.file.not_exist'], body=self.i18n['message.file.not_exist.body'].format( bold(url)), type_=MessageType.ERROR) return False return self.file_downloader.download(file_url=url, watcher=watcher, output_path=electron_path, cwd=ELECTRON_PATH)
def launch_sources_app(self, root_password: str, watcher: ProcessWatcher) -> bool: deb_config = self.configman.get_config() sources_app = deb_config.get('pkg_sources.app') if isinstance(sources_app, str) and sources_app not in self.known_sources_apps: watcher.show_message(title=self._i18n['popup.title.error'], body=self._i18n['debian.action.sources.unsupported'].format(app=bold(sources_app))) return False if sources_app: if not which(sources_app): watcher.show_message(title=self._i18n['popup.title.error'], body=self._i18n['debian.action.sources.not_installed'], type_=MessageType.ERROR) return False Popen(sources_app, shell=True) return True for app in self.get_installed_source_apps(): Popen(app, shell=True) return True watcher.show_message(title=self._i18n['popup.title.error'], body=self._i18n['debian.action.sources.not_installed'], type_=MessageType.ERROR) return False
def _download(self, pkg: AppImage, watcher: ProcessWatcher) -> Optional[Tuple[str, str]]: appimage_url = pkg.url_download_latest_version if pkg.update else pkg.url_download if not appimage_url: watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.download.no_url'].format(app=bold(pkg.name)), type_=MessageType.ERROR) return file_name = appimage_url.split('/')[-1] pkg.version = pkg.latest_version pkg.url_download = appimage_url try: Path(DOWNLOAD_DIR).mkdir(exist_ok=True, parents=True) except OSError: watcher.show_message(title=self.i18n['error'], body=self.i18n['error.mkdir'].format(dir=bold(DOWNLOAD_DIR)), type_=MessageType.ERROR) return file_path = f'{DOWNLOAD_DIR}/{file_name}' downloaded = self.file_downloader.download(file_url=pkg.url_download, watcher=watcher, output_path=file_path, cwd=str(Path.home())) if not downloaded: watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.download.error'].format(bold(pkg.url_download)), type_=MessageType.ERROR) return return file_name, file_path
def uninstall(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher) -> bool: handler = ProcessHandler(watcher) watcher.change_progress(10) info = pacman.get_info_dict(pkg.name) watcher.change_progress(50) if info.get('required by'): pkname = bold(pkg.name) msg = '{}:<br/><br/>{}<br/><br/>{}'.format( self.i18n['arch.uninstall.required_by'].format(pkname), bold(info['required by']), self.i18n['arch.uninstall.required_by.advice'].format(pkname)) watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.WARNING) return False uninstalled = self._uninstall(pkg.name, root_password, handler) watcher.change_progress(100) return uninstalled
def upgrade(self, requirements: UpgradeRequirements, root_password: Optional[str], watcher: ProcessWatcher) -> bool: not_upgraded = [] for req in requirements.to_upgrade: watcher.change_status(f"{self.i18n['manage_window.status.upgrading']} {req.pkg.name} ({req.pkg.version})...") download_data = None if not req.pkg.imported: download_data = self._download(req.pkg, watcher) if not download_data: not_upgraded.append(req.pkg) watcher.change_substatus('') continue if not self.uninstall(req.pkg, root_password, watcher).success: not_upgraded.append(req.pkg) watcher.change_substatus('') continue if not self._install(pkg=req.pkg, watcher=watcher, pre_downloaded_file=download_data).success: not_upgraded.append(req.pkg) watcher.change_substatus('') continue self.cache_to_disk(req.pkg, None, False) all_failed = len(not_upgraded) == len(requirements.to_upgrade) if not_upgraded: pkgs_str = ''.join((f'<li>{app.name}</li>' for app in not_upgraded)) watcher.show_message(title=self.i18n['error' if all_failed else 'warning'].capitalize(), body=self.i18n['appimage.upgrade.failed'].format(apps=f'<ul>{pkgs_str}</ul>'), type_=MessageType.ERROR if all_failed else MessageType.WARNING) watcher.change_substatus('') return not all_failed
def upgrade(self, requirements: UpgradeRequirements, root_password: str, watcher: ProcessWatcher) -> bool: for req in requirements.to_upgrade: watcher.change_status("{} {} ({})...".format( self.i18n['manage_window.status.upgrading'], req.pkg.name, req.pkg.version)) if not self.uninstall(req.pkg, root_password, watcher): watcher.show_message( title=self.i18n['error'], body=self.i18n['appimage.error.uninstall_current_version'], type_=MessageType.ERROR) watcher.change_substatus('') return False if not self.install(req.pkg, root_password, watcher): watcher.change_substatus('') return False self.cache_to_disk(req.pkg, None, False) watcher.change_substatus('') return True
def install(self, pkg: WebApplication, root_password: str, watcher: ProcessWatcher) -> bool: continue_install, install_options = self._ask_install_options( pkg, watcher) if not continue_install: watcher.print("Installation aborted by the user") return False watcher.change_substatus(self.i18n['web.env.checking']) handler = ProcessHandler(watcher) env_settings = self.env_updater.read_settings() local_config = read_config() if local_config['environment'][ 'system'] and not nativefier.is_available(): watcher.show_message( title=self.i18n['error'].capitalize(), body=self.i18n['web.install.global_nativefier.unavailable']. format(n=bold('Nativefier'), app=bold(pkg.name)) + '.', type_=MessageType.ERROR) return False env_components = self.env_updater.check_environment( app=pkg, local_config=local_config, env=env_settings, is_x86_x64_arch=self.context.is_system_x86_64()) comps_to_update = [c for c in env_components if c.update] if comps_to_update and not self._ask_update_permission( comps_to_update, watcher): return False if not self.env_updater.update(components=comps_to_update, handler=handler): 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) app_id, treated_name = self._gen_app_id(pkg.name) pkg.id = app_id app_dir = '{}/{}'.format(INSTALLED_PATH, app_id) watcher.change_substatus( self.i18n['web.install.substatus.checking_fixes']) fix = self._get_fix_for( url_no_protocol=self._strip_url_protocol(pkg.url)) fix_path = '{}/fix.js'.format(app_dir) if fix: # just adding the fix as an installation option. The file will be written later self.logger.info('Fix found for {}'.format(pkg.url)) watcher.print('Fix found for {}'.format(pkg.url)) install_options.append('--inject={}'.format(fix_path)) # if a custom icon is defined for an app suggestion: icon_path, icon_bytes = None, None if pkg.icon_url and pkg.save_icon and not { o for o in install_options if o.startswith('--icon') }: download = self._download_suggestion_icon(pkg, app_dir) if download and download[1]: icon_path, icon_bytes = download[0], download[1] pkg.custom_icon = icon_path # writting the icon in a temporary folder to be used by the nativefier process temp_icon_path = '{}/{}'.format(TEMP_PATH, pkg.icon_url.split('/')[-1]) install_options.append('--icon={}'.format(temp_icon_path)) self.logger.info("Writing a temp suggestion icon at {}".format( temp_icon_path)) with open(temp_icon_path, 'wb+') as f: f.write(icon_bytes) watcher.change_substatus( self.i18n['web.install.substatus.call_nativefier'].format( bold('nativefier'))) electron_version = str( next((c for c in env_components if c.id == 'electron')).version) installed = handler.handle_simple( nativefier.install(url=pkg.url, name=app_id, output_dir=app_dir, electron_version=electron_version, system=bool( local_config['environment']['system']), cwd=INSTALLED_PATH, extra_options=install_options)) 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, treated_name) os.rename(inner_dir, temp_dir) shutil.rmtree(app_dir) os.rename(temp_dir, app_dir) # injecting a fix if fix: self.logger.info('Writting JS fix at {}'.format(fix_path)) with open(fix_path, 'w+') as f: f.write(fix) # persisting the custom suggestion icon in the defitive directory if icon_bytes: self.logger.info( "Writting the final custom suggestion icon at {}".format( icon_path)) with open(icon_path, 'wb+') as f: f.write(icon_bytes) 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 watcher.change_substatus(self.i18n['web.install.substatus.shortcut']) desktop_entry_path = self._gen_desktop_entry_path(app_id) entry_content = self._gen_desktop_entry_content(pkg) Path(DESKTOP_ENTRIES_DIR).mkdir(parents=True, exist_ok=True) with open(desktop_entry_path, 'w+') as f: f.write(entry_content) pkg.desktop_entry = desktop_entry_path if '--tray=start-in-tray' in install_options: autostart_dir = '{}/.config/autostart'.format(Path.home()) Path(autostart_dir).mkdir(parents=True, exist_ok=True) with open(pkg.get_autostart_path(), 'w+') as f: f.write(entry_content) if install_options: pkg.options_set = install_options return True
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() autostart_path = pkg.get_autostart_path() if os.path.exists(autostart_path): try: os.remove(autostart_path) except: watcher.show_message( title=self.i18n['error'], body=self.i18n['web.uninstall.error.remove'].format( bold(autostart_path)), type_=MessageType.WARNING) traceback.print_exc() config_path = pkg.get_config_dir() if config_path and os.path.exists(config_path): try: shutil.rmtree(config_path) except: watcher.show_message( title=self.i18n['error'], body=self.i18n['web.uninstall.error.remove'].format( bold(config_path)), type_=MessageType.WARNING) traceback.print_exc() return True
def downgrade(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher) -> bool: handler = ProcessHandler(watcher) app_build_dir = '{}/build_{}'.format(BUILD_DIR, int(time.time())) watcher.change_progress(5) try: if not os.path.exists(app_build_dir): build_dir = handler.handle( SystemProcess( new_subprocess(['mkdir', '-p', app_build_dir]))) if build_dir: watcher.change_progress(10) watcher.change_substatus(self.i18n['arch.clone'].format( bold(pkg.name))) clone = handler.handle( SystemProcess(subproc=new_subprocess( ['git', 'clone', URL_GIT.format(pkg.name)], cwd=app_build_dir), check_error_output=False)) watcher.change_progress(30) if clone: watcher.change_substatus( self.i18n['arch.downgrade.reading_commits']) clone_path = '{}/{}'.format(app_build_dir, pkg.name) pkgbuild_path = '{}/PKGBUILD'.format(clone_path) commits = run_cmd("git log", cwd=clone_path) watcher.change_progress(40) if commits: commit_list = re.findall(r'commit (.+)\n', commits) if commit_list: if len(commit_list) > 1: for idx in range(1, len(commit_list)): commit = commit_list[idx] with open(pkgbuild_path) as f: pkgdict = aur.map_pkgbuild( f.read()) if not handler.handle( SystemProcess( subproc=new_subprocess( [ 'git', 'reset', '--hard', commit ], cwd=clone_path), check_error_output=False)): watcher.print( 'Could not downgrade anymore. Aborting...' ) return False if '{}-{}'.format( pkgdict.get('pkgver'), pkgdict.get( 'pkgrel')) == pkg.version: # current version found watcher.change_substatus(self.i18n[ 'arch.downgrade.version_found'] ) break watcher.change_substatus( self. i18n['arch.downgrade.install_older']) return self._make_pkg(pkg.name, pkg.maintainer, root_password, handler, app_build_dir, clone_path, dependency=False, skip_optdeps=True) else: watcher.show_message( title=self. i18n['arch.downgrade.error'], body=self. i18n['arch.downgrade.impossible']. format(pkg.name), type_=MessageType.ERROR) return False watcher.show_message( title=self.i18n['error'], body=self.i18n['arch.downgrade.no_commits'], type_=MessageType.ERROR) return False finally: if os.path.exists(app_build_dir): handler.handle( SystemProcess( subproc=new_subprocess(['rm', '-rf', app_build_dir]))) return False
def install(self, pkg: FlatpakApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult: flatpak_config = self.configman.get_config() install_level = flatpak_config['installation_level'] if install_level is not None: self.logger.info( "Default Flaptak installation level defined: {}".format( install_level)) if install_level not in ('user', 'system'): watcher.show_message( title=self.i18n['error'].capitalize(), body=self.i18n['flatpak.install.bad_install_level.body']. format(field=bold('installation_level'), file=bold(CONFIG_FILE)), type_=MessageType.ERROR) return TransactionResult(success=False, installed=[], removed=[]) pkg.installation = install_level else: user_level = watcher.request_confirmation( title=self.i18n['flatpak.install.install_level.title'], body=self.i18n['flatpak.install.install_level.body'].format( bold(pkg.name)), confirmation_label=self.i18n['no'].capitalize(), deny_label=self.i18n['yes'].capitalize()) pkg.installation = 'user' if user_level else 'system' remotes = flatpak.list_remotes() handler = ProcessHandler(watcher) if pkg.installation == 'user' and not remotes['user']: handler.handle_simple(flatpak.set_default_remotes('user')) elif pkg.installation == 'system' and not remotes['system']: if user.is_root(): handler.handle_simple(flatpak.set_default_remotes('system')) else: valid, user_password = watcher.request_root_password() if not valid: watcher.print('Operation aborted') return TransactionResult(success=False, installed=[], removed=[]) else: if not handler.handle_simple( flatpak.set_default_remotes( 'system', user_password))[0]: watcher.show_message( title=self.i18n['error'].capitalize(), body=self. i18n['flatpak.remotes.system_flathub.error'], type_=MessageType.ERROR) watcher.print("Operation cancelled") return TransactionResult(success=False, installed=[], removed=[]) # retrieving all installed so it will be possible to know the additional installed runtimes after the operation succeeds flatpak_version = flatpak.get_version() installed = flatpak.list_installed(flatpak_version) installed_by_level = { '{}:{}:{}'.format(p['id'], p['name'], p['branch']) for p in installed if p['installation'] == pkg.installation } if installed else None if not self._make_exports_dir(handler.watcher): return TransactionResult(success=False, installed=[], removed=[]) installed, output = handler.handle_simple( flatpak.install(str(pkg.id), pkg.origin, pkg.installation)) if not installed and 'error: No ref chosen to resolve matches' in output: ref_opts = RE_INSTALL_REFS.findall(output) if ref_opts and len(ref_opts) > 1: view_opts = [ InputOption(label=o, value=o.strip()) for o in ref_opts if o ] ref_select = SingleSelectComponent(type_=SelectViewType.RADIO, options=view_opts, default_option=view_opts[0], label='') if watcher.request_confirmation( title=self.i18n['flatpak.install.ref_choose.title'], body=self.i18n['flatpak.install.ref_choose.body']. format(bold(pkg.name)), components=[ref_select], confirmation_label=self.i18n['proceed'].capitalize(), deny_label=self.i18n['cancel'].capitalize()): ref = ref_select.get_selected() installed, output = handler.handle_simple( flatpak.install(ref, pkg.origin, pkg.installation)) pkg.ref = ref pkg.runtime = 'runtime' in ref else: watcher.print('Aborted by the user') return TransactionResult.fail() else: return TransactionResult.fail() if installed: try: fields = flatpak.get_fields(str(pkg.id), pkg.branch, ['Ref', 'Branch']) if fields: pkg.ref = fields[0] pkg.branch = fields[1] except: traceback.print_exc() if installed: new_installed = [pkg] current_installed = flatpak.list_installed(flatpak_version) current_installed_by_level = [ p for p in current_installed if p['installation'] == pkg.installation ] if current_installed else None if current_installed_by_level and (not installed_by_level or len(current_installed_by_level) > len(installed_by_level) + 1): pkg_key = '{}:{}:{}'.format(pkg.id, pkg.name, pkg.branch) net_available = self.context.is_internet_available() for p in current_installed_by_level: current_key = '{}:{}:{}'.format(p['id'], p['name'], p['branch']) if current_key != pkg_key and (not installed_by_level or current_key not in installed_by_level): new_installed.append( self._map_to_model(app_json=p, installed=True, disk_loader=disk_loader, internet=net_available)) return TransactionResult(success=installed, installed=new_installed, removed=[]) else: return TransactionResult.fail()
def show_optdeps_not_installed(depnames: Iterable[str], watcher: ProcessWatcher, i18n: I18n): deps = ', '.join((bold(d) for d in depnames)) watcher.show_message(title=i18n['error'].capitalize(), body=i18n['arch.install.optdep.error'].format(deps), type_=MessageType.ERROR)
def uninstall(self, pkg: DebianPackage, root_password: str, watcher: ProcessWatcher, disk_loader: Optional[DiskCacheLoader], purge: bool = False) -> TransactionResult: config_ = self.configman.get_config() purge_ = purge or config_.get('remove.purge', False) watcher.change_substatus(self._i18n['debian.simulate_operation']) transaction = self.aptitude.simulate_removal((pkg.name,), purge=purge_) if not transaction or not transaction.to_remove: return TransactionResult.fail() if pkg not in transaction.to_remove: watcher.show_message(title=self._i18n['popup.title.error'], body=self._i18n['debian.remove.impossible'].format(pkg=bold(pkg.name)), type_=MessageType.ERROR) return TransactionResult.fail() watcher.change_substatus('') deps = tuple(p for p in transaction.to_remove if p.name != pkg.name) if deps: # updates are required to be filled in case the dependencies are currently displayed on the view updates = dict() fill_updates = Thread(target=self._fill_updates, args=(updates,)) fill_updates.start() deps_data = self.aptitude.show((p.name for p in deps), attrs=('description', 'maintainer', 'section')) if deps_data: for p in deps: fill_show_data(p, deps_data.get(p.name)) if not self.view.confirm_removal(source_pkg=pkg.name, dependencies=deps, watcher=watcher): return TransactionResult.fail() fill_updates.join() if updates: for p in deps: latest_version = updates.get(p.name) if latest_version is not None and p.version != latest_version: p.latest_version = latest_version p.update = True watcher.change_substatus(self._i18n['debian.uninstall.removing']) handler = ProcessHandler(watcher) to_remove = tuple(p.name for p in transaction.to_remove) with self.output_handler.start(watcher=watcher, targets=to_remove, action=AptitudeAction.REMOVE) as handle: removed, _ = handler.handle_simple(self.aptitude.remove(packages=to_remove, root_password=root_password, purge=purge_), output_handler=handle) if not removed: return TransactionResult.fail() watcher.change_substatus(self._i18n['debian.uninstall.validating']) current_installed_names = set(self.aptitude.read_installed_names()) watcher.change_substatus('') all_removed, apps_removed, not_removed_names = [], set(), set() for p in transaction.to_remove: if p.name not in current_installed_names: instance = p if p != pkg else pkg all_removed.append(instance) if instance.app: apps_removed.add(instance.app) instance.installed = False instance.version = instance.latest_version instance.update = False instance.bind_app(None) else: not_removed_names.add(p.name) if apps_removed: # updating apps index watcher.print(self._i18n['debian.app_index.updating'] + ' ...') watcher.change_substatus(self._i18n['debian.app_index.updating']) indexed_apps = set(self.app_indexer.read_index()) if indexed_apps: new_index = indexed_apps.difference(apps_removed) try: self.app_indexer.update_index(new_index, update_timestamp=False) self._update_apps_index(new_index) self._log.info(f"Debian applications removed from the index: " f"{', '.join((a.name for a in apps_removed))}") except ApplicationIndexError: pass watcher.change_substatus('') success = True if not_removed_names: success = pkg.name not in not_removed_names not_removed_str = ', '.join((bold(p) for p in sorted(not_removed_names))) watcher.show_message(title=self._i18n[f"popup.title.{'warning' if success else 'error'}"], body=self._i18n['debian.uninstall.failed_to_remove'].format(no=len(not_removed_names), pkgs=not_removed_str), type_=MessageType.WARNING if success else MessageType.ERROR) return TransactionResult(success=success, installed=None, removed=all_removed)
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 show_optdep_not_installed(depname: str, watcher: ProcessWatcher, i18n: I18n): watcher.show_message(title=i18n['error'], body=i18n['arch.install.optdep.error'].format( bold(depname)), type_=MessageType.ERROR)
def install(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool: handler = ProcessHandler(watcher) out_dir = INSTALLATION_PATH + pkg.name.lower() 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 False 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 False 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 False 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 = RE_DESKTOP_EXEC.sub( 'Exec={}\n'.format(file_path), de_content) extracted_icon = self._find_icon_file(extracted_folder) if extracted_icon: icon_path = out_dir + '/logo.' + extracted_icon.split( '.')[-1] shutil.copy( '{}/{}'.format(extracted_folder, 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) shutil.rmtree(extracted_folder) return True 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 False
def show_dep_not_found(depname: str, i18n: I18n, watcher: ProcessWatcher): watcher.show_message(title=i18n['arch.install.dep_not_found.title'], body=i18n['arch.install.dep_not_found.body'].format( bold(depname)), type_=MessageType.ERROR)