def update_references(ctx: Context, replacements: Dict[ComponentId, ComponentId]): """ Update references across an AEA project. Caveat: the update is done in a sequential manner. There is no check of multiple updates, due to the occurrence of transitive relations. E.g. replacements as {c1: c2, c2: c3} might lead to c1 replaced with c3 instead of c2. :param ctx: the context. :param replacements: mapping from old component ids to new component ids. :return: None. """ # preprocess replacement so to index them by component type replacements_by_type: Dict[ComponentType, Dict[PublicId, PublicId]] = {} for old, new in replacements.items(): replacements_by_type.setdefault(old.component_type, {})[old.public_id] = new.public_id aea_project_root = Path(ctx.cwd) # update agent configuration agent_config = load_item_config(PackageType.AGENT.value, aea_project_root) replace_component_ids(agent_config, replacements_by_type) dump_item_config(agent_config, aea_project_root) # update every (non-vendor) AEA package. for package_path in get_non_vendor_package_path(aea_project_root): package_type = PackageType(package_path.parent.name[:-1]) package_config = load_item_config(package_type.value, package_path) replace_component_ids(package_config, replacements_by_type) dump_item_config(package_config, package_path)
def fingerprint_all(ctx: Context) -> None: """ Fingerprint all non-vendor packages. :param ctx: the CLI context. :return: None """ aea_project_path = Path(ctx.cwd) for package_path in get_non_vendor_package_path(aea_project_path): item_type = package_path.parent.name[:-1] config = load_item_config(item_type, package_path) fingerprint_item(ctx, item_type, config.public_id)
def update_aea_version_in_nonvendor_packages(cwd: str) -> None: """ Update aea_version in non-vendor packages. :param cwd: the current working directory. :return: None """ for package_path in get_non_vendor_package_path(Path(cwd)): package_type = PackageType(package_path.parent.name[:-1]) package_config = load_item_config(package_type.value, package_path) update_aea_version_range(package_config) dump_item_config(package_config, package_path)
def test_upgrade_shared_dependencies(self): """Test upgrade shared dependencies.""" result = self.run_cli_command("-s", "upgrade", cwd=self._get_cwd()) assert result.exit_code == 0 agent_config: AgentConfig = cast( AgentConfig, load_item_config(PackageType.AGENT.value, Path(self.current_agent_context)), ) assert OefSearchMessage.protocol_id in agent_config.protocols assert SOEF_PUBLIC_ID in agent_config.connections assert OEF_PUBLIC_ID in agent_config.connections
def get_item_config(cls, package_id: PackageId) -> PackageConfiguration: """Get item config for item,_type and public_id.""" item_config = load_item_config( str(package_id.package_type), package_path=cls.get_component_directory(package_id), ) if (package_id.author != item_config.author) or (package_id.name != item_config.name): raise click.ClickException( f"Error loading {package_id} configuration, author/name do not match: {item_config.public_id}" ) return item_config
def _compute_replacements( ctx: Context, old_component_ids: Set[ComponentId]) -> Dict[ComponentId, ComponentId]: """Compute replacements from old component ids to new components ids.""" agent_config = load_item_config(PackageType.AGENT.value, Path(ctx.cwd)) new_component_ids = list(agent_config.package_dependencies) replacements: Dict[ComponentId, ComponentId] = dict() for old_component_id in old_component_ids: same_prefix = list( filter(old_component_id.same_prefix, new_component_ids)) enforce(len(same_prefix) < 2, "More than one component id found.") if len(same_prefix) > 0: replacements[old_component_id] = same_prefix[0] return replacements
def add_item(ctx: Context, item_type: str, item_public_id: PublicId) -> None: """ Add an item. :param ctx: Context object. :param item_type: the item type. :param item_public_id: the item public id. :return: None """ click.echo(f"Adding {item_type} '{item_public_id}'...") if is_item_present(ctx.cwd, ctx.agent_config, item_type, item_public_id): present_item_id = get_item_id_present(ctx.agent_config, item_type, item_public_id) raise click.ClickException( "A {} with id '{}' already exists. Aborting...".format( item_type, present_item_id)) dest_path = get_package_path(ctx.cwd, item_type, item_public_id) is_local = ctx.config.get("is_local") is_mixed = ctx.config.get("is_mixed") ctx.clean_paths.append(dest_path) if is_mixed: package_path = fetch_item_mixed(ctx, item_type, item_public_id, dest_path) elif is_local: package_path = find_item_locally_or_distributed( ctx, item_type, item_public_id, dest_path) else: package_path = fetch_package(item_type, public_id=item_public_id, cwd=ctx.cwd, dest=dest_path) item_config = load_item_config(item_type, package_path) if not ctx.config.get( "skip_consistency_check") and not is_fingerprint_correct( package_path, item_config): # pragma: no cover raise click.ClickException( "Failed to add an item with incorrect fingerprint.") _add_item_deps(ctx, item_type, item_config) register_item(ctx, item_type, item_config.public_id) click.echo(f"Successfully added {item_type} '{item_config.public_id}'.")
def add_item(ctx: Context, item_type: str, item_public_id: PublicId) -> None: """ Add an item. :param ctx: Context object. :param item_type: the item type. :param item_public_id: the item public id. :return: None """ agent_name = cast(str, ctx.agent_config.agent_name) click.echo("Adding {} '{}' to the agent '{}'...".format( item_type, item_public_id, agent_name)) if is_item_present(ctx, item_type, item_public_id): raise click.ClickException( "A {} with id '{}/{}' already exists. Aborting...".format( item_type, item_public_id.author, item_public_id.name)) dest_path = get_package_path(ctx, item_type, item_public_id) is_local = ctx.config.get("is_local") ctx.clean_paths.append(dest_path) if item_public_id in [DEFAULT_CONNECTION, *LOCAL_PROTOCOLS, DEFAULT_SKILL]: source_path = find_item_in_distribution(ctx, item_type, item_public_id) package_path = copy_package_directory(source_path, dest_path) elif is_local: source_path = find_item_locally(ctx, item_type, item_public_id) package_path = copy_package_directory(source_path, dest_path) else: package_path = fetch_package(item_type, public_id=item_public_id, cwd=ctx.cwd, dest=dest_path) item_config = load_item_config(item_type, package_path) if not is_fingerprint_correct(package_path, item_config): # pragma: no cover raise click.ClickException( "Failed to add an item with incorrect fingerprint.") _add_item_deps(ctx, item_type, item_config) register_item(ctx, item_type, item_public_id)
def _try_get_connection_multiaddress( click_context, crypto: Crypto, connection_id: PublicId, host_field: Optional[str], port_field: Optional[str], uri_field: str, ) -> str: """ Try to get the connection multiaddress. The host and the port options have the precedence over the uri option. :param click_context: the click context object. :param crypto: the crypto. :param connection_id: the connection id. :param host_field: the host field. :param port_field: the port field. :param uri_field: the uri field. :return: the multiaddress. """ ctx = cast(Context, click_context.obj) if connection_id not in ctx.agent_config.connections: raise ValueError(f"Cannot find connection with the public id {connection_id}.") package_path = Path( get_package_path_unified(ctx.cwd, ctx.agent_config, CONNECTION, connection_id) ) connection_config = cast( ConnectionConfig, load_item_config(CONNECTION, package_path) ) host, port = _read_host_and_port_from_config( connection_config, uri_field, host_field, port_field ) try: multiaddr = MultiAddr(host, port, crypto.public_key) return multiaddr.format() except Exception as e: raise ClickException(f"An error occurred while creating the multiaddress: {e}")
def _eject_item( ctx: Context, item_type: str, public_id: PublicId, quiet: bool = True, with_symlinks: bool = False, ) -> None: """ Eject item from installed (vendor) to custom folder. :param ctx: context object. :param item_type: item type. :param public_id: item public ID. :param quiet: if false, the function will ask the user in case of recursive eject. :return: None :raises: ClickException if item is absent at source path or present at destenation path. """ # we know cli_author is set because of the above checks. cli_author: str = cast(str, ctx.config.get("cli_author")) item_type_plural = item_type + "s" if not is_item_present( ctx.cwd, ctx.agent_config, item_type, public_id, is_vendor=True, with_version=True, ): # pragma: no cover raise click.ClickException( f"{item_type.title()} {public_id} not found in agent's vendor items." ) src = get_package_path(ctx.cwd, item_type, public_id) dst = get_package_path(ctx.cwd, item_type, public_id, is_vendor=False) if is_item_present(ctx.cwd, ctx.agent_config, item_type, public_id, is_vendor=False): # pragma: no cover raise click.ClickException( f"{item_type.title()} {public_id} is already a non-vendor package." ) configuration = load_item_config(item_type, Path(src)) if public_id.package_version.is_latest: # get 'concrete' public id, in case it is 'latest' component_prefix = ComponentType( item_type), public_id.author, public_id.name component_id = get_latest_component_id_from_prefix( ctx.agent_config, component_prefix) # component id is necessarily found, due to the checks above. public_id = cast(ComponentId, component_id).public_id package_id = PackageId(PackageType(item_type), public_id) click.echo( f"Ejecting item {package_id.package_type.value} {str(package_id.public_id)}" ) # first, eject all the vendor packages that depend on this item_remover = ItemRemoveHelper(ctx, ignore_non_vendor=True) reverse_dependencies = ( item_remover.get_agent_dependencies_with_reverse_dependencies()) reverse_reachable_dependencies = reachable_nodes(reverse_dependencies, {package_id}) # the reversed topological order of a graph # is the topological order of the reverse graph. eject_order = list( reversed(find_topological_order(reverse_reachable_dependencies))) eject_order.remove(package_id) if len(eject_order) > 0 and not quiet: click.echo( f"The following vendor packages will be ejected: {eject_order}") answer = click.confirm("Do you want to proceed?") if not answer: click.echo("Aborted.") return for dependency_package_id in eject_order: # 'dependency_package_id' depends on 'package_id', # so we need to eject it first _eject_item( ctx, dependency_package_id.package_type.value, dependency_package_id.public_id, quiet=True, ) # copy the vendor package into the non-vendor packages ctx.clean_paths.append(dst) copy_package_directory(Path(src), dst) new_public_id = PublicId(cli_author, public_id.name, DEFAULT_VERSION) current_version = Version(aea.__version__) new_aea_range = ( configuration.aea_version if configuration.aea_version_specifiers.contains(current_version) else compute_specifier_from_version(current_version)) update_item_config( item_type, Path(dst), author=new_public_id.author, version=new_public_id.version, aea_version=new_aea_range, ) update_item_public_id_in_init(item_type, Path(dst), new_public_id) shutil.rmtree(src) # update references in all the other packages component_type = ComponentType(item_type_plural[:-1]) old_component_id = ComponentId(component_type, public_id) new_component_id = ComponentId(component_type, new_public_id) update_references(ctx, {old_component_id: new_component_id}) # need to reload agent configuration with the updated references try_to_load_agent_config(ctx) # replace import statements in all the non-vendor packages replace_all_import_statements(Path(ctx.cwd), ComponentType(item_type), public_id, new_public_id) # fingerprint all (non-vendor) packages fingerprint_all(ctx) if with_symlinks: click.echo( "Adding symlinks from vendor to non-vendor and packages to vendor folders." ) create_symlink_vendor_to_local(ctx, item_type, new_public_id) create_symlink_packages_to_vendor(ctx) click.echo( f"Successfully ejected {item_type} {public_id} to {dst} as {new_public_id}." )