diff --git a/ethpm_cli/_utils/ipfs.py b/ethpm_cli/_utils/ipfs.py index de41a64..a02ad1a 100644 --- a/ethpm_cli/_utils/ipfs.py +++ b/ethpm_cli/_utils/ipfs.py @@ -1,4 +1,23 @@ +import json +from pathlib import Path +from typing import Tuple + +from eth_typing import URI from ethpm.backends.ipfs import BaseIPFSBackend, InfuraIPFSBackend, LocalIPFSBackend +from ethpm.validation.manifest import validate_manifest_against_schema + + +def pin_local_manifest(manifest_path: Path) -> Tuple[str, str, URI]: + manifest_output = json.loads(manifest_path.read_text()) + validate_manifest_against_schema(manifest_output) + package_name = manifest_output["package_name"] + package_version = manifest_output["version"] + + ipfs_backend = get_ipfs_backend() + ipfs_data = ipfs_backend.pin_assets(manifest_path) + + manifest_uri = URI(f"ipfs://{ipfs_data[0]['Hash']}") + return (package_name, package_version, manifest_uri) def get_ipfs_backend(ipfs: bool = False) -> BaseIPFSBackend: diff --git a/ethpm_cli/parser.py b/ethpm_cli/parser.py index eef0ea2..f465407 100644 --- a/ethpm_cli/parser.py +++ b/ethpm_cli/parser.py @@ -1,9 +1,11 @@ import argparse from pathlib import Path +from typing import Union from eth_utils import humanize_hash from ethpm.constants import SUPPORTED_CHAIN_IDS +from ethpm_cli._utils.ipfs import pin_local_manifest from ethpm_cli._utils.logger import cli_logger from ethpm_cli._utils.solc import compile_contracts, generate_solc_input from ethpm_cli._utils.xdg import get_xdg_ethpmcli_root @@ -108,6 +110,37 @@ def add_alias_arg_to_parser(parser: argparse.ArgumentParser) -> None: ) +def add_manifest_path_to_parser( + parser: Union[argparse.ArgumentParser, argparse._MutuallyExclusiveGroup], + help_msg: str, +) -> None: + parser.add_argument( + "--manifest-path", + dest="manifest_path", + action="store", + type=Path, + help=help_msg, + ) + + +def add_package_name_to_parser(parser: argparse.ArgumentParser, help_msg: str,) -> None: + parser.add_argument( + "--package-name", dest="package_name", action="store", type=str, help=help_msg + ) + + +def add_package_version_to_parser( + parser: argparse.ArgumentParser, help_msg: str, +) -> None: + parser.add_argument( + "--package-version", + dest="package_version", + action="store", + type=str, + help=help_msg, + ) + + parser = argparse.ArgumentParser(description="ethpm-cli") ethpm_parser = parser.add_subparsers(help="CLI commands", dest="command") @@ -117,34 +150,34 @@ def add_alias_arg_to_parser(parser: argparse.ArgumentParser) -> None: # -# todo: extend to pin & release a local manifest def release_cmd(args: argparse.Namespace) -> None: config = Config(args) - release_package(args.package_name, args.package_version, args.manifest_uri, config) + + if args.manifest_path: + (package_name, package_version, manifest_uri) = pin_local_manifest( + args.manifest_path + ) + cli_logger.info( + f"Retrieving manifest info from local file @ {args.manifest_path} " + ) + + release_package(package_name, package_version, manifest_uri, config) + cli_logger.info(f"{package_name} v{package_version} @ {manifest_uri} ") + else: + release_package( + args.package_name, args.package_version, args.manifest_uri, config + ) + cli_logger.info( + f"{args.package_name} v{args.package_version} @ {args.manifest_uri} " + ) + active_registry = get_active_registry(config.xdg_ethpmcli_root / REGISTRY_STORE) - cli_logger.info( - f"{args.package_name} v{args.package_version} @ {args.manifest_uri} " - f"released to registry @ {active_registry.uri}." - ) + cli_logger.info(f"released to registry @ {active_registry.uri}.") release_parser = ethpm_parser.add_parser( "release", help="Release a package on the active registry." ) -release_parser.add_argument( - "--package-name", - dest="package_name", - action="store", - type=str, - help="Package name of package you want to release. Must match `package_name` in manifest.", -) -release_parser.add_argument( - "--package-version", - dest="package_version", - action="store", - type=str, - help="Version of package you want to release. Must match the `version` field in manifest.", -) release_parser.add_argument( "--manifest-uri", dest="manifest_uri", @@ -152,6 +185,18 @@ def release_cmd(args: argparse.Namespace) -> None: type=str, help="Content addressed URI at which the manifest for released package is located.", ) +release_group = release_parser.add_mutually_exclusive_group() +add_manifest_path_to_parser( + release_group, "Local path to target manifest used for release.", +) +add_package_name_to_parser( + release_parser, + "Package name of package you want to release. Must match `package_name` in manifest.", +) +add_package_version_to_parser( + release_parser, + "Version of package you want to release. Must match the `version` field in manifest.", +) add_keyfile_password_arg_to_parser(release_parser) release_parser.set_defaults(func=release_cmd) @@ -334,19 +379,13 @@ def create_basic_cmd(args: argparse.Namespace) -> None: "The generated manifest will package up all available sources and contract types " "available in the solidity compiler output found in given project directory.", ) -create_basic_parser.add_argument( - "--package-name", - dest="package_name", - action="store", - type=str, - help="Package name for generating manifest with `basic` command.", +add_package_name_to_parser( + create_basic_parser, + "Package name for generating manifest with `basic-manifest` command.", ) -create_basic_parser.add_argument( - "--package-version", - dest="package_version", - action="store", - type=str, - help="Package version for generating manifest with `basic` command.", +add_package_version_to_parser( + create_basic_parser, + "Package version for generating manifest with `basic-manifest` command.", ) add_project_dir_arg_to_parser(create_basic_parser) create_basic_parser.set_defaults(func=create_basic_cmd) @@ -365,12 +404,8 @@ def create_basic_cmd(args: argparse.Namespace) -> None: help="Start CLI wizard for building custom manifests from the " "solidity compiler output found in given project directory.", ) -create_wizard_parser.add_argument( - "--manifest-path", - dest="manifest_path", - action="store", - type=Path, - help="Path of target manifest to amend.", +add_manifest_path_to_parser( + create_wizard_parser, "Path of target manifest to amend.", ) add_project_dir_arg_to_parser(create_wizard_parser) create_wizard_parser.set_defaults(func=create_wizard_cmd) @@ -452,26 +487,20 @@ def install_action(args: argparse.Namespace) -> None: type=str, help="IPFS / Github / Etherscan / Registry URI of target package.", ) -install_parser.add_argument( - "--package-name", - dest="package_name", - action="store", - type=str, - help="Package name to use when installing a package from etherscan URIs.", -) -install_parser.add_argument( - "--package-version", - dest="package_version", - action="store", - type=str, - help="Package version to use when installing a package from etherscan URIs.", -) install_parser.add_argument( "--local-ipfs", dest="local_ipfs", action="store_true", help="Flag to use locally running IPFS node rather than defualting to Infura.", ) +add_package_name_to_parser( + install_parser, + "Package name to use when installing a package from etherscan URIs.", +) +add_package_version_to_parser( + install_parser, + "Package version to use when installing a package from etherscan URIs.", +) add_alias_arg_to_parser(install_parser) add_ethpm_dir_arg_to_parser(install_parser) install_parser.set_defaults(func=install_action) diff --git a/tests/core/_utils/test_ipfs.py b/tests/core/_utils/test_ipfs.py new file mode 100644 index 0000000..6af4d54 --- /dev/null +++ b/tests/core/_utils/test_ipfs.py @@ -0,0 +1,14 @@ +import json + +from ethpm_cli._utils.ipfs import pin_local_manifest + + +def test_pin_local_manifest(test_assets_dir): + local_manifest_path = test_assets_dir / "owned" / "1.0.0.json" + expected_manifest = json.loads(local_manifest_path.read_text()) + (package_name, package_version, manifest_uri) = pin_local_manifest( + local_manifest_path + ) + assert package_name == expected_manifest["package_name"] + assert package_version == expected_manifest["version"] + assert manifest_uri == "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW"