diff --git a/HISTORY.rst b/HISTORY.rst index 36d0c4fa..94bb9be7 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -34,6 +34,7 @@ PlatformIO Core 5 * Show package details * Check for conflicting names in the PlatformIO Trusted Registry * Check for duplicates and used version + * Validate package manifest - Added a new option ``--non-interactive`` to `pio package publish `__ command @@ -235,24 +236,24 @@ Please check `Migration guide from 4.x to 5.0 `__. +See `PlatformIO Core 4.0 history `__. PlatformIO Core 3 ----------------- -See `PlatformIO Core 3.0 history `__. +See `PlatformIO Core 3.0 history `__. PlatformIO Core 2 ----------------- -See `PlatformIO Core 2.0 history `__. +See `PlatformIO Core 2.0 history `__. PlatformIO Core 1 ----------------- -See `PlatformIO Core 1.0 history `__. +See `PlatformIO Core 1.0 history `__. PlatformIO Core Preview ----------------------- -See `PlatformIO Core Preview history `__. +See `PlatformIO Core Preview history `__. diff --git a/platformio/commands/package.py b/platformio/commands/package.py index 861de42d..1cc0cfb6 100644 --- a/platformio/commands/package.py +++ b/platformio/commands/package.py @@ -24,6 +24,7 @@ from platformio.clients.account import AccountClient from platformio.clients.registry import RegistryClient from platformio.exception import UserSideException from platformio.package.manifest.parser import ManifestParserFactory +from platformio.package.manifest.schema import ManifestSchema, ManifestValidationError from platformio.package.meta import PackageSpec, PackageType from platformio.package.pack import PackagePacker from platformio.package.unpack import FileUnpacker, TARArchiver @@ -39,6 +40,45 @@ def validate_datetime(ctx, param, value): # pylint: disable=unused-argument return value +def load_manifest_from_archive(path): + return ManifestSchema().load_manifest( + ManifestParserFactory.new_from_archive(path).as_dict() + ) + + +def check_package_duplicates( + owner, type, name, version +): # pylint: disable=redefined-builtin + items = ( + RegistryClient() + .list_packages(filters=dict(types=[type], names=[name])) + .get("items") + ) + if not items: + return True + # duplicated version by owner + if any( + item["owner"]["username"] == owner and item["version"]["name"] == version + for item in items + ): + raise UserSideException( + "The package `%s/%s@%s` is already published in the registry" + % (owner, name, version) + ) + other_owners = [ + item["owner"]["username"] + for item in items + if item["owner"]["username"] != owner + ] + if other_owners: + click.secho( + "\nWarning! A package with the name `%s` is already published by the next " + "owners: %s\n" % (name, ", ".join(other_owners)), + fg="yellow", + ) + return True + + @click.group("package", short_help="Package manager") def cli(): pass @@ -57,6 +97,12 @@ def cli(): def package_pack(package, output): p = PackagePacker(package) archive_path = p.pack(output) + # validate manifest + try: + load_manifest_from_archive(archive_path) + except ManifestValidationError as e: + os.remove(archive_path) + raise e click.secho('Wrote a tarball to "%s"' % archive_path, fg="green") @@ -107,7 +153,7 @@ def package_publish( # pylint: disable=too-many-arguments, too-many-locals archive_path = p.pack() type_ = PackageType.from_archive(archive_path) - manifest = ManifestParserFactory.new_from_archive(archive_path).as_dict() + manifest = load_manifest_from_archive(archive_path) name = manifest.get("name") version = manifest.get("version") data = [ @@ -119,7 +165,7 @@ def package_publish( # pylint: disable=too-many-arguments, too-many-locals click.echo(tabulate(data, tablefmt="plain")) # look for duplicates - _check_duplicates(owner, type_, name, version) + check_package_duplicates(owner, type_, name, version) if not non_interactive: click.confirm( @@ -142,37 +188,6 @@ def package_publish( # pylint: disable=too-many-arguments, too-many-locals click.secho(response.get("message"), fg="green") -def _check_duplicates(owner, type, name, version): # pylint: disable=redefined-builtin - items = ( - RegistryClient() - .list_packages(filters=dict(types=[type], names=[name])) - .get("items") - ) - if not items: - return True - # duplicated version by owner - if any( - item["owner"]["username"] == owner and item["version"]["name"] == version - for item in items - ): - raise UserSideException( - "The package `%s/%s@%s` is already published in the registry" - % (owner, name, version) - ) - other_owners = [ - item["owner"]["username"] - for item in items - if item["owner"]["username"] != owner - ] - if other_owners: - click.secho( - "\nWarning! A package with the name `%s` is already published by the next " - "owners: `%s`\n" % (name, ", ".join(other_owners)), - fg="yellow", - ) - return True - - @cli.command("unpublish", short_help="Remove a pushed package from the registry") @click.argument( "package", required=True, metavar="[/][@]"