def _map_to_model(self, app_json: dict, installed: bool, disk_loader: DiskCacheLoader, internet: bool = True) -> FlatpakApplication: app = FlatpakApplication(**app_json, i18n=self.i18n) app.installed = installed api_data = self.api_cache.get(app_json['id']) expired_data = api_data and api_data.get( 'expires_at') and api_data['expires_at'] <= datetime.utcnow() if not api_data or expired_data: if not app.runtime: if disk_loader: disk_loader.fill(app) # preloading cached disk data if internet: FlatpakAsyncDataLoader( app=app, api_cache=self.api_cache, manager=self, context=self.context, category_cache=self.category_cache).start() else: app.fill_cached_data(api_data) return app
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 ignore_update(self, pkg: FlatpakApplication): ignored_keys = self._read_ignored_updates() pkg_key = pkg.get_update_ignore_key() if pkg_key not in ignored_keys: ignored_keys.add(pkg_key) self._write_ignored_updates(ignored_keys) pkg.updates_ignored = True
def revert_ignored_update(self, pkg: FlatpakApplication): ignored_keys = self._read_ignored_updates() if ignored_keys: pkg_key = pkg.get_update_ignore_key() if pkg_key in ignored_keys: ignored_keys.remove(pkg_key) self._write_ignored_updates(ignored_keys) pkg.updates_ignored = False
def install(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool: res = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.install(pkg.id, pkg.origin), wrong_error_phrase='Warning')) if res: try: fields = flatpak.get_fields(pkg.id, pkg.branch, ['Ref', 'Branch']) if fields: pkg.ref = fields[0] pkg.branch = fields[1] except: traceback.print_exc() return res
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 test__sort_deps__one_app_one_runtime(self): pkgs = [ FlatpakApplication(id="org.gnome.gedit", name='Gedit', runtime=False), FlatpakApplication(id="org.gnome.Platform", name='org.gnome.Platform', runtime=True), ] sorted_list = self.manager.sort_update_order(pkgs) self.assertIsInstance(sorted_list, list) self.assertEqual(len(pkgs), len(sorted_list)) self.assertEqual(pkgs[1], sorted_list[0]) self.assertEqual(pkgs[0], sorted_list[1])
def test__sort_deps__only_apps(self): pkgs = [ FlatpakApplication(id="org.gnome.gedit", name='Gedit', runtime=False), FlatpakApplication(id="com.spotify.Client", name='com.spotify.Client', runtime=False), ] sorted_list = self.manager.sort_update_order(pkgs) self.assertIsInstance(sorted_list, list) self.assertEqual(len(pkgs), len(sorted_list)) for pkg in sorted_list: self.assertIn(pkg, pkgs)
def get_history(self, pkg: FlatpakApplication, full_commit_str: bool = False) -> PackageHistory: pkg.commit = flatpak.get_commit(pkg.id, pkg.branch, pkg.installation) pkg_commit = pkg.commit if pkg.commit else None if pkg_commit and not full_commit_str: pkg_commit = pkg_commit[0:8] commits = flatpak.get_app_commits_data(pkg.ref, pkg.origin, pkg.installation, full_str=full_commit_str) status_idx = 0 commit_found = False if pkg_commit is None and len(commits) > 1 and commits[0]['commit'] == '(null)': del commits[0] pkg_commit = commits[0] commit_found = True if not commit_found: for idx, data in enumerate(commits): if data['commit'] == pkg_commit: status_idx = idx commit_found = True break if not commit_found and pkg_commit and commits[0]['commit'] == '(null)': commits[0]['commit'] = pkg_commit return PackageHistory(pkg=pkg, history=commits, pkg_status_idx=status_idx)
def test_sort_deps_two_apps_two_runtimes_one_partial(self): gnome_platform = FlatpakApplication(id="org.gnome.Platform", name='Platform', runtime=True) gnome_locale = FlatpakApplication(id="org.gnome.Platform.Locale", name="Locale", runtime=True, partial=True) gnome_locale.base_id = gnome_platform.id pkgs = [ FlatpakApplication(id="org.gnome.gedit", name='Gedit', runtime=False), gnome_platform, FlatpakApplication(id="com.spotify.Client", name='Spotify', runtime=False), FlatpakApplication(id="org.freedesktop.Platform.GL.default", name='default', runtime=True), gnome_locale ] sorted_list = self.manager.sort_update_order(pkgs) self.assertIsInstance(sorted_list, list) self.assertEqual(len(pkgs), len(sorted_list)) self.assertEqual(pkgs[4], sorted_list[0]) self.assertEqual(pkgs[1], sorted_list[1]) self.assertEqual(pkgs[3], sorted_list[2]) self.assertEqual(pkgs[0], sorted_list[3]) self.assertEqual(pkgs[2], sorted_list[4])
def _map_to_model(self, app_json: dict, installed: bool, disk_loader: Optional[DiskCacheLoader], internet: bool = True) -> Tuple[FlatpakApplication, Optional[FlatpakAsyncDataLoader]]: app = FlatpakApplication(**app_json, i18n=self.i18n) app.installed = installed api_data = self.api_cache.get(app_json['id']) if app.runtime and app.latest_version is None: app.latest_version = app.version expired_data = api_data and api_data.get('expires_at') and api_data['expires_at'] <= datetime.utcnow() data_loader: Optional[FlatpakAsyncDataLoader] = None if not api_data or expired_data: if not app.runtime: if disk_loader: disk_loader.fill(app) # preloading cached disk data if internet: data_loader = FlatpakAsyncDataLoader(app=app, api_cache=self.api_cache, manager=self, context=self.context, category_cache=self.category_cache) data_loader.start() else: app.fill_cached_data(api_data) app.status = PackageStatus.READY return app, data_loader
def get_history(self, pkg: FlatpakApplication) -> PackageHistory: pkg.commit = flatpak.get_commit(pkg.id, pkg.branch) commits = flatpak.get_app_commits_data(pkg.ref, pkg.origin) status_idx = 0 for idx, data in enumerate(commits): if data['commit'] == pkg.commit: status_idx = idx break return PackageHistory(pkg=pkg, history=commits, pkg_status_idx=status_idx)
def test_sort_deps_two_apps_two_runtimes_two_partials(self): gnome_platform = FlatpakApplication(id="org.gnome.Platform", name='Platform', runtime=True) gnome_locale = FlatpakApplication(id="org.gnome.Platform.Locale", name="Locale", runtime=True, partial=True) gnome_locale.base_id = gnome_platform.id platform_default = FlatpakApplication( id="org.freedesktop.Platform.GL.default", name='Default', runtime=True) platform_locale = FlatpakApplication( id="org.freedesktop.Platform.GL.Locale", name='Locale', runtime=True, partial=True) platform_locale.base_id = platform_default.id pkgs = [ platform_locale, FlatpakApplication(id="org.gnome.gedit", name='Gedit', runtime=False), gnome_platform, FlatpakApplication(id="com.spotify.Client", name='Spotify', runtime=False), platform_default, gnome_locale ] sorted_list = self.manager.sort_update_order(pkgs) self.assertIsInstance(sorted_list, list) self.assertEqual(len(pkgs), len(sorted_list)) self.assertEqual(platform_default.id, sorted_list[0].id) self.assertEqual(platform_locale.id, sorted_list[1].id) self.assertEqual(gnome_locale.id, sorted_list[2].id) self.assertEqual(gnome_platform.id, sorted_list[3].id) self.assertEqual('org.gnome.gedit', sorted_list[4].id) self.assertEqual('com.spotify.Client', sorted_list[5].id)
def test_sort_deps_two_apps_two_runtimes(self): pkgs = [ FlatpakApplication(id="org.gnome.gedit", name='Gedit', runtime=False), FlatpakApplication(id="org.gnome.Platform", name='Platform', runtime=True), FlatpakApplication(id="com.spotify.Client", name='Spotify', runtime=False), FlatpakApplication(id="org.freedesktop.Platform.GL.default", name='default', runtime=True) ] sorted_list = self.manager.sort_update_order(pkgs) self.assertIsInstance(sorted_list, list) self.assertEqual(len(pkgs), len(sorted_list)) self.assertEqual(pkgs[1], sorted_list[0]) self.assertEqual(pkgs[3], sorted_list[1]) self.assertEqual(pkgs[0], sorted_list[2]) self.assertEqual(pkgs[2], sorted_list[3])
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 read_installed(self, disk_loader: Optional[DiskCacheLoader], limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = None, wait_async_data: bool = False) -> SearchResult: version = flatpak.get_version() updates, required_runtimes = list(), dict() thread_updates, thread_runtimes = None, None if internet_available: thread_updates = Thread(target=self._add_updates, args=(version, updates)) thread_updates.start() if version >= VERSION_1_12: thread_runtimes = Thread(target=self._fill_required_runtime_updates, args=(required_runtimes,)) thread_runtimes.start() installed = flatpak.list_installed(version) update_map = None if thread_updates: thread_updates.join() update_map = updates[0] models = {} data_loaders: Optional[List[FlatpakAsyncDataLoader]] = [] if wait_async_data else None if installed: for app_json in installed: model, loader = self._map_to_model(app_json=app_json, installed=True, disk_loader=disk_loader, internet=internet_available) model.update = False models[model.get_update_id(version)] = model if loader and data_loaders is not None: data_loaders.append(loader) if update_map: for update_id in update_map['full']: model_with_update = models.get(update_id) if model_with_update: model_with_update.update = True else: # it is a new component that must be installed update_id_split = update_id.split('/') new_app = FlatpakApplication(id=update_id_split[0], branch=update_id_split[1], installation=update_id_split[2], name=update_id_split[0].split('.')[-1].strip(), version=update_id_split[1], arch='x86_64' if self.context.is_system_x86_64() else 'x86', origin=update_id_split[3] if len(update_id_split) == 4 else None) new_app.update_component = True # mark as "update component" new_app.installed = True # faking the "installed" status to be displayed as an update new_app.update = True new_app.update_ref() models[update_id] = new_app if version >= VERSION_1_2: for partial_update_id in update_map['partial']: partial_data = partial_update_id.split('/') for model in models.values(): if model.installation == partial_data[2] and model.branch == partial_data[1]: if model.id == partial_data[0]: model.update = True break elif model.id in partial_data[0]: partial_model = model.gen_partial(partial_data[0]) partial_model.update = True models[partial_update_id] = partial_model break if thread_runtimes: thread_runtimes.join() if required_runtimes: for installation in ('system', 'user'): installation_runtimes = required_runtimes.get(installation) if installation_runtimes: for ref, origin in installation_runtimes: ref_split = ref.split('/') models[f'{installation}.'] = FlatpakApplication(id=ref_split[1], ref=ref, origin=origin, name=ref_split[1], version=ref_split[-1], latest_version=ref_split[-1], runtime=True, installation=installation, installed=False, update_component=True, update=True) if models: ignored = self._read_ignored_updates() if ignored: for model in models.values(): if model.get_update_ignore_key() in ignored: model.updates_ignored = True if data_loaders: for loader in data_loaders: loader.join() return SearchResult([*models.values()], None, len(models))