Implement pio pkg update command // Issue #3373

This commit is contained in:
Ivan Kravets
2022-03-20 15:40:44 +02:00
parent 346580d955
commit 435a526140
16 changed files with 703 additions and 145 deletions

View File

@ -8,7 +8,7 @@ PlatformIO Core 5
**A professional collaborative platform for embedded development** **A professional collaborative platform for embedded development**
5.3.0 (2022-02-??) 5.3.0 (2022-??-??)
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
* **Package Management** * **Package Management**
@ -17,7 +17,9 @@ PlatformIO Core 5
* `pio pkg exec <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_exec.html>`_ - run command from package tool (`issue #4163 <https://github.com/platformio/platformio-core/issues/4163>`_) * `pio pkg exec <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_exec.html>`_ - run command from package tool (`issue #4163 <https://github.com/platformio/platformio-core/issues/4163>`_)
* `pio pkg install <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_install.html>`_ - install the project dependencies or custom packages * `pio pkg install <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_install.html>`_ - install the project dependencies or custom packages
* `pio pkg outdated <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_pack.html>`__ - check for project outdated packages * `pio pkg outdated <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_outdated.html>`__ - check for project outdated packages
* `pio pkg update <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_update.html>`__ - update the project dependencies or custom packages
* `pio pkg uninstall <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_uninstall.html>`_ - uninstall the project dependencies or custom packages
- Added support for dependencies declared in a "tool" type package - Added support for dependencies declared in a "tool" type package
- Ignore files according to the patterns declared in ".gitignore" when using `pio package pack <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_pack.html>`__ command (`issue #4188 <https://github.com/platformio/platformio-core/issues/4188>`_) - Ignore files according to the patterns declared in ".gitignore" when using `pio package pack <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_pack.html>`__ command (`issue #4188 <https://github.com/platformio/platformio-core/issues/4188>`_)

2
docs

Submodule docs updated: 7aa5b89963...cbf179f826

View File

@ -237,8 +237,13 @@ def lib_uninstall(ctx, libraries, save, silent):
def lib_update( # pylint: disable=too-many-arguments def lib_update( # pylint: disable=too-many-arguments
ctx, libraries, only_check, dry_run, silent, json_output ctx, libraries, only_check, dry_run, silent, json_output
): ):
storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY]
only_check = dry_run or only_check only_check = dry_run or only_check
if only_check and not json_output:
raise exception.UserSideException(
"This command is deprecated, please use `pio pkg outdated` instead"
)
storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY]
json_result = {} json_result = {}
for storage_dir in storage_dirs: for storage_dir in storage_dirs:
if not json_output: if not json_output:
@ -278,7 +283,7 @@ def lib_update( # pylint: disable=too-many-arguments
None if isinstance(library, PackageItem) else PackageSpec(library) None if isinstance(library, PackageItem) else PackageSpec(library)
) )
try: try:
lm.update(library, to_spec=to_spec, only_check=only_check) lm.update(library, to_spec=to_spec)
except UnknownPackageError as e: except UnknownPackageError as e:
if library not in lib_deps: if library not in lib_deps:
raise e raise e
@ -529,7 +534,7 @@ def lib_show(library, json_output):
@click.argument("config_url") @click.argument("config_url")
def lib_register(config_url): # pylint: disable=unused-argument def lib_register(config_url): # pylint: disable=unused-argument
raise exception.UserSideException( raise exception.UserSideException(
"This command is deprecated. Please use `pio package publish` command." "This command is deprecated. Please use `pio pkg publish` command."
) )

View File

@ -21,6 +21,7 @@ from platformio.package.commands.pack import package_pack_cmd
from platformio.package.commands.publish import package_publish_cmd from platformio.package.commands.publish import package_publish_cmd
from platformio.package.commands.uninstall import package_uninstall_cmd from platformio.package.commands.uninstall import package_uninstall_cmd
from platformio.package.commands.unpublish import package_unpublish_cmd from platformio.package.commands.unpublish import package_unpublish_cmd
from platformio.package.commands.update import package_update_cmd
@click.group( @click.group(
@ -28,11 +29,12 @@ from platformio.package.commands.unpublish import package_unpublish_cmd
commands=[ commands=[
package_exec_cmd, package_exec_cmd,
package_install_cmd, package_install_cmd,
package_uninstall_cmd,
package_outdated_cmd, package_outdated_cmd,
package_pack_cmd, package_pack_cmd,
package_publish_cmd, package_publish_cmd,
package_uninstall_cmd,
package_unpublish_cmd, package_unpublish_cmd,
package_update_cmd,
], ],
short_help="Package Manager", short_help="Package Manager",
) )

View File

@ -273,8 +273,13 @@ def platform_uninstall(platforms):
@click.option("-s", "--silent", is_flag=True, help="Suppress progress reporting") @click.option("-s", "--silent", is_flag=True, help="Suppress progress reporting")
@click.option("--json-output", is_flag=True) @click.option("--json-output", is_flag=True)
def platform_update( # pylint: disable=too-many-locals, too-many-arguments def platform_update( # pylint: disable=too-many-locals, too-many-arguments
platforms, only_packages, only_check, dry_run, silent, json_output platforms, only_check, dry_run, silent, json_output, **_
): ):
if only_check and not json_output:
raise UserSideException(
"This command is deprecated, please use `pio pkg outdated` instead"
)
pm = PlatformPackageManager() pm = PlatformPackageManager()
pm.set_log_level(logging.WARN if silent else logging.DEBUG) pm.set_log_level(logging.WARN if silent else logging.DEBUG)
platforms = platforms or pm.get_installed() platforms = platforms or pm.get_installed()
@ -322,7 +327,7 @@ def platform_update( # pylint: disable=too-many-locals, too-many-arguments
) )
) )
click.echo("--------") click.echo("--------")
pm.update(platform, only_packages=only_packages, only_check=only_check) pm.update(platform)
click.echo() click.echo()
return True return True

View File

@ -42,7 +42,8 @@ def cli(ctx, core_packages, only_check, dry_run):
only_check = dry_run or only_check only_check = dry_run or only_check
update_core_packages(only_check) if not only_check:
update_core_packages()
if core_packages: if core_packages:
return return

View File

@ -22,6 +22,7 @@ import click
from platformio import VERSION, __version__, app, exception from platformio import VERSION, __version__, app, exception
from platformio.clients.http import fetch_remote_content from platformio.clients.http import fetch_remote_content
from platformio.compat import IS_WINDOWS from platformio.compat import IS_WINDOWS
from platformio.package.manager.core import update_core_packages
from platformio.proc import exec_command, get_pythonexe_path from platformio.proc import exec_command, get_pythonexe_path
from platformio.project.helpers import get_project_cache_dir from platformio.project.helpers import get_project_cache_dir
@ -29,6 +30,7 @@ from platformio.project.helpers import get_project_cache_dir
@click.command("upgrade", short_help="Upgrade PlatformIO to the latest version") @click.command("upgrade", short_help="Upgrade PlatformIO to the latest version")
@click.option("--dev", is_flag=True, help="Use development branch") @click.option("--dev", is_flag=True, help="Use development branch")
def cli(dev): def cli(dev):
update_core_packages()
if not dev and __version__ == get_latest_version(): if not dev and __version__ == get_latest_version():
return click.secho( return click.secho(
"You're up-to-date!\nPlatformIO %s is currently the " "You're up-to-date!\nPlatformIO %s is currently the "

View File

@ -150,13 +150,10 @@ def after_upgrade(ctx):
return return
else: else:
click.secho("Please wait while upgrading PlatformIO...", fg="yellow") click.secho("Please wait while upgrading PlatformIO...", fg="yellow")
try:
cleanup_content_cache("http")
except: # pylint: disable=bare-except
pass
# Update PlatformIO's Core packages # Update PlatformIO's Core packages
update_core_packages(silent=True) cleanup_content_cache("http")
update_core_packages()
u = Upgrader(last_version, __version__) u = Upgrader(last_version, __version__)
if u.run(ctx): if u.run(ctx):
@ -219,7 +216,7 @@ def check_platformio_upgrade():
http.ensure_internet_on(raise_exception=True) http.ensure_internet_on(raise_exception=True)
# Update PlatformIO Core packages # Update PlatformIO Core packages
update_core_packages(silent=True) update_core_packages()
latest_version = get_latest_version() latest_version = get_latest_version()
if pepver_to_semver(latest_version) <= pepver_to_semver(__version__): if pepver_to_semver(latest_version) <= pepver_to_semver(__version__):

View File

@ -0,0 +1,252 @@
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import os
import click
from platformio import fs
from platformio.package.manager.library import LibraryPackageManager
from platformio.package.manager.platform import PlatformPackageManager
from platformio.package.manager.tool import ToolPackageManager
from platformio.package.meta import PackageSpec
from platformio.project.config import ProjectConfig
from platformio.project.savedeps import pkg_to_save_spec, save_project_dependencies
@click.command(
"update", short_help="Update the project dependencies or custom packages"
)
@click.option(
"-d",
"--project-dir",
default=os.getcwd,
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
)
@click.option("-e", "--environment", "environments", multiple=True)
@click.option("-p", "--platform", "platforms", multiple=True)
@click.option("-t", "--tool", "tools", multiple=True)
@click.option("-l", "--library", "libraries", multiple=True)
@click.option(
"--no-save",
is_flag=True,
help="Prevent saving specified packages to `platformio.ini`",
)
@click.option("--skip-dependencies", is_flag=True, help="Skip package dependencies")
@click.option("-g", "--global", is_flag=True, help="Update global packages")
@click.option(
"--storage-dir",
default=None,
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
help="Custom Package Manager storage for global packages",
)
@click.option("-s", "--silent", is_flag=True, help="Suppress progress reporting")
def package_update_cmd(**options):
if options.get("global"):
update_global_dependencies(options)
else:
update_project_dependencies(options)
def update_global_dependencies(options):
pm = PlatformPackageManager(options.get("storage_dir"))
tm = ToolPackageManager(options.get("storage_dir"))
lm = LibraryPackageManager(options.get("storage_dir"))
for obj in (pm, tm, lm):
obj.set_log_level(logging.WARN if options.get("silent") else logging.DEBUG)
for spec in options.get("platforms"):
pm.update(
from_spec=spec,
to_spec=spec,
skip_dependencies=options.get("skip_dependencies"),
)
for spec in options.get("tools"):
tm.update(
from_spec=spec,
to_spec=spec,
skip_dependencies=options.get("skip_dependencies"),
)
for spec in options.get("libraries", []):
lm.update(
from_spec=spec,
to_spec=spec,
skip_dependencies=options.get("skip_dependencies"),
)
def update_project_dependencies(options):
environments = options["environments"]
with fs.cd(options["project_dir"]):
config = ProjectConfig.get_instance()
config.validate(environments)
for env in config.envs():
if environments and env not in environments:
continue
if not options["silent"]:
click.echo(
"Resolving %s environment packages..." % click.style(env, fg="cyan")
)
already_up_to_date = not update_project_env_dependencies(env, options)
if not options["silent"] and already_up_to_date:
click.secho("Already up-to-date.", fg="green")
def update_project_env_dependencies(project_env, options=None):
options = options or {}
updated_conds = []
# custom platforms
if options.get("platforms"):
updated_conds.append(_update_project_env_custom_platforms(project_env, options))
# custom tools
if options.get("tools"):
updated_conds.append(_update_project_env_custom_tools(project_env, options))
# custom ibraries
if options.get("libraries"):
updated_conds.append(_update_project_env_custom_libraries(project_env, options))
# declared dependencies
if not updated_conds:
updated_conds = [
_update_project_env_platform(project_env, options),
_update_project_env_libraries(project_env, options),
]
return any(updated_conds)
def _update_project_env_platform(project_env, options):
config = ProjectConfig.get_instance()
pm = PlatformPackageManager()
if options.get("silent"):
pm.set_log_level(logging.WARN)
spec = config.get(f"env:{project_env}", "platform")
if not spec:
return None
cur_pkg = pm.get_package(spec)
if not cur_pkg:
return None
new_pkg = PlatformPackageManager().update(
cur_pkg,
to_spec=spec,
project_env=project_env,
skip_dependencies=options.get("skip_dependencies"),
)
return cur_pkg != new_pkg
def _update_project_env_custom_platforms(project_env, options):
already_up_to_date = True
pm = PlatformPackageManager()
if not options.get("silent"):
pm.set_log_level(logging.DEBUG)
for spec in options.get("platforms"):
cur_pkg = pm.get_package(spec)
new_pkg = pm.update(
cur_pkg,
to_spec=spec,
project_env=project_env,
skip_dependencies=options.get("skip_dependencies"),
)
if cur_pkg != new_pkg:
already_up_to_date = False
return not already_up_to_date
def _update_project_env_custom_tools(project_env, options):
already_up_to_date = True
tm = ToolPackageManager()
if not options.get("silent"):
tm.set_log_level(logging.DEBUG)
specs_to_save = []
for tool in options.get("tools"):
spec = PackageSpec(tool)
cur_pkg = tm.get_package(spec)
new_pkg = tm.update(
cur_pkg,
to_spec=spec,
skip_dependencies=options.get("skip_dependencies"),
)
if cur_pkg != new_pkg:
already_up_to_date = False
specs_to_save.append(pkg_to_save_spec(new_pkg, spec))
if not options.get("no_save") and specs_to_save:
save_project_dependencies(
os.getcwd(),
specs_to_save,
scope="platform_packages",
action="add",
environments=[project_env],
)
return not already_up_to_date
def _update_project_env_libraries(project_env, options):
already_up_to_date = True
config = ProjectConfig.get_instance()
lm = LibraryPackageManager(
os.path.join(config.get("platformio", "libdeps_dir"), project_env)
)
if options.get("silent"):
lm.set_log_level(logging.WARN)
for library in config.get(f"env:{project_env}", "lib_deps"):
spec = PackageSpec(library)
# skip built-in dependencies
if not spec.external and not spec.owner:
continue
cur_pkg = lm.get_package(spec)
if cur_pkg:
new_pkg = lm.update(
cur_pkg,
to_spec=spec,
skip_dependencies=options.get("skip_dependencies"),
)
if cur_pkg != new_pkg:
already_up_to_date = False
return not already_up_to_date
def _update_project_env_custom_libraries(project_env, options):
already_up_to_date = True
config = ProjectConfig.get_instance()
lm = LibraryPackageManager(
os.path.join(config.get("platformio", "libdeps_dir"), project_env)
)
if not options.get("silent"):
lm.set_log_level(logging.DEBUG)
specs_to_save = []
for library in options.get("libraries") or []:
spec = PackageSpec(library)
cur_pkg = lm.get_package(spec)
new_pkg = lm.update(
cur_pkg,
to_spec=spec,
skip_dependencies=options.get("skip_dependencies"),
)
if cur_pkg != new_pkg:
already_up_to_date = False
specs_to_save.append(pkg_to_save_spec(new_pkg, spec))
if not options.get("no_save") and specs_to_save:
save_project_dependencies(
os.getcwd(),
specs_to_save,
scope="lib_deps",
action="add",
environments=[project_env],
)
return not already_up_to_date

View File

@ -12,12 +12,10 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import logging
import os import os
import click import click
from platformio.clients.http import ensure_internet_on
from platformio.package.exception import UnknownPackageError from platformio.package.exception import UnknownPackageError
from platformio.package.meta import PackageItem, PackageOutdatedResult, PackageSpec from platformio.package.meta import PackageItem, PackageOutdatedResult, PackageSpec
from platformio.package.vcsclient import VCSBaseException, VCSClientFactory from platformio.package.vcsclient import VCSBaseException, VCSClientFactory
@ -26,9 +24,11 @@ from platformio.package.vcsclient import VCSBaseException, VCSClientFactory
class PackageManagerUpdateMixin(object): class PackageManagerUpdateMixin(object):
def outdated(self, pkg, spec=None): def outdated(self, pkg, spec=None):
assert isinstance(pkg, PackageItem) assert isinstance(pkg, PackageItem)
assert not spec or isinstance(spec, PackageSpec)
assert pkg.metadata assert pkg.metadata
if spec and not isinstance(spec, PackageSpec):
spec = PackageSpec(spec)
if not os.path.isdir(pkg.path): if not os.path.isdir(pkg.path):
return PackageOutdatedResult(current=pkg.metadata.version) return PackageOutdatedResult(current=pkg.metadata.version)
@ -82,82 +82,35 @@ class PackageManagerUpdateMixin(object):
self, self,
from_spec, from_spec,
to_spec=None, to_spec=None,
only_check=False, skip_dependencies=False,
show_incompatible=True,
): ):
pkg = self.get_package(from_spec) pkg = self.get_package(from_spec)
if not pkg or not pkg.metadata: if not pkg or not pkg.metadata:
raise UnknownPackageError(from_spec) raise UnknownPackageError(from_spec)
silent = not self.log.isEnabledFor(logging.INFO)
if not silent:
click.echo(
"{} {:<45} {:<35}".format(
"Checking" if only_check else "Updating",
click.style(pkg.metadata.spec.humanize(), fg="cyan"),
"%s @ %s" % (pkg.metadata.version, to_spec.requirements)
if to_spec and to_spec.requirements
else str(pkg.metadata.version),
),
nl=False,
)
if not ensure_internet_on():
if not silent:
click.echo("[%s]" % (click.style("Off-line", fg="yellow")))
return pkg
outdated = self.outdated(pkg, to_spec) outdated = self.outdated(pkg, to_spec)
if not silent: if not outdated.is_outdated(allow_incompatible=False):
self.print_outdated_state(outdated, only_check, show_incompatible) self.log.debug(
click.style(
if only_check or not outdated.is_outdated(allow_incompatible=False): "{name} @ {version} is already up-to-date".format(
**pkg.metadata.as_dict()
),
fg="yellow",
)
)
return pkg return pkg
self.log.info(
"Updating %s @ %s"
% (click.style(pkg.metadata.name, fg="cyan"), pkg.metadata.version)
)
try: try:
self.lock() self.lock()
return self._update(pkg, outdated) return self._update(pkg, outdated, skip_dependencies)
finally: finally:
self.unlock() self.unlock()
@staticmethod def _update(self, pkg, outdated, skip_dependencies=False):
def print_outdated_state(outdated, only_check, show_incompatible):
if outdated.detached:
return click.echo("[%s]" % (click.style("Detached", fg="yellow")))
if (
not outdated.latest
or outdated.current == outdated.latest
or (not show_incompatible and outdated.current == outdated.wanted)
):
return click.echo("[%s]" % (click.style("Up-to-date", fg="green")))
if outdated.wanted and outdated.current == outdated.wanted:
return click.echo(
"[%s]" % (click.style("Incompatible %s" % outdated.latest, fg="yellow"))
)
if only_check:
return click.echo(
"[%s]"
% (
click.style(
"Outdated %s" % str(outdated.wanted or outdated.latest),
fg="red",
)
)
)
return click.echo(
"[%s]"
% (
click.style(
"Updating to %s" % str(outdated.wanted or outdated.latest),
fg="green",
)
)
)
def _update(self, pkg, outdated):
if pkg.metadata.spec.external: if pkg.metadata.spec.external:
vcs = VCSClientFactory.new(pkg.path, pkg.metadata.spec.url) vcs = VCSClientFactory.new(pkg.path, pkg.metadata.spec.url)
assert vcs.update() assert vcs.update()
@ -165,23 +118,15 @@ class PackageManagerUpdateMixin(object):
pkg.dump_meta() pkg.dump_meta()
return pkg return pkg
new_pkg = self.install( # uninstall existing version
self.uninstall(pkg, skip_dependencies=True)
return self.install(
PackageSpec( PackageSpec(
id=pkg.metadata.spec.id, id=pkg.metadata.spec.id,
owner=pkg.metadata.spec.owner, owner=pkg.metadata.spec.owner,
name=pkg.metadata.spec.name, name=pkg.metadata.spec.name,
requirements=outdated.wanted or outdated.latest, requirements=outdated.wanted or outdated.latest,
) ),
skip_dependencies=skip_dependencies,
) )
if new_pkg:
old_pkg = self.get_package(
PackageSpec(
id=pkg.metadata.spec.id,
owner=pkg.metadata.spec.owner,
name=pkg.metadata.name,
requirements=pkg.metadata.version,
)
)
if old_pkg:
self.uninstall(old_pkg, skip_dependencies=True)
return new_pkg

View File

@ -55,17 +55,15 @@ def get_core_package_dir(name, auto_install=True):
return pm.get_package(spec).path return pm.get_package(spec).path
def update_core_packages(only_check=False, silent=False): def update_core_packages():
pm = ToolPackageManager() pm = ToolPackageManager()
for name, requirements in __core_packages__.items(): for name, requirements in __core_packages__.items():
spec = PackageSpec(owner="platformio", name=name, requirements=requirements) spec = PackageSpec(owner="platformio", name=name, requirements=requirements)
pkg = pm.get_package(spec) try:
if not pkg: pm.update(spec, spec)
continue except UnknownPackageError:
if not silent or pm.outdated(pkg, spec).is_outdated(): pass
pm.update(pkg, spec, only_check=only_check) remove_unnecessary_core_packages()
if not only_check:
remove_unnecessary_core_packages()
return True return True

View File

@ -85,45 +85,28 @@ class PlatformPackageManager(BasePackageManager): # pylint: disable=too-many-an
p.on_uninstalled() p.on_uninstalled()
return pkg return pkg
def update( # pylint: disable=arguments-differ, too-many-arguments def update( # pylint: disable=arguments-differ
self, self,
from_spec, from_spec,
to_spec=None, to_spec=None,
only_check=False, skip_dependencies=False,
show_incompatible=True, project_env=None,
only_packages=False,
): ):
pkg = self.get_package(from_spec) pkg = self.get_package(from_spec)
if not pkg or not pkg.metadata: if not pkg or not pkg.metadata:
raise UnknownPackageError(from_spec) raise UnknownPackageError(from_spec)
pkg = super(PlatformPackageManager, self).update(
from_spec,
to_spec,
)
p = PlatformFactory.new(pkg) p = PlatformFactory.new(pkg)
# set logging level for underlying tool manager # set logging level for underlying tool manager
p.pm.set_log_level(self.log.getEffectiveLevel()) p.pm.set_log_level(self.log.getEffectiveLevel())
pkgs_before = [item.metadata.name for item in p.get_installed_packages()] if project_env:
p.configure_project_packages(project_env)
new_pkg = None if not skip_dependencies:
missed_pkgs = set() p.update_packages()
if not only_packages: return pkg
new_pkg = super(PlatformPackageManager, self).update(
from_spec,
to_spec,
only_check=only_check,
show_incompatible=show_incompatible,
)
p = PlatformFactory.new(new_pkg)
missed_pkgs = set(pkgs_before) & set(p.packages)
missed_pkgs -= set(
item.metadata.name for item in p.get_installed_packages()
)
p.update_packages(only_check)
if missed_pkgs:
p.install_packages(
with_packages=list(missed_pkgs), skip_default_package=True
)
return new_pkg or pkg
@util.memoized(expire="5s") @util.memoized(expire="5s")
def get_installed_boards(self): def get_installed_boards(self):

View File

@ -0,0 +1,356 @@
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# pylint: disable=unused-argument
import os
from platformio import fs
from platformio.package.commands.install import package_install_cmd
from platformio.package.commands.update import package_update_cmd
from platformio.package.exception import UnknownPackageError
from platformio.package.manager.library import LibraryPackageManager
from platformio.package.manager.platform import PlatformPackageManager
from platformio.package.manager.tool import ToolPackageManager
from platformio.package.meta import PackageSpec
from platformio.project.config import ProjectConfig
PROJECT_OUTDATED_CONFIG_TPL = """
[env:devkit]
platform = platformio/atmelavr@^2
framework = arduino
board = attiny88
lib_deps = milesburton/DallasTemperature@~3.8.0
"""
PROJECT_UPDATED_CONFIG_TPL = """
[env:devkit]
platform = platformio/atmelavr@<4
framework = arduino
board = attiny88
lib_deps = milesburton/DallasTemperature@^3.8.0
"""
def pkgs_to_specs(pkgs):
return [
PackageSpec(name=pkg.metadata.name, requirements=pkg.metadata.version)
for pkg in pkgs
]
def test_global_packages(
clirunner, validate_cliresult, func_isolated_pio_core, tmp_path
):
# libraries
result = clirunner.invoke(
package_install_cmd,
["--global", "-l", "bblanchon/ArduinoJson@^5"],
)
validate_cliresult(result)
assert pkgs_to_specs(LibraryPackageManager().get_installed()) == [
PackageSpec("ArduinoJson@5.13.4")
]
# update to the latest version
result = clirunner.invoke(
package_update_cmd,
["--global", "-l", "bblanchon/ArduinoJson"],
)
validate_cliresult(result)
pkgs = LibraryPackageManager().get_installed()
assert len(pkgs) == 1
assert pkgs[0].metadata.version.major > 5
# custom storage
storage_dir = tmp_path / "custom_lib_storage"
storage_dir.mkdir()
result = clirunner.invoke(
package_install_cmd,
[
"--global",
"--storage-dir",
str(storage_dir),
"-l",
"bblanchon/ArduinoJson@^5",
],
)
validate_cliresult(result)
assert pkgs_to_specs(LibraryPackageManager(storage_dir).get_installed()) == [
PackageSpec("ArduinoJson@5.13.4")
]
# update to the latest version
result = clirunner.invoke(
package_update_cmd,
["--global", "--storage-dir", str(storage_dir), "-l", "bblanchon/ArduinoJson"],
)
validate_cliresult(result)
pkgs = LibraryPackageManager(storage_dir).get_installed()
assert len(pkgs) == 1
assert pkgs[0].metadata.version.major > 5
# tools
result = clirunner.invoke(
package_install_cmd,
["--global", "-t", "platformio/framework-arduino-avr-attiny@~1.4"],
)
validate_cliresult(result)
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("framework-arduino-avr-attiny@1.4.1")
]
# update to the latest version
result = clirunner.invoke(
package_update_cmd,
["--global", "-t", "platformio/framework-arduino-avr-attiny@^1"],
)
validate_cliresult(result)
pkgs = ToolPackageManager().get_installed()
assert len(pkgs) == 1
assert pkgs[0].metadata.version.major == 1
assert pkgs[0].metadata.version.minor > 4
# platforms
result = clirunner.invoke(
package_install_cmd,
["--global", "-p", "platformio/atmelavr@^2", "--skip-dependencies"],
)
validate_cliresult(result)
assert pkgs_to_specs(PlatformPackageManager().get_installed()) == [
PackageSpec("atmelavr@2.2.0")
]
# update to the latest version
result = clirunner.invoke(
package_update_cmd,
["--global", "-p", "platformio/atmelavr", "--skip-dependencies"],
)
validate_cliresult(result)
pkgs = PlatformPackageManager().get_installed()
assert len(pkgs) == 1
assert pkgs[0].metadata.version.major > 2
# update unknown package
result = clirunner.invoke(
package_update_cmd,
["--global", "-l", "platformio/unknown_package_for_update"],
)
assert isinstance(result.exception, UnknownPackageError)
def test_project(clirunner, validate_cliresult, isolated_pio_core, tmp_path):
project_dir = tmp_path / "project"
project_dir.mkdir()
(project_dir / "platformio.ini").write_text(PROJECT_OUTDATED_CONFIG_TPL)
result = clirunner.invoke(
package_install_cmd,
["-d", str(project_dir)],
)
validate_cliresult(result)
with fs.cd(str(project_dir)):
config = ProjectConfig()
lm = LibraryPackageManager(
os.path.join(config.get("platformio", "libdeps_dir"), "devkit")
)
assert pkgs_to_specs(lm.get_installed()) == [
PackageSpec("DallasTemperature@3.8.1"),
PackageSpec("OneWire@2.3.6"),
]
assert pkgs_to_specs(PlatformPackageManager().get_installed()) == [
PackageSpec("atmelavr@2.2.0")
]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("framework-arduino-avr-attiny@1.3.2"),
PackageSpec("toolchain-atmelavr@1.50400.190710"),
]
assert config.get("env:devkit", "lib_deps") == [
"milesburton/DallasTemperature@~3.8.0"
]
# update packages
(project_dir / "platformio.ini").write_text(PROJECT_UPDATED_CONFIG_TPL)
result = clirunner.invoke(package_update_cmd)
validate_cliresult(result)
config = ProjectConfig()
lm = LibraryPackageManager(
os.path.join(config.get("platformio", "libdeps_dir"), "devkit")
)
pkgs = PlatformPackageManager().get_installed()
assert len(pkgs) == 1
assert pkgs[0].metadata.name == "atmelavr"
assert pkgs[0].metadata.version.major == 3
assert pkgs_to_specs(lm.get_installed()) == [
PackageSpec("DallasTemperature@3.9.1"),
PackageSpec("OneWire@2.3.6"),
]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("framework-arduino-avr-attiny@1.3.2"),
PackageSpec("toolchain-atmelavr@1.70300.191015"),
PackageSpec("toolchain-atmelavr@1.50400.190710"),
]
assert config.get("env:devkit", "lib_deps") == [
"milesburton/DallasTemperature@^3.8.0"
]
# update again
result = clirunner.invoke(package_update_cmd)
validate_cliresult(result)
assert "Already up-to-date." in result.output
# update again in the silent ,pde
result = clirunner.invoke(package_update_cmd, ["--silent"])
validate_cliresult(result)
assert not result.output
def test_custom_project_libraries(
clirunner, validate_cliresult, isolated_pio_core, tmp_path
):
project_dir = tmp_path / "project"
project_dir.mkdir()
(project_dir / "platformio.ini").write_text(PROJECT_OUTDATED_CONFIG_TPL)
spec = "milesburton/DallasTemperature@~3.8.0"
result = clirunner.invoke(
package_install_cmd,
["-d", str(project_dir), "-e", "devkit", "-l", spec],
)
validate_cliresult(result)
with fs.cd(str(project_dir)):
config = ProjectConfig()
assert config.get("env:devkit", "lib_deps") == [spec]
lm = LibraryPackageManager(
os.path.join(config.get("platformio", "libdeps_dir"), "devkit")
)
assert pkgs_to_specs(lm.get_installed()) == [
PackageSpec("DallasTemperature@3.8.1"),
PackageSpec("OneWire@2.3.6"),
]
# update package
result = clirunner.invoke(
package_update_cmd,
["-e", "devkit", "-l", "milesburton/DallasTemperature@^3.8.0"],
)
assert ProjectConfig().get("env:devkit", "lib_deps") == [
"milesburton/DallasTemperature@^3.8.0"
]
# try again
result = clirunner.invoke(
package_update_cmd,
["-e", "devkit", "-l", "milesburton/DallasTemperature@^3.8.0"],
)
validate_cliresult(result)
assert "Already up-to-date." in result.output
# install library without saving to config
result = clirunner.invoke(
package_update_cmd,
["-e", "devkit", "-l", "milesburton/DallasTemperature@^3", "--no-save"],
)
validate_cliresult(result)
assert "Already up-to-date." in result.output
config = ProjectConfig()
lm = LibraryPackageManager(
os.path.join(config.get("platformio", "libdeps_dir"), "devkit")
)
assert pkgs_to_specs(lm.get_installed()) == [
PackageSpec("DallasTemperature@3.9.1"),
PackageSpec("OneWire@2.3.6"),
]
assert config.get("env:devkit", "lib_deps") == [
"milesburton/DallasTemperature@^3.8.0"
]
# unknown libraries
result = clirunner.invoke(
package_update_cmd, ["-l", "platformio/unknown_library"]
)
assert isinstance(result.exception, UnknownPackageError)
def test_custom_project_tools(
clirunner, validate_cliresult, func_isolated_pio_core, tmp_path
):
project_dir = tmp_path / "project"
project_dir.mkdir()
(project_dir / "platformio.ini").write_text(PROJECT_OUTDATED_CONFIG_TPL)
spec = "toolchain-atmelavr@~1.50400.0"
result = clirunner.invoke(
package_install_cmd,
["-d", str(project_dir), "-e", "devkit", "-t", spec],
)
validate_cliresult(result)
with fs.cd(str(project_dir)):
assert ProjectConfig().get("env:devkit", "platform_packages") == [
"platformio/toolchain-atmelavr@~1.50400.0"
]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("toolchain-atmelavr@1.50400.190710")
]
result = clirunner.invoke(
package_update_cmd,
["-e", "devkit", "-t", "toolchain-atmelavr@^1"],
)
validate_cliresult(result)
assert ProjectConfig().get("env:devkit", "platform_packages") == [
"platformio/toolchain-atmelavr@^1"
]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("toolchain-atmelavr@1.70300.191015")
]
# install without saving to config
result = clirunner.invoke(
package_update_cmd,
["-e", "devkit", "-t", "toolchain-atmelavr@~1.70300.191015", "--no-save"],
)
validate_cliresult(result)
assert "Already up-to-date." in result.output
assert ProjectConfig().get("env:devkit", "platform_packages") == [
"platformio/toolchain-atmelavr@^1"
]
# unknown tool
result = clirunner.invoke(package_update_cmd, ["-t", "platformio/unknown_tool"])
assert isinstance(result.exception, UnknownPackageError)
def test_custom_project_platforms(
clirunner, validate_cliresult, func_isolated_pio_core, tmp_path
):
project_dir = tmp_path / "project"
project_dir.mkdir()
(project_dir / "platformio.ini").write_text(PROJECT_OUTDATED_CONFIG_TPL)
spec = "atmelavr@^2"
result = clirunner.invoke(
package_install_cmd,
["-d", str(project_dir), "-e", "devkit", "-p", spec, "--skip-dependencies"],
)
validate_cliresult(result)
with fs.cd(str(project_dir)):
assert pkgs_to_specs(PlatformPackageManager().get_installed()) == [
PackageSpec("atmelavr@2.2.0")
]
assert ProjectConfig().get("env:devkit", "platform") == "platformio/atmelavr@^2"
# update
result = clirunner.invoke(
package_install_cmd,
["-e", "devkit", "-p", "platformio/atmelavr@^3", "--skip-dependencies"],
)
validate_cliresult(result)
assert pkgs_to_specs(PlatformPackageManager().get_installed()) == [
PackageSpec("atmelavr@3.4.0"),
PackageSpec("atmelavr@2.2.0"),
]
assert ProjectConfig().get("env:devkit", "platform") == "platformio/atmelavr@^2"
# unknown platform
result = clirunner.invoke(package_install_cmd, ["-p", "unknown_platform"])
assert isinstance(result.exception, UnknownPackageError)

View File

@ -17,6 +17,7 @@
import json import json
import os import os
import pytest
import semantic_version import semantic_version
from platformio.clients.registry import RegistryClient from platformio.clients.registry import RegistryClient
@ -225,5 +226,13 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
result = clirunner.invoke( result = clirunner.invoke(
cmd_lib, ["-d", str(storage_dir), "update", "--dry-run", "ArduinoJson @ ^5"] cmd_lib, ["-d", str(storage_dir), "update", "--dry-run", "ArduinoJson @ ^5"]
) )
with pytest.raises(
AssertionError,
match="This command is deprecated",
):
validate_cliresult(result)
result = clirunner.invoke(
cmd_lib, ["-d", str(storage_dir), "update", "ArduinoJson @ ^5"]
)
validate_cliresult(result) validate_cliresult(result)
assert "Incompatible" in result.stdout assert "ArduinoJson @ 5.13.4 is already up-to-date" in result.stdout

View File

@ -253,8 +253,8 @@ def test_global_lib_update(clirunner, validate_cliresult, strip_ansi):
# update rest libraries # update rest libraries
result = clirunner.invoke(cmd_lib, ["-g", "update"]) result = clirunner.invoke(cmd_lib, ["-g", "update"])
validate_cliresult(result) validate_cliresult(result)
assert result.output.count("[Detached]") == 1 assert result.output.count("+sha.") == 4
assert result.output.count("[Up-to-date]") == 13 assert result.output.count("already up-to-date") == 14
# update unknown library # update unknown library
result = clirunner.invoke(cmd_lib, ["-g", "update", "Unknown"]) result = clirunner.invoke(cmd_lib, ["-g", "update", "Unknown"])
@ -290,17 +290,18 @@ def test_global_lib_uninstall(
items1 = [d.basename for d in isolated_pio_core.join("lib").listdir()] items1 = [d.basename for d in isolated_pio_core.join("lib").listdir()]
items2 = [ items2 = [
"ArduinoJson",
"ArduinoJson@src-69ebddd821f771debe7ee734d3c7fa81",
"AsyncMqttClient", "AsyncMqttClient",
"AsyncTCP",
"ESP32WebServer",
"ESPAsyncTCP",
"NeoPixelBus",
"PJON",
"PJON@src-79de467ebe19de18287becff0a1fb42d",
"platformio-libmirror", "platformio-libmirror",
"PubSubClient", "PubSubClient",
"ArduinoJson@src-69ebddd821f771debe7ee734d3c7fa81",
"ESPAsyncTCP@1.2.0",
"AsyncTCP",
"ArduinoJson",
"ESPAsyncTCP",
"ESP32WebServer",
"PJON",
"NeoPixelBus",
"PJON@src-79de467ebe19de18287becff0a1fb42d",
"SomeLib", "SomeLib",
] ]
assert set(items1) == set(items2) assert set(items1) == set(items2)

View File

@ -19,7 +19,7 @@ from platformio.commands.update import cli as cmd_update
def test_update(clirunner, validate_cliresult, isolated_pio_core): def test_update(clirunner, validate_cliresult, isolated_pio_core):
matches = ("Platform Manager", "Library Manager") matches = ("Platform Manager", "Library Manager")
result = clirunner.invoke(cmd_update, ["--only-check"]) result = clirunner.invoke(cmd_update)
validate_cliresult(result) validate_cliresult(result)
assert all(m in result.output for m in matches) assert all(m in result.output for m in matches)
result = clirunner.invoke(cmd_update) result = clirunner.invoke(cmd_update)