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)
Exemple #3
0
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)
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #6
0
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
Exemple #7
0
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}'.")
Exemple #8
0
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)
Exemple #9
0
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}")
Exemple #10
0
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}."
    )