Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AEA-242 Local push and publish commands re-implemented to use context generat… #612

Merged
merged 8 commits into from
Jan 10, 2020
49 changes: 44 additions & 5 deletions aea/cli/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
logger = logging.getLogger("aea")
logger = default_logging_config(logger)

DEFAULT_VERSION = "0.1.0"
DEFAULT_REGISTRY_PATH = str(Path("./", "packages"))
DEFAULT_CONNECTION = PublicId.from_string("fetchai/stub:0.1.0") # type: PublicId
DEFAULT_SKILL = PublicId.from_string("fetchai/error:0.1.0") # type: PublicId
Expand Down Expand Up @@ -101,18 +102,20 @@ def get_dependencies(self) -> Dependencies:
pass_ctx = click.make_pass_decorator(Context)


def _try_to_load_agent_config(ctx: Context):
def _try_to_load_agent_config(ctx: Context, exit_on_except: bool = True):
try:
path = Path(DEFAULT_AEA_CONFIG_FILE)
fp = open(str(path), mode="r", encoding="utf-8")
ctx.agent_config = ctx.agent_loader.load(fp)
logging.config.dictConfig(ctx.agent_config.logging_config)
except FileNotFoundError:
logger.error("Agent configuration file '{}' not found in the current directory.".format(DEFAULT_AEA_CONFIG_FILE))
sys.exit(1)
if exit_on_except:
logger.error("Agent configuration file '{}' not found in the current directory.".format(DEFAULT_AEA_CONFIG_FILE))
sys.exit(1)
except jsonschema.exceptions.ValidationError:
logger.error("Agent configuration file '{}' is invalid. Please check the documentation.".format(DEFAULT_AEA_CONFIG_FILE))
sys.exit(1)
if exit_on_except:
logger.error("Agent configuration file '{}' is invalid. Please check the documentation.".format(DEFAULT_AEA_CONFIG_FILE))
sys.exit(1)


def _try_to_load_protocols(ctx: Context):
Expand Down Expand Up @@ -248,3 +251,39 @@ def convert(self, value, param, ctx):
return PublicId.from_string(value)
except ValueError:
raise click.BadParameter(value)


def try_get_item_source_path(path: str, item_type_plural: str, item_name: str) -> str:
"""
Get the item source path.

:param path: the source path root
:param item_type_plural: the item type (plural)
:param item_name: the item name

:return: the item source path
"""
source_path = os.path.join(path, item_type_plural, item_name)
if not os.path.exists(source_path):
raise click.ClickException(
'Item "{}" not found in source folder.'.format(item_name)
)
return source_path


def try_get_item_target_path(path: str, item_type_plural: str, item_name: str) -> str:
"""
Get the item target path.

:param path: the target path root
:param item_type_plural: the item type (plural)
:param item_name: the item name

:return: the item target path
"""
target_path = os.path.join(path, item_type_plural, item_name)
if os.path.exists(target_path):
raise click.ClickException(
'Item "{}" already exists in target folder.'.format(item_name)
)
return target_path
2 changes: 1 addition & 1 deletion aea/cli/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ def delete(ctx: Context, agent_name):
"""Delete an agent."""
path = Path(agent_name)

# check that the target folder is an AEA project.
cwd = os.getcwd()
try:
# check that the target folder is an AEA project.
os.chdir(agent_name)
fp = open(DEFAULT_AEA_CONFIG_FILE, mode="r", encoding="utf-8")
ctx.agent_config = ctx.agent_loader.load(fp)
Expand Down
4 changes: 2 additions & 2 deletions aea/cli/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

import aea
from aea.cli.add import connection, skill
from aea.cli.common import Context, logger, DEFAULT_REGISTRY_PATH, DEFAULT_CONNECTION, DEFAULT_SKILL, DEFAULT_LEDGER
from aea.cli.common import Context, logger, DEFAULT_REGISTRY_PATH, DEFAULT_CONNECTION, DEFAULT_SKILL, DEFAULT_LEDGER, DEFAULT_VERSION
from aea.configurations.base import DEFAULT_AEA_CONFIG_FILE, AgentConfig


Expand Down Expand Up @@ -76,7 +76,7 @@ def create(click_context, agent_name):
logger.info("Creating config file {}".format(DEFAULT_AEA_CONFIG_FILE))
config_file = open(os.path.join(agent_name, DEFAULT_AEA_CONFIG_FILE), "w")
agent_config = AgentConfig(agent_name=agent_name, aea_version=aea.__version__,
author="", version="0.1.0", license="", fingerprint="",
author="", version=DEFAULT_VERSION, license="", fingerprint="",
registry_path=os.path.join("..", DEFAULT_REGISTRY_PATH), description="")
agent_config.default_connection = DEFAULT_CONNECTION
agent_config.default_ledger = DEFAULT_LEDGER
Expand Down
35 changes: 30 additions & 5 deletions aea/cli/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,44 @@

"""Implementation of the 'aea fetch' subcommand."""
import click
from distutils.dir_util import copy_tree
import os

from aea.cli.common import PublicIdParameter
from aea.cli.registry.fetch import fetch_agent, fetch_agent_locally
from aea.cli.common import Context, pass_ctx, PublicIdParameter, DEFAULT_REGISTRY_PATH, try_get_item_source_path
from aea.cli.registry.fetch import fetch_agent
from aea.configurations.base import PublicId


@click.command(name='fetch')
@click.option(
'--registry', is_flag=True, help="For fetching agent from Registry."
)
@click.argument('public-id', type=PublicIdParameter(), required=True)
def fetch(public_id, registry):
@pass_ctx
def fetch(ctx: Context, public_id, registry):
"""Fetch Agent from Registry."""
if not registry:
fetch_agent_locally(public_id)
_fetch_agent_locally(ctx, public_id)
else:
fetch_agent(public_id)
fetch_agent(ctx, public_id)


def _fetch_agent_locally(ctx: Context, public_id: PublicId) -> None:
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
"""
Fetch Agent from local packages.

:param ctx: Context
:param public_id: public ID of agent to be fetched.

:return: None
"""
packages_path = os.path.basename(DEFAULT_REGISTRY_PATH)
source_path = try_get_item_source_path(packages_path, 'agents', public_id.name)
target_path = os.path.join(ctx.cwd, public_id.name)
if os.path.exists(target_path):
raise click.ClickException(
'Item "{}" already exists in target folder.'.format(public_id.name)
)
copy_tree(source_path, target_path)
click.echo('Agent {} successfully fetched.'.format(public_id.name))
# TODO: install all dependencies recursively
32 changes: 29 additions & 3 deletions aea/cli/publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,43 @@

"""Implementation of the 'aea publish' subcommand."""
import click
import os
from shutil import copyfile

from aea.cli.registry.publish import publish_agent, save_agent_locally
from aea.cli.common import pass_ctx, Context, _try_to_load_agent_config, try_get_item_target_path, DEFAULT_AEA_CONFIG_FILE
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
from aea.cli.registry.publish import publish_agent


@click.command(name='publish')
@click.option(
'--registry', is_flag=True, help="For publishing agent to Registry."
)
def publish(registry):
@pass_ctx
def publish(ctx: Context, registry):
"""Publish Agent to Registry."""
_try_to_load_agent_config(ctx)
if not registry:
save_agent_locally()
# TODO: check agent dependencies are available in local packages dir.
_save_agent_locally(ctx)
else:
publish_agent()


def _save_agent_locally(ctx: Context) -> None:
"""
Save agent to local packages.

:param ctx: the context

:return: None
"""
item_type_plural = 'agents'

target_dir = try_get_item_target_path(ctx.agent_config.registry_path, item_type_plural, ctx.agent_config.name)
if not os.path.exists(target_dir):
os.makedirs(target_dir, exist_ok=True)

source_path = os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE)
target_path = os.path.join(target_dir, DEFAULT_AEA_CONFIG_FILE)
copyfile(source_path, target_path)
click.echo('Agent "{}" successfully saved in packages folder.'.format(ctx.agent_config.name))
51 changes: 43 additions & 8 deletions aea/cli/push.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

"""Implementation of the 'aea push' subcommand."""
import click
from shutil import copytree

from aea.cli.common import pass_ctx, Context, PublicIdParameter
from aea.cli.registry.push import push_item, save_item_locally
from aea.cli.common import pass_ctx, Context, PublicIdParameter, try_get_item_target_path, try_get_item_source_path, _try_to_load_agent_config
from aea.cli.registry.push import push_item
from aea.configurations.base import PublicId


@click.group()
Expand All @@ -31,6 +33,7 @@
@pass_ctx
def push(ctx: Context, registry):
"""Push item to Registry or save it in local packages."""
_try_to_load_agent_config(ctx)
ctx.set_config("registry", registry)


Expand All @@ -40,9 +43,9 @@ def push(ctx: Context, registry):
def connection(ctx: Context, connection_id):
"""Push connection to Registry or save it in local packages."""
if not ctx.config.get("registry"):
save_item_locally('connection', connection_id)
_save_item_locally(ctx, 'connection', connection_id)
else:
push_item('connection', connection_id.name)
push_item(ctx, 'connection', connection_id)


@push.command(name='protocol')
Expand All @@ -51,9 +54,9 @@ def connection(ctx: Context, connection_id):
def protocol(ctx: Context, protocol_id):
"""Push protocol to Registry or save it in local packages."""
if not ctx.config.get("registry"):
save_item_locally('protocol', protocol_id)
_save_item_locally(ctx, 'protocol', protocol_id)
else:
push_item('protocol', protocol_id.name)
push_item(ctx, 'protocol', protocol_id)


@push.command(name='skill')
Expand All @@ -62,6 +65,38 @@ def protocol(ctx: Context, protocol_id):
def skill(ctx: Context, skill_id):
"""Push skill to Registry or save it in local packages."""
if not ctx.config.get("registry"):
save_item_locally('skill', skill_id)
_save_item_locally(ctx, 'skill', skill_id)
else:
push_item('skill', skill_id.name)
push_item(ctx, 'skill', skill_id)


def _save_item_locally(ctx: Context, item_type: str, item_id: PublicId) -> None:
"""
Save item to local packages.

:param item_type: str type of item (connection/protocol/skill).
:param item_id: the public id of the item.
:param packages_path: path to packages dir

:return: None
"""
item_type_plural = item_type + 's'

source_path = try_get_item_source_path(ctx.cwd, item_type_plural, item_id.name)
target_path = try_get_item_target_path(ctx.agent_config.registry_path, item_type_plural, item_id.name)
# _check_package_public_id(source_path, item_type, item_id)
copytree(source_path, target_path)
click.echo('{} "{}" successfully saved in packages folder.'.format(item_type.title(), item_id))


# TODO: clarify whether this is indeed needed
# def _check_package_public_id(source_path, item_type, item_id):
# config = load_yaml(os.path.join(source_path, item_type + ".yaml"))
# item_author = config.get("author", "")
# item_name = config.get("name", "")
# item_version = config.get("version", "")
# if item_id.name != item_name or item_id.author != item_author or item_id.version != item_version:
# raise click.ClickException(
# "Version or author do not match. Expected '{}', found '{}'"
# .format(item_id, item_author + "/" + item_name + ":" + item_version)
# )
46 changes: 4 additions & 42 deletions aea/cli/registry/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,32 @@
#
# ------------------------------------------------------------------------------
"""Methods for CLI fetch functionality."""
from typing import Union

import click
import os

from distutils.dir_util import copy_tree
from shutil import rmtree

from aea.cli.common import DEFAULT_REGISTRY_PATH
from aea.cli.common import Context
from aea.cli.registry.utils import (
request_api, download_file, extract, fetch_package
)
from aea.configurations.base import PublicId


def fetch_agent(public_id: Union[PublicId, str]) -> None:
def fetch_agent(ctx: Context, public_id: PublicId) -> None:
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
"""
Fetch Agent from Registry.

:param public_id: str public ID of desirable Agent.

:return: None
"""
if isinstance(public_id, str):
public_id = PublicId.from_string(public_id)
author, name, version = public_id.author, public_id.name, public_id.version
api_path = '/agents/{}/{}/{}'.format(author, name, version)
resp = request_api('GET', api_path)
file_url = resp['file']

cwd = os.getcwd()
target_folder = os.path.join(cwd, name)
target_folder = os.path.join(ctx.cwd, name)

for item_type in ('connection', 'skill', 'protocol'):
item_type_plural = item_type + 's'
Expand All @@ -62,41 +56,9 @@ def fetch_agent(public_id: Union[PublicId, str]) -> None:
.format(name, e)
)

filepath = download_file(file_url, cwd)
filepath = download_file(file_url, ctx.cwd)
extract(filepath, target_folder)
click.echo(
'Agent {} successfully fetched to {}.'
.format(name, target_folder)
)


def _get_agent_source_path(item_name: str) -> str:
packages_path = os.path.basename(DEFAULT_REGISTRY_PATH)
target_path = os.path.join(packages_path, 'agents', item_name)
if not os.path.exists(target_path):
raise click.ClickException(
'Agent "{}" not found in packages folder.'.format(item_name)
)
return target_path


def fetch_agent_locally(public_id: Union[PublicId, str]) -> None:
"""
Fetch Agent from local packages.

:param public_id: str public ID of desirable Agent.

:return: None
"""
if isinstance(public_id, str):
public_id = PublicId.from_string(public_id)

name = public_id.name
source_dir = _get_agent_source_path(name)
cwd = os.getcwd()
target_dir = os.path.join(cwd, name)
copy_tree(source_dir, target_dir)
click.echo(
'Agent {} successfully saved in {}.'
.format(name, cwd)
)
Loading