Exemple #1
0
def update_aea_version_specifiers(old_version: Version, new_version: Version) -> bool:
    """
    Update aea_version specifier set in docs.

    :param old_version: the old version.
    :param new_version: the new version.
    :return: True if the update has been done, False otherwise.
    """
    old_specifier_set = compute_specifier_from_version(old_version)
    new_specifier_set = compute_specifier_from_version(new_version)
    print(f"Old version specifier: {old_specifier_set}")
    print(f"New version specifier: {new_specifier_set}")
    old_specifier_set_regex = re.compile(str(old_specifier_set).replace(" ", " *"))
    if old_specifier_set == new_specifier_set:
        print("Not updating version specifier - they haven't changed.")
        return False
    for file in filter(lambda p: not p.is_dir(), Path(".").rglob("*")):
        dir_root = Path(file.parts[0])
        if dir_root in IGNORE_DIRS:
            print(f"Skipping '{file}'...")
            continue
        print(
            f"Replacing '{old_specifier_set}' with '{new_specifier_set}' in '{file}'... ",
            end="",
        )
        try:
            content = file.read_text()
        except UnicodeDecodeError as e:
            print(f"Cannot read {file}: {str(e)}. Continue...")
        else:
            if old_specifier_set_regex.search(content) is not None:
                content = old_specifier_set_regex.sub(new_specifier_set, content)
                file.write_text(content)
    return True
Exemple #2
0
def test_compute_specifier_from_version():
    """Test function 'compute_specifier_from_version'."""

    version = "0.1.5"
    expected_range = ">=0.1.0, <0.2.0"
    assert expected_range == compute_specifier_from_version(Version(version))

    version = "1.1.5"
    expected_range = ">=1.1.0, <1.2.0"
    assert expected_range == compute_specifier_from_version(Version(version))
Exemple #3
0
def _update_agent_config(ctx: Context):
    """
    Update agent configurations.

    :param ctx: the context.
    :return: None
    """
    # update aea_version in case current framework version is different
    version = Version(aea.__version__)
    if not ctx.agent_config.aea_version_specifiers.contains(version):
        new_aea_version = compute_specifier_from_version(version)
        old_aea_version = ctx.agent_config.aea_version
        click.echo(
            f"Updating AEA version specifier from {old_aea_version} to {new_aea_version}."
        )
        ctx.agent_config.aea_version = new_aea_version

    # update author name if it is different
    cli_author = ctx.config.get("cli_author")
    if cli_author and ctx.agent_config.author != cli_author:
        click.echo(
            f"Updating author from {ctx.agent_config.author} to {cli_author}")
        ctx.agent_config._author = cli_author  # pylint: disable=protected-access
        ctx.agent_config.version = DEFAULT_VERSION

    ctx.dump_agent_config()
Exemple #4
0
def _crete_agent_config(ctx: Context, agent_name: str, set_author: str) -> AgentConfig:
    """
    Create agent config.

    :param ctx: context object.
    :param agent_name: agent name.
    :param set_author: author name to set.

    :return: AgentConfig object.
    """
    agent_config = AgentConfig(
        agent_name=agent_name,
        aea_version=compute_specifier_from_version(Version(aea.__version__)),
        author=set_author,
        version=DEFAULT_VERSION,
        license_=DEFAULT_LICENSE,
        registry_path=os.path.join("..", DEFAULT_REGISTRY_PATH),
        description="",
        default_ledger=DEFAULT_LEDGER,
        default_connection=str(PublicId.from_str(DEFAULT_CONNECTION).to_any()),
    )

    with open(os.path.join(agent_name, DEFAULT_AEA_CONFIG_FILE), "w") as config_file:
        ctx.agent_loader.dump(agent_config, config_file)

    return agent_config
Exemple #5
0
def update_aea_version_range(
        package_configuration: PackageConfiguration) -> None:
    """Update 'aea_version' range."""
    version = get_current_aea_version()
    if not package_configuration.aea_version_specifiers.contains(version):
        new_aea_version = compute_specifier_from_version(version)
        old_aea_version = package_configuration.aea_version
        click.echo(
            f"Updating AEA version specifier from {old_aea_version} to {new_aea_version}."
        )
        package_configuration.aea_version = new_aea_version
Exemple #6
0
    def assert_dependency_updated(
        self,
        item_type: ComponentType,
        package_name: str,
        package_type: str,
        expected: Set[PublicId],
    ):
        """Assert dependency is updated."""
        package_path = Path(self._get_cwd(), item_type.to_plural(), package_name)
        component_config = load_component_configuration(item_type, package_path)
        assert hasattr(component_config, package_type), "Test is not well-written."
        assert getattr(component_config, package_type) == expected  # type: ignore

        expected_version_range = compute_specifier_from_version(
            get_current_aea_version()
        )
        assert component_config.aea_version == expected_version_range
Exemple #7
0
    def test_nothing_to_upgrade(self, mock_click_echo):
        """Test nothing to upgrade, and additionally, that 'aea_version' is correct."""
        result = self.run_cli_command("upgrade", cwd=self._get_cwd())
        assert result.exit_code == 0
        mock_click_echo.assert_any_call("Starting project upgrade...")
        mock_click_echo.assert_any_call(
            "Updating AEA version specifier from ==0.1.0 to >=0.10.0, <0.11.0."
        )

        # test 'aea_version' of agent configuration is upgraded
        expected_aea_version_specifier = compute_specifier_from_version(
            get_current_aea_version()
        )
        agent_config = self.load_agent_config(self.current_agent_context)
        assert agent_config.aea_version == expected_aea_version_specifier
        assert agent_config.author == self.author
        assert agent_config.version == DEFAULT_VERSION
Exemple #8
0
class TestUpgradeNonVendorDependencies(AEATestCaseEmpty):
    """
    Test that the command 'aea upgrade' correctly updates non-vendor package data.

    In particular, check that 'aea upgrade' updates:
    - the public ids of the package dependencies and the 'aea_version' field.
    - the 'aea_version' field in case it is not compatible with the current version.

    The test works as follows:
    - scaffold a package, one for each possible package type;
    - add the protocol "fetchai/default:0.11.0" as dependency to each of them.
    - add the skill "fetchai/error:0.11.0"; this will also add the default protocol.
      add it also as dependency of non-vendor skill.
    - run 'aea upgrade'
    - check that the reference to "fetchai/default" in each scaffolded package
      has the new version.
    """

    capture_log = True
    IS_EMPTY = True
    old_default_protocol_id = PublicId(
        DefaultMessage.protocol_id.author, DefaultMessage.protocol_id.name, "0.11.0"
    )
    old_error_skill_id = PublicId(
        ERROR_SKILL_PUBLIC_ID.author, ERROR_SKILL_PUBLIC_ID.name, "0.11.0"
    )
    old_aea_version_range = compute_specifier_from_version(Version("0.1.0"))

    @classmethod
    def scaffold_item(
        cls, item_type: str, name: str, skip_consistency_check: bool = False
    ) -> Result:
        """Override default behaviour by adding a custom dependency to the scaffolded item."""
        result = super(TestUpgradeNonVendorDependencies, cls).scaffold_item(
            item_type, name, skip_consistency_check
        )
        # add custom dependency (a protocol) to each package
        # that supports dependencies (only connections and skills)
        if item_type in {ComponentType.CONNECTION.value, ComponentType.SKILL.value}:
            cls.nested_set_config(
                f"{ComponentType(item_type).to_plural()}.{name}.protocols",
                [str(cls.old_default_protocol_id)],
            )

        # add the vendor skill as dependency of the non-vendor skill
        if item_type == ComponentType.SKILL.value:
            cls.nested_set_config(
                f"{ComponentType(item_type).to_plural()}.{name}.skills",
                [str(cls.old_error_skill_id)],
            )

        # update 'aea_version' to an old version range.
        cls.nested_set_config(
            f"{ComponentType(item_type).to_plural()}.{name}.aea_version",
            str(cls.old_aea_version_range),
        )
        return result

    @classmethod
    def setup_class(cls):
        """Set up test case."""
        super(TestUpgradeNonVendorDependencies, cls).setup_class()
        cls.scaffold_item("protocol", "my_protocol", skip_consistency_check=True)
        cls.scaffold_item("connection", "my_connection", skip_consistency_check=True)
        cls.scaffold_item("contract", "my_contract", skip_consistency_check=True)
        cls.scaffold_item("skill", "my_skill", skip_consistency_check=True)
        cls.run_cli_command(
            "--skip-consistency-check",
            "add",
            "skill",
            str(cls.old_error_skill_id),
            cwd=cls._get_cwd(),
        )
        cls.run_cli_command(
            "--skip-consistency-check", "upgrade", "--local", cwd=cls._get_cwd()
        )

    def test_agent_config_updated(self):
        """Test the agent configuration is updated."""
        loader = ConfigLoader.from_configuration_type(PackageType.AGENT)
        with Path(self._get_cwd(), DEFAULT_AEA_CONFIG_FILE).open() as fp:
            agent_config = loader.load(fp)
        assert DefaultMessage.protocol_id in agent_config.protocols
        assert ERROR_SKILL_PUBLIC_ID in agent_config.skills

    def test_non_vendor_update_references_to_upgraded_packages(
        self,
    ):  # pylint: disable=unused-argument
        """Test that dependencies in non-vendor packages are updated correctly after upgrade."""
        self.assert_dependency_updated(
            ComponentType.CONNECTION,
            "my_connection",
            "protocols",
            {DefaultMessage.protocol_id},
        )
        self.assert_dependency_updated(
            ComponentType.SKILL, "my_skill", "protocols", {DefaultMessage.protocol_id}
        )
        self.assert_dependency_updated(
            ComponentType.SKILL, "my_skill", "skills", {ERROR_SKILL_PUBLIC_ID}
        )

    def assert_dependency_updated(
        self,
        item_type: ComponentType,
        package_name: str,
        package_type: str,
        expected: Set[PublicId],
    ):
        """Assert dependency is updated."""
        package_path = Path(self._get_cwd(), item_type.to_plural(), package_name)
        component_config = load_component_configuration(item_type, package_path)
        assert hasattr(component_config, package_type), "Test is not well-written."
        assert getattr(component_config, package_type) == expected  # type: ignore

        expected_version_range = compute_specifier_from_version(
            get_current_aea_version()
        )
        assert component_config.aea_version == expected_version_range
Exemple #9
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}."
    )