def _add_profile(self, table, show_env, show_dependencies, element_count, profile: Profile, sync: bool, dependencies_iplist: list): # Profile header table.new_row().new_double_separator(element_count) pfname = profile.name if profile.is_current: pfname += " " + self.tm.PROFILE_CURRENT("[current]") header_text = ("{label_theme}Profile:{reset_theme} {profile_name} ({sync_state})").format( label_theme=self.tm.LABEL, reset_theme=self.tm.RESET, profile_name=pfname, sync_state="sync" if sync else "not sync" ) table.new_row().new_separator().new_cell(header_text, halign=HAlign.CENTER).new_hspan(element_count - 3).new_separator() # Environment if show_env: env = [] Environment.build(self.ws_env, profile.build_environment()).print_env(kv_consumer=lambda k, v: env.append("{0}={1}".format(k, v))) if len(env) > 0: table.new_row().new_separator(element_count) table.new_row().new_separator().new_cell(self.tm.LABEL("Environment"), halign=HAlign.CENTER).new_separator().new_cell("\n".join(env)).new_hspan( 2 ).new_separator() # Packages header if len(profile.packages) > 0: table.new_row().new_separator(element_count) table.new_row().new_separator().new_cell(self.tm.LABEL("Packages"), halign=HAlign.CENTER).new_separator().new_cell( self.tm.LABEL("Identifier"), halign=HAlign.CENTER ).new_separator().new_cell(self.tm.LABEL("Description"), halign=HAlign.CENTER).new_separator() # Included packages included_pkgmap = OrderedDict() for pi in profile.packages: included_pkgmap[pi] = None for ip in dependencies_iplist: if ip.identifier == pi: included_pkgmap[pi] = ip break table.new_row().new_separator(element_count) self._add_packages_rows(table, "Included", included_pkgmap) # Dependencies if show_dependencies and len(dependencies_iplist) > 0: depends_pkgmap = OrderedDict() for ip in dependencies_iplist: if ip not in included_pkgmap.values(): depends_pkgmap[ip.identifier] = ip if len(depends_pkgmap) > 0: table.new_row().new_separator(element_count) self._add_packages_rows(table, "Dependencies" if len(depends_pkgmap) > 1 else "Dependency", depends_pkgmap)
def test_conditional_install(self): self.pm.install_packages(PackageIdentifier.parse_list(["condition_1.0"])) self.check_content(self.pm.list_installed_packages(), ["condition_1.0", "condition-B_1.0", "condition-D_1.0", "condition-F_1.0", "condition-H_1.0"]) self.pm.install_packages(PackageIdentifier.parse_list(["condition_1.0"]), env=Environment("test", {"FOO": "BAR"})) self.check_content( self.pm.list_installed_packages(), ["condition_1.0", "condition-A_1.0", "condition-B_1.0", "condition-C_1.0", "condition-D_1.0", "condition-F_1.0", "condition-H_1.0"], ) self.pm.update_user_environment(set_map={"FOO2": "BAR2", "HELLO": "WoRld"}) env = Environment.build(self.pm.build_builtin_environment(), self.pm.build_user_environment(), Environment("test", {"FOO": "BAR"})) self.pm.install_packages(PackageIdentifier.parse_list(["condition_1.0"]), env=env) self.check_content( self.pm.list_installed_packages(), [ "condition_1.0", "condition-A_1.0", "condition-B_1.0", "condition-C_1.0", "condition-D_1.0", "condition-E_1.0", "condition-F_1.0", "condition-G_1.0", "condition-H_1.0", ], ) self.pm.uninstall_packages(PackageIdentifier.parse_list(["condition_1.0"])) self.check_content(self.pm.list_installed_packages(), [])
def __execute_steps(self, pi: PackageIdentifier, ipmap: dict, se_func: callable, env: Environment = None): # Find the package ip = find_manifest(pi, ipmap) # The environment if env is None: env = Environment.build(self.build_builtin_environment(), self.build_user_environment()) # build the dependencies deps = DependencyUtils.installed([pi], ipmap, env=env, ignore_unknown=True) # Update env env.append(self.build_packages_environment(deps)) # Fix PREREQ_ROOT env.set_variable("LEAF_PREREQ_ROOT", self.install_folder) # The Variable resolver vr = VariableResolver(ip, ipmap.values()) # Execute steps se = StepExecutor(self.logger, ip, vr, env=env) se_func(se)
def test_environment(self): pm = PackageManager() env = Environment.build(pm.build_builtin_environment(), pm.build_user_environment(), Environment("test", {"FOO": "BAR"})) rend = EnvironmentRenderer(env) with self.assertStdout(template_out="env.out"): self.loggerManager.print_renderer(rend)
def test_depends_with_custom_env(self): env = Environment.build(self.pm.build_builtin_environment(), self.pm.build_user_environment(), Environment("Custom env", {})) deps = DependencyUtils.install( PackageIdentifier.parse_list(["condition_1.0"]), self.pm.list_available_packages(), self.pm.list_installed_packages(), env=env ) self.__assert_deps(deps, ["condition-B_1.0", "condition-D_1.0", "condition-F_1.0", "condition-H_1.0", "condition_1.0"], AvailablePackage) self.pm.update_user_environment(set_map={"FOO": "HELLO"}) env = Environment.build(self.pm.build_builtin_environment(), self.pm.build_user_environment(), Environment("Custom env", {})) deps = DependencyUtils.install( PackageIdentifier.parse_list(["condition_1.0"]), self.pm.list_available_packages(), self.pm.list_installed_packages(), env=env ) self.__assert_deps(deps, ["condition-A_1.0", "condition-D_1.0", "condition-F_1.0", "condition-H_1.0", "condition_1.0"], AvailablePackage) self.pm.update_user_environment(set_map={"FOO": "HELLO"}) env = Environment.build(self.pm.build_builtin_environment(), self.pm.build_user_environment(), Environment("Custom env", {"FOO": "BAR"})) deps = DependencyUtils.install( PackageIdentifier.parse_list(["condition_1.0"]), self.pm.list_available_packages(), self.pm.list_installed_packages(), env=env ) self.__assert_deps(deps, ["condition-A_1.0", "condition-C_1.0", "condition-F_1.0", "condition_1.0"], AvailablePackage)
def install_prereq(self, pilist: list, tmp_install_folder: Path, apmap: dict = None, env: Environment = None, raise_on_error: bool = True): """ Install given prereg available package in alternative root folder @return: error count """ if apmap is None: apmap = self.list_available_packages() # Get packages to install aplist = [find_manifest(pi, apmap) for pi in pilist] errors = 0 if len(aplist) > 0: self.logger.print_verbose( "Installing {count} pre-required package(s) in {folder}". format(count=len(aplist), folder=tmp_install_folder)) if env is None: env = Environment.build(self.build_builtin_environment(), self.build_user_environment()) env.append( Environment("Prereq", {"LEAF_PREREQ_ROOT": tmp_install_folder})) for prereqap in aplist: try: prereqla = self.__download_ap(prereqap) prereqip = self.__extract_artifact( prereqla, env, tmp_install_folder, keep_folder_on_error=True) self.logger.print_verbose( "Prereq package {ip.identifier} is OK".format( ip=prereqip)) except Exception as e: if raise_on_error: raise e self.logger.print_verbose( "Prereq package {ap.identifier} has error: {error}". format(ap=prereqap, error=e)) errors += 1 return errors
def execute(self, args, uargs): pm = PackageManager() env = Environment.build(pm.build_builtin_environment(), pm.build_user_environment()) install_list, uninstall_list = DependencyUtils.upgrade( None if len(args.packages) == 0 else args.packages, pm.list_available_packages(), pm.list_installed_packages(), env=env) pm.logger.print_verbose( "{count} package(s) to be upgraded: {text}".format( count=len(install_list), text=" ".join([str(ap.identifier) for ap in install_list]))) if args.clean: pm.logger.print_verbose( "{count} package(s) to be removed: {text}".format( count=len(uninstall_list), text=" ".join( [str(ip.identifier) for ip in uninstall_list]))) if len(install_list) == 0: pm.logger.print_default("No package to upgrade") else: pm.install_packages(map(IDENTIFIER_GETTER, install_list), env=env) if len(uninstall_list) > 0: if args.clean: pm.uninstall_packages( map(IDENTIFIER_GETTER, uninstall_list)) else: pm.logger.print_default( "These packages can be removed:", " ".join([str(ip.identifier) for ip in uninstall_list]))
def install_packages(self, pilist: list, env: Environment = None, keep_folder_on_error: bool = False): """ Compute dependency tree, check compatibility, download from remotes and extract needed packages @return: InstalledPackage list """ with self.application_lock.acquire(): prereq_install_folder = None ipmap = self.list_installed_packages() apmap = self.list_available_packages() out = [] # Build env to resolve dynamic dependencies if env is None: env = Environment.build(self.build_builtin_environment(), self.build_user_environment()) try: ap_to_install = DependencyUtils.install(pilist, apmap, ipmap, env=env) # Check leaf min version min_version = check_leaf_min_version(ap_to_install) if min_version: raise LeafOutOfDateException( "You need to upgrade leaf to v{version} to install {text}" .format(version=min_version, text=", ".join([ str(ap.identifier) for ap in ap_to_install ]))) # Check nothing to do if len(ap_to_install) == 0: self.logger.print_default("All packages are installed") else: # Check available size download_totalsize = 0 for ap in ap_to_install: if ap.size is not None: download_totalsize += ap.size fs_check_free_space(self.download_cache_folder, download_totalsize) # Confirm text = ", ".join( [str(ap.identifier) for ap in ap_to_install]) self.logger.print_quiet( "Packages to install: {packages}".format( packages=text)) if download_totalsize > 0: self.logger.print_default( "Total size:", sizeof_fmt(download_totalsize)) self.print_with_confirm(raise_on_decline=True) # Install prereq prereq_to_install = DependencyUtils.prereq(pilist, apmap, ipmap, env=env) if len(prereq_to_install) > 0: self.logger.print_default("Check required packages") prereq_install_folder = mkdir_tmp_leaf_dir() self.install_prereq( [p.identifier for p in prereq_to_install], prereq_install_folder, apmap=apmap, env=env) # Download ap list self.logger.print_default( "Downloading {size} package(s)".format( size=len(ap_to_install))) la_to_install = [] for ap in ap_to_install: la_to_install.append(self.__download_ap(ap)) # Check the extracted size extracted_totalsize = 0 for la in la_to_install: if la.final_size is not None: extracted_totalsize += la.final_size else: extracted_totalsize += la.get_total_size() fs_check_free_space(self.install_folder, extracted_totalsize) # Extract la list for la in la_to_install: self.logger.print_default( "[{current}/{total}] Installing {la.identifier}". format(current=(len(out) + 1), total=len(la_to_install), la=la)) ip = self.__extract_artifact( la, env, self.install_folder, ipmap=ipmap, keep_folder_on_error=keep_folder_on_error) out.append(ip) finally: if not keep_folder_on_error and prereq_install_folder is not None: self.logger.print_verbose( "Remove prereq root folder {folder}".format( folder=prereq_install_folder)) rmtree_force(prereq_install_folder) return out
def build_pf_environment(self, profile: Profile): return Environment.build(self.build_builtin_environment(), self.build_user_environment(), self.build_ws_environment(), profile.build_environment())
def execute(self, args, uargs): wm = self.get_workspacemanager(check_initialized=False) ipmap = wm.list_installed_packages() searching_iplist = None env = None if args.package is not None: # User forces the package env = Environment.build(wm.build_builtin_environment(), wm.build_user_environment()) searching_iplist = DependencyUtils.installed([args.package], ipmap, env=env) env.append(wm.build_packages_environment(searching_iplist)) elif wm.is_initialized: # We are in a workspace, use the current profile pfname = wm.current_profile_name profile = wm.get_profile(pfname) wm.is_profile_sync(profile, raise_if_not_sync=True) searching_iplist = wm.get_profile_dependencies(profile) env = wm.build_full_environment(profile) else: # Use installed packages searching_iplist = sorted(ipmap.values(), key=IDENTIFIER_GETTER) # Execute if args.binary is None: # Print mode scope = "installed packages" if args.package is not None: scope = args.package elif wm.is_initialized: scope = "workspace" rend = EntrypointListRenderer(scope) rend.extend(searching_iplist) wm.print_renderer(rend, verbosity=Verbosity.QUIET if args.oneline else Verbosity.DEFAULT) elif args.oneline: # User gave BIN and --oneline raise LeafException( "You must specify a binary or '--oneline', not both", hints=[ "Run 'leaf run --oneline' to list all binaries", "Run 'leaf run {bin} -- --oneline {uargs}' pass --oneline to the binary" .format(bin=args.binary, uargs=" ".join(uargs)), ], ) else: # Search entry point candidate_ip = None for ip in searching_iplist: if args.binary in ip.binaries: if candidate_ip is None: candidate_ip = ip elif candidate_ip.name != ip.name: raise LeafException( "Binary {bin} is declared by multiple packages". format(bin=args.binary)) elif ip.identifier > candidate_ip.identifier: candidate_ip = ip if candidate_ip is None: raise LeafException( "Cannot find binary {bin}".format(bin=args.binary)) if env is None: env = Environment.build(wm.build_builtin_environment(), wm.build_user_environment()) env.append( wm.build_packages_environment( DependencyUtils.installed([candidate_ip.identifier], ipmap=ipmap, env=env))) ep = candidate_ip.binaries[args.binary] vr = VariableResolver(candidate_ip, ipmap.values()) return execute_command(vr.resolve(ep.command), *uargs, print_stdout=True, env=env)
def install_packages(self, items: list, env: Environment = None, keep_folder_on_error: bool = False): """ Compute dependency tree, check compatibility, download from remotes and extract needed packages @return: InstalledPackage list """ with self.application_lock.acquire(): ipmap = self.list_installed_packages() apmap = self.list_available_packages() pilist = [] for item in items: if isinstance(item, PackageIdentifier): # Package identifier is given pilist.append(item) elif PackageIdentifier.is_valid_identifier(item): # Package identifier string given pilist.append(PackageIdentifier.parse(item)) else: # If leaf artifacts are given, add/replace identifiers of available packages la = LeafArtifact(Path(item)) pilist.append(la.identifier) apmap[la.identifier] = la out = [] # Build env to resolve dynamic dependencies if env is None: env = Environment.build(self.build_builtin_environment(), self.build_user_environment()) ap_to_install = DependencyUtils.install(pilist, apmap, ipmap, env=env) # Check leaf min version min_version = check_leaf_min_version(ap_to_install) if min_version: raise LeafOutOfDateException( "You need to upgrade leaf to v{version} to install {text}". format(version=min_version, text=", ".join( [str(ap.identifier) for ap in ap_to_install]))) # Check nothing to do if len(ap_to_install) == 0: self.logger.print_default("All packages are installed") else: # Check available size download_totalsize = 0 download_count = 0 for ap in [ ap for ap in ap_to_install if isinstance(ap, AvailablePackage) ]: download_count += 1 if ap.size is not None: download_totalsize += ap.size fs_check_free_space(self.download_cache_folder, download_totalsize) # Confirm text = ", ".join([str(ap.identifier) for ap in ap_to_install]) self.logger.print_quiet( "Packages to install: {packages}".format(packages=text)) if download_totalsize > 0: self.logger.print_default("Total size:", sizeof_fmt(download_totalsize)) self.print_with_confirm(raise_on_decline=True) # Install prereq prereq_to_install = DependencyUtils.prereq( [ap.identifier for ap in ap_to_install], apmap, ipmap, env=env) if len(prereq_to_install) > 0: try: self.__install_prereq( prereq_to_install, ipmap, env=env, keep_folder_on_error=keep_folder_on_error) except BaseException as e: raise PrereqException(e) # Download ap list self.logger.print_default( "Downloading {size} package(s)".format( size=download_count)) la_to_install = [] for mf in ap_to_install: if isinstance(mf, AvailablePackage): la_to_install.append(self.__download_ap(mf)) elif isinstance(mf, LeafArtifact): la_to_install.append(mf) # Check the extracted size extracted_totalsize = 0 for la in la_to_install: if la.final_size is not None: extracted_totalsize += la.final_size else: extracted_totalsize += la.get_total_size() fs_check_free_space(self.install_folder, extracted_totalsize) # Extract la list for la in la_to_install: self.logger.print_default( "[{current}/{total}] Installing {la.identifier}". format(current=(len(out) + 1), total=len(la_to_install), la=la)) ip = self.__extract_artifact( la, env, ipmap, keep_folder_on_error=keep_folder_on_error) out.append(ip) return out
def execute(self, args, uargs): pm = PackageManager() env = None # If the user specified env values, build a complete env if args.custom_envlist is not None: env = Environment.build( pm.build_builtin_environment(), pm.build_user_environment(), Environment("Custom env", env_list_to_map(args.custom_envlist))) items = None if args.dependency_type == "available": items = DependencyUtils.install(PackageIdentifier.parse_list( args.packages), pm.list_available_packages(), {}, env=env) elif args.dependency_type == "install": items = DependencyUtils.install(PackageIdentifier.parse_list( args.packages), pm.list_available_packages(), pm.list_installed_packages(), env=env) elif args.dependency_type == "installed": items = DependencyUtils.installed(PackageIdentifier.parse_list( args.packages), pm.list_installed_packages(), env=env, ignore_unknown=True) elif args.dependency_type == "uninstall": items = DependencyUtils.uninstall(PackageIdentifier.parse_list( args.packages), pm.list_installed_packages(), env=env) elif args.dependency_type == "prereq": items = DependencyUtils.prereq(PackageIdentifier.parse_list( args.packages), pm.list_available_packages(), pm.list_installed_packages(), env=env) elif args.dependency_type == "upgrade": items, _ = DependencyUtils.upgrade( None if len(args.packages) == 0 else args.packages, pm.list_available_packages(), pm.list_installed_packages(), env=env) elif args.dependency_type == "rdepends": mfmap = OrderedDict() mfmap.update( DependencyUtils.rdepends(PackageIdentifier.parse_list( args.packages), pm.list_available_packages(), env=env)) mfmap.update( DependencyUtils.rdepends(PackageIdentifier.parse_list( args.packages), pm.list_installed_packages(), env=env)) items = mfmap.values() else: raise ValueError() rend = ManifestListRenderer() rend.extend(items) pm.print_renderer(rend)
def execute(self, args, uargs): wm = self.get_workspacemanager(check_initialized=False) ipmap = wm.list_installed_packages() searching_iplist = None if args.package is not None: # User forces the package searching_iplist = DependencyUtils.installed( [args.package], ipmap, env=Environment.build(wm.build_builtin_environment(), wm.build_user_environment())) else: # Use installed packages searching_iplist = keep_latest_mf(ipmap.values()) topics = self.get_topics(searching_iplist) if args.topic is not None: topic = self.find_topic(topics, args.topic) fmt = args.format if fmt is None: # No format given if LeafSettings.HELP_DEFAULT_FORMAT.value in topic.resources.keys( ): # Default format is available fmt = LeafSettings.HELP_DEFAULT_FORMAT.value elif len(topic.resources.keys()) == 1: # Only one format available fmt = next(iter(topic.resources.keys())) if fmt is None or fmt not in topic.resources.keys(): # Ensure that this topic is available for needed format raise LeafException( "You need to specify a format for topic '{topic}'".format( topic=args.topic), hints=[ "For example 'leaf help --format {fmt} {topic}'". format(fmt=fmt, topic=args.topic) for fmt in topic.resources.keys() ], ) # Resolve resource path since it can contain @{} variables resource = VariableResolver(topic.installed_package, ipmap.values()).resolve( topic.resources[fmt]) if fmt == "man": # If format is 'man', use manpage reader command = [ "man", "-P", "cat" ] if LeafSettings.NON_INTERACTIVE.as_boolean() else ["man"] command.append(resource) subprocess.check_call(command) else: # Use default resource handler subprocess.check_call( [LeafSettings.HELP_DEFAULT_OPEN.value, resource]) else: # Print mode scope = "installed packages" if args.package is not None: scope = args.package rend = HelpTopicListRenderer(scope, filter_format=args.format) rend.extend(searching_iplist) wm.print_renderer(rend)
def execute(self, args, uargs): pm = PackageManager() wm = self.get_workspacemanager() profilename = wm.current_profile_name profile = wm.get_profile(profilename) env = Environment.build(pm.build_builtin_environment(), pm.build_user_environment()) install_list, upgraded_list = DependencyUtils.upgrade( None if len(args.packages) == 0 else args.packages, pm.list_available_packages(), pm.list_installed_packages(), env=env) pm.logger.print_verbose( "{count} package(s) to be upgraded: {text}".format( count=len(install_list), text=" ".join([str(ap.identifier) for ap in install_list]))) if args.clean: pm.logger.print_verbose( "{count} package(s) to be removed: {text}".format( count=len(upgraded_list), text=" ".join([str(ip.identifier) for ip in upgraded_list]))) if len(install_list) == 0: pm.logger.print_default("No package to upgrade") else: pm.install_packages(map(IDENTIFIER_GETTER, install_list), env=env) if len(upgraded_list) > 0: if args.clean: pm.uninstall_packages(map(IDENTIFIER_GETTER, upgraded_list)) else: pm.logger.print_default( "Packages upgraded:", " ".join([str(ip.identifier) for ip in upgraded_list])) pm.logger.print_default( 'Hint: Use "leaf profile config -p {PACKAGENAME}" to add these packages to your workspace profile' ) update_pilist = install_list profile_pkg_map = profile.pkg_map installed_packages = group_package_identifiers_by_name( wm.list_installed_packages()) pkg_list = args.packages if args.packages else profile_pkg_map.keys() for pkg in pkg_list: pi = None if pkg in installed_packages.keys(): # Get latest version pi = installed_packages[pkg][-1] if pi is not None and pi not in update_pilist: # Get PI in profile previous_pi = PackageIdentifier(pi.name, profile_pkg_map[ pi.name]) if pi.name in profile_pkg_map else None if previous_pi is None: # Package is not in profile yet, add it update_pilist.append(pi) elif previous_pi != pi: # Package is already in profile with a different version, update it update_pilist.append(pi) else: # Package is already in profile with same version, do nothing pass if len(update_pilist) == 0: pm.logger.print_default( "Packages are already in profile with same version") else: pm.logger.print_default( "Packages to be updated in profile:", " ".join([str(pi) for pi in update_pilist])) profile.add_packages(update_pilist) wm.update_profile(profile)