From 83b00ac80c3e1bda01556a670d0dfea9099a33fd Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 17 May 2022 19:47:15 +0300 Subject: [PATCH 01/74] Remove `pio update` test --- tests/commands/test_update.py | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 tests/commands/test_update.py diff --git a/tests/commands/test_update.py b/tests/commands/test_update.py deleted file mode 100644 index 6edddfa3..00000000 --- a/tests/commands/test_update.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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 - -from platformio.commands.update import cli as cmd_update - - -def test_update(clirunner, validate_cliresult, isolated_pio_core): - matches = ("Platform Manager", "Library Manager") - result = clirunner.invoke(cmd_update) - validate_cliresult(result) - assert all(m in result.output for m in matches) - result = clirunner.invoke(cmd_update) - validate_cliresult(result) - assert all(m in result.output for m in matches) From f2bdb17c55cb6b9f85b63843e8c5a3021cbec833 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 17 May 2022 19:47:33 +0300 Subject: [PATCH 02/74] Bump version to 6.0.2a1 --- platformio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/__init__.py b/platformio/__init__.py index 097088c0..3f5c2fa2 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -14,7 +14,7 @@ import sys -VERSION = (6, 0, 1) +VERSION = (6, 0, "2a1") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" From 0ceae62701731f8b32c34d7993a34dea34aea59c Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 17 May 2022 21:11:29 +0300 Subject: [PATCH 03/74] Better informing about deprecated commands --- platformio/commands/lib/command.py | 42 +++++++++++++++++++++++----- platformio/commands/platform.py | 44 +++++++++++++++++++++++++----- 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/platformio/commands/lib/command.py b/platformio/commands/lib/command.py index 7e3f08e1..367a80d6 100644 --- a/platformio/commands/lib/command.py +++ b/platformio/commands/lib/command.py @@ -69,13 +69,6 @@ def get_project_global_lib_dir(): @click.pass_context def cli(ctx, **options): in_silence = PlatformioCLI.in_silence() - if not in_silence: - click.secho( - "\nWARNING!!! This command is deprecated and will be removed in " - "the next releases. \nPlease use `pio pkg` instead.\n", - fg="yellow", - ) - storage_cmds = ("install", "uninstall", "update", "list") # skip commands that don't need storage folder if ctx.invoked_subcommand not in storage_cmds or ( @@ -148,6 +141,11 @@ def cli(ctx, **options): def lib_install( # pylint: disable=too-many-arguments,unused-argument ctx, libraries, save, silent, interactive, force ): + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg install` instead.\n", + fg="yellow", + ) storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY] storage_libdeps = ctx.meta.get(CTX_META_STORAGE_LIBDEPS_KEY, []) @@ -211,6 +209,11 @@ def _save_deps(ctx, pkgs, action="add"): @click.option("-s", "--silent", is_flag=True, help="Suppress progress reporting") @click.pass_context def lib_uninstall(ctx, libraries, save, silent): + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg uninstall` instead.\n", + fg="yellow", + ) storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY] uninstalled_pkgs = {} for storage_dir in storage_dirs: @@ -246,6 +249,13 @@ def lib_update( # pylint: disable=too-many-arguments "This command is deprecated, please use `pio pkg outdated` instead" ) + if not json_output: + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg update` instead.\n", + fg="yellow", + ) + storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY] json_result = {} for storage_dir in storage_dirs: @@ -305,6 +315,12 @@ def lib_update( # pylint: disable=too-many-arguments @click.option("--json-output", is_flag=True) @click.pass_context def lib_list(ctx, json_output): + if not json_output: + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg list` instead.\n", + fg="yellow", + ) storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY] json_result = {} for storage_dir in storage_dirs: @@ -348,6 +364,12 @@ def lib_list(ctx, json_output): help="Do not prompt, automatically paginate with delay", ) def lib_search(query, json_output, page, noninteractive, **filters): + if not json_output: + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg search` instead.\n", + fg="yellow", + ) regclient = LibraryPackageManager().get_registry_client_instance() if not query: query = [] @@ -444,6 +466,12 @@ def lib_builtin(storage, json_output): @click.argument("library", metavar="[LIBRARY]") @click.option("--json-output", is_flag=True) def lib_show(library, json_output): + if not json_output: + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg show` instead.\n", + fg="yellow", + ) lm = LibraryPackageManager() lm.set_log_level(logging.ERROR if json_output else logging.DEBUG) lib_id = lm.reveal_registry_package_id(library) diff --git a/platformio/commands/platform.py b/platformio/commands/platform.py index ae80566e..d9e8af2d 100644 --- a/platformio/commands/platform.py +++ b/platformio/commands/platform.py @@ -18,7 +18,6 @@ import os import click -from platformio.commands import PlatformioCLI from platformio.commands.boards import print_boards from platformio.exception import UserSideException from platformio.package.exception import UnknownPackageError @@ -31,18 +30,19 @@ from platformio.platform.factory import PlatformFactory @click.group(short_help="Platform manager", hidden=True) def cli(): - if not PlatformioCLI.in_silence(): - click.secho( - "\nWARNING!!! This command is deprecated and will be removed in " - "the next releases. \nPlease use `pio pkg` instead.\n", - fg="yellow", - ) + pass @cli.command("search", short_help="Search for development platform") @click.argument("query", required=False) @click.option("--json-output", is_flag=True) def platform_search(query, json_output): + if not json_output: + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg search` instead.\n", + fg="yellow", + ) platforms = [] for platform in _get_registry_platforms(): if query == "all": @@ -94,6 +94,12 @@ def platform_frameworks(query, json_output): @cli.command("list", short_help="List installed development platforms") @click.option("--json-output", is_flag=True) def platform_list(json_output): + if not json_output: + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg list` instead.\n", + fg="yellow", + ) platforms = [] pm = PlatformPackageManager() for pkg in pm.get_installed(): @@ -112,6 +118,12 @@ def platform_list(json_output): @click.argument("platform") @click.option("--json-output", is_flag=True) def platform_show(platform, json_output): # pylint: disable=too-many-branches + if not json_output: + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg show` instead.\n", + fg="yellow", + ) data = _get_platform_data(platform) if not data: raise UnknownPlatform(platform) @@ -195,6 +207,12 @@ def platform_install( # pylint: disable=too-many-arguments,too-many-locals silent, force, ): + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg install` instead.\n", + fg="yellow", + ) + def _find_pkg_names(p, candidates): result = [] for candidate in candidates: @@ -251,6 +269,11 @@ def platform_install( # pylint: disable=too-many-arguments,too-many-locals @cli.command("uninstall", short_help="Uninstall development platform") @click.argument("platforms", nargs=-1, required=True, metavar="[PLATFORM...]") def platform_uninstall(platforms): + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg uninstall` instead.\n", + fg="yellow", + ) pm = PlatformPackageManager() pm.set_log_level(logging.DEBUG) for platform in platforms: @@ -285,6 +308,13 @@ def platform_update( # pylint: disable=too-many-locals, too-many-arguments "This command is deprecated, please use `pio pkg outdated` instead" ) + if not json_output: + click.secho( + "\nWARNING: This command is deprecated and will be removed in " + "the next releases. \nPlease use `pio pkg update` instead.\n", + fg="yellow", + ) + pm = PlatformPackageManager() pm.set_log_level(logging.WARN if silent else logging.DEBUG) platforms = platforms or pm.get_installed() From 1585b829be5e341ec5475c27531de3c8d1dc8c2d Mon Sep 17 00:00:00 2001 From: Valerii Koval Date: Wed, 18 May 2022 15:54:54 +0300 Subject: [PATCH 04/74] Add CI workflow for popular PlatformIO projects (#4273) * Add CI workflow for popular PlatformIO projects * Run projects CI on each commit * Run mega2560 env for Marlin project * Run projects CI on several OS with Python 3.9 * Use the latest version of 3rd party actions Co-authored-by: Ivan Kravets --- .github/workflows/projects.yml | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/projects.yml diff --git a/.github/workflows/projects.yml b/.github/workflows/projects.yml new file mode 100644 index 00000000..29a01714 --- /dev/null +++ b/.github/workflows/projects.yml @@ -0,0 +1,66 @@ +name: Projects + +on: [push, pull_request] + +jobs: + build: + strategy: + fail-fast: false + matrix: + project: + - marlin: + repository: "MarlinFirmware/Marlin" + folder: "Marlin" + config_dir: "Marlin" + env_name: "mega2560" + - esphome: + repository: "esphome/esphome" + folder: "esphome" + config_dir: "esphome" + env_name: "esp32-arduino" + - smartknob: + repository: "scottbez1/smartknob" + folder: "smartknob" + config_dir: "smartknob/firmware" + env_name: "view" + - espurna: + repository: "xoseperez/espurna" + folder: "espurna" + config_dir: "espurna/code" + env_name: "nodemcu-lolin" + - OpenMQTTGateway: + repository: "1technophile/OpenMQTTGateway" + folder: "OpenMQTTGateway" + config_dir: "OpenMQTTGateway" + env_name: "esp32-m5atom" + os: [ubuntu-latest, windows-latest, macos-latest] + + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + with: + submodules: "recursive" + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: 3.9 + + - name: Install PlatformIO + run: pip install -U . + + - name: Check out ${{ matrix.project.repository }} + uses: actions/checkout@v2 + with: + submodules: "recursive" + repository: ${{ matrix.project.repository }} + path: ${{ matrix.project.folder }} + + - name: Install ESPHome dependencies + # Requires esptool package as it's used in a custom prescript + if: ${{ contains(matrix.project.repository, 'esphome') }} + run: pip install esptool==3.* + + - name: Compile ${{ matrix.project.repository }} + run: pio run -d ${{ matrix.project.config_dir }} -e ${{ matrix.project.env_name }} + From 45fcb40a5c06d400cb15af9940b6eb407a88f32d Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 18 May 2022 16:16:51 +0300 Subject: [PATCH 05/74] Dynamically load refactored dev-platform module --- platformio/managers/__init__.py | 13 ------------- platformio/managers/platform.py | 16 ---------------- platformio/platform/factory.py | 20 ++++++++++++-------- 3 files changed, 12 insertions(+), 37 deletions(-) delete mode 100644 platformio/managers/__init__.py delete mode 100644 platformio/managers/platform.py diff --git a/platformio/managers/__init__.py b/platformio/managers/__init__.py deleted file mode 100644 index b0514903..00000000 --- a/platformio/managers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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. diff --git a/platformio/managers/platform.py b/platformio/managers/platform.py deleted file mode 100644 index 9ef5e646..00000000 --- a/platformio/managers/platform.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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. - -# Backward compatibility with legacy dev-platforms -from platformio.platform.base import PlatformBase # pylint: disable=unused-import diff --git a/platformio/platform/factory.py b/platformio/platform/factory.py index 2df9e1b4..7b6348ac 100644 --- a/platformio/platform/factory.py +++ b/platformio/platform/factory.py @@ -14,11 +14,12 @@ import os import re +import sys from platformio import fs from platformio.compat import load_python_module from platformio.package.meta import PackageItem -from platformio.platform.base import PlatformBase +from platformio.platform import base from platformio.platform.exception import UnknownPlatform @@ -28,15 +29,18 @@ class PlatformFactory(object): name = re.sub(r"[^\da-z\_]+", "", name, flags=re.I) return "%sPlatform" % name.lower().capitalize() - @staticmethod - def load_module(name, path): + @classmethod + def load_platform_module(cls, name, path, by_fallback=False): try: return load_python_module("platformio.platform.%s" % name, path) - except ImportError: + except ImportError as exc: + if "platformio.managers" in str(exc) and not by_fallback: + sys.modules["platformio.managers.platform"] = base + return cls.load_platform_module(name, path, by_fallback=True) raise UnknownPlatform(name) @classmethod - def new(cls, pkg_or_spec, autoinstall=False) -> PlatformBase: + def new(cls, pkg_or_spec, autoinstall=False) -> base.PlatformBase: # pylint: disable=import-outside-toplevel from platformio.package.manager.platform import PlatformPackageManager @@ -72,16 +76,16 @@ class PlatformFactory(object): platform_cls = None if os.path.isfile(os.path.join(platform_dir, "platform.py")): platform_cls = getattr( - cls.load_module( + cls.load_platform_module( platform_name, os.path.join(platform_dir, "platform.py") ), cls.get_clsname(platform_name), ) else: platform_cls = type( - str(cls.get_clsname(platform_name)), (PlatformBase,), {} + str(cls.get_clsname(platform_name)), (base.PlatformBase,), {} ) _instance = platform_cls(os.path.join(platform_dir, "platform.json")) - assert isinstance(_instance, PlatformBase) + assert isinstance(_instance, base.PlatformBase) return _instance From ccc7d9c9a41de065ca4a162c1476a633eca08d4f Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 18 May 2022 16:17:20 +0300 Subject: [PATCH 06/74] Update CI badges --- README.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index ff10f8f2..4bdda76f 100644 --- a/README.rst +++ b/README.rst @@ -8,18 +8,18 @@ PlatformIO Core .. image:: https://github.com/platformio/platformio-core/workflows/Core/badge.svg :target: https://docs.platformio.org/en/latest/core/index.html :alt: CI Build for PlatformIO Core -.. image:: https://github.com/platformio/platformio-core/workflows/Examples/badge.svg - :target: https://github.com/platformio/platformio-examples - :alt: CI Build for dev-platform examples .. image:: https://github.com/platformio/platformio-core/workflows/Docs/badge.svg :target: https://docs.platformio.org?utm_source=github&utm_medium=core :alt: CI Build for Docs +.. image:: https://github.com/platformio/platformio-core/workflows/Examples/badge.svg + :target: https://github.com/platformio/platformio-examples + :alt: CI Build for dev-platform examples +.. image:: https://github.com/platformio/platformio-core/workflows/Projects/badge.svg + :target: https://docs.platformio.org/en/latest/tutorials/index.html#projects + :alt: CI Build for the Community Projects .. image:: https://img.shields.io/pypi/v/platformio.svg :target: https://pypi.python.org/pypi/platformio/ :alt: Latest Version -.. image:: https://img.shields.io/badge/license-Apache%202.0-blue.svg - :target: https://pypi.python.org/pypi/platformio/ - :alt: License .. image:: https://img.shields.io/badge/PlatformIO-Labs-orange.svg :alt: PlatformIO Labs :target: https://piolabs.com/?utm_source=github&utm_medium=core From d2d46f4aea4d3510f6b68e34b261a9221cd48f68 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 18 May 2022 18:47:27 +0300 Subject: [PATCH 07/74] Ignore "DeprecationWarning: the load_module() method is deprecated and slated for removal in Python 3.12" SCons warning --- pytest.ini | 6 ------ tox.ini | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index b3dbaa57..00000000 --- a/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -filterwarnings = - error - # Marshmallow - ignore:distutils Version classes are deprecated. Use packaging.version instead. - ignore:The distutils package is deprecated and slated for removal in Python \ No newline at end of file diff --git a/tox.ini b/tox.ini index 0ee9c0ec..92555cf5 100644 --- a/tox.ini +++ b/tox.ini @@ -19,6 +19,12 @@ envlist = py36,py37,py38,py39 profile = black known_third_party=OpenSSL, SCons, jsonrpc, twisted, zope +[pytest] +filterwarnings = + error + # SCons + ignore:.*_ImportRedirect.find_spec() + [testenv] passenv = * usedevelop = True From 3c2afeba898e2336fe06efd1d02e11782ac0e1fc Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 18 May 2022 22:18:15 +0300 Subject: [PATCH 08/74] Fix account related tests --- platformio/commands/account.py | 28 ++- tests/commands/test_account_org_team.py | 290 +++++++++++------------- tests/conftest.py | 6 +- 3 files changed, 148 insertions(+), 176 deletions(-) diff --git a/platformio/commands/account.py b/platformio/commands/account.py index 48b26717..6e17fa40 100644 --- a/platformio/commands/account.py +++ b/platformio/commands/account.py @@ -83,7 +83,7 @@ def validate_password(value): def account_register(username, email, password, firstname, lastname): client = AccountClient() client.registration(username, email, password, firstname, lastname) - return click.secho( + click.secho( "An account has been successfully created. " "Please check your mail to activate your account and verify your email address.", fg="green", @@ -96,14 +96,14 @@ def account_register(username, email, password, firstname, lastname): def account_login(username, password): client = AccountClient() client.login(username, password) - return click.secho("Successfully logged in!", fg="green") + click.secho("Successfully logged in!", fg="green") @cli.command("logout", short_help="Log out of PlatformIO Account") def account_logout(): client = AccountClient() client.logout() - return click.secho("Successfully logged out!", fg="green") + click.secho("Successfully logged out!", fg="green") @cli.command("password", short_help="Change password") @@ -112,7 +112,7 @@ def account_logout(): def account_password(old_password, new_password): client = AccountClient() client.change_password(old_password, new_password) - return click.secho("Password successfully changed!", fg="green") + click.secho("Password successfully changed!", fg="green") @cli.command("token", short_help="Get or regenerate Authentication Token") @@ -123,8 +123,9 @@ def account_token(password, regenerate, json_output): client = AccountClient() auth_token = client.auth_token(password, regenerate) if json_output: - return click.echo(json.dumps({"status": "success", "result": auth_token})) - return click.secho("Personal Authentication Token: %s" % auth_token, fg="green") + click.echo(json.dumps({"status": "success", "result": auth_token})) + return + click.secho("Personal Authentication Token: %s" % auth_token, fg="green") @cli.command("forgot", short_help="Forgot password") @@ -132,7 +133,7 @@ def account_token(password, regenerate, json_output): def account_forgot(username): client = AccountClient() client.forgot_password(username) - return click.secho( + click.secho( "If this account is registered, we will send the " "further instructions to your email.", fg="green", @@ -171,11 +172,13 @@ def account_update(current_password, **kwargs): except AccountNotAuthorized: pass if email_changed: - return click.secho( + click.secho( "Please check your mail to verify your new email address and re-login. ", fg="yellow", ) - return click.secho("Please re-login.", fg="yellow") + return None + click.secho("Please re-login.", fg="yellow") + return None @cli.command("destroy", short_help="Destroy account") @@ -192,7 +195,7 @@ def account_destroy(): client.logout() except AccountNotAuthorized: pass - return click.secho( + click.secho( "User account has been destroyed.", fg="green", ) @@ -205,7 +208,8 @@ def account_show(offline, json_output): client = AccountClient() info = client.get_account_info(offline) if json_output: - return click.echo(json.dumps(info)) + click.echo(json.dumps(info)) + return click.echo() if info.get("profile"): print_profile(info["profile"]) @@ -213,7 +217,7 @@ def account_show(offline, json_output): print_packages(info["packages"]) if info.get("subscriptions"): print_subscriptions(info["subscriptions"]) - return click.echo() + click.echo() def print_profile(profile): diff --git a/tests/commands/test_account_org_team.py b/tests/commands/test_account_org_team.py index f04af536..53344502 100644 --- a/tests/commands/test_account_org_team.py +++ b/tests/commands/test_account_org_team.py @@ -26,42 +26,46 @@ from platformio.commands.org import cli as cmd_org from platformio.commands.team import cli as cmd_team pytestmark = pytest.mark.skipif( - not (os.environ.get("TEST_EMAIL_LOGIN") and os.environ.get("TEST_EMAIL_PASSWORD")), - reason="requires TEST_EMAIL_LOGIN, TEST_EMAIL_PASSWORD environ variables", + not all( + os.environ.get(name) + for name in ( + "TEST_EMAIL_LOGIN", + "TEST_EMAIL_PASSWORD", + "TEST_EMAIL_IMAP_SERVER", + ) + ), + reason=( + "requires TEST_EMAIL_LOGIN, TEST_EMAIL_PASSWORD, " + "and TEST_EMAIL_IMAP_SERVER environment variables" + ), ) -username = None -email = None -splited_email = None -firstname = None -lastname = None -password = None +USER_NAME = "test-piocore-%s" % str(random.randint(0, 100000)) +USER_EMAIL = os.environ.get("TEST_EMAIL_LOGIN", "").replace("@", f"+{USER_NAME}@") +USER_PASSWORD = f"Qwerty-{random.randint(0, 100000)}" +USER_FIRST_NAME = "FirstName" +USER_LAST_NAME = "LastName" -orgname = None -display_name = None -second_username = None +ORG_NAME = "testorg-piocore-%s" % str(random.randint(0, 100000)) +ORG_DISPLAY_NAME = "Test Org for PIO Core" +EXISTING_OWNER = "piolabs" -teamname = None -team_description = None +TEAM_NAME = "test-" + str(random.randint(0, 100000)) +TEAM_DESCRIPTION = "team for CI test" -def test_prepare(): - global username, email, splited_email, firstname, lastname - global password, orgname, display_name, second_username, teamname, team_description - - username = "test-piocore-%s" % str(random.randint(0, 100000)) - splited_email = os.environ.get("TEST_EMAIL_LOGIN").split("@") - email = "%s+%s@%s" % (splited_email[0], username, splited_email[1]) - firstname = "Test" - lastname = "User" - password = "Qwerty123!" - - orgname = "testorg-piocore-%s" % str(random.randint(0, 100000)) - display_name = "Test Org for PIO Core" - second_username = "ivankravets" - - teamname = "test-" + str(random.randint(0, 100000)) - team_description = "team for CI test" +def verify_account(email_contents): + link = ( + email_contents.split("Click on the link below to start this process.")[1] + .split("This link will expire within 12 hours.")[0] + .strip() + ) + with requests.Session() as session: + result = session.get(link).text + link = result.split(' 0 + assert result.exit_code != 0 assert result.exception - assert "You are not authorized! Please log in to PIO Account" in str( - result.exception - ) + assert "You are not authorized!" in str(result.exception) + # use env tokem os.environ["PLATFORMIO_AUTH_TOKEN"] = token result = clirunner.invoke( cmd_account, - ["token", "--password", password, "--json-output"], + ["show", "--json-output"], ) validate_cliresult(result) json_result = json.loads(result.output.strip()) - assert json_result - assert json_result.get("status") == "success" - assert json_result.get("result") == token + assert json_result.get("user_id") + assert json_result.get("profile").get("username") == USER_NAME + assert json_result.get("profile").get("email") == USER_EMAIL os.environ.pop("PLATFORMIO_AUTH_TOKEN") result = clirunner.invoke( cmd_account, - ["login", "-u", username, "-p", password], + ["login", "-u", USER_NAME, "-p", USER_PASSWORD], ) validate_cliresult(result) @@ -234,7 +206,7 @@ def test_account_change_password(clirunner, validate_cliresult, isolated_pio_cor [ "password", "--old-password", - password, + USER_PASSWORD, "--new-password", new_password, ], @@ -242,11 +214,12 @@ def test_account_change_password(clirunner, validate_cliresult, isolated_pio_cor validate_cliresult(result) assert "Password successfully changed!" in result.output - clirunner.invoke(cmd_account, ["logout"]) + result = clirunner.invoke(cmd_account, ["logout"]) + validate_cliresult(result) result = clirunner.invoke( cmd_account, - ["login", "-u", username, "-p", new_password], + ["login", "-u", USER_NAME, "-p", new_password], ) validate_cliresult(result) @@ -257,7 +230,7 @@ def test_account_change_password(clirunner, validate_cliresult, isolated_pio_cor "--old-password", new_password, "--new-password", - password, + USER_PASSWORD, ], ) validate_cliresult(result) @@ -266,30 +239,29 @@ def test_account_change_password(clirunner, validate_cliresult, isolated_pio_cor def test_account_update( clirunner, validate_cliresult, receive_email, isolated_pio_core ): - global username - global email - global firstname - global lastname + global USER_NAME, USER_EMAIL, USER_FIRST_NAME, USER_LAST_NAME - firstname = "First " + str(random.randint(0, 100000)) - lastname = "Last" + str(random.randint(0, 100000)) + USER_NAME = "test-piocore-%s" % str(random.randint(0, 100000)) + USER_EMAIL = os.environ.get("TEST_EMAIL_LOGIN", "").replace( + "@", f"+new-{USER_NAME}@" + ) + USER_FIRST_NAME = "First " + str(random.randint(0, 100000)) + USER_LAST_NAME = "Last" + str(random.randint(0, 100000)) - username = "username" + str(random.randint(0, 100000)) - email = "%s+new-%s@%s" % (splited_email[0], username, splited_email[1]) result = clirunner.invoke( cmd_account, [ "update", "--current-password", - password, + USER_PASSWORD, "--firstname", - firstname, + USER_FIRST_NAME, "--lastname", - lastname, + USER_LAST_NAME, "--username", - username, + USER_NAME, "--email", - email, + USER_EMAIL, ], ) validate_cliresult(result) @@ -298,18 +270,7 @@ def test_account_update( "Please check your mail to verify your new email address and re-login. " in result.output ) - - result = receive_email(email) - link = ( - result.split("Click on the link below to start this process.")[1] - .split("This link will expire within 12 hours.")[0] - .strip() - ) - session = requests.Session() - result = session.get(link).text - link = result.split(' 0 assert result.exception - assert "You are not authorized! Please log in to PIO Account" in str( - result.exception - ) + assert "You are not authorized!" in str(result.exception) result = clirunner.invoke( cmd_account, - ["login", "-u", username, "-p", password], + ["login", "-u", USER_NAME, "-p", USER_PASSWORD], ) validate_cliresult(result) -# def test_account_destroy_with_linked_resources( +# def _test_account_destroy_with_linked_resources( # clirunner, validate_cliresult, receive_email, isolated_pio_core, tmpdir_factory # ): # package_url = "https://github.com/bblanchon/ArduinoJson/archive/v6.11.0.tar.gz" @@ -363,44 +322,47 @@ def test_account_update( def test_org_create(clirunner, validate_cliresult, isolated_pio_core): result = clirunner.invoke( cmd_org, - ["create", "--email", email, "--displayname", display_name, orgname], + ["create", "--email", USER_EMAIL, "--displayname", ORG_DISPLAY_NAME, ORG_NAME], ) validate_cliresult(result) def test_org_list(clirunner, validate_cliresult, isolated_pio_core): - # pio org list result = clirunner.invoke(cmd_org, ["list", "--json-output"]) validate_cliresult(result) json_result = json.loads(result.output.strip()) assert json_result == [ { - "orgname": orgname, - "displayname": display_name, - "email": email, + "orgname": ORG_NAME, + "displayname": ORG_DISPLAY_NAME, + "email": USER_EMAIL, "owners": [ - {"username": username, "firstname": firstname, "lastname": lastname} + { + "username": USER_NAME, + "firstname": USER_FIRST_NAME, + "lastname": USER_LAST_NAME, + } ], } ] def test_org_add_owner(clirunner, validate_cliresult, isolated_pio_core): - result = clirunner.invoke(cmd_org, ["add", orgname, second_username]) + result = clirunner.invoke(cmd_org, ["add", ORG_NAME, EXISTING_OWNER]) validate_cliresult(result) result = clirunner.invoke(cmd_org, ["list", "--json-output"]) validate_cliresult(result) - assert second_username in result.output + assert EXISTING_OWNER in result.output def test_org_remove_owner(clirunner, validate_cliresult, isolated_pio_core): - result = clirunner.invoke(cmd_org, ["remove", orgname, second_username]) + result = clirunner.invoke(cmd_org, ["remove", ORG_NAME, EXISTING_OWNER]) validate_cliresult(result) result = clirunner.invoke(cmd_org, ["list", "--json-output"]) validate_cliresult(result) - assert second_username not in result.output + assert EXISTING_OWNER not in result.output def test_org_update(clirunner, validate_cliresult, isolated_pio_core): @@ -411,8 +373,8 @@ def test_org_update(clirunner, validate_cliresult, isolated_pio_core): cmd_org, [ "update", - orgname, - "--new-orgname", + ORG_NAME, + "--orgname", new_orgname, "--displayname", new_display_name, @@ -427,9 +389,13 @@ def test_org_update(clirunner, validate_cliresult, isolated_pio_core): { "orgname": new_orgname, "displayname": new_display_name, - "email": email, + "email": USER_EMAIL, "owners": [ - {"username": username, "firstname": firstname, "lastname": lastname} + { + "username": USER_NAME, + "firstname": USER_FIRST_NAME, + "lastname": USER_LAST_NAME, + } ], } ] @@ -439,10 +405,10 @@ def test_org_update(clirunner, validate_cliresult, isolated_pio_core): [ "update", new_orgname, - "--new-orgname", - orgname, + "--orgname", + ORG_NAME, "--displayname", - display_name, + ORG_DISPLAY_NAME, ], ) validate_cliresult(result) @@ -453,9 +419,9 @@ def test_team_create(clirunner, validate_cliresult, isolated_pio_core): cmd_team, [ "create", - "%s:%s" % (orgname, teamname), + "%s:%s" % (ORG_NAME, TEAM_NAME), "--description", - team_description, + TEAM_DESCRIPTION, ], ) validate_cliresult(result) @@ -464,55 +430,55 @@ def test_team_create(clirunner, validate_cliresult, isolated_pio_core): def test_team_list(clirunner, validate_cliresult, isolated_pio_core): result = clirunner.invoke( cmd_team, - ["list", "%s" % orgname, "--json-output"], + ["list", "%s" % ORG_NAME, "--json-output"], ) validate_cliresult(result) json_result = json.loads(result.output.strip()) for item in json_result: del item["id"] assert json_result == [ - {"name": teamname, "description": team_description, "members": []} + {"name": TEAM_NAME, "description": TEAM_DESCRIPTION, "members": []} ] -def test_team_add_member(clirunner, validate_cliresult, isolated_pio_core): +def _test_team_add_member(clirunner, validate_cliresult, isolated_pio_core): result = clirunner.invoke( cmd_team, - ["add", "%s:%s" % (orgname, teamname), second_username], + ["add", "%s:%s" % (ORG_NAME, TEAM_NAME), EXISTING_OWNER], ) validate_cliresult(result) result = clirunner.invoke( cmd_team, - ["list", "%s" % orgname, "--json-output"], + ["list", "%s" % ORG_NAME, "--json-output"], ) validate_cliresult(result) - assert second_username in result.output + assert EXISTING_OWNER in result.output -def test_team_remove(clirunner, validate_cliresult, isolated_pio_core): +def _test_team_remove(clirunner, validate_cliresult, isolated_pio_core): result = clirunner.invoke( cmd_team, - ["remove", "%s:%s" % (orgname, teamname), second_username], + ["remove", "%s:%s" % (ORG_NAME, TEAM_NAME), EXISTING_OWNER], ) validate_cliresult(result) result = clirunner.invoke( cmd_team, - ["list", "%s" % orgname, "--json-output"], + ["list", "%s" % ORG_NAME, "--json-output"], ) validate_cliresult(result) - assert second_username not in result.output + assert EXISTING_OWNER not in result.output -def test_team_update(clirunner, validate_cliresult, receive_email, isolated_pio_core): +def _test_team_update(clirunner, validate_cliresult, receive_email, isolated_pio_core): new_teamname = "new-" + str(random.randint(0, 100000)) newteam_description = "Updated Description" result = clirunner.invoke( cmd_team, [ "update", - "%s:%s" % (orgname, teamname), + "%s:%s" % (ORG_NAME, TEAM_NAME), "--name", new_teamname, "--description", @@ -523,7 +489,7 @@ def test_team_update(clirunner, validate_cliresult, receive_email, isolated_pio_ result = clirunner.invoke( cmd_team, - ["list", "%s" % orgname, "--json-output"], + ["list", "%s" % ORG_NAME, "--json-output"], ) validate_cliresult(result) json_result = json.loads(result.output.strip()) @@ -537,20 +503,22 @@ def test_team_update(clirunner, validate_cliresult, receive_email, isolated_pio_ cmd_team, [ "update", - "%s:%s" % (orgname, new_teamname), + "%s:%s" % (ORG_NAME, new_teamname), "--name", - teamname, + TEAM_NAME, "--description", - team_description, + TEAM_DESCRIPTION, ], ) validate_cliresult(result) def test_cleanup(clirunner, validate_cliresult, receive_email, isolated_pio_core): - result = clirunner.invoke(cmd_team, ["destroy", "%s:%s" % (orgname, teamname)], "y") + result = clirunner.invoke( + cmd_team, ["destroy", "%s:%s" % (ORG_NAME, TEAM_NAME)], "y" + ) validate_cliresult(result) - result = clirunner.invoke(cmd_org, ["destroy", orgname], "y") + result = clirunner.invoke(cmd_org, ["destroy", ORG_NAME], "y") validate_cliresult(result) result = clirunner.invoke(cmd_account, ["destroy"], "y") validate_cliresult(result) diff --git a/tests/conftest.py b/tests/conftest.py index 6f4a6088..76b53dd8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -93,9 +93,9 @@ def without_internet(monkeypatch): @pytest.fixture def receive_email(): # pylint:disable=redefined-outer-name, too-many-locals def _receive_email(from_who): - test_email = os.environ.get("TEST_EMAIL_LOGIN") - test_password = os.environ.get("TEST_EMAIL_PASSWORD") - imap_server = os.environ.get("TEST_EMAIL_IMAP_SERVER") or "imap.gmail.com" + test_email = os.environ["TEST_EMAIL_LOGIN"] + test_password = os.environ["TEST_EMAIL_PASSWORD"] + imap_server = os.environ["TEST_EMAIL_IMAP_SERVER"] def get_body(msg): if msg.is_multipart(): From 4dfc561551d615acd2f24cb78e2b6656faa400d6 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 18 May 2022 22:18:53 +0300 Subject: [PATCH 09/74] Dynamically import legacy platform module --- platformio/platform/factory.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platformio/platform/factory.py b/platformio/platform/factory.py index 7b6348ac..d9aef293 100644 --- a/platformio/platform/factory.py +++ b/platformio/platform/factory.py @@ -29,14 +29,13 @@ class PlatformFactory(object): name = re.sub(r"[^\da-z\_]+", "", name, flags=re.I) return "%sPlatform" % name.lower().capitalize() - @classmethod - def load_platform_module(cls, name, path, by_fallback=False): + @staticmethod + def load_platform_module(cls, name, path): + # support for legacy dev-platforms + sys.modules["platformio.managers.platform"] = base try: return load_python_module("platformio.platform.%s" % name, path) except ImportError as exc: - if "platformio.managers" in str(exc) and not by_fallback: - sys.modules["platformio.managers.platform"] = base - return cls.load_platform_module(name, path, by_fallback=True) raise UnknownPlatform(name) @classmethod From 7badd54c89d7fccd6c66096d60003d9bfe81da97 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 18 May 2022 22:23:09 +0300 Subject: [PATCH 10/74] Exclude "esphome" from testing projects for macOS and Windows --- .github/workflows/projects.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/projects.yml b/.github/workflows/projects.yml index 29a01714..4799c885 100644 --- a/.github/workflows/projects.yml +++ b/.github/workflows/projects.yml @@ -34,6 +34,12 @@ jobs: config_dir: "OpenMQTTGateway" env_name: "esp32-m5atom" os: [ubuntu-latest, windows-latest, macos-latest] + exclude: + - os: macos-latest + project: esphome + exclude: + - os: windows-latest + project: esphome runs-on: ${{ matrix.os }} steps: From 2e2735a49c2b8425addb32202bc4ed6b39083f9f Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 18 May 2022 22:28:05 +0300 Subject: [PATCH 11/74] Typo fix --- platformio/platform/factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/platform/factory.py b/platformio/platform/factory.py index d9aef293..7679b59f 100644 --- a/platformio/platform/factory.py +++ b/platformio/platform/factory.py @@ -30,7 +30,7 @@ class PlatformFactory(object): return "%sPlatform" % name.lower().capitalize() @staticmethod - def load_platform_module(cls, name, path): + def load_platform_module(name, path): # support for legacy dev-platforms sys.modules["platformio.managers.platform"] = base try: From bb8b115a0b998cef572704d30b8fc88aa6996ae3 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 18 May 2022 22:29:40 +0300 Subject: [PATCH 12/74] Fix projects CI workflow --- .github/workflows/projects.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/projects.yml b/.github/workflows/projects.yml index 4799c885..dcd93ea4 100644 --- a/.github/workflows/projects.yml +++ b/.github/workflows/projects.yml @@ -37,7 +37,6 @@ jobs: exclude: - os: macos-latest project: esphome - exclude: - os: windows-latest project: esphome From 0c4c4ac657db4e187cdcad431e505486385ddce3 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 18 May 2022 23:14:15 +0300 Subject: [PATCH 13/74] Use globals() instead of sys.modules --- platformio/builder/tools/piolib.py | 8 ++++---- platformio/package/vcsclient.py | 5 ++--- platformio/platform/factory.py | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index 2372c3de..6529fbb0 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -60,16 +60,16 @@ class LibBuilderFactory(object): elif used_frameworks: clsname = "%sLibBuilder" % used_frameworks[0].capitalize() - obj = getattr(sys.modules[__name__], clsname)(env, path, verbose=verbose) + obj = globals()[clsname](env, path, verbose=verbose) # Handle PlatformIOLibBuilder.manifest.build.builder # pylint: disable=protected-access if isinstance(obj, PlatformIOLibBuilder) and obj._manifest.get("build", {}).get( "builder" ): - obj = getattr( - sys.modules[__name__], obj._manifest.get("build", {}).get("builder") - )(env, path, verbose=verbose) + obj = globals()[obj._manifest.get("build", {}).get("builder")]( + env, path, verbose=verbose + ) assert isinstance(obj, LibBuilderBase) return obj diff --git a/platformio/package/vcsclient.py b/platformio/package/vcsclient.py index ed7434b0..dc4c090d 100644 --- a/platformio/package/vcsclient.py +++ b/platformio/package/vcsclient.py @@ -15,7 +15,6 @@ import os import re import subprocess -import sys from urllib.parse import urlparse from platformio import proc @@ -47,12 +46,12 @@ class VCSClientFactory(object): if not type_: raise VCSBaseException("VCS: Unknown repository type %s" % remote_url) try: - obj = getattr(sys.modules[__name__], "%sClient" % type_.capitalize())( + obj = globals()["%sClient" % type_.capitalize()]( src_dir, remote_url, tag, silent ) assert isinstance(obj, VCSClientBase) return obj - except (AttributeError, AssertionError): + except (KeyError, AssertionError): raise VCSBaseException("VCS: Unknown repository type %s" % remote_url) diff --git a/platformio/platform/factory.py b/platformio/platform/factory.py index 7679b59f..0345cb2a 100644 --- a/platformio/platform/factory.py +++ b/platformio/platform/factory.py @@ -31,11 +31,11 @@ class PlatformFactory(object): @staticmethod def load_platform_module(name, path): - # support for legacy dev-platforms + # backward compatibiility with the legacy dev-platforms sys.modules["platformio.managers.platform"] = base try: return load_python_module("platformio.platform.%s" % name, path) - except ImportError as exc: + except ImportError: raise UnknownPlatform(name) @classmethod From be0acaed40b30e2e0aef40ce030c75475f835cb8 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 12:02:52 +0300 Subject: [PATCH 14/74] Cleanup CI configs --- .github/workflows/core.yml | 8 ++++++-- .github/workflows/projects.yml | 5 ----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 0a0e9a16..3e2e7785 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -14,15 +14,18 @@ jobs: python-version: "3.6" - os: windows-latest python-version: "3.10" + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: "recursive" + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} + - name: Install dependencies run: | python -m pip install --upgrade pip @@ -31,6 +34,7 @@ jobs: - name: Python Lint run: | tox -e lint + - name: Integration Tests env: TEST_EMAIL_LOGIN: ${{ secrets.TEST_EMAIL_LOGIN }} diff --git a/.github/workflows/projects.yml b/.github/workflows/projects.yml index dcd93ea4..29a01714 100644 --- a/.github/workflows/projects.yml +++ b/.github/workflows/projects.yml @@ -34,11 +34,6 @@ jobs: config_dir: "OpenMQTTGateway" env_name: "esp32-m5atom" os: [ubuntu-latest, windows-latest, macos-latest] - exclude: - - os: macos-latest - project: esphome - - os: windows-latest - project: esphome runs-on: ${{ matrix.os }} steps: From 61383f9b085656ae1edfde7f581286cc85cef30b Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 12:18:08 +0300 Subject: [PATCH 15/74] Pass extra envs to the tox --- .github/workflows/core.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 3e2e7785..043a9f8f 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -37,6 +37,7 @@ jobs: - name: Integration Tests env: + TOX_TESTENV_PASSENV: ${{ secrets.TOX_TESTENV_PASSENV }} TEST_EMAIL_LOGIN: ${{ secrets.TEST_EMAIL_LOGIN }} TEST_EMAIL_PASSWORD: ${{ secrets.TEST_EMAIL_PASSWORD }} TEST_EMAIL_IMAP_SERVER: ${{ secrets.TEST_EMAIL_IMAP_SERVER }} From 341955826597529cc3c79cc5f505a99d486bfb9c Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 13:29:48 +0300 Subject: [PATCH 16/74] Use private Github Actions environment --- .github/workflows/core.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 043a9f8f..f704f8af 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -15,7 +15,9 @@ jobs: - os: windows-latest python-version: "3.10" + environment: private runs-on: ${{ matrix.os }} + steps: - uses: actions/checkout@v3 with: @@ -37,7 +39,6 @@ jobs: - name: Integration Tests env: - TOX_TESTENV_PASSENV: ${{ secrets.TOX_TESTENV_PASSENV }} TEST_EMAIL_LOGIN: ${{ secrets.TEST_EMAIL_LOGIN }} TEST_EMAIL_PASSWORD: ${{ secrets.TEST_EMAIL_PASSWORD }} TEST_EMAIL_IMAP_SERVER: ${{ secrets.TEST_EMAIL_IMAP_SERVER }} From 9da7c42be4c5d706c086e6244d70ee92af814505 Mon Sep 17 00:00:00 2001 From: Valerii Koval Date: Thu, 19 May 2022 14:31:00 +0300 Subject: [PATCH 17/74] Exclude ESPHome from projects CI on Windows --- .github/workflows/projects.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/projects.yml b/.github/workflows/projects.yml index 29a01714..1e21bed1 100644 --- a/.github/workflows/projects.yml +++ b/.github/workflows/projects.yml @@ -34,6 +34,9 @@ jobs: config_dir: "OpenMQTTGateway" env_name: "esp32-m5atom" os: [ubuntu-latest, windows-latest, macos-latest] + exclude: + - os: windows-latest + project: {"esphome": "", "repository": "esphome/esphome", "folder": "esphome", "config_dir": "esphome", "env_name": "esp32-arduino"} runs-on: ${{ matrix.os }} steps: From f4d976945089ec55c1654534b105e51bb2c7cfc0 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 14:36:24 +0300 Subject: [PATCH 18/74] Move ino2cpp tests to the misc folder --- tests/{ino2cpp => misc}/__init__.py | 0 tests/misc/ino2cpp/__init__.py | 13 +++++++++++++ .../ino2cpp/examples}/basic/basic.ino | 0 .../ino2cpp/examples}/multifiles/bar.ino | 0 .../ino2cpp/examples}/multifiles/foo.pde | 0 .../ino2cpp/examples}/strmultilines/main.ino | 0 tests/{ => misc/ino2cpp}/test_ino2cpp.py | 12 ++++++------ tests/{ => misc}/test_maintenance.py | 0 tests/{ => misc}/test_misc.py | 0 9 files changed, 19 insertions(+), 6 deletions(-) rename tests/{ino2cpp => misc}/__init__.py (100%) create mode 100644 tests/misc/ino2cpp/__init__.py rename tests/{ino2cpp => misc/ino2cpp/examples}/basic/basic.ino (100%) rename tests/{ino2cpp => misc/ino2cpp/examples}/multifiles/bar.ino (100%) rename tests/{ino2cpp => misc/ino2cpp/examples}/multifiles/foo.pde (100%) rename tests/{ino2cpp => misc/ino2cpp/examples}/strmultilines/main.ino (100%) rename tests/{ => misc/ino2cpp}/test_ino2cpp.py (79%) rename tests/{ => misc}/test_maintenance.py (100%) rename tests/{ => misc}/test_misc.py (100%) diff --git a/tests/ino2cpp/__init__.py b/tests/misc/__init__.py similarity index 100% rename from tests/ino2cpp/__init__.py rename to tests/misc/__init__.py diff --git a/tests/misc/ino2cpp/__init__.py b/tests/misc/ino2cpp/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/tests/misc/ino2cpp/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/tests/ino2cpp/basic/basic.ino b/tests/misc/ino2cpp/examples/basic/basic.ino similarity index 100% rename from tests/ino2cpp/basic/basic.ino rename to tests/misc/ino2cpp/examples/basic/basic.ino diff --git a/tests/ino2cpp/multifiles/bar.ino b/tests/misc/ino2cpp/examples/multifiles/bar.ino similarity index 100% rename from tests/ino2cpp/multifiles/bar.ino rename to tests/misc/ino2cpp/examples/multifiles/bar.ino diff --git a/tests/ino2cpp/multifiles/foo.pde b/tests/misc/ino2cpp/examples/multifiles/foo.pde similarity index 100% rename from tests/ino2cpp/multifiles/foo.pde rename to tests/misc/ino2cpp/examples/multifiles/foo.pde diff --git a/tests/ino2cpp/strmultilines/main.ino b/tests/misc/ino2cpp/examples/strmultilines/main.ino similarity index 100% rename from tests/ino2cpp/strmultilines/main.ino rename to tests/misc/ino2cpp/examples/strmultilines/main.ino diff --git a/tests/test_ino2cpp.py b/tests/misc/ino2cpp/test_ino2cpp.py similarity index 79% rename from tests/test_ino2cpp.py rename to tests/misc/ino2cpp/test_ino2cpp.py index d1434df7..9c997822 100644 --- a/tests/test_ino2cpp.py +++ b/tests/misc/ino2cpp/test_ino2cpp.py @@ -17,16 +17,16 @@ from os.path import dirname, isdir, join, normpath from platformio.commands.ci import cli as cmd_ci -INOTEST_DIR = normpath(join(dirname(__file__), "ino2cpp")) +EXAMPLES_DIR = normpath(join(dirname(__file__), "examples")) def pytest_generate_tests(metafunc): if "piotest_dir" not in metafunc.fixturenames: return test_dirs = [] - for name in listdir(INOTEST_DIR): - if isdir(join(INOTEST_DIR, name)): - test_dirs.append(join(INOTEST_DIR, name)) + for name in listdir(EXAMPLES_DIR): + if isdir(join(EXAMPLES_DIR, name)): + test_dirs.append(join(EXAMPLES_DIR, name)) test_dirs.sort() metafunc.parametrize("piotest_dir", test_dirs) @@ -37,10 +37,10 @@ def test_example(clirunner, validate_cliresult, piotest_dir): def test_warning_line(clirunner, validate_cliresult): - result = clirunner.invoke(cmd_ci, [join(INOTEST_DIR, "basic"), "-b", "uno"]) + result = clirunner.invoke(cmd_ci, [join(EXAMPLES_DIR, "basic"), "-b", "uno"]) validate_cliresult(result) assert 'basic.ino:16:14: warning: #warning "Line number is 16"' in result.output assert 'basic.ino:46:2: warning: #warning "Line number is 46"' in result.output - result = clirunner.invoke(cmd_ci, [join(INOTEST_DIR, "strmultilines"), "-b", "uno"]) + result = clirunner.invoke(cmd_ci, [join(EXAMPLES_DIR, "strmultilines"), "-b", "uno"]) validate_cliresult(result) assert 'main.ino:75:2: warning: #warning "Line 75"' in result.output diff --git a/tests/test_maintenance.py b/tests/misc/test_maintenance.py similarity index 100% rename from tests/test_maintenance.py rename to tests/misc/test_maintenance.py diff --git a/tests/test_misc.py b/tests/misc/test_misc.py similarity index 100% rename from tests/test_misc.py rename to tests/misc/test_misc.py From 06ed9ba77d4324fecdde96109f27ae15e98999e1 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 17:55:27 +0300 Subject: [PATCH 19/74] Fixed an issue when "build_src_flags" were applied outside project scope // Resolve #4277 --- HISTORY.rst | 5 ++++ platformio/builder/tools/piolib.py | 18 ++++++++------ platformio/builder/tools/platformio.py | 8 +------ tests/commands/test_run.py | 33 ++++++++++++++++++++++++++ tests/misc/ino2cpp/test_ino2cpp.py | 4 +++- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 9426f0dd..baae69bf 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -12,6 +12,11 @@ PlatformIO Core 6 **A professional collaborative platform for declarative, safety-critical, and test-driven embedded development.** +6.0.2 (2022-??-??) +~~~~~~~~~~~~~~~~~~ + +* Fixed an issue when the `build_src_flags `__ were applied outside the project scope (`issue #4277 `_) + 6.0.1 (2022-05-17) ~~~~~~~~~~~~~~~~~~ diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index 6529fbb0..b93962fb 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -1027,14 +1027,15 @@ def IsCompatibleLibBuilder(env, lb, verbose=int(ARGUMENTS.get("PIOVERBOSE", 0))) return True -def GetLibBuilders(env): # pylint: disable=too-many-branches - if DefaultEnvironment().get("__PIO_LIB_BUILDERS", None) is not None: +def GetLibBuilders(_): # pylint: disable=too-many-branches + env = DefaultEnvironment() + if env.get("__PIO_LIB_BUILDERS", None) is not None: return sorted( - DefaultEnvironment()["__PIO_LIB_BUILDERS"], + env["__PIO_LIB_BUILDERS"], key=lambda lb: 0 if lb.is_dependent else 1, ) - DefaultEnvironment().Replace(__PIO_LIB_BUILDERS=[]) + env.Replace(__PIO_LIB_BUILDERS=[]) verbose = int(ARGUMENTS.get("PIOVERBOSE", 0)) found_incompat = False @@ -1060,13 +1061,13 @@ def GetLibBuilders(env): # pylint: disable=too-many-branches ) continue if env.IsCompatibleLibBuilder(lb): - DefaultEnvironment().Append(__PIO_LIB_BUILDERS=[lb]) + env.Append(__PIO_LIB_BUILDERS=[lb]) else: found_incompat = True for lb in env.get("EXTRA_LIB_BUILDERS", []): if env.IsCompatibleLibBuilder(lb): - DefaultEnvironment().Append(__PIO_LIB_BUILDERS=[lb]) + env.Append(__PIO_LIB_BUILDERS=[lb]) else: found_incompat = True @@ -1077,7 +1078,7 @@ def GetLibBuilders(env): # pylint: disable=too-many-branches "ldf-compat-mode\n" ) - return DefaultEnvironment()["__PIO_LIB_BUILDERS"] + return env["__PIO_LIB_BUILDERS"] def ConfigureProjectLibBuilder(env): @@ -1126,7 +1127,9 @@ def ConfigureProjectLibBuilder(env): if lb.depbuilders: _print_deps_tree(lb, level + 1) + print(1, env.get("CPPDEFINES")) project = ProjectAsLibBuilder(env, "$PROJECT_DIR") + print(2, env.get("CPPDEFINES")) ldf_mode = LibBuilderBase.lib_ldf_mode.fget(project) # pylint: disable=no-member click.echo("LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf") @@ -1137,6 +1140,7 @@ def ConfigureProjectLibBuilder(env): project.install_dependencies() + print(3, env.get("CPPDEFINES")) lib_builders = env.GetLibBuilders() click.echo("Found %d compatible libraries" % len(lib_builders)) diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index fde67426..12e57f69 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -143,6 +143,7 @@ def ProcessProgramDeps(env): def ProcessProjectDeps(env): project_lib_builder = env.ConfigureProjectLibBuilder() + projenv = project_lib_builder.env # prepend project libs to the beginning of list env.Prepend(LIBS=project_lib_builder.build()) @@ -155,13 +156,6 @@ def ProcessProjectDeps(env): } ) - projenv = env.Clone() - - # CPPPATH from dependencies - projenv.PrependUnique(CPPPATH=project_lib_builder.env.get("CPPPATH")) - # extra build flags from `platformio.ini` - projenv.ProcessFlags(env.get("SRC_BUILD_FLAGS")) - if "test" in env.GetBuildType(): projenv.BuildSources( "$BUILD_TEST_DIR", "$PROJECT_TEST_DIR", "$PIOTEST_SRC_FILTER" diff --git a/tests/commands/test_run.py b/tests/commands/test_run.py index f5935668..504c3b00 100644 --- a/tests/commands/test_run.py +++ b/tests/commands/test_run.py @@ -29,6 +29,8 @@ def test_build_flags(clirunner, validate_cliresult, tmpdir): [env:native] platform = native extra_scripts = extra.py +lib_ldf_mode = deep+ +build_src_flags = -DI_AM_ONLY_SRC_FLAG build_flags = ; -DCOMMENTED_MACRO %s ; inline comment @@ -46,6 +48,12 @@ projenv.Append(CPPDEFINES="POST_SCRIPT_MACRO") tmpdir.mkdir("src").join("main.cpp").write( """ +#ifdef I_AM_ONLY_SRC_FLAG +#include +#else +#error "I_AM_ONLY_SRC_FLAG" +#endif + #if !defined(TEST_INT) || TEST_INT != 13 #error "TEST_INT" #endif @@ -54,6 +62,10 @@ projenv.Append(CPPDEFINES="POST_SCRIPT_MACRO") #error "TEST_STR_SPACE" #endif +#ifndef I_AM_COMPONENT +#error "I_AM_COMPONENT" +#endif + #ifndef POST_SCRIPT_MACRO #error "POST_SCRIPT_MACRO" #endif @@ -66,6 +78,27 @@ int main() { } """ ) + component_dir = tmpdir.mkdir("lib").mkdir("component") + component_dir.join("component.h").write( + """ +#define I_AM_COMPONENT + +#ifndef I_AM_ONLY_SRC_FLAG +#error "I_AM_ONLY_SRC_FLAG" +#endif + +void dummy(void); + """ + ) + component_dir.join("component.cpp").write( + """ +#ifdef I_AM_ONLY_SRC_FLAG +#error "I_AM_ONLY_SRC_FLAG" +#endif + +void dummy(void ) {}; + """ + ) result = clirunner.invoke(cmd_run, ["--project-dir", str(tmpdir), "--verbose"]) validate_cliresult(result) diff --git a/tests/misc/ino2cpp/test_ino2cpp.py b/tests/misc/ino2cpp/test_ino2cpp.py index 9c997822..72e3bcdd 100644 --- a/tests/misc/ino2cpp/test_ino2cpp.py +++ b/tests/misc/ino2cpp/test_ino2cpp.py @@ -41,6 +41,8 @@ def test_warning_line(clirunner, validate_cliresult): validate_cliresult(result) assert 'basic.ino:16:14: warning: #warning "Line number is 16"' in result.output assert 'basic.ino:46:2: warning: #warning "Line number is 46"' in result.output - result = clirunner.invoke(cmd_ci, [join(EXAMPLES_DIR, "strmultilines"), "-b", "uno"]) + result = clirunner.invoke( + cmd_ci, [join(EXAMPLES_DIR, "strmultilines"), "-b", "uno"] + ) validate_cliresult(result) assert 'main.ino:75:2: warning: #warning "Line 75"' in result.output From e3fea07596ec1db8f7018145c124827e984f2b69 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 18:23:06 +0300 Subject: [PATCH 20/74] Add deployment workflow --- .github/workflows/core.yml | 5 --- .github/workflows/deployment.yml | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/deployment.yml diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index f704f8af..df920e4e 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -15,7 +15,6 @@ jobs: - os: windows-latest python-version: "3.10" - environment: private runs-on: ${{ matrix.os }} steps: @@ -38,10 +37,6 @@ jobs: tox -e lint - name: Integration Tests - env: - TEST_EMAIL_LOGIN: ${{ secrets.TEST_EMAIL_LOGIN }} - TEST_EMAIL_PASSWORD: ${{ secrets.TEST_EMAIL_PASSWORD }} - TEST_EMAIL_IMAP_SERVER: ${{ secrets.TEST_EMAIL_IMAP_SERVER }} run: | tox -e testcore diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml new file mode 100644 index 00000000..0c1326eb --- /dev/null +++ b/.github/workflows/deployment.yml @@ -0,0 +1,52 @@ +name: Deployment + +on: + push: + branches: + - main + - "releases/**" + +jobs: + deployment: + runs-on: ubuntu-latest + environment: production + + steps: + - uses: actions/checkout@v3 + with: + submodules: "recursive" + + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: "3.9" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox + + - name: Deployment Tests + env: + TEST_EMAIL_LOGIN: ${{ secrets.TEST_EMAIL_LOGIN }} + TEST_EMAIL_PASSWORD: ${{ secrets.TEST_EMAIL_PASSWORD }} + TEST_EMAIL_IMAP_SERVER: ${{ secrets.TEST_EMAIL_IMAP_SERVER }} + run: | + tox -e testcore + + - name: Publish package to PyPI + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + + - name: Slack Notification + uses: homoluctus/slatify@master + if: failure() + with: + type: ${{ job.status }} + job_name: '*Core*' + commit: true + url: ${{ secrets.SLACK_BUILD_WEBHOOK }} + token: ${{ secrets.SLACK_GITHUB_TOKEN }} From 1e000027c70fff2182e476dc93be706903879aef Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 18:32:27 +0300 Subject: [PATCH 21/74] Fix master branch --- .github/workflows/deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 0c1326eb..ad5d2c3d 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -3,7 +3,7 @@ name: Deployment on: push: branches: - - main + - master - "releases/**" jobs: From e27c1c39e412fc8bd4fe3e0e6ea8b4a6fe01f9f7 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 18:38:17 +0300 Subject: [PATCH 22/74] Remove debug messages --- platformio/builder/tools/piolib.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index b93962fb..6c813313 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -1127,9 +1127,7 @@ def ConfigureProjectLibBuilder(env): if lb.depbuilders: _print_deps_tree(lb, level + 1) - print(1, env.get("CPPDEFINES")) project = ProjectAsLibBuilder(env, "$PROJECT_DIR") - print(2, env.get("CPPDEFINES")) ldf_mode = LibBuilderBase.lib_ldf_mode.fget(project) # pylint: disable=no-member click.echo("LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf") @@ -1140,7 +1138,6 @@ def ConfigureProjectLibBuilder(env): project.install_dependencies() - print(3, env.get("CPPDEFINES")) lib_builders = env.GetLibBuilders() click.echo("Found %d compatible libraries" % len(lib_builders)) From 37c6f207470100479a8468a109c5c99b7a17774c Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 19:10:54 +0300 Subject: [PATCH 23/74] Test only popular dev-platforms --- .github/workflows/examples.yml | 22 ++++++++-------------- scripts/install_devplatforms.py | 19 +++++++++++-------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 6e9fc7df..4906bbaf 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -7,15 +7,15 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-18.04, windows-latest, macos-latest] + os: [ubuntu-latest, windows-latest, macos-latest] python-version: [3.7] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: "recursive" - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -23,26 +23,22 @@ jobs: python -m pip install --upgrade pip pip install tox + - name: Configure dev-platform installer + env: + PIO_INSTALL_DEVPLATFORM_OWNERNAMES: "platformio" + PIO_INSTALL_DEVPLATFORM_NAMES: "aceinna_imu,atmelavr,atmelmegaavr,atmelsam,espressif32,espressif8266,nordicnrf52,raspberrypi,ststm32,teensy" + - name: Run on Linux if: startsWith(matrix.os, 'ubuntu') - env: - PIO_INSTALL_DEVPLATFORMS_OWNERNAMES: "platformio" - PIO_INSTALL_DEVPLATFORMS_IGNORE: "ststm8,infineonxmc,intel_mcs51" run: | - # ChipKIT issue: install 32-bit support for GCC PIC32 - sudo apt-get install libc6-i386 # Free space sudo apt clean docker rmi $(docker image ls -aq) df -h - # Run tox -e testexamples - name: Run on macOS if: startsWith(matrix.os, 'macos') - env: - PIO_INSTALL_DEVPLATFORMS_OWNERNAMES: "platformio" - PIO_INSTALL_DEVPLATFORMS_IGNORE: "ststm8,infineonxmc,microchippic32,lattice_ice40,gd32v" run: | df -h tox -e testexamples @@ -52,8 +48,6 @@ jobs: env: PLATFORMIO_CORE_DIR: C:/pio PLATFORMIO_WORKSPACE_DIR: C:/pio-workspace/$PROJECT_HASH - PIO_INSTALL_DEVPLATFORMS_OWNERNAMES: "platformio" - PIO_INSTALL_DEVPLATFORMS_IGNORE: "ststm8,infineonxmc,riscv_gap" run: | tox -e testexamples diff --git a/scripts/install_devplatforms.py b/scripts/install_devplatforms.py index 80526d90..26b4a2ff 100644 --- a/scripts/install_devplatforms.py +++ b/scripts/install_devplatforms.py @@ -22,35 +22,38 @@ import click @click.command() @click.option("--desktop", is_flag=True, default=False) @click.option( - "--ignore", - envvar="PIO_INSTALL_DEVPLATFORMS_IGNORE", - help="Ignore names split by comma", + "--names", + envvar="PIO_INSTALL_DEVPLATFORM_NAMES", + help="Install specified platform (split by comma)", ) @click.option( "--ownernames", - envvar="PIO_INSTALL_DEVPLATFORMS_OWNERNAMES", - help="Filter dev-platforms by ownernames (split by comma)", + envvar="PIO_INSTALL_DEVPLATFORM_OWNERNAMES", + help="Filter by ownernames (split by comma)", ) -def main(desktop, ignore, ownernames): +def main(desktop, names, ownernames): platforms = json.loads( subprocess.check_output(["pio", "platform", "search", "--json-output"]).decode() ) - ignore = [n.strip() for n in (ignore or "").split(",") if n.strip()] + names = [n.strip() for n in (names or "").split(",") if n.strip()] ownernames = [n.strip() for n in (ownernames or "").split(",") if n.strip()] for platform in platforms: skip = [ not desktop and platform["forDesktop"], - platform["name"] in ignore, + names and platform["name"] not in names, ownernames and platform["ownername"] not in ownernames, ] if any(skip): continue + print(platform['name']) + continue subprocess.check_call( [ "pio", "pkg", "install", "--global", + "--skip-dependencies", "--platform", "{ownername}/{name}".format(**platform), ] From 720732eba6b6da135ff6abe86ecf6f2b7b0396f5 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 19:16:52 +0300 Subject: [PATCH 24/74] Fix env section location --- .github/workflows/examples.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 4906bbaf..613bc918 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -2,31 +2,31 @@ name: Examples on: [push, pull_request] +env: + PIO_INSTALL_DEVPLATFORM_OWNERNAMES: "platformio" + PIO_INSTALL_DEVPLATFORM_NAMES: "aceinna_imu,atmelavr,atmelmegaavr,atmelsam,espressif32,espressif8266,nordicnrf52,raspberrypi,ststm32,teensy" + jobs: build: strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: [3.7] runs-on: ${{ matrix.os }} + steps: - uses: actions/checkout@v3 with: submodules: "recursive" - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python uses: actions/setup-python@v3 with: - python-version: ${{ matrix.python-version }} + python-version: "3.7" - name: Install dependencies run: | python -m pip install --upgrade pip pip install tox - - name: Configure dev-platform installer - env: - PIO_INSTALL_DEVPLATFORM_OWNERNAMES: "platformio" - PIO_INSTALL_DEVPLATFORM_NAMES: "aceinna_imu,atmelavr,atmelmegaavr,atmelsam,espressif32,espressif8266,nordicnrf52,raspberrypi,ststm32,teensy" - name: Run on Linux if: startsWith(matrix.os, 'ubuntu') From 97e2d24cd16e850ea7633daaa7ac4f74e45d2df2 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 19:18:38 +0300 Subject: [PATCH 25/74] Use Python 3.9 for CI examples --- .github/workflows/examples.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 613bc918..115817c4 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -2,9 +2,6 @@ name: Examples on: [push, pull_request] -env: - PIO_INSTALL_DEVPLATFORM_OWNERNAMES: "platformio" - PIO_INSTALL_DEVPLATFORM_NAMES: "aceinna_imu,atmelavr,atmelmegaavr,atmelsam,espressif32,espressif8266,nordicnrf52,raspberrypi,ststm32,teensy" jobs: build: @@ -13,21 +10,25 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} + env: + PIO_INSTALL_DEVPLATFORM_OWNERNAMES: "platformio" + PIO_INSTALL_DEVPLATFORM_NAMES: "aceinna_imu,atmelavr,atmelmegaavr,atmelsam,espressif32,espressif8266,nordicnrf52,raspberrypi,ststm32,teensy" steps: - uses: actions/checkout@v3 with: submodules: "recursive" + - name: Set up Python uses: actions/setup-python@v3 with: - python-version: "3.7" + python-version: "3.9" + - name: Install dependencies run: | python -m pip install --upgrade pip pip install tox - - name: Run on Linux if: startsWith(matrix.os, 'ubuntu') run: | From 9d2adb37f3778191ce24de4edc3f2948e6494c15 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 19:32:08 +0300 Subject: [PATCH 26/74] Sync examples --- examples | 2 +- scripts/install_devplatforms.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/examples b/examples index 8464bbb5..605ebe3c 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit 8464bbb5d96022ade33f92ca829c6401fb067d6a +Subproject commit 605ebe3c8c6ab6fdab2fafb1078c5eddf60f0520 diff --git a/scripts/install_devplatforms.py b/scripts/install_devplatforms.py index 26b4a2ff..20481ab4 100644 --- a/scripts/install_devplatforms.py +++ b/scripts/install_devplatforms.py @@ -45,8 +45,6 @@ def main(desktop, names, ownernames): ] if any(skip): continue - print(platform['name']) - continue subprocess.check_call( [ "pio", From 9b141bf5a87721dc804355c2847dcf7496e5b0f3 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 19 May 2022 21:23:30 +0300 Subject: [PATCH 27/74] Control Unit Testing verbosity with a new test_verbosity_level configuration option // Resolve #4276 --- HISTORY.rst | 6 ++++-- docs | 2 +- platformio/project/options.py | 16 +++++++++++++++- platformio/test/command.py | 14 +++++++++++--- platformio/test/runners/base.py | 12 +++++++----- platformio/test/runners/doctest.py | 2 +- platformio/test/runners/googletest.py | 2 +- platformio/test/runners/unity.py | 2 +- tests/commands/test_test.py | 2 ++ 9 files changed, 43 insertions(+), 15 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index baae69bf..e42131cd 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,6 +4,7 @@ Release Notes .. |PIOCONF| replace:: `"platformio.ini" `__ configuration file .. |LDF| replace:: `LDF `__ .. |INTERPOLATION| replace:: `Interpolation of Values `__ +.. |UNITTESTING| replace:: `Unit Testing `__ .. _release_notes_6: @@ -15,7 +16,8 @@ PlatformIO Core 6 6.0.2 (2022-??-??) ~~~~~~~~~~~~~~~~~~ -* Fixed an issue when the `build_src_flags `__ were applied outside the project scope (`issue #4277 `_) +* Control |UNITTESTING| verbosity with a new `test_verbosity_level `__ configuration option (`issue #4276 `_) +* Fixed an issue when the `build_src_flags `__ option was applied outside the project scope (`issue #4277 `_) 6.0.1 (2022-05-17) ~~~~~~~~~~~~~~~~~~ @@ -58,7 +60,7 @@ Please check the `Migration guide from 5.x to 6.0 `_ solution and its documentation + - Refactored from scratch |UNITTESTING| solution and its documentation - New: `Test Hierarchy `_ (`issue #4135 `_) - New: `Doctest `__ testing framework (`issue #4240 `_) - New: `GoogleTest `__ testing and mocking framework (`issue #3572 `_) diff --git a/docs b/docs index 5bf0037c..87c9ffa9 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 5bf0037c6679cccffbb835c30d729cd19120651b +Subproject commit 87c9ffa9ec156b51c7dc57e55db499991ba26100 diff --git a/platformio/project/options.py b/platformio/project/options.py index 02a8e580..42656c36 100644 --- a/platformio/project/options.py +++ b/platformio/project/options.py @@ -675,7 +675,8 @@ ProjectOptions = OrderedDict( ConfigEnvOption( group="test", name="test_speed", - description="A connection speed (baud rate) to communicate with a target device", + description="A connection speed (baud rate) to communicate with " + "a target device", type=click.INT, default=115200, ), @@ -696,6 +697,19 @@ ProjectOptions = OrderedDict( "and returns results to the standard output" ), ), + ConfigEnvOption( + group="test", + name="test_verbosity_level", + description=( + "Verbosity level: " + "0=normal verbosity (default), " + "1=raw testing output, " + "2=base verbosity for buidling/uploading, " + "3=extra verbosity for building/uploading" + ), + type=click.IntRange(min=0, max=3), + default=0, + ), # Debug ConfigEnvOption( group="debug", diff --git a/platformio/test/command.py b/platformio/test/command.py index cfc2ea3d..f6feac25 100644 --- a/platformio/test/command.py +++ b/platformio/test/command.py @@ -85,7 +85,12 @@ from platformio.test.runners.factory import TestRunnerFactory @click.option("--list-tests", is_flag=True) @click.option("--json-output-path", type=click.Path(resolve_path=True)) @click.option("--junit-output-path", type=click.Path(resolve_path=True)) -@click.option("--verbose", "-v", is_flag=True) +@click.option( + "--verbose", + "-v", + count=True, + help="Increase verbosity level, maximum is 3 levels (-vvv), see docs for details", +) @click.pass_context def test_cmd( # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin ctx, @@ -121,7 +126,7 @@ def test_cmd( # pylint: disable=too-many-arguments,too-many-locals,redefined-bu test_names = sorted(set(s.test_name for s in test_suites)) if not verbose: - click.echo("Verbose mode can be enabled via `-v, --verbose` option") + click.echo("Verbosity level can be increased via `-v, --verbose` option") click.secho("Collected %d tests" % len(test_names), bold=True, nl=not verbose) if verbose: click.echo(" (%s)" % ", ".join(test_names)) @@ -134,7 +139,10 @@ def test_cmd( # pylint: disable=too-many-arguments,too-many-locals,redefined-bu test_suite, project_config, TestRunnerOptions( - verbose=verbose, + verbose=verbose + or project_config.get( + f"env:{test_suite.env_name}", "test_verbosity_level" + ), without_building=without_building, without_uploading=without_uploading, without_testing=without_testing, diff --git a/platformio/test/runners/base.py b/platformio/test/runners/base.py index ead621e5..2e49dc08 100644 --- a/platformio/test/runners/base.py +++ b/platformio/test/runners/base.py @@ -28,7 +28,7 @@ CTX_META_TEST_RUNNING_NAME = __name__ + ".test_running_name" class TestRunnerOptions: # pylint: disable=too-many-instance-attributes def __init__( # pylint: disable=too-many-arguments self, - verbose=False, + verbose=0, without_building=False, without_uploading=False, without_testing=False, @@ -96,6 +96,8 @@ class TestRunnerBase: self.setup() for stage in ("building", "uploading", "testing"): getattr(self, f"stage_{stage}")() + if self.options.verbose: + click.echo() except Exception as exc: # pylint: disable=broad-except click.secho(str(exc), fg="red", err=True) self.test_suite.add_case( @@ -126,7 +128,7 @@ class TestRunnerBase: except ReturnErrorCode: raise UnitTestSuiteError( "Building stage has failed, see errors above. " - "Use `pio test --verbose` option to enable verbose output." + "Use `pio test -vvv` option to enable verbose output." ) def stage_uploading(self): @@ -145,7 +147,7 @@ class TestRunnerBase: except ReturnErrorCode: raise UnitTestSuiteError( "Uploading stage has failed, see errors above. " - "Use `pio test --verbose` option to enable verbose output." + "Use `pio test -vvv` option to enable verbose output." ) def stage_testing(self): @@ -179,8 +181,8 @@ class TestRunnerBase: run_cmd, project_conf=self.project_config.path, upload_port=self.options.upload_port, - verbose=self.options.verbose, - silent=not self.options.verbose, + verbose=self.options.verbose > 2, + silent=self.options.verbose < 2, environment=[self.test_suite.env_name], disable_auto_clean="nobuild" in targets, target=targets, diff --git a/platformio/test/runners/doctest.py b/platformio/test/runners/doctest.py index 15f6b311..d0fc931f 100644 --- a/platformio/test/runners/doctest.py +++ b/platformio/test/runners/doctest.py @@ -119,7 +119,7 @@ class DoctestTestRunner(TestRunnerBase): click.echo(line, nl=False) test_case = self._tc_parser.parse(line) - if test_case: + if test_case and not self.options.verbose: click.echo(test_case.humanize()) self.test_suite.add_case(test_case) diff --git a/platformio/test/runners/googletest.py b/platformio/test/runners/googletest.py index b3687bad..70e237e5 100644 --- a/platformio/test/runners/googletest.py +++ b/platformio/test/runners/googletest.py @@ -110,7 +110,7 @@ class GoogletestTestRunner(TestRunnerBase): click.echo(line, nl=False) test_case = self._tc_parser.parse(line) - if test_case: + if test_case and not self.options.verbose: click.echo(test_case.humanize()) self.test_suite.add_case(test_case) diff --git a/platformio/test/runners/unity.py b/platformio/test/runners/unity.py index 6e0eae5a..c3604c0c 100644 --- a/platformio/test/runners/unity.py +++ b/platformio/test/runners/unity.py @@ -264,7 +264,7 @@ void unityOutputComplete(void) { unittest_uart_end(); } return test_case = self.parse_test_case(line) - if test_case: + if test_case and not self.options.verbose: click.echo(test_case.humanize()) if all(s in line for s in ("Tests", "Failures", "Ignored")): diff --git a/tests/commands/test_test.py b/tests/commands/test_test.py index db7acb13..99fe99f4 100644 --- a/tests/commands/test_test.py +++ b/tests/commands/test_test.py @@ -94,6 +94,7 @@ def test_group_and_custom_runner(clirunner, validate_cliresult, tmp_path: Path): [env:native] platform = native test_framework = custom +test_verbosity_level = 1 """ ) test_dir = project_dir / "test" @@ -187,6 +188,7 @@ int main() { ["-d", str(project_dir), "-e", "native", "--verbose"], ) validate_cliresult(result) + assert "1 Tests 0 Failures 0 Ignored" in result.output assert "Called from my_extra_fun" in result.output assert "CustomTestRunner::TearDown called" in result.output assert "Disabled test suite" not in result.output From f7e24f2093e88380fae52d3f9eb26767c281f118 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 20 May 2022 10:56:42 +0300 Subject: [PATCH 28/74] Drop "test_verbosity_level" configuration option // Issue #4276 --- HISTORY.rst | 2 +- docs | 2 +- platformio/project/options.py | 13 ------------- platformio/test/command.py | 7 ++----- tests/commands/test_test.py | 1 - 5 files changed, 4 insertions(+), 21 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index e42131cd..6685876e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -16,7 +16,7 @@ PlatformIO Core 6 6.0.2 (2022-??-??) ~~~~~~~~~~~~~~~~~~ -* Control |UNITTESTING| verbosity with a new `test_verbosity_level `__ configuration option (`issue #4276 `_) +* Control |UNITTESTING| verbosity with a new multilevel `pio test -v `__ command option (`issue #4276 `_) * Fixed an issue when the `build_src_flags `__ option was applied outside the project scope (`issue #4277 `_) 6.0.1 (2022-05-17) diff --git a/docs b/docs index 87c9ffa9..552e995f 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 87c9ffa9ec156b51c7dc57e55db499991ba26100 +Subproject commit 552e995fc57a1c4838c8a34041f48ca9919b09c5 diff --git a/platformio/project/options.py b/platformio/project/options.py index 42656c36..ad96b24c 100644 --- a/platformio/project/options.py +++ b/platformio/project/options.py @@ -697,19 +697,6 @@ ProjectOptions = OrderedDict( "and returns results to the standard output" ), ), - ConfigEnvOption( - group="test", - name="test_verbosity_level", - description=( - "Verbosity level: " - "0=normal verbosity (default), " - "1=raw testing output, " - "2=base verbosity for buidling/uploading, " - "3=extra verbosity for building/uploading" - ), - type=click.IntRange(min=0, max=3), - default=0, - ), # Debug ConfigEnvOption( group="debug", diff --git a/platformio/test/command.py b/platformio/test/command.py index f6feac25..325bbb47 100644 --- a/platformio/test/command.py +++ b/platformio/test/command.py @@ -126,7 +126,7 @@ def test_cmd( # pylint: disable=too-many-arguments,too-many-locals,redefined-bu test_names = sorted(set(s.test_name for s in test_suites)) if not verbose: - click.echo("Verbosity level can be increased via `-v, --verbose` option") + click.echo("Verbosity level can be increased via `-v, -vv, or -vvv` option") click.secho("Collected %d tests" % len(test_names), bold=True, nl=not verbose) if verbose: click.echo(" (%s)" % ", ".join(test_names)) @@ -139,10 +139,7 @@ def test_cmd( # pylint: disable=too-many-arguments,too-many-locals,redefined-bu test_suite, project_config, TestRunnerOptions( - verbose=verbose - or project_config.get( - f"env:{test_suite.env_name}", "test_verbosity_level" - ), + verbose=verbose, without_building=without_building, without_uploading=without_uploading, without_testing=without_testing, diff --git a/tests/commands/test_test.py b/tests/commands/test_test.py index 99fe99f4..71094794 100644 --- a/tests/commands/test_test.py +++ b/tests/commands/test_test.py @@ -94,7 +94,6 @@ def test_group_and_custom_runner(clirunner, validate_cliresult, tmp_path: Path): [env:native] platform = native test_framework = custom -test_verbosity_level = 1 """ ) test_dir = project_dir / "test" From 598769fe1b300e95329ceb98c4ba7fdbaab38376 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 20 May 2022 10:57:24 +0300 Subject: [PATCH 29/74] Bump version to 6.0.2a2 --- platformio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/__init__.py b/platformio/__init__.py index 3f5c2fa2..ad159c32 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -14,7 +14,7 @@ import sys -VERSION = (6, 0, "2a1") +VERSION = (6, 0, "2a2") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" From f5e0ccecc34e031f4d3eb967f586c605dcaa37be Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 20 May 2022 14:44:54 +0300 Subject: [PATCH 30/74] Docs: Sync dev-platforms --- docs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs b/docs index 552e995f..0c16a7c4 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 552e995fc57a1c4838c8a34041f48ca9919b09c5 +Subproject commit 0c16a7c4da5f06005040c743f6827d6703675a03 From 626640cc05061545e6023a7a1490db3001bccd4e Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 20 May 2022 21:10:10 +0300 Subject: [PATCH 31/74] Accurate information about broken test suites in PIO Core 5.0 // Resolve #4279 --- docs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs b/docs index 0c16a7c4..5c3659ac 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 0c16a7c4da5f06005040c743f6827d6703675a03 +Subproject commit 5c3659ac2fb3bd27e54ca0fe3d151c7a200158d8 From 87f2e86928428420d3b4be29b796af9788f11d14 Mon Sep 17 00:00:00 2001 From: Sebastian Guarino Date: Wed, 25 May 2022 13:54:08 +0200 Subject: [PATCH 32/74] Fix filter argument used in remote agent (#4291) * Fix filter argument used in remote agent * Linting * Renamed reserved variable name to filter_ --- platformio/commands/remote/client/agent_service.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platformio/commands/remote/client/agent_service.py b/platformio/commands/remote/client/agent_service.py index b24f605d..0501021a 100644 --- a/platformio/commands/remote/client/agent_service.py +++ b/platformio/commands/remote/client/agent_service.py @@ -135,7 +135,7 @@ class RemoteAgentService(RemoteClientBase): def _process_cmd_test(self, options): return self._process_cmd_run_or_test("test", options) - def _process_cmd_run_or_test( # pylint: disable=too-many-locals,too-many-branches + def _process_cmd_run_or_test( # pylint: disable=too-many-locals,too-many-branches,too-many-statements self, command, options ): assert options and "project_id" in options @@ -172,6 +172,8 @@ class RemoteAgentService(RemoteClientBase): cmd_args.extend(["-e", env]) for target in options.get("target", []): cmd_args.extend(["-t", target]) + for filter_ in options.get("filter", []): + cmd_args.extend(["-f", filter_]) for ignore in options.get("ignore", []): cmd_args.extend(["-i", ignore]) if options.get("upload_port", False): From 5f812409d476801b05afc4a243d0a5e229a0b6c5 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 25 May 2022 19:23:24 +0300 Subject: [PATCH 33/74] Fixed an issue with debugging assembly files without preprocessor (".s") --- HISTORY.rst | 1 + docs | 2 +- platformio/builder/tools/piomisc.py | 10 +++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 6685876e..05fa7288 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -18,6 +18,7 @@ PlatformIO Core 6 * Control |UNITTESTING| verbosity with a new multilevel `pio test -v `__ command option (`issue #4276 `_) * Fixed an issue when the `build_src_flags `__ option was applied outside the project scope (`issue #4277 `_) +* Fixed an issue with debugging assembly files without preprocessor (".s") 6.0.1 (2022-05-17) ~~~~~~~~~~~~~~~~~~ diff --git a/docs b/docs index 5c3659ac..17a3f640 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 5c3659ac2fb3bd27e54ca0fe3d151c7a200158d8 +Subproject commit 17a3f640ac409056db131f2093dd7319fca9157c diff --git a/platformio/builder/tools/piomisc.py b/platformio/builder/tools/piomisc.py index 75fec40a..76ec2e37 100644 --- a/platformio/builder/tools/piomisc.py +++ b/platformio/builder/tools/piomisc.py @@ -114,7 +114,15 @@ def ConfigureDebugTarget(env): ] if optimization_flags: - env.AppendUnique(ASFLAGS=optimization_flags, LINKFLAGS=optimization_flags) + env.AppendUnique( + ASFLAGS=[ + # skip -O flags for assembler + f + for f in optimization_flags + if f.startswith("-g") + ], + LINKFLAGS=optimization_flags, + ) def GetExtraScripts(env, scope): From 6f6460fd4e932931b7ccc20a8e2dda73623034f7 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 26 May 2022 19:10:58 +0300 Subject: [PATCH 34/74] Minor improvements --- platformio/builder/tools/piolib.py | 14 +++++++------- platformio/builder/tools/platformio.py | 21 +++++++-------------- platformio/test/runners/googletest.py | 4 ++-- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index 6c813313..0a75d935 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -111,7 +111,7 @@ class LibBuilderFactory(object): return [] -class LibBuilderBase(object): +class LibBuilderBase: CLASSIC_SCANNER = SCons.Scanner.C.CScanner() CCONDITIONAL_SCANNER = SCons.Scanner.C.CConditionalScanner() @@ -514,7 +514,7 @@ class ArduinoLibBuilder(LibBuilderBase): return os.path.join(self.path, "include") def get_include_dirs(self): - include_dirs = LibBuilderBase.get_include_dirs(self) + include_dirs = super().get_include_dirs() if os.path.isdir(os.path.join(self.path, "src")): return include_dirs if os.path.isdir(os.path.join(self.path, "utility")): @@ -612,7 +612,7 @@ class MbedLibBuilder(LibBuilderBase): return LibBuilderBase.src_dir.fget(self) # pylint: disable=no-member def get_include_dirs(self): - include_dirs = LibBuilderBase.get_include_dirs(self) + include_dirs = super().get_include_dirs() if self.path not in include_dirs: include_dirs.append(self.path) @@ -833,7 +833,7 @@ class PlatformIOLibBuilder(LibBuilderBase): return util.items_in_list(frameworks, self._manifest.get("frameworks") or ["*"]) def get_include_dirs(self): - include_dirs = LibBuilderBase.get_include_dirs(self) + include_dirs = super().get_include_dirs() # backwards compatibility with PlatformIO 2.0 if ( @@ -872,14 +872,14 @@ class ProjectAsLibBuilder(LibBuilderBase): project_include_dir = self.env.subst("$PROJECT_INCLUDE_DIR") if os.path.isdir(project_include_dir): include_dirs.append(project_include_dir) - for include_dir in LibBuilderBase.get_include_dirs(self): + for include_dir in super().get_include_dirs(): if include_dir not in include_dirs: include_dirs.append(include_dir) return include_dirs def get_search_files(self): # project files - items = LibBuilderBase.get_search_files(self) + items = super().get_search_files() # test files if "test" in self.env.GetBuildType(): items.extend( @@ -994,7 +994,7 @@ class ProjectAsLibBuilder(LibBuilderBase): def build(self): self.is_built = True # do not build Project now - result = LibBuilderBase.build(self) + result = super().build() self.env.PrependUnique(CPPPATH=self.get_include_dirs()) return result diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index 12e57f69..0c943319 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -168,7 +168,7 @@ def ProcessProjectDeps(env): if not env.get("PIOBUILDFILES") and not COMMAND_LINE_TARGETS: sys.stderr.write( "Error: Nothing to build. Please put your source code files " - "to '%s' folder\n" % env.subst("$PROJECT_SRC_DIR") + "to the '%s' folder\n" % env.subst("$PROJECT_SRC_DIR") ) env.Exit(1) @@ -321,23 +321,16 @@ def BuildFrameworks(env, frameworks): ) env.Exit(1) - board_frameworks = env.BoardConfig().get("frameworks", []) - if frameworks == ["platformio"]: - if board_frameworks: - frameworks.insert(0, board_frameworks[0]) - else: - sys.stderr.write("Error: Please specify `board` in `platformio.ini`\n") - env.Exit(1) - - for f in frameworks: - if f == "arduino": - # Arduino IDE appends .o the end of filename + supported_frameworks = env.BoardConfig().get("frameworks", []) + for name in frameworks: + if name == "arduino": + # Arduino IDE appends .o to the end of filename Builder.match_splitext = scons_patched_match_splitext if "nobuild" not in COMMAND_LINE_TARGETS: env.ConvertInoToCpp() - if f in board_frameworks: - SConscript(env.GetFrameworkScript(f), exports="env") + if name in supported_frameworks: + SConscript(env.GetFrameworkScript(name), exports="env") else: sys.stderr.write("Error: This board doesn't support %s framework!\n" % f) env.Exit(1) diff --git a/platformio/test/runners/googletest.py b/platformio/test/runners/googletest.py index 70e237e5..11e51ff6 100644 --- a/platformio/test/runners/googletest.py +++ b/platformio/test/runners/googletest.py @@ -22,7 +22,7 @@ from platformio.test.result import TestCase, TestCaseSource, TestStatus from platformio.test.runners.base import TestRunnerBase -class DoctestTestCaseParser: +class GoogletestTestCaseParser: # Examples: # [ RUN ] FooTest.Bar @@ -95,7 +95,7 @@ class GoogletestTestRunner(TestRunnerBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self._tc_parser = DoctestTestCaseParser() + self._tc_parser = GoogletestTestCaseParser() os.environ["GTEST_COLOR"] = "no" # disable ANSI symbols def configure_build_env(self, env): From ea94f6515919b6467ded2fbe6b70887b042a1c47 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 26 May 2022 19:11:33 +0300 Subject: [PATCH 35/74] Minor improvements --- platformio/builder/tools/platformio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index 0c943319..6e51154d 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -332,7 +332,7 @@ def BuildFrameworks(env, frameworks): if name in supported_frameworks: SConscript(env.GetFrameworkScript(name), exports="env") else: - sys.stderr.write("Error: This board doesn't support %s framework!\n" % f) + sys.stderr.write("Error: This board doesn't support %s framework!\n" % name) env.Exit(1) From 460a983ab23d62cfdc2c1ec70531d5bba964b634 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 26 May 2022 19:15:15 +0300 Subject: [PATCH 36/74] Do not export empty scopes to the build environment --- platformio/builder/tools/piolib.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index 0a75d935..c8388b13 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -453,11 +453,17 @@ class LibBuilderBase: def build(self): libs = [] + shared_scopes = ("CPPPATH", "LIBPATH", "LIBS", "LINKFLAGS") for lb in self.depbuilders: libs.extend(lb.build()) # copy shared information to self env - for key in ("CPPPATH", "LIBPATH", "LIBS", "LINKFLAGS"): - self.env.PrependUnique(**{key: lb.env.get(key)}) + self.env.PrependUnique( + **{ + scope: lb.env.get(scope) + for scope in shared_scopes + if lb.env.get(scope) + } + ) for lb in self._circular_deps: self.env.PrependUnique(CPPPATH=lb.get_include_dirs()) @@ -472,8 +478,13 @@ class LibBuilderBase: for lb in self.env.GetLibBuilders(): if self == lb or not lb.is_built: continue - for key in ("CPPPATH", "LIBPATH", "LIBS", "LINKFLAGS"): - self.env.PrependUnique(**{key: lb.env.get(key)}) + self.env.PrependUnique( + **{ + scope: lb.env.get(scope) + for scope in shared_scopes + if lb.env.get(scope) + } + ) do_not_archive = not self.lib_archive if not do_not_archive: From 73dd29c59ccf2348abccce50c00cfd253b016ae4 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 26 May 2022 19:18:21 +0300 Subject: [PATCH 37/74] Follow symbolic links during searching for the unit test suites // Resolve #4288 --- HISTORY.rst | 1 + platformio/test/helpers.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 05fa7288..75e667e2 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -17,6 +17,7 @@ PlatformIO Core 6 ~~~~~~~~~~~~~~~~~~ * Control |UNITTESTING| verbosity with a new multilevel `pio test -v `__ command option (`issue #4276 `_) +* Follow symbolic links during searching for the unit test suites (`issue #4288 `_) * Fixed an issue when the `build_src_flags `__ option was applied outside the project scope (`issue #4277 `_) * Fixed an issue with debugging assembly files without preprocessor (".s") diff --git a/platformio/test/helpers.py b/platformio/test/helpers.py index a8e2f818..a789a6ea 100644 --- a/platformio/test/helpers.py +++ b/platformio/test/helpers.py @@ -24,7 +24,7 @@ def list_test_names(project_config): if not os.path.isdir(test_dir): raise TestDirNotExistsError(test_dir) names = [] - for root, _, __ in os.walk(test_dir): + for root, _, __ in os.walk(test_dir, followlinks=True): if not os.path.basename(root).startswith("test_"): continue names.append(os.path.relpath(root, test_dir).replace("\\", "/")) From bd052d0ce0db0e09d2b81e16a8d8d9a82c8a7ac2 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 26 May 2022 19:55:23 +0300 Subject: [PATCH 38/74] Show a warning when testing an empty project without a test suite // Resolve #4278 --- HISTORY.rst | 1 + platformio/builder/tools/platformio.py | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 75e667e2..b83a4034 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -18,6 +18,7 @@ PlatformIO Core 6 * Control |UNITTESTING| verbosity with a new multilevel `pio test -v `__ command option (`issue #4276 `_) * Follow symbolic links during searching for the unit test suites (`issue #4288 `_) +* Show a warning when testing an empty project without a test suite (`issue #4278 `_) * Fixed an issue when the `build_src_flags `__ option was applied outside the project scope (`issue #4277 `_) * Fixed an issue with debugging assembly files without preprocessor (".s") diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index 6e51154d..9476f685 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -165,11 +165,17 @@ def ProcessProjectDeps(env): "$BUILD_SRC_DIR", "$PROJECT_SRC_DIR", env.get("SRC_FILTER") ) - if not env.get("PIOBUILDFILES") and not COMMAND_LINE_TARGETS: - sys.stderr.write( - "Error: Nothing to build. Please put your source code files " - "to the '%s' folder\n" % env.subst("$PROJECT_SRC_DIR") - ) + if not env.get("PIOBUILDFILES"): + if not COMMAND_LINE_TARGETS: + sys.stderr.write( + "Error: Nothing to build. Please put your source code files " + "to the '%s' folder\n" % env.subst("$PROJECT_SRC_DIR") + ) + elif "test" in env.GetBuildType(): + sys.stderr.write( + "Error: Nothing to build. Please put your test suites " + "to the '%s' folder\n" % env.subst("$PROJECT_TEST_DIR") + ) env.Exit(1) Export("projenv") From 87dffa36b8b56122fbe9c74a3b59bd3169a03264 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 26 May 2022 22:29:51 +0300 Subject: [PATCH 39/74] Improved support for user inputs --- docs | 2 +- platformio/builder/main.py | 2 +- platformio/builder/tools/piointegration.py | 22 +++++++++++++++------- platformio/builder/tools/platformio.py | 5 +++-- platformio/project/helpers.py | 6 +++--- platformio/test/exception.py | 4 ++-- 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/docs b/docs index 17a3f640..26527c1f 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 17a3f640ac409056db131f2093dd7319fca9157c +Subproject commit 26527c1f6ab013b5cf11174ef087355efc580b04 diff --git a/platformio/builder/main.py b/platformio/builder/main.py index 616f4a0a..6aeee4f4 100644 --- a/platformio/builder/main.py +++ b/platformio/builder/main.py @@ -225,7 +225,7 @@ if "envdump" in COMMAND_LINE_TARGETS: click.echo(env.Dump()) env.Exit(0) -if set(["_idedata", "idedata"]) & set(COMMAND_LINE_TARGETS): +if env.IsIntegrationDump(): projenv = None try: Import("projenv") diff --git a/platformio/builder/tools/piointegration.py b/platformio/builder/tools/piointegration.py index 36989f06..f776bccf 100644 --- a/platformio/builder/tools/piointegration.py +++ b/platformio/builder/tools/piointegration.py @@ -19,10 +19,15 @@ import os import SCons.Defaults # pylint: disable=import-error import SCons.Subst # pylint: disable=import-error +from SCons.Script import COMMAND_LINE_TARGETS # pylint: disable=import-error from platformio.proc import exec_command, where_is_program +def IsIntegrationDump(_): + return set(["_idedata", "idedata"]) & set(COMMAND_LINE_TARGETS) + + def DumpIntegrationIncludes(env): result = dict(build=[], compatlib=[], toolchain=[]) @@ -60,7 +65,7 @@ def DumpIntegrationIncludes(env): return result -def _get_gcc_defines(env): +def get_gcc_defines(env): items = [] try: sysenv = os.environ.copy() @@ -83,7 +88,7 @@ def _get_gcc_defines(env): return items -def _dump_defines(env): +def dump_defines(env): defines = [] # global symbols for item in SCons.Defaults.processDefines(env.get("CPPDEFINES", [])): @@ -108,12 +113,12 @@ def _dump_defines(env): # built-in GCC marcos # if env.GetCompilerType() == "gcc": - # defines.extend(_get_gcc_defines(env)) + # defines.extend(get_gcc_defines(env)) return defines -def _get_svd_path(env): +def dump_svd_path(env): svd_path = env.GetProjectOption("debug_svd_path") if svd_path: return os.path.abspath(svd_path) @@ -146,13 +151,13 @@ def DumpIntegrationData(env, globalenv): data = { "env_name": env["PIOENV"], "libsource_dirs": [env.subst(item) for item in env.GetLibSourceDirs()], - "defines": _dump_defines(env), + "defines": dump_defines(env), "includes": env.DumpIntegrationIncludes(), "cc_path": where_is_program(env.subst("$CC"), env.subst("${ENV['PATH']}")), "cxx_path": where_is_program(env.subst("$CXX"), env.subst("${ENV['PATH']}")), "gdb_path": where_is_program(env.subst("$GDB"), env.subst("${ENV['PATH']}")), "prog_path": env.subst("$PROG_PATH"), - "svd_path": _get_svd_path(env), + "svd_path": dump_svd_path(env), "compiler_type": env.GetCompilerType(), "targets": globalenv.DumpTargets(), "extra": dict( @@ -162,7 +167,9 @@ def DumpIntegrationData(env, globalenv): ] ), } - data["extra"].update(env.get("IDE_EXTRA_DATA", {})) + data["extra"].update( + env.get("INTEGRATION_EXTRA_DATA", env.get("IDE_EXTRA_DATA", {})) + ) env_ = env.Clone() # https://github.com/platformio/platformio-atom-ide/issues/34 @@ -191,6 +198,7 @@ def exists(_): def generate(env): + env.AddMethod(IsIntegrationDump) env.AddMethod(DumpIntegrationIncludes) env.AddMethod(DumpIntegrationData) return env diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index 9476f685..60309b55 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -171,12 +171,13 @@ def ProcessProjectDeps(env): "Error: Nothing to build. Please put your source code files " "to the '%s' folder\n" % env.subst("$PROJECT_SRC_DIR") ) - elif "test" in env.GetBuildType(): + env.Exit(1) + if "test" in env.GetBuildType(): sys.stderr.write( "Error: Nothing to build. Please put your test suites " "to the '%s' folder\n" % env.subst("$PROJECT_TEST_DIR") ) - env.Exit(1) + env.Exit(1) Export("projenv") diff --git a/platformio/project/helpers.py b/platformio/project/helpers.py index bc8d324a..c7b786c8 100644 --- a/platformio/project/helpers.py +++ b/platformio/project/helpers.py @@ -126,7 +126,7 @@ def load_build_metadata(project_dir, env_or_envs, cache=False): env_names = [env_names] with fs.cd(project_dir): - result = _load_cached_project_ide_data(project_dir, env_names) if cache else {} + result = _get_cached_build_metadata(project_dir, env_names) if cache else {} missed_env_names = set(env_names) - set(result.keys()) if missed_env_names: result.update(_load_build_metadata(project_dir, missed_env_names)) @@ -154,10 +154,10 @@ def _load_build_metadata(project_dir, env_names): raise result.exception if '"includes":' not in result.output: raise exception.PlatformioException(result.output) - return _load_cached_project_ide_data(project_dir, env_names) + return _get_cached_build_metadata(project_dir, env_names) -def _load_cached_project_ide_data(project_dir, env_names): +def _get_cached_build_metadata(project_dir, env_names): build_dir = ProjectConfig.get_instance( os.path.join(project_dir, "platformio.ini") ).get("platformio", "build_dir") diff --git a/platformio/test/exception.py b/platformio/test/exception.py index 2d8c790c..048e7399 100644 --- a/platformio/test/exception.py +++ b/platformio/test/exception.py @@ -25,8 +25,8 @@ class TestDirNotExistsError(UnitTestError, UserSideException): "A test folder '{0}' does not exist.\nPlease create 'test' " "directory in the project root and put a test set.\n" "More details about Unit " - "Testing: https://docs.platformio.org/page/plus/" - "unit-testing.html" + "Testing: https://docs.platformio.org/en/latest/advanced/" + "unit-testing/index.html" ) From 440bb1e6f414b316ef9faefff9b62f90d27507a5 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 26 May 2022 22:30:20 +0300 Subject: [PATCH 40/74] Bump version to 6.0.2b1 --- platformio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/__init__.py b/platformio/__init__.py index ad159c32..33b75af8 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -14,7 +14,7 @@ import sys -VERSION = (6, 0, "2a2") +VERSION = (6, 0, "2b1") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" From 22a0a20666f4d53053646d0d4e00141fb1233e54 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Sat, 28 May 2022 22:07:44 +0300 Subject: [PATCH 41/74] Update deps --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5df72834..51853205 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ minimal_requirements = [ "pyelftools>=0.27,<1", "pyserial==3.*", "requests==2.*", - "semantic_version==2.9.*", + "semantic_version==2.10.*", "tabulate==0.8.*", "zeroconf<1", ] From a19c4dbcdaf3b8edaab685988e30d660d7ace5f3 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Sat, 28 May 2022 22:09:14 +0300 Subject: [PATCH 42/74] Show a warning when testing an empty project without a test suite // Resolve #4278 --- HISTORY.rst | 1 + platformio/builder/tools/platformio.py | 27 +++++++++++++------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index b83a4034..b6bb65ca 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -19,6 +19,7 @@ PlatformIO Core 6 * Control |UNITTESTING| verbosity with a new multilevel `pio test -v `__ command option (`issue #4276 `_) * Follow symbolic links during searching for the unit test suites (`issue #4288 `_) * Show a warning when testing an empty project without a test suite (`issue #4278 `_) +* Improved support for `Asking for input (prompts) `_ * Fixed an issue when the `build_src_flags `__ option was applied outside the project scope (`issue #4277 `_) * Fixed an issue with debugging assembly files without preprocessor (".s") diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index 60309b55..69875a70 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -157,28 +157,29 @@ def ProcessProjectDeps(env): ) if "test" in env.GetBuildType(): + build_files_before_nums = len(env.get("PIOBUILDFILES", [])) projenv.BuildSources( "$BUILD_TEST_DIR", "$PROJECT_TEST_DIR", "$PIOTEST_SRC_FILTER" ) - if "test" not in env.GetBuildType() or env.GetProjectOption("test_build_src"): - projenv.BuildSources( - "$BUILD_SRC_DIR", "$PROJECT_SRC_DIR", env.get("SRC_FILTER") - ) - - if not env.get("PIOBUILDFILES"): - if not COMMAND_LINE_TARGETS: - sys.stderr.write( - "Error: Nothing to build. Please put your source code files " - "to the '%s' folder\n" % env.subst("$PROJECT_SRC_DIR") - ) - env.Exit(1) - if "test" in env.GetBuildType(): + if len(env.get("PIOBUILDFILES", [])) - build_files_before_nums < 1: sys.stderr.write( "Error: Nothing to build. Please put your test suites " "to the '%s' folder\n" % env.subst("$PROJECT_TEST_DIR") ) env.Exit(1) + if "test" not in env.GetBuildType() or env.GetProjectOption("test_build_src"): + projenv.BuildSources( + "$BUILD_SRC_DIR", "$PROJECT_SRC_DIR", env.get("SRC_FILTER") + ) + + if not env.get("PIOBUILDFILES") and not COMMAND_LINE_TARGETS: + sys.stderr.write( + "Error: Nothing to build. Please put your source code files " + "to the '%s' folder\n" % env.subst("$PROJECT_SRC_DIR") + ) + env.Exit(1) + Export("projenv") From 19006378a8bb5cf4c48678dbcc64962431ffcdaf Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 14:27:45 +0300 Subject: [PATCH 43/74] Sync docs --- docs | 2 +- examples | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs b/docs index 26527c1f..b2c2a76d 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 26527c1f6ab013b5cf11174ef087355efc580b04 +Subproject commit b2c2a76d48ceab8f6306cd8778ca3f20b0a9cb56 diff --git a/examples b/examples index 605ebe3c..3ddfa997 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit 605ebe3c8c6ab6fdab2fafb1078c5eddf60f0520 +Subproject commit 3ddfa9975ccbf1909f88dd8dd04e424a7590a94c From b568eb68d605550cf59cbc75f7e7498b750e958c Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 18:53:53 +0300 Subject: [PATCH 44/74] Docs: refactor installation guide and FAQ --- docs | 2 +- platformio/__main__.py | 2 +- platformio/exception.py | 4 ++-- platformio/maintenance.py | 4 ++-- scripts/99-platformio-udev.rules | 2 +- scripts/docspregen.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs b/docs index b2c2a76d..a443af8c 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit b2c2a76d48ceab8f6306cd8778ca3f20b0a9cb56 +Subproject commit a443af8c11ef5c00aaced056fc297f79dee1f218 diff --git a/platformio/__main__.py b/platformio/__main__.py index 07816eda..464f2fb9 100644 --- a/platformio/__main__.py +++ b/platformio/__main__.py @@ -120,7 +120,7 @@ An unexpected error occurred. Further steps: `pip install -U platformio` command * Try to find answer in FAQ Troubleshooting section - https://docs.platformio.org/page/faq.html + https://docs.platformio.org/page/faq/index.html * Report this problem to the developers https://github.com/platformio/platformio-core/issues diff --git a/platformio/exception.py b/platformio/exception.py index 03382a55..5c0b44ea 100644 --- a/platformio/exception.py +++ b/platformio/exception.py @@ -56,7 +56,7 @@ class MissedUdevRules(InvalidUdevRules): MESSAGE = ( "Warning! Please install `99-platformio-udev.rules`. \nMore details: " - "https://docs.platformio.org/page/faq.html#platformio-udev-rules" + "https://docs.platformio.org/en/latest/core/installation/udev-rules.html" ) @@ -65,7 +65,7 @@ class OutdatedUdevRules(InvalidUdevRules): MESSAGE = ( "Warning! Your `{0}` are outdated. Please update or reinstall them." "\nMore details: " - "https://docs.platformio.org/page/faq.html#platformio-udev-rules" + "https://docs.platformio.org/en/latest/core/installation/udev-rules.html" ) diff --git a/platformio/maintenance.py b/platformio/maintenance.py index dccd8086..775de286 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -142,8 +142,8 @@ def after_upgrade(ctx): ) click.secho("Please remove multiple PIO Cores from a system:", fg="yellow") click.secho( - "https://docs.platformio.org/page/faq.html" - "#multiple-platformio-cores-in-a-system", + "https://docs.platformio.org/en/latest/core" + "/installation/troubleshooting.html", fg="cyan", ) click.secho("*" * terminal_width, fg="yellow") diff --git a/scripts/99-platformio-udev.rules b/scripts/99-platformio-udev.rules index 6fd87d00..574b6fe6 100644 --- a/scripts/99-platformio-udev.rules +++ b/scripts/99-platformio-udev.rules @@ -16,7 +16,7 @@ # # INSTALLATION # -# Please visit > https://docs.platformio.org/en/latest/faq.html#platformio-udev-rules +# Please visit > https://docs.platformio.org/en/latest/core/installation/udev-rules.html # ##################################################################################### diff --git a/scripts/docspregen.py b/scripts/docspregen.py index 90dc887d..2846c7da 100644 --- a/scripts/docspregen.py +++ b/scripts/docspregen.py @@ -338,7 +338,7 @@ Packages .. warning:: **Linux Users**: - * Install "udev" rules :ref:`faq_udev_rules` + * Install "udev" rules :ref:`platformio_udev_rules` * Raspberry Pi users, please read this article `Enable serial port on Raspberry Pi `__. """ From cf558036d01a4ee7ae4805a4038ee7a2a940de76 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 20:22:48 +0300 Subject: [PATCH 45/74] Sync docs --- docs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs b/docs index a443af8c..24524cad 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit a443af8c11ef5c00aaced056fc297f79dee1f218 +Subproject commit 24524cad974ff59a8e0b59cdbb1c058dea12617e From 2564b9eb7822d94a4db1d620bac08d2008dcf5d7 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 20:29:35 +0300 Subject: [PATCH 46/74] Move http module to the root --- platformio/builder/tools/piolib.py | 2 +- platformio/clients/account.py | 2 +- platformio/clients/registry.py | 6 +++--- platformio/commands/home/rpc/handlers/os.py | 2 +- platformio/commands/upgrade.py | 2 +- platformio/{clients => }/http.py | 0 platformio/maintenance.py | 8 ++++---- platformio/package/manager/_registry.py | 2 +- platformio/package/manager/platform.py | 2 +- platformio/package/manifest/parser.py | 2 +- platformio/package/manifest/schema.py | 2 +- tests/conftest.py | 2 +- tests/misc/test_misc.py | 3 +-- 13 files changed, 17 insertions(+), 18 deletions(-) rename platformio/{clients => }/http.py (100%) diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index c8388b13..104e8496 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -31,8 +31,8 @@ from SCons.Script import DefaultEnvironment # pylint: disable=import-error from platformio import exception, fs, util from platformio.builder.tools import platformio as piotool -from platformio.clients.http import HTTPClientError, InternetIsOffline from platformio.compat import IS_WINDOWS, hashlib_encode_data, string_types +from platformio.http import HTTPClientError, InternetIsOffline from platformio.package.exception import ( MissingPackageManifestError, UnknownPackageError, diff --git a/platformio/clients/account.py b/platformio/clients/account.py index 2afe6fbe..a2eb0c28 100644 --- a/platformio/clients/account.py +++ b/platformio/clients/account.py @@ -16,8 +16,8 @@ import os import time from platformio import __accounts_api__, app -from platformio.clients.http import HTTPClient, HTTPClientError from platformio.exception import PlatformioException +from platformio.http import HTTPClient, HTTPClientError class AccountError(PlatformioException): diff --git a/platformio/clients/registry.py b/platformio/clients/registry.py index 75bfd99b..1682f7b1 100644 --- a/platformio/clients/registry.py +++ b/platformio/clients/registry.py @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +# pylint: disable=too-many-arguments + from platformio import __registry_mirror_hosts__, fs from platformio.clients.account import AccountClient, AccountError -from platformio.clients.http import HTTPClient, HTTPClientError - -# pylint: disable=too-many-arguments +from platformio.http import HTTPClient, HTTPClientError class RegistryClient(HTTPClient): diff --git a/platformio/commands/home/rpc/handlers/os.py b/platformio/commands/home/rpc/handlers/os.py index 7342b669..c6ddcaa6 100644 --- a/platformio/commands/home/rpc/handlers/os.py +++ b/platformio/commands/home/rpc/handlers/os.py @@ -24,9 +24,9 @@ import click from platformio import __default_requests_timeout__, fs from platformio.cache import ContentCache -from platformio.clients.http import ensure_internet_on from platformio.commands.home import helpers from platformio.device.list import list_logical_devices +from platformio.http import ensure_internet_on class OSRPC: diff --git a/platformio/commands/upgrade.py b/platformio/commands/upgrade.py index 0cba9e74..7664732a 100644 --- a/platformio/commands/upgrade.py +++ b/platformio/commands/upgrade.py @@ -20,8 +20,8 @@ from zipfile import ZipFile import click from platformio import VERSION, __version__, app, exception -from platformio.clients.http import fetch_remote_content from platformio.compat import IS_WINDOWS +from platformio.http import fetch_remote_content from platformio.package.manager.core import update_core_packages from platformio.proc import exec_command, get_pythonexe_path from platformio.project.helpers import get_project_cache_dir diff --git a/platformio/clients/http.py b/platformio/http.py similarity index 100% rename from platformio/clients/http.py rename to platformio/http.py diff --git a/platformio/maintenance.py b/platformio/maintenance.py index 775de286..9a897d62 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -21,11 +21,11 @@ import semantic_version from platformio import __version__, app, exception, fs, telemetry from platformio.cache import cleanup_content_cache -from platformio.clients import http from platformio.commands import PlatformioCLI from platformio.commands.platform import platform_update as cmd_platform_update from platformio.commands.system.prune import calculate_unnecessary_system_data from platformio.commands.upgrade import get_latest_version +from platformio.http import HTTPClientError, InternetIsOffline, ensure_internet_on from platformio.package.manager.core import update_core_packages from platformio.package.manager.tool import ToolPackageManager from platformio.package.meta import PackageSpec @@ -51,8 +51,8 @@ def on_platformio_end(ctx, result): # pylint: disable=unused-argument check_platformio_upgrade() check_prune_system() except ( - http.HTTPClientError, - http.InternetIsOffline, + HTTPClientError, + InternetIsOffline, exception.GetLatestVersionError, ): click.secho( @@ -213,7 +213,7 @@ def check_platformio_upgrade(): if not last_checked_time: return - http.ensure_internet_on(raise_exception=True) + ensure_internet_on(raise_exception=True) # Update PlatformIO Core packages update_core_packages() diff --git a/platformio/package/manager/_registry.py b/platformio/package/manager/_registry.py index b45a17f2..de0464d2 100644 --- a/platformio/package/manager/_registry.py +++ b/platformio/package/manager/_registry.py @@ -20,8 +20,8 @@ import click from platformio import __registry_mirror_hosts__ from platformio.cache import ContentCache -from platformio.clients.http import HTTPClient from platformio.clients.registry import RegistryClient +from platformio.http import HTTPClient from platformio.package.exception import UnknownPackageError from platformio.package.meta import PackageSpec from platformio.package.version import cast_version_to_semver diff --git a/platformio/package/manager/platform.py b/platformio/package/manager/platform.py index 6d0cc040..41f7b41e 100644 --- a/platformio/package/manager/platform.py +++ b/platformio/package/manager/platform.py @@ -15,7 +15,7 @@ import os from platformio import util -from platformio.clients.http import HTTPClientError, InternetIsOffline +from platformio.http import HTTPClientError, InternetIsOffline from platformio.package.exception import UnknownPackageError from platformio.package.manager.base import BasePackageManager from platformio.package.manager.core import get_installed_core_packages diff --git a/platformio/package/manifest/parser.py b/platformio/package/manifest/parser.py index a5e0f837..8735bcff 100644 --- a/platformio/package/manifest/parser.py +++ b/platformio/package/manifest/parser.py @@ -21,8 +21,8 @@ import tarfile from urllib.parse import urlparse from platformio import util -from platformio.clients.http import fetch_remote_content from platformio.compat import get_object_members, string_types +from platformio.http import fetch_remote_content from platformio.package.exception import ManifestParserError, UnknownManifestError from platformio.project.helpers import is_platformio_project diff --git a/platformio/package/manifest/schema.py b/platformio/package/manifest/schema.py index c8f69e0c..8258dfe8 100644 --- a/platformio/package/manifest/schema.py +++ b/platformio/package/manifest/schema.py @@ -22,7 +22,7 @@ import requests import semantic_version from marshmallow import Schema, ValidationError, fields, validate, validates -from platformio.clients.http import fetch_remote_content +from platformio.http import fetch_remote_content from platformio.package.exception import ManifestValidationError from platformio.util import memoized diff --git a/tests/conftest.py b/tests/conftest.py index 76b53dd8..b26aaf49 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,7 +20,7 @@ import time import pytest from click.testing import CliRunner -from platformio.clients import http +from platformio import http def pytest_configure(config): diff --git a/tests/misc/test_misc.py b/tests/misc/test_misc.py index 9b8fc5d4..5596b9a2 100644 --- a/tests/misc/test_misc.py +++ b/tests/misc/test_misc.py @@ -17,8 +17,7 @@ import pytest import requests -from platformio import __check_internet_hosts__, proc -from platformio.clients import http +from platformio import __check_internet_hosts__, http, proc from platformio.clients.registry import RegistryClient From 1f75430fabb555c3e0b932bf4c25221ed7496044 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 20:36:18 +0300 Subject: [PATCH 47/74] Move registry client to the package module --- platformio/commands/access.py | 2 +- platformio/package/commands/publish.py | 2 +- platformio/package/commands/search.py | 2 +- platformio/package/commands/show.py | 2 +- platformio/package/commands/unpublish.py | 2 +- platformio/package/manager/_registry.py | 2 +- platformio/{clients => package}/registry.py | 0 tests/commands/test_lib.py | 2 +- tests/misc/test_misc.py | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename platformio/{clients => package}/registry.py (100%) diff --git a/platformio/commands/access.py b/platformio/commands/access.py index 74f48e2b..ccd4150a 100644 --- a/platformio/commands/access.py +++ b/platformio/commands/access.py @@ -20,9 +20,9 @@ import re import click from tabulate import tabulate -from platformio.clients.registry import RegistryClient from platformio.commands.account import validate_username from platformio.commands.team import validate_orgname_teamname +from platformio.package.registry import RegistryClient def validate_client(value): diff --git a/platformio/package/commands/publish.py b/platformio/package/commands/publish.py index 150fdb19..89cc5c01 100644 --- a/platformio/package/commands/publish.py +++ b/platformio/package/commands/publish.py @@ -22,12 +22,12 @@ from tabulate import tabulate from platformio import fs 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 from platformio.package.meta import PackageType from platformio.package.pack import PackagePacker +from platformio.package.registry import RegistryClient from platformio.package.unpack import FileUnpacker, TARArchiver diff --git a/platformio/package/commands/search.py b/platformio/package/commands/search.py index 57ec76ec..787d94dd 100644 --- a/platformio/package/commands/search.py +++ b/platformio/package/commands/search.py @@ -17,7 +17,7 @@ import math import click from platformio import util -from platformio.clients.registry import RegistryClient +from platformio.package.registry import RegistryClient @click.command("search", short_help="Search for packages") diff --git a/platformio/package/commands/show.py b/platformio/package/commands/show.py index ce50bc88..5fd45f62 100644 --- a/platformio/package/commands/show.py +++ b/platformio/package/commands/show.py @@ -18,10 +18,10 @@ import click from tabulate import tabulate from platformio import fs, util -from platformio.clients.registry import RegistryClient from platformio.exception import UserSideException from platformio.package.manager._registry import PackageManagerRegistryMixin from platformio.package.meta import PackageSpec, PackageType +from platformio.package.registry import RegistryClient @click.command("show", short_help="Show package information") diff --git a/platformio/package/commands/unpublish.py b/platformio/package/commands/unpublish.py index 3185144e..88646095 100644 --- a/platformio/package/commands/unpublish.py +++ b/platformio/package/commands/unpublish.py @@ -15,8 +15,8 @@ import click from platformio.clients.account import AccountClient -from platformio.clients.registry import RegistryClient from platformio.package.meta import PackageSpec, PackageType +from platformio.package.registry import RegistryClient @click.command("unpublish", short_help="Remove a pushed package from the registry") diff --git a/platformio/package/manager/_registry.py b/platformio/package/manager/_registry.py index de0464d2..7e00c78f 100644 --- a/platformio/package/manager/_registry.py +++ b/platformio/package/manager/_registry.py @@ -20,10 +20,10 @@ import click from platformio import __registry_mirror_hosts__ from platformio.cache import ContentCache -from platformio.clients.registry import RegistryClient from platformio.http import HTTPClient from platformio.package.exception import UnknownPackageError from platformio.package.meta import PackageSpec +from platformio.package.registry import RegistryClient from platformio.package.version import cast_version_to_semver diff --git a/platformio/clients/registry.py b/platformio/package/registry.py similarity index 100% rename from platformio/clients/registry.py rename to platformio/package/registry.py diff --git a/tests/commands/test_lib.py b/tests/commands/test_lib.py index 9429aaba..3825a4e1 100644 --- a/tests/commands/test_lib.py +++ b/tests/commands/test_lib.py @@ -20,9 +20,9 @@ import os import pytest import semantic_version -from platformio.clients.registry import RegistryClient from platformio.commands.lib.command import cli as cmd_lib from platformio.package.meta import PackageType +from platformio.package.registry import RegistryClient from platformio.package.vcsclient import VCSClientFactory from platformio.project.config import ProjectConfig diff --git a/tests/misc/test_misc.py b/tests/misc/test_misc.py index 5596b9a2..e63b8451 100644 --- a/tests/misc/test_misc.py +++ b/tests/misc/test_misc.py @@ -18,7 +18,7 @@ import pytest import requests from platformio import __check_internet_hosts__, http, proc -from platformio.clients.registry import RegistryClient +from platformio.package.registry import RegistryClient def test_platformio_cli(): From 4aebf8c9d77b6005ff8db7bb0227fca7e116165d Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 21:00:22 +0300 Subject: [PATCH 48/74] Refactor account module --- .../{clients/account.py => account/client.py} | 0 platformio/account/commands/__init__.py | 13 + platformio/account/commands/destroy.py | 37 +++ platformio/account/commands/forgot.py | 29 ++ platformio/account/commands/login.py | 26 ++ platformio/account/commands/logout.py | 24 ++ platformio/account/commands/password.py | 26 ++ platformio/account/commands/register.py | 52 +++ platformio/account/commands/show.py | 116 +++++++ platformio/account/commands/token.py | 32 ++ platformio/account/commands/update.py | 59 ++++ platformio/account/helpers.py | 48 +++ platformio/commands/access.py | 2 +- platformio/commands/account.py | 300 ++---------------- .../commands/home/rpc/handlers/account.py | 2 +- platformio/commands/org.py | 4 +- platformio/commands/remote/factory/client.py | 2 +- platformio/commands/team.py | 2 +- platformio/http.py | 2 +- platformio/package/commands/publish.py | 2 +- platformio/package/commands/unpublish.py | 2 +- platformio/package/registry.py | 2 +- 22 files changed, 496 insertions(+), 286 deletions(-) rename platformio/{clients/account.py => account/client.py} (100%) create mode 100644 platformio/account/commands/__init__.py create mode 100644 platformio/account/commands/destroy.py create mode 100644 platformio/account/commands/forgot.py create mode 100644 platformio/account/commands/login.py create mode 100644 platformio/account/commands/logout.py create mode 100644 platformio/account/commands/password.py create mode 100644 platformio/account/commands/register.py create mode 100644 platformio/account/commands/show.py create mode 100644 platformio/account/commands/token.py create mode 100644 platformio/account/commands/update.py create mode 100644 platformio/account/helpers.py diff --git a/platformio/clients/account.py b/platformio/account/client.py similarity index 100% rename from platformio/clients/account.py rename to platformio/account/client.py diff --git a/platformio/account/commands/__init__.py b/platformio/account/commands/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/account/commands/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/platformio/account/commands/destroy.py b/platformio/account/commands/destroy.py new file mode 100644 index 00000000..5a4f4dd6 --- /dev/null +++ b/platformio/account/commands/destroy.py @@ -0,0 +1,37 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient, AccountNotAuthorized + + +@click.command("destroy", short_help="Destroy account") +def account_destroy_cmd(): + client = AccountClient() + click.confirm( + "Are you sure you want to delete the %s user account?\n" + "Warning! All linked data will be permanently removed and can not be restored." + % client.get_logged_username(), + abort=True, + ) + client.destroy_account() + try: + client.logout() + except AccountNotAuthorized: + pass + click.secho( + "User account has been destroyed.", + fg="green", + ) diff --git a/platformio/account/commands/forgot.py b/platformio/account/commands/forgot.py new file mode 100644 index 00000000..4af8f5fa --- /dev/null +++ b/platformio/account/commands/forgot.py @@ -0,0 +1,29 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient + + +@click.command("forgot", short_help="Forgot password") +@click.option("--username", prompt="Username or email") +def account_forgot_cmd(username): + client = AccountClient() + client.forgot_password(username) + click.secho( + "If this account is registered, we will send the " + "further instructions to your email.", + fg="green", + ) diff --git a/platformio/account/commands/login.py b/platformio/account/commands/login.py new file mode 100644 index 00000000..b9471bfd --- /dev/null +++ b/platformio/account/commands/login.py @@ -0,0 +1,26 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient + + +@click.command("login", short_help="Log in to PlatformIO Account") +@click.option("-u", "--username", prompt="Username or email") +@click.option("-p", "--password", prompt=True, hide_input=True) +def account_login_cmd(username, password): + client = AccountClient() + client.login(username, password) + click.secho("Successfully logged in!", fg="green") diff --git a/platformio/account/commands/logout.py b/platformio/account/commands/logout.py new file mode 100644 index 00000000..243a7f14 --- /dev/null +++ b/platformio/account/commands/logout.py @@ -0,0 +1,24 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient + + +@click.command("logout", short_help="Log out of PlatformIO Account") +def account_logout_cmd(): + client = AccountClient() + client.logout() + click.secho("Successfully logged out!", fg="green") diff --git a/platformio/account/commands/password.py b/platformio/account/commands/password.py new file mode 100644 index 00000000..4f129c08 --- /dev/null +++ b/platformio/account/commands/password.py @@ -0,0 +1,26 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient + + +@click.command("password", short_help="Change password") +@click.option("--old-password", prompt=True, hide_input=True) +@click.option("--new-password", prompt=True, hide_input=True, confirmation_prompt=True) +def account_password_cmd(old_password, new_password): + client = AccountClient() + client.change_password(old_password, new_password) + click.secho("Password successfully changed!", fg="green") diff --git a/platformio/account/commands/register.py b/platformio/account/commands/register.py new file mode 100644 index 00000000..b5ec532d --- /dev/null +++ b/platformio/account/commands/register.py @@ -0,0 +1,52 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient +from platformio.account.helpers import ( + validate_email, + validate_password, + validate_username, +) + + +@click.command("register", short_help="Create new PlatformIO Account") +@click.option( + "-u", + "--username", + prompt=True, + callback=lambda _, __, value: validate_username(value), +) +@click.option( + "-e", "--email", prompt=True, callback=lambda _, __, value: validate_email(value) +) +@click.option( + "-p", + "--password", + prompt=True, + hide_input=True, + confirmation_prompt=True, + callback=lambda _, __, value: validate_password(value), +) +@click.option("--firstname", prompt=True) +@click.option("--lastname", prompt=True) +def account_register_cmd(username, email, password, firstname, lastname): + client = AccountClient() + client.registration(username, email, password, firstname, lastname) + click.secho( + "An account has been successfully created. " + "Please check your mail to activate your account and verify your email address.", + fg="green", + ) diff --git a/platformio/account/commands/show.py b/platformio/account/commands/show.py new file mode 100644 index 00000000..f107f4f5 --- /dev/null +++ b/platformio/account/commands/show.py @@ -0,0 +1,116 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 json + +import click +from tabulate import tabulate + +from platformio import util +from platformio.account.client import AccountClient + + +@click.command("show", short_help="PlatformIO Account information") +@click.option("--offline", is_flag=True) +@click.option("--json-output", is_flag=True) +def account_show_cmd(offline, json_output): + client = AccountClient() + info = client.get_account_info(offline) + if json_output: + click.echo(json.dumps(info)) + return + click.echo() + if info.get("profile"): + print_profile(info["profile"]) + if info.get("packages"): + print_packages(info["packages"]) + if info.get("subscriptions"): + print_subscriptions(info["subscriptions"]) + click.echo() + + +def print_profile(profile): + click.secho("Profile", fg="cyan", bold=True) + click.echo("=" * len("Profile")) + data = [] + if profile.get("username"): + data.append(("Username:", profile["username"])) + if profile.get("email"): + data.append(("Email:", profile["email"])) + if profile.get("firstname"): + data.append(("First name:", profile["firstname"])) + if profile.get("lastname"): + data.append(("Last name:", profile["lastname"])) + click.echo(tabulate(data, tablefmt="plain")) + + +def print_packages(packages): + click.echo() + click.secho("Packages", fg="cyan") + click.echo("=" * len("Packages")) + for package in packages: + click.echo() + click.secho(package.get("name"), bold=True) + click.echo("-" * len(package.get("name"))) + if package.get("description"): + click.echo(package.get("description")) + data = [] + expire = "-" + if "subscription" in package: + expire = util.parse_datetime( + package["subscription"].get("end_at") + or package["subscription"].get("next_bill_at") + ).strftime("%Y-%m-%d") + data.append(("Expire:", expire)) + services = [] + for key in package: + if not key.startswith("service."): + continue + if isinstance(package[key], dict): + services.append(package[key].get("title")) + else: + services.append(package[key]) + if services: + data.append(("Services:", ", ".join(services))) + click.echo(tabulate(data, tablefmt="plain")) + + +def print_subscriptions(subscriptions): + click.echo() + click.secho("Subscriptions", fg="cyan") + click.echo("=" * len("Subscriptions")) + for subscription in subscriptions: + click.echo() + click.secho(subscription.get("product_name"), bold=True) + click.echo("-" * len(subscription.get("product_name"))) + data = [("State:", subscription.get("status"))] + begin_at = util.parse_datetime(subscription.get("begin_at")).strftime("%c") + data.append(("Start date:", begin_at or "-")) + end_at = subscription.get("end_at") + if end_at: + end_at = util.parse_datetime(subscription.get("end_at")).strftime("%c") + data.append(("End date:", end_at or "-")) + next_bill_at = subscription.get("next_bill_at") + if next_bill_at: + next_bill_at = util.parse_datetime( + subscription.get("next_bill_at") + ).strftime("%c") + data.append(("Next payment:", next_bill_at or "-")) + data.append( + ("Edit:", click.style(subscription.get("update_url"), fg="blue") or "-") + ) + data.append( + ("Cancel:", click.style(subscription.get("cancel_url"), fg="blue") or "-") + ) + click.echo(tabulate(data, tablefmt="plain")) diff --git a/platformio/account/commands/token.py b/platformio/account/commands/token.py new file mode 100644 index 00000000..0f06e367 --- /dev/null +++ b/platformio/account/commands/token.py @@ -0,0 +1,32 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 json + +import click + +from platformio.account.client import AccountClient + + +@click.command("token", short_help="Get or regenerate Authentication Token") +@click.option("-p", "--password", prompt=True, hide_input=True) +@click.option("--regenerate", is_flag=True) +@click.option("--json-output", is_flag=True) +def account_token_cmd(password, regenerate, json_output): + client = AccountClient() + auth_token = client.auth_token(password, regenerate) + if json_output: + click.echo(json.dumps({"status": "success", "result": auth_token})) + return + click.secho("Personal Authentication Token: %s" % auth_token, fg="green") diff --git a/platformio/account/commands/update.py b/platformio/account/commands/update.py new file mode 100644 index 00000000..e198867d --- /dev/null +++ b/platformio/account/commands/update.py @@ -0,0 +1,59 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient, AccountNotAuthorized +from platformio.account.helpers import validate_email, validate_username + + +@click.command("update", short_help="Update profile information") +@click.option("--current-password", prompt=True, hide_input=True) +@click.option("--username") +@click.option("--email") +@click.option("--firstname") +@click.option("--lastname") +def account_update_cmd(current_password, **kwargs): + client = AccountClient() + profile = client.get_profile() + new_profile = profile.copy() + if not any(kwargs.values()): + for field in profile: + new_profile[field] = click.prompt( + field.replace("_", " ").capitalize(), default=profile[field] + ) + if field == "email": + validate_email(new_profile[field]) + if field == "username": + validate_username(new_profile[field]) + else: + new_profile.update({key: value for key, value in kwargs.items() if value}) + client.update_profile(new_profile, current_password) + click.secho("Profile successfully updated!", fg="green") + username_changed = new_profile["username"] != profile["username"] + email_changed = new_profile["email"] != profile["email"] + if not username_changed and not email_changed: + return None + try: + client.logout() + except AccountNotAuthorized: + pass + if email_changed: + click.secho( + "Please check your mail to verify your new email address and re-login. ", + fg="yellow", + ) + return None + click.secho("Please re-login.", fg="yellow") + return None diff --git a/platformio/account/helpers.py b/platformio/account/helpers.py new file mode 100644 index 00000000..adf9413e --- /dev/null +++ b/platformio/account/helpers.py @@ -0,0 +1,48 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 re + +import click + + +def validate_username(value, field="username"): + value = str(value).strip() + if not re.match(r"^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,37}$", value, flags=re.I): + raise click.BadParameter( + "Invalid %s format. " + "%s must contain only alphanumeric characters " + "or single hyphens, cannot begin or end with a hyphen, " + "and must not be longer than 38 characters." + % (field.lower(), field.capitalize()) + ) + return value + + +def validate_email(value): + value = str(value).strip() + if not re.match(r"^[a-z\d_.+-]+@[a-z\d\-]+\.[a-z\d\-.]+$", value, flags=re.I): + raise click.BadParameter("Invalid email address") + return value + + +def validate_password(value): + value = str(value).strip() + if not re.match(r"^(?=.*[a-z])(?=.*\d).{8,}$", value): + raise click.BadParameter( + "Invalid password format. " + "Password must contain at least 8 characters" + " including a number and a lowercase letter" + ) + return value diff --git a/platformio/commands/access.py b/platformio/commands/access.py index ccd4150a..6337c0f4 100644 --- a/platformio/commands/access.py +++ b/platformio/commands/access.py @@ -20,7 +20,7 @@ import re import click from tabulate import tabulate -from platformio.commands.account import validate_username +from platformio.account.helpers import validate_username from platformio.commands.team import validate_orgname_teamname from platformio.package.registry import RegistryClient diff --git a/platformio/commands/account.py b/platformio/commands/account.py index 6e17fa40..135445d4 100644 --- a/platformio/commands/account.py +++ b/platformio/commands/account.py @@ -12,285 +12,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -# pylint: disable=unused-argument - -import json -import re - import click -from tabulate import tabulate -from platformio import util -from platformio.clients.account import AccountClient, AccountNotAuthorized +from platformio.account.commands.destroy import account_destroy_cmd +from platformio.account.commands.forgot import account_forgot_cmd +from platformio.account.commands.login import account_login_cmd +from platformio.account.commands.logout import account_logout_cmd +from platformio.account.commands.password import account_password_cmd +from platformio.account.commands.register import account_register_cmd +from platformio.account.commands.show import account_show_cmd +from platformio.account.commands.token import account_token_cmd +from platformio.account.commands.update import account_update_cmd -@click.group("account", short_help="Manage PlatformIO account") +@click.group( + "account", + commands=[ + account_destroy_cmd, + account_forgot_cmd, + account_login_cmd, + account_logout_cmd, + account_password_cmd, + account_register_cmd, + account_show_cmd, + account_token_cmd, + account_update_cmd, + ], + short_help="Manage PlatformIO account", +) def cli(): pass - - -def validate_username(value, field="username"): - value = str(value).strip() - if not re.match(r"^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,37}$", value, flags=re.I): - raise click.BadParameter( - "Invalid %s format. " - "%s must contain only alphanumeric characters " - "or single hyphens, cannot begin or end with a hyphen, " - "and must not be longer than 38 characters." - % (field.lower(), field.capitalize()) - ) - return value - - -def validate_email(value): - value = str(value).strip() - if not re.match(r"^[a-z\d_.+-]+@[a-z\d\-]+\.[a-z\d\-.]+$", value, flags=re.I): - raise click.BadParameter("Invalid email address") - return value - - -def validate_password(value): - value = str(value).strip() - if not re.match(r"^(?=.*[a-z])(?=.*\d).{8,}$", value): - raise click.BadParameter( - "Invalid password format. " - "Password must contain at least 8 characters" - " including a number and a lowercase letter" - ) - return value - - -@cli.command("register", short_help="Create new PlatformIO Account") -@click.option( - "-u", - "--username", - prompt=True, - callback=lambda _, __, value: validate_username(value), -) -@click.option( - "-e", "--email", prompt=True, callback=lambda _, __, value: validate_email(value) -) -@click.option( - "-p", - "--password", - prompt=True, - hide_input=True, - confirmation_prompt=True, - callback=lambda _, __, value: validate_password(value), -) -@click.option("--firstname", prompt=True) -@click.option("--lastname", prompt=True) -def account_register(username, email, password, firstname, lastname): - client = AccountClient() - client.registration(username, email, password, firstname, lastname) - click.secho( - "An account has been successfully created. " - "Please check your mail to activate your account and verify your email address.", - fg="green", - ) - - -@cli.command("login", short_help="Log in to PlatformIO Account") -@click.option("-u", "--username", prompt="Username or email") -@click.option("-p", "--password", prompt=True, hide_input=True) -def account_login(username, password): - client = AccountClient() - client.login(username, password) - click.secho("Successfully logged in!", fg="green") - - -@cli.command("logout", short_help="Log out of PlatformIO Account") -def account_logout(): - client = AccountClient() - client.logout() - click.secho("Successfully logged out!", fg="green") - - -@cli.command("password", short_help="Change password") -@click.option("--old-password", prompt=True, hide_input=True) -@click.option("--new-password", prompt=True, hide_input=True, confirmation_prompt=True) -def account_password(old_password, new_password): - client = AccountClient() - client.change_password(old_password, new_password) - click.secho("Password successfully changed!", fg="green") - - -@cli.command("token", short_help="Get or regenerate Authentication Token") -@click.option("-p", "--password", prompt=True, hide_input=True) -@click.option("--regenerate", is_flag=True) -@click.option("--json-output", is_flag=True) -def account_token(password, regenerate, json_output): - client = AccountClient() - auth_token = client.auth_token(password, regenerate) - if json_output: - click.echo(json.dumps({"status": "success", "result": auth_token})) - return - click.secho("Personal Authentication Token: %s" % auth_token, fg="green") - - -@cli.command("forgot", short_help="Forgot password") -@click.option("--username", prompt="Username or email") -def account_forgot(username): - client = AccountClient() - client.forgot_password(username) - click.secho( - "If this account is registered, we will send the " - "further instructions to your email.", - fg="green", - ) - - -@cli.command("update", short_help="Update profile information") -@click.option("--current-password", prompt=True, hide_input=True) -@click.option("--username") -@click.option("--email") -@click.option("--firstname") -@click.option("--lastname") -def account_update(current_password, **kwargs): - client = AccountClient() - profile = client.get_profile() - new_profile = profile.copy() - if not any(kwargs.values()): - for field in profile: - new_profile[field] = click.prompt( - field.replace("_", " ").capitalize(), default=profile[field] - ) - if field == "email": - validate_email(new_profile[field]) - if field == "username": - validate_username(new_profile[field]) - else: - new_profile.update({key: value for key, value in kwargs.items() if value}) - client.update_profile(new_profile, current_password) - click.secho("Profile successfully updated!", fg="green") - username_changed = new_profile["username"] != profile["username"] - email_changed = new_profile["email"] != profile["email"] - if not username_changed and not email_changed: - return None - try: - client.logout() - except AccountNotAuthorized: - pass - if email_changed: - click.secho( - "Please check your mail to verify your new email address and re-login. ", - fg="yellow", - ) - return None - click.secho("Please re-login.", fg="yellow") - return None - - -@cli.command("destroy", short_help="Destroy account") -def account_destroy(): - client = AccountClient() - click.confirm( - "Are you sure you want to delete the %s user account?\n" - "Warning! All linked data will be permanently removed and can not be restored." - % client.get_logged_username(), - abort=True, - ) - client.destroy_account() - try: - client.logout() - except AccountNotAuthorized: - pass - click.secho( - "User account has been destroyed.", - fg="green", - ) - - -@cli.command("show", short_help="PlatformIO Account information") -@click.option("--offline", is_flag=True) -@click.option("--json-output", is_flag=True) -def account_show(offline, json_output): - client = AccountClient() - info = client.get_account_info(offline) - if json_output: - click.echo(json.dumps(info)) - return - click.echo() - if info.get("profile"): - print_profile(info["profile"]) - if info.get("packages"): - print_packages(info["packages"]) - if info.get("subscriptions"): - print_subscriptions(info["subscriptions"]) - click.echo() - - -def print_profile(profile): - click.secho("Profile", fg="cyan", bold=True) - click.echo("=" * len("Profile")) - data = [] - if profile.get("username"): - data.append(("Username:", profile["username"])) - if profile.get("email"): - data.append(("Email:", profile["email"])) - if profile.get("firstname"): - data.append(("First name:", profile["firstname"])) - if profile.get("lastname"): - data.append(("Last name:", profile["lastname"])) - click.echo(tabulate(data, tablefmt="plain")) - - -def print_packages(packages): - click.echo() - click.secho("Packages", fg="cyan") - click.echo("=" * len("Packages")) - for package in packages: - click.echo() - click.secho(package.get("name"), bold=True) - click.echo("-" * len(package.get("name"))) - if package.get("description"): - click.echo(package.get("description")) - data = [] - expire = "-" - if "subscription" in package: - expire = util.parse_datetime( - package["subscription"].get("end_at") - or package["subscription"].get("next_bill_at") - ).strftime("%Y-%m-%d") - data.append(("Expire:", expire)) - services = [] - for key in package: - if not key.startswith("service."): - continue - if isinstance(package[key], dict): - services.append(package[key].get("title")) - else: - services.append(package[key]) - if services: - data.append(("Services:", ", ".join(services))) - click.echo(tabulate(data, tablefmt="plain")) - - -def print_subscriptions(subscriptions): - click.echo() - click.secho("Subscriptions", fg="cyan") - click.echo("=" * len("Subscriptions")) - for subscription in subscriptions: - click.echo() - click.secho(subscription.get("product_name"), bold=True) - click.echo("-" * len(subscription.get("product_name"))) - data = [("State:", subscription.get("status"))] - begin_at = util.parse_datetime(subscription.get("begin_at")).strftime("%c") - data.append(("Start date:", begin_at or "-")) - end_at = subscription.get("end_at") - if end_at: - end_at = util.parse_datetime(subscription.get("end_at")).strftime("%c") - data.append(("End date:", end_at or "-")) - next_bill_at = subscription.get("next_bill_at") - if next_bill_at: - next_bill_at = util.parse_datetime( - subscription.get("next_bill_at") - ).strftime("%c") - data.append(("Next payment:", next_bill_at or "-")) - data.append( - ("Edit:", click.style(subscription.get("update_url"), fg="blue") or "-") - ) - data.append( - ("Cancel:", click.style(subscription.get("cancel_url"), fg="blue") or "-") - ) - click.echo(tabulate(data, tablefmt="plain")) diff --git a/platformio/commands/home/rpc/handlers/account.py b/platformio/commands/home/rpc/handlers/account.py index 5336d600..23777437 100644 --- a/platformio/commands/home/rpc/handlers/account.py +++ b/platformio/commands/home/rpc/handlers/account.py @@ -14,7 +14,7 @@ from ajsonrpc.core import JSONRPC20DispatchException -from platformio.clients.account import AccountClient +from platformio.account.client import AccountClient class AccountRPC: diff --git a/platformio/commands/org.py b/platformio/commands/org.py index 7f003cb0..bd569e76 100644 --- a/platformio/commands/org.py +++ b/platformio/commands/org.py @@ -19,8 +19,8 @@ import json import click from tabulate import tabulate -from platformio.clients.account import AccountClient -from platformio.commands.account import validate_email, validate_username +from platformio.account.client import AccountClient +from platformio.account.helpers import validate_email, validate_username @click.group("org", short_help="Manage organizations") diff --git a/platformio/commands/remote/factory/client.py b/platformio/commands/remote/factory/client.py index 712449d5..2565e6ad 100644 --- a/platformio/commands/remote/factory/client.py +++ b/platformio/commands/remote/factory/client.py @@ -16,8 +16,8 @@ from twisted.cred import credentials # pylint: disable=import-error from twisted.internet import defer, protocol, reactor # pylint: disable=import-error from twisted.spread import pb # pylint: disable=import-error +from platformio.account.client import AccountClient from platformio.app import get_host_id -from platformio.clients.account import AccountClient class RemoteClientFactory(pb.PBClientFactory, protocol.ReconnectingClientFactory): diff --git a/platformio/commands/team.py b/platformio/commands/team.py index 6733a420..aac68d1e 100644 --- a/platformio/commands/team.py +++ b/platformio/commands/team.py @@ -20,7 +20,7 @@ import re import click from tabulate import tabulate -from platformio.clients.account import AccountClient +from platformio.account.client import AccountClient def validate_orgname_teamname(value, teamname_validate=False): diff --git a/platformio/http.py b/platformio/http.py index 86fb8cae..5f5fbdd2 100644 --- a/platformio/http.py +++ b/platformio/http.py @@ -115,7 +115,7 @@ class HTTPClient(object): ) if with_authorization and "Authorization" not in headers: # pylint: disable=import-outside-toplevel - from platformio.clients.account import AccountClient + from platformio.account.client import AccountClient headers["Authorization"] = ( "Bearer %s" % AccountClient().fetch_authentication_token() diff --git a/platformio/package/commands/publish.py b/platformio/package/commands/publish.py index 89cc5c01..dfa3e371 100644 --- a/platformio/package/commands/publish.py +++ b/platformio/package/commands/publish.py @@ -21,7 +21,7 @@ import click from tabulate import tabulate from platformio import fs -from platformio.clients.account import AccountClient +from platformio.account.client import AccountClient from platformio.exception import UserSideException from platformio.package.manifest.parser import ManifestParserFactory from platformio.package.manifest.schema import ManifestSchema diff --git a/platformio/package/commands/unpublish.py b/platformio/package/commands/unpublish.py index 88646095..b1194b9e 100644 --- a/platformio/package/commands/unpublish.py +++ b/platformio/package/commands/unpublish.py @@ -14,7 +14,7 @@ import click -from platformio.clients.account import AccountClient +from platformio.account.client import AccountClient from platformio.package.meta import PackageSpec, PackageType from platformio.package.registry import RegistryClient diff --git a/platformio/package/registry.py b/platformio/package/registry.py index 1682f7b1..2c465e0c 100644 --- a/platformio/package/registry.py +++ b/platformio/package/registry.py @@ -15,7 +15,7 @@ # pylint: disable=too-many-arguments from platformio import __registry_mirror_hosts__, fs -from platformio.clients.account import AccountClient, AccountError +from platformio.account.client import AccountClient, AccountError from platformio.http import HTTPClient, HTTPClientError From 6e5aee5ef39219554d604936079f773f1f80a377 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 21:02:59 +0300 Subject: [PATCH 49/74] Rename "registry" module to the "client" --- platformio/commands/access.py | 2 +- platformio/package/{registry.py => client.py} | 0 platformio/package/commands/publish.py | 2 +- platformio/package/commands/search.py | 2 +- platformio/package/commands/show.py | 2 +- platformio/package/commands/unpublish.py | 2 +- platformio/package/manager/_registry.py | 2 +- tests/commands/test_lib.py | 2 +- tests/misc/test_misc.py | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename platformio/package/{registry.py => client.py} (100%) diff --git a/platformio/commands/access.py b/platformio/commands/access.py index 6337c0f4..d35d0830 100644 --- a/platformio/commands/access.py +++ b/platformio/commands/access.py @@ -22,7 +22,7 @@ from tabulate import tabulate from platformio.account.helpers import validate_username from platformio.commands.team import validate_orgname_teamname -from platformio.package.registry import RegistryClient +from platformio.package.client import RegistryClient def validate_client(value): diff --git a/platformio/package/registry.py b/platformio/package/client.py similarity index 100% rename from platformio/package/registry.py rename to platformio/package/client.py diff --git a/platformio/package/commands/publish.py b/platformio/package/commands/publish.py index dfa3e371..ee7e4a25 100644 --- a/platformio/package/commands/publish.py +++ b/platformio/package/commands/publish.py @@ -23,11 +23,11 @@ from tabulate import tabulate from platformio import fs from platformio.account.client import AccountClient from platformio.exception import UserSideException +from platformio.package.client import RegistryClient from platformio.package.manifest.parser import ManifestParserFactory from platformio.package.manifest.schema import ManifestSchema from platformio.package.meta import PackageType from platformio.package.pack import PackagePacker -from platformio.package.registry import RegistryClient from platformio.package.unpack import FileUnpacker, TARArchiver diff --git a/platformio/package/commands/search.py b/platformio/package/commands/search.py index 787d94dd..d93b9f81 100644 --- a/platformio/package/commands/search.py +++ b/platformio/package/commands/search.py @@ -17,7 +17,7 @@ import math import click from platformio import util -from platformio.package.registry import RegistryClient +from platformio.package.client import RegistryClient @click.command("search", short_help="Search for packages") diff --git a/platformio/package/commands/show.py b/platformio/package/commands/show.py index 5fd45f62..c1ee28bb 100644 --- a/platformio/package/commands/show.py +++ b/platformio/package/commands/show.py @@ -19,9 +19,9 @@ from tabulate import tabulate from platformio import fs, util from platformio.exception import UserSideException +from platformio.package.client import RegistryClient from platformio.package.manager._registry import PackageManagerRegistryMixin from platformio.package.meta import PackageSpec, PackageType -from platformio.package.registry import RegistryClient @click.command("show", short_help="Show package information") diff --git a/platformio/package/commands/unpublish.py b/platformio/package/commands/unpublish.py index b1194b9e..9c1d067e 100644 --- a/platformio/package/commands/unpublish.py +++ b/platformio/package/commands/unpublish.py @@ -15,8 +15,8 @@ import click from platformio.account.client import AccountClient +from platformio.package.client import RegistryClient from platformio.package.meta import PackageSpec, PackageType -from platformio.package.registry import RegistryClient @click.command("unpublish", short_help="Remove a pushed package from the registry") diff --git a/platformio/package/manager/_registry.py b/platformio/package/manager/_registry.py index 7e00c78f..2fc11e1d 100644 --- a/platformio/package/manager/_registry.py +++ b/platformio/package/manager/_registry.py @@ -21,9 +21,9 @@ import click from platformio import __registry_mirror_hosts__ from platformio.cache import ContentCache from platformio.http import HTTPClient +from platformio.package.client import RegistryClient from platformio.package.exception import UnknownPackageError from platformio.package.meta import PackageSpec -from platformio.package.registry import RegistryClient from platformio.package.version import cast_version_to_semver diff --git a/tests/commands/test_lib.py b/tests/commands/test_lib.py index 3825a4e1..dcfcab20 100644 --- a/tests/commands/test_lib.py +++ b/tests/commands/test_lib.py @@ -21,8 +21,8 @@ import pytest import semantic_version from platformio.commands.lib.command import cli as cmd_lib +from platformio.package.client import RegistryClient from platformio.package.meta import PackageType -from platformio.package.registry import RegistryClient from platformio.package.vcsclient import VCSClientFactory from platformio.project.config import ProjectConfig diff --git a/tests/misc/test_misc.py b/tests/misc/test_misc.py index e63b8451..a7d776e1 100644 --- a/tests/misc/test_misc.py +++ b/tests/misc/test_misc.py @@ -18,7 +18,7 @@ import pytest import requests from platformio import __check_internet_hosts__, http, proc -from platformio.package.registry import RegistryClient +from platformio.package.client import RegistryClient def test_platformio_cli(): From 6b7e8ebe97eba534ce8ca4ef25cc9236e825e872 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 21:04:19 +0300 Subject: [PATCH 50/74] Bump version to 6.0.2rc1 --- platformio/__init__.py | 2 +- platformio/clients/__init__.py | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 platformio/clients/__init__.py diff --git a/platformio/__init__.py b/platformio/__init__.py index 33b75af8..b30c04d0 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -14,7 +14,7 @@ import sys -VERSION = (6, 0, "2b1") +VERSION = (6, 0, "2rc1") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/clients/__init__.py b/platformio/clients/__init__.py deleted file mode 100644 index b0514903..00000000 --- a/platformio/clients/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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. From 0ce78858339031e70800eecdd0acb97d0bf15984 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 22:23:06 +0300 Subject: [PATCH 51/74] Fix module import --- platformio/account/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 platformio/account/__init__.py diff --git a/platformio/account/__init__.py b/platformio/account/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/account/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. From e2892d5d4ccd297a67a0fd3cae35bfa7a23c081f Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 30 May 2022 22:23:36 +0300 Subject: [PATCH 52/74] Bump version to 6.0.2rc2 --- platformio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/__init__.py b/platformio/__init__.py index b30c04d0..1a12e8df 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -14,7 +14,7 @@ import sys -VERSION = (6, 0, "2rc1") +VERSION = (6, 0, "2rc2") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" From 506a08c7cf80dbba174c780d86faac49e9931370 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 14:04:49 +0300 Subject: [PATCH 53/74] Fix tests --- tests/commands/pkg/test_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/pkg/test_install.py b/tests/commands/pkg/test_install.py index d2aa25d6..3f3fcc96 100644 --- a/tests/commands/pkg/test_install.py +++ b/tests/commands/pkg/test_install.py @@ -395,7 +395,7 @@ def test_custom_project_libraries( ) assert pkgs_to_specs(lm.get_installed()) == [ PackageSpec("ArduinoJson@5.13.4"), - PackageSpec("Nanopb@0.4.6+3"), + PackageSpec("Nanopb@0.4.6+4"), ] assert config.get("env:devkit", "lib_deps") == [ "bblanchon/ArduinoJson@^5", From dcecd5f922aec5e4d92c68713e342cf0b46abd26 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 17:07:56 +0300 Subject: [PATCH 54/74] Refactor handling of CLI commands --- platformio/__main__.py | 4 +- .../{commands/account.py => account/cli.py} | 0 platformio/cli.py | 96 +++++++++++++++++++ platformio/commands/__init__.py | 73 -------------- platformio/commands/debug.py | 17 ---- platformio/commands/lib/command.py | 2 +- platformio/commands/remote/command.py | 2 +- platformio/commands/test.py | 17 ---- platformio/debug/{command.py => cli.py} | 3 + platformio/debug/helpers.py | 2 +- .../device/command.py => device/cli.py} | 0 platformio/maintenance.py | 2 +- .../{commands/pkg.py => package/cli.py} | 0 platformio/package/manager/base.py | 2 +- .../{commands/project.py => project/cli.py} | 0 platformio/telemetry.py | 2 +- platformio/test/{command.py => cli.py} | 3 + tests/commands/test_account_org_team.py | 2 +- tests/commands/test_lib_complex.py | 2 +- tests/commands/test_test.py | 2 +- 20 files changed, 113 insertions(+), 118 deletions(-) rename platformio/{commands/account.py => account/cli.py} (100%) create mode 100644 platformio/cli.py delete mode 100644 platformio/commands/debug.py delete mode 100644 platformio/commands/test.py rename platformio/debug/{command.py => cli.py} (99%) rename platformio/{commands/device/command.py => device/cli.py} (100%) rename platformio/{commands/pkg.py => package/cli.py} (100%) rename platformio/{commands/project.py => project/cli.py} (100%) rename platformio/test/{command.py => cli.py} (99%) diff --git a/platformio/__main__.py b/platformio/__main__.py index 464f2fb9..c7f8e245 100644 --- a/platformio/__main__.py +++ b/platformio/__main__.py @@ -19,7 +19,7 @@ from traceback import format_exc import click from platformio import __version__, exception, maintenance -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.compat import IS_CYGWIN, ensure_python3 @@ -27,7 +27,7 @@ from platformio.compat import IS_CYGWIN, ensure_python3 cls=PlatformioCLI, context_settings=dict(help_option_names=["-h", "--help"]) ) @click.version_option(__version__, prog_name="PlatformIO Core") -@click.option("--force", "-f", is_flag=True, help="DEPRECATED") +@click.option("--force", "-f", is_flag=True, help="DEPRECATED", hidden=True) @click.option("--caller", "-c", help="Caller ID (service)") @click.option("--no-ansi", is_flag=True, help="Do not print ANSI control characters") @click.pass_context diff --git a/platformio/commands/account.py b/platformio/account/cli.py similarity index 100% rename from platformio/commands/account.py rename to platformio/account/cli.py diff --git a/platformio/cli.py b/platformio/cli.py new file mode 100644 index 00000000..bdb46a3d --- /dev/null +++ b/platformio/cli.py @@ -0,0 +1,96 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 importlib +from pathlib import Path + +import click + + +class PlatformioCLI(click.MultiCommand): + + leftover_args = [] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._pio_root_path = Path(__file__).parent + self._pio_cmd_aliases = dict(package="pkg") + + def _find_pio_commands(self): + def _to_module_path(p): + return ( + "platformio." + ".".join(p.relative_to(self._pio_root_path).parts)[:-3] + ) + + result = {} + for p in self._pio_root_path.rglob("cli.py"): + # skip this module + if p.parent == self._pio_root_path: + continue + cmd_name = p.parent.name + result[self._pio_cmd_aliases.get(cmd_name, cmd_name)] = _to_module_path(p) + + # find legacy commands + for p in (self._pio_root_path / "commands").iterdir(): + if p.name.startswith("_"): + continue + if (p / "command.py").is_file(): + result[p.name] = _to_module_path(p / "command.py") + elif p.name.endswith(".py"): + result[p.name[:-3]] = _to_module_path(p) + + return result + + @staticmethod + def in_silence(): + args = PlatformioCLI.leftover_args + return args and any( + [ + args[0] == "debug" and "--interpreter" in " ".join(args), + args[0] == "upgrade", + "--json-output" in args, + "--version" in args, + ] + ) + + def invoke(self, ctx): + PlatformioCLI.leftover_args = ctx.args + if hasattr(ctx, "protected_args"): + PlatformioCLI.leftover_args = ctx.protected_args + ctx.args + return super().invoke(ctx) + + def list_commands(self, ctx): + return sorted(list(self._find_pio_commands())) + + def get_command(self, ctx, cmd_name): + commands = self._find_pio_commands() + if cmd_name not in commands: + return self._handle_obsolate_command(ctx, cmd_name) + module = importlib.import_module(commands[cmd_name]) + return getattr(module, "cli") + + @staticmethod + def _handle_obsolate_command(ctx, cmd_name): + # pylint: disable=import-outside-toplevel + if cmd_name == "init": + from platformio.project.commands.init import project_init_cmd + + return project_init_cmd + + if cmd_name == "package": + from platformio.package.cli import cli + + return cli + + raise click.UsageError('No such command "%s"' % cmd_name, ctx) diff --git a/platformio/commands/__init__.py b/platformio/commands/__init__.py index 22cacc60..b0514903 100644 --- a/platformio/commands/__init__.py +++ b/platformio/commands/__init__.py @@ -11,76 +11,3 @@ # 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 os - -import click - - -class PlatformioCLI(click.MultiCommand): - - leftover_args = [] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._pio_cmds_dir = os.path.dirname(__file__) - - @staticmethod - def in_silence(): - args = PlatformioCLI.leftover_args - return args and any( - [ - args[0] == "debug" and "--interpreter" in " ".join(args), - args[0] == "upgrade", - "--json-output" in args, - "--version" in args, - ] - ) - - def invoke(self, ctx): - PlatformioCLI.leftover_args = ctx.args - if hasattr(ctx, "protected_args"): - PlatformioCLI.leftover_args = ctx.protected_args + ctx.args - return super().invoke(ctx) - - def list_commands(self, ctx): - cmds = [] - for cmd_name in os.listdir(self._pio_cmds_dir): - if cmd_name.startswith("__init__"): - continue - if os.path.isfile(os.path.join(self._pio_cmds_dir, cmd_name, "command.py")): - cmds.append(cmd_name) - elif cmd_name.endswith(".py"): - cmds.append(cmd_name[:-3]) - cmds.sort() - return cmds - - def get_command(self, ctx, cmd_name): - mod = None - try: - mod_path = "platformio.commands." + cmd_name - if os.path.isfile(os.path.join(self._pio_cmds_dir, cmd_name, "command.py")): - mod_path = "platformio.commands.%s.command" % cmd_name - mod = __import__(mod_path, None, None, ["cli"]) - except ImportError: - try: - return self._handle_obsolate_command(cmd_name) - except AttributeError: - pass - raise click.UsageError('No such command "%s"' % cmd_name, ctx) - return mod.cli - - @staticmethod - def _handle_obsolate_command(name): - # pylint: disable=import-outside-toplevel - if name == "init": - from platformio.project.commands.init import project_init_cmd - - return project_init_cmd - - if name == "package": - from platformio.commands.pkg import cli - - return cli - - raise AttributeError() diff --git a/platformio/commands/debug.py b/platformio/commands/debug.py deleted file mode 100644 index 3ab61d9b..00000000 --- a/platformio/commands/debug.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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-import - -from platformio.debug.command import debug_cmd as cli diff --git a/platformio/commands/lib/command.py b/platformio/commands/lib/command.py index 367a80d6..3b9af87e 100644 --- a/platformio/commands/lib/command.py +++ b/platformio/commands/lib/command.py @@ -24,7 +24,7 @@ import click from tabulate import tabulate from platformio import exception, fs, util -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.commands.lib.helpers import get_builtin_libs, save_project_libdeps from platformio.package.exception import NotGlobalLibDir, UnknownPackageError from platformio.package.manager.library import LibraryPackageManager diff --git a/platformio/commands/remote/command.py b/platformio/commands/remote/command.py index 5f97d983..acc0d310 100644 --- a/platformio/commands/remote/command.py +++ b/platformio/commands/remote/command.py @@ -34,7 +34,7 @@ from platformio.device.commands.monitor import ( from platformio.package.manager.core import inject_contrib_pysite from platformio.project.exception import NotPlatformIOProjectError from platformio.project.options import ProjectOptions -from platformio.test.command import test_cmd +from platformio.test.cli import test_cmd @click.group("remote", short_help="Remote Development") diff --git a/platformio/commands/test.py b/platformio/commands/test.py deleted file mode 100644 index df641161..00000000 --- a/platformio/commands/test.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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-import - -from platformio.test.command import test_cmd as cli diff --git a/platformio/debug/command.py b/platformio/debug/cli.py similarity index 99% rename from platformio/debug/command.py rename to platformio/debug/cli.py index e8fd6290..489261c9 100644 --- a/platformio/debug/command.py +++ b/platformio/debug/cli.py @@ -93,6 +93,9 @@ def debug_cmd( ) +cli = debug_cmd + + def _debug_in_project_dir( ctx, project_dir, diff --git a/platformio/debug/helpers.py b/platformio/debug/helpers.py index cd87f141..0b15d70d 100644 --- a/platformio/debug/helpers.py +++ b/platformio/debug/helpers.py @@ -20,7 +20,7 @@ from fnmatch import fnmatch from hashlib import sha1 from io import BytesIO -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.commands.run.command import cli as cmd_run from platformio.commands.run.command import print_processing_header from platformio.compat import IS_WINDOWS, is_bytes diff --git a/platformio/commands/device/command.py b/platformio/device/cli.py similarity index 100% rename from platformio/commands/device/command.py rename to platformio/device/cli.py diff --git a/platformio/maintenance.py b/platformio/maintenance.py index 9a897d62..c3f07e74 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -21,7 +21,7 @@ import semantic_version from platformio import __version__, app, exception, fs, telemetry from platformio.cache import cleanup_content_cache -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.commands.platform import platform_update as cmd_platform_update from platformio.commands.system.prune import calculate_unnecessary_system_data from platformio.commands.upgrade import get_latest_version diff --git a/platformio/commands/pkg.py b/platformio/package/cli.py similarity index 100% rename from platformio/commands/pkg.py rename to platformio/package/cli.py diff --git a/platformio/package/manager/base.py b/platformio/package/manager/base.py index 07ca2f5b..b692140f 100644 --- a/platformio/package/manager/base.py +++ b/platformio/package/manager/base.py @@ -21,7 +21,7 @@ import click import semantic_version from platformio import fs, util -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.compat import ci_strings_are_equal from platformio.package.exception import ManifestException, MissingPackageManifestError from platformio.package.lockfile import LockFile diff --git a/platformio/commands/project.py b/platformio/project/cli.py similarity index 100% rename from platformio/commands/project.py rename to platformio/project/cli.py diff --git a/platformio/telemetry.py b/platformio/telemetry.py index ba721792..c0fbf97d 100644 --- a/platformio/telemetry.py +++ b/platformio/telemetry.py @@ -28,7 +28,7 @@ from traceback import format_exc import requests from platformio import __version__, app, exception, util -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.compat import hashlib_encode_data, string_types from platformio.proc import is_ci, is_container from platformio.project.helpers import is_platformio_project diff --git a/platformio/test/command.py b/platformio/test/cli.py similarity index 99% rename from platformio/test/command.py rename to platformio/test/cli.py index 325bbb47..25279d7d 100644 --- a/platformio/test/command.py +++ b/platformio/test/cli.py @@ -175,6 +175,9 @@ def test_cmd( # pylint: disable=too-many-arguments,too-many-locals,redefined-bu raise exception.ReturnErrorCode(1) +cli = test_cmd + + def print_suite_header(test_suite): click.echo( "Processing %s in %s environment" diff --git a/tests/commands/test_account_org_team.py b/tests/commands/test_account_org_team.py index 53344502..29d85971 100644 --- a/tests/commands/test_account_org_team.py +++ b/tests/commands/test_account_org_team.py @@ -21,7 +21,7 @@ import random import pytest import requests -from platformio.commands.account import cli as cmd_account +from platformio.account.cli import cli as cmd_account from platformio.commands.org import cli as cmd_org from platformio.commands.team import cli as cmd_team diff --git a/tests/commands/test_lib_complex.py b/tests/commands/test_lib_complex.py index ef0c0c4b..31f61fc6 100644 --- a/tests/commands/test_lib_complex.py +++ b/tests/commands/test_lib_complex.py @@ -17,7 +17,7 @@ import json import re -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.commands.lib.command import cli as cmd_lib from platformio.package.exception import UnknownPackageError from platformio.util import strip_ansi_codes diff --git a/tests/commands/test_test.py b/tests/commands/test_test.py index 71094794..343dbd05 100644 --- a/tests/commands/test_test.py +++ b/tests/commands/test_test.py @@ -21,7 +21,7 @@ import pytest from platformio import proc from platformio.fs import load_json -from platformio.test.command import test_cmd as pio_test_cmd +from platformio.test.cli import test_cmd as pio_test_cmd def test_calculator_example(tmp_path: Path): From 27ccdc76a0b23fe8ee01bda8f86038dd66666fc9 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 17:09:22 +0300 Subject: [PATCH 55/74] Move "system" command to the root --- platformio/maintenance.py | 2 +- platformio/{commands => }/system/__init__.py | 0 .../{commands/system/command.py => system/cli.py} | 12 ++++++------ platformio/{commands => }/system/completion.py | 0 platformio/{commands => }/system/prune.py | 0 5 files changed, 7 insertions(+), 7 deletions(-) rename platformio/{commands => }/system/__init__.py (100%) rename platformio/{commands/system/command.py => system/cli.py} (98%) rename platformio/{commands => }/system/completion.py (100%) rename platformio/{commands => }/system/prune.py (100%) diff --git a/platformio/maintenance.py b/platformio/maintenance.py index c3f07e74..925d2f3a 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -23,13 +23,13 @@ from platformio import __version__, app, exception, fs, telemetry from platformio.cache import cleanup_content_cache from platformio.cli import PlatformioCLI from platformio.commands.platform import platform_update as cmd_platform_update -from platformio.commands.system.prune import calculate_unnecessary_system_data from platformio.commands.upgrade import get_latest_version from platformio.http import HTTPClientError, InternetIsOffline, ensure_internet_on from platformio.package.manager.core import update_core_packages from platformio.package.manager.tool import ToolPackageManager from platformio.package.meta import PackageSpec from platformio.package.version import pepver_to_semver +from platformio.system.prune import calculate_unnecessary_system_data def on_platformio_start(ctx, force, caller): diff --git a/platformio/commands/system/__init__.py b/platformio/system/__init__.py similarity index 100% rename from platformio/commands/system/__init__.py rename to platformio/system/__init__.py diff --git a/platformio/commands/system/command.py b/platformio/system/cli.py similarity index 98% rename from platformio/commands/system/command.py rename to platformio/system/cli.py index a75007d6..605624c0 100644 --- a/platformio/commands/system/command.py +++ b/platformio/system/cli.py @@ -20,21 +20,21 @@ import click from tabulate import tabulate from platformio import __version__, compat, fs, proc, util -from platformio.commands.system.completion import ( +from platformio.package.manager.library import LibraryPackageManager +from platformio.package.manager.platform import PlatformPackageManager +from platformio.package.manager.tool import ToolPackageManager +from platformio.project.config import ProjectConfig +from platformio.system.completion import ( ShellType, get_completion_install_path, install_completion_code, uninstall_completion_code, ) -from platformio.commands.system.prune import ( +from platformio.system.prune import ( prune_cached_data, prune_core_packages, prune_platform_packages, ) -from platformio.package.manager.library import LibraryPackageManager -from platformio.package.manager.platform import PlatformPackageManager -from platformio.package.manager.tool import ToolPackageManager -from platformio.project.config import ProjectConfig @click.group("system", short_help="Miscellaneous system commands") diff --git a/platformio/commands/system/completion.py b/platformio/system/completion.py similarity index 100% rename from platformio/commands/system/completion.py rename to platformio/system/completion.py diff --git a/platformio/commands/system/prune.py b/platformio/system/prune.py similarity index 100% rename from platformio/commands/system/prune.py rename to platformio/system/prune.py From d2be7033e992f0e7ed5a54cc90fb5bc0db35f0b7 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 17:12:59 +0300 Subject: [PATCH 56/74] Move "remote" command to the root --- platformio/{commands => }/remote/__init__.py | 0 platformio/{commands => }/remote/ac/__init__.py | 0 platformio/{commands => }/remote/ac/base.py | 0 platformio/{commands => }/remote/ac/process.py | 2 +- platformio/{commands => }/remote/ac/psync.py | 4 ++-- platformio/{commands => }/remote/ac/serial.py | 2 +- .../{commands/remote/command.py => remote/cli.py} | 14 +++++++------- .../{commands => }/remote/client/__init__.py | 0 .../{commands => }/remote/client/agent_list.py | 2 +- .../{commands => }/remote/client/agent_service.py | 8 ++++---- .../{commands => }/remote/client/async_base.py | 2 +- platformio/{commands => }/remote/client/base.py | 4 ++-- .../{commands => }/remote/client/device_list.py | 2 +- .../{commands => }/remote/client/device_monitor.py | 2 +- .../{commands => }/remote/client/run_or_test.py | 4 ++-- .../{commands => }/remote/client/update_core.py | 2 +- .../{commands => }/remote/factory/__init__.py | 0 platformio/{commands => }/remote/factory/client.py | 0 platformio/{commands => }/remote/factory/ssl.py | 0 platformio/{commands => }/remote/projectsync.py | 0 20 files changed, 24 insertions(+), 24 deletions(-) rename platformio/{commands => }/remote/__init__.py (100%) rename platformio/{commands => }/remote/ac/__init__.py (100%) rename platformio/{commands => }/remote/ac/base.py (100%) rename platformio/{commands => }/remote/ac/process.py (95%) rename platformio/{commands => }/remote/ac/psync.py (93%) rename platformio/{commands => }/remote/ac/serial.py (96%) rename platformio/{commands/remote/command.py => remote/cli.py} (95%) rename platformio/{commands => }/remote/client/__init__.py (100%) rename platformio/{commands => }/remote/client/agent_list.py (94%) rename platformio/{commands => }/remote/client/agent_service.py (96%) rename platformio/{commands => }/remote/client/async_base.py (97%) rename platformio/{commands => }/remote/client/base.py (97%) rename platformio/{commands => }/remote/client/device_list.py (96%) rename platformio/{commands => }/remote/client/device_monitor.py (99%) rename platformio/{commands => }/remote/client/run_or_test.py (98%) rename platformio/{commands => }/remote/client/update_core.py (92%) rename platformio/{commands => }/remote/factory/__init__.py (100%) rename platformio/{commands => }/remote/factory/client.py (100%) rename platformio/{commands => }/remote/factory/ssl.py (100%) rename platformio/{commands => }/remote/projectsync.py (100%) diff --git a/platformio/commands/remote/__init__.py b/platformio/remote/__init__.py similarity index 100% rename from platformio/commands/remote/__init__.py rename to platformio/remote/__init__.py diff --git a/platformio/commands/remote/ac/__init__.py b/platformio/remote/ac/__init__.py similarity index 100% rename from platformio/commands/remote/ac/__init__.py rename to platformio/remote/ac/__init__.py diff --git a/platformio/commands/remote/ac/base.py b/platformio/remote/ac/base.py similarity index 100% rename from platformio/commands/remote/ac/base.py rename to platformio/remote/ac/base.py diff --git a/platformio/commands/remote/ac/process.py b/platformio/remote/ac/process.py similarity index 95% rename from platformio/commands/remote/ac/process.py rename to platformio/remote/ac/process.py index 9e4f6989..91da456e 100644 --- a/platformio/commands/remote/ac/process.py +++ b/platformio/remote/ac/process.py @@ -16,7 +16,7 @@ import os from twisted.internet import protocol, reactor # pylint: disable=import-error -from platformio.commands.remote.ac.base import AsyncCommandBase +from platformio.remote.ac.base import AsyncCommandBase class ProcessAsyncCmd(protocol.ProcessProtocol, AsyncCommandBase): diff --git a/platformio/commands/remote/ac/psync.py b/platformio/remote/ac/psync.py similarity index 93% rename from platformio/commands/remote/ac/psync.py rename to platformio/remote/ac/psync.py index 87789ed8..8728737c 100644 --- a/platformio/commands/remote/ac/psync.py +++ b/platformio/remote/ac/psync.py @@ -17,8 +17,8 @@ import os import zlib from io import BytesIO -from platformio.commands.remote.ac.base import AsyncCommandBase -from platformio.commands.remote.projectsync import PROJECT_SYNC_STAGE, ProjectSync +from platformio.remote.ac.base import AsyncCommandBase +from platformio.remote.projectsync import PROJECT_SYNC_STAGE, ProjectSync class ProjectSyncAsyncCmd(AsyncCommandBase): diff --git a/platformio/commands/remote/ac/serial.py b/platformio/remote/ac/serial.py similarity index 96% rename from platformio/commands/remote/ac/serial.py rename to platformio/remote/ac/serial.py index d0181f9c..7f7f83f9 100644 --- a/platformio/commands/remote/ac/serial.py +++ b/platformio/remote/ac/serial.py @@ -17,7 +17,7 @@ from time import sleep from twisted.internet import protocol, reactor # pylint: disable=import-error from twisted.internet.serialport import SerialPort # pylint: disable=import-error -from platformio.commands.remote.ac.base import AsyncCommandBase +from platformio.remote.ac.base import AsyncCommandBase class SerialPortAsyncCmd(protocol.Protocol, AsyncCommandBase): diff --git a/platformio/commands/remote/command.py b/platformio/remote/cli.py similarity index 95% rename from platformio/commands/remote/command.py rename to platformio/remote/cli.py index acc0d310..6df9d8a1 100644 --- a/platformio/commands/remote/command.py +++ b/platformio/remote/cli.py @@ -60,14 +60,14 @@ def remote_agent(): type=click.Path(file_okay=False, dir_okay=True, writable=True, resolve_path=True), ) def remote_agent_start(name, share, working_dir): - from platformio.commands.remote.client.agent_service import RemoteAgentService + from platformio.remote.client.agent_service import RemoteAgentService RemoteAgentService(name, share, working_dir).connect() @remote_agent.command("list", short_help="List active agents") def remote_agent_list(): - from platformio.commands.remote.client.agent_list import AgentListClient + from platformio.remote.client.agent_list import AgentListClient AgentListClient().connect() @@ -84,7 +84,7 @@ def remote_agent_list(): ) @click.pass_obj def remote_update(agents, only_check, dry_run): - from platformio.commands.remote.client.update_core import UpdateCoreClient + from platformio.remote.client.update_core import UpdateCoreClient UpdateCoreClient("update", agents, dict(only_check=only_check or dry_run)).connect() @@ -120,7 +120,7 @@ def remote_run( verbose, ): - from platformio.commands.remote.client.run_or_test import RunOrTestClient + from platformio.remote.client.run_or_test import RunOrTestClient cr = RunOrTestClient( "run", @@ -213,7 +213,7 @@ def remote_test( # pylint: disable=redefined-builtin verbose, ): - from platformio.commands.remote.client.run_or_test import RunOrTestClient + from platformio.remote.client.run_or_test import RunOrTestClient cr = RunOrTestClient( "test", @@ -263,7 +263,7 @@ def remote_device(): @click.option("--json-output", is_flag=True) @click.pass_obj def device_list(agents, json_output): - from platformio.commands.remote.client.device_list import DeviceListClient + from platformio.remote.client.device_list import DeviceListClient DeviceListClient(agents, json_output).connect() @@ -346,7 +346,7 @@ def device_list(agents, json_output): @click.pass_obj @click.pass_context def device_monitor(ctx, agents, **kwargs): - from platformio.commands.remote.client.device_monitor import DeviceMonitorClient + from platformio.remote.client.device_monitor import DeviceMonitorClient if kwargs["sock"]: return DeviceMonitorClient(agents, **kwargs).connect() diff --git a/platformio/commands/remote/client/__init__.py b/platformio/remote/client/__init__.py similarity index 100% rename from platformio/commands/remote/client/__init__.py rename to platformio/remote/client/__init__.py diff --git a/platformio/commands/remote/client/agent_list.py b/platformio/remote/client/agent_list.py similarity index 94% rename from platformio/commands/remote/client/agent_list.py rename to platformio/remote/client/agent_list.py index df1de28b..103c0f8c 100644 --- a/platformio/commands/remote/client/agent_list.py +++ b/platformio/remote/client/agent_list.py @@ -16,7 +16,7 @@ from datetime import datetime import click -from platformio.commands.remote.client.base import RemoteClientBase +from platformio.remote.client.base import RemoteClientBase class AgentListClient(RemoteClientBase): diff --git a/platformio/commands/remote/client/agent_service.py b/platformio/remote/client/agent_service.py similarity index 96% rename from platformio/commands/remote/client/agent_service.py rename to platformio/remote/client/agent_service.py index 0501021a..a2f0a05a 100644 --- a/platformio/commands/remote/client/agent_service.py +++ b/platformio/remote/client/agent_service.py @@ -18,13 +18,13 @@ from twisted.logger import LogLevel # pylint: disable=import-error from twisted.spread import pb # pylint: disable=import-error from platformio import proc -from platformio.commands.remote.ac.process import ProcessAsyncCmd -from platformio.commands.remote.ac.psync import ProjectSyncAsyncCmd -from platformio.commands.remote.ac.serial import SerialPortAsyncCmd -from platformio.commands.remote.client.base import RemoteClientBase from platformio.device.list import list_serial_ports from platformio.project.config import ProjectConfig from platformio.project.exception import NotPlatformIOProjectError +from platformio.remote.ac.process import ProcessAsyncCmd +from platformio.remote.ac.psync import ProjectSyncAsyncCmd +from platformio.remote.ac.serial import SerialPortAsyncCmd +from platformio.remote.client.base import RemoteClientBase class RemoteAgentService(RemoteClientBase): diff --git a/platformio/commands/remote/client/async_base.py b/platformio/remote/client/async_base.py similarity index 97% rename from platformio/commands/remote/client/async_base.py rename to platformio/remote/client/async_base.py index a07e110b..488802a5 100644 --- a/platformio/commands/remote/client/async_base.py +++ b/platformio/remote/client/async_base.py @@ -15,7 +15,7 @@ import click from twisted.spread import pb # pylint: disable=import-error -from platformio.commands.remote.client.base import RemoteClientBase +from platformio.remote.client.base import RemoteClientBase class AsyncClientBase(RemoteClientBase): diff --git a/platformio/commands/remote/client/base.py b/platformio/remote/client/base.py similarity index 97% rename from platformio/commands/remote/client/base.py rename to platformio/remote/client/base.py index 7ca7be3b..fe2c4fb4 100644 --- a/platformio/commands/remote/client/base.py +++ b/platformio/remote/client/base.py @@ -26,8 +26,8 @@ from twisted.spread import pb # pylint: disable=import-error from zope.interface import provider # pylint: disable=import-error from platformio import __pioremote_endpoint__, __version__, app, exception, maintenance -from platformio.commands.remote.factory.client import RemoteClientFactory -from platformio.commands.remote.factory.ssl import SSLContextFactory +from platformio.remote.factory.client import RemoteClientFactory +from platformio.remote.factory.ssl import SSLContextFactory class RemoteClientBase( # pylint: disable=too-many-instance-attributes diff --git a/platformio/commands/remote/client/device_list.py b/platformio/remote/client/device_list.py similarity index 96% rename from platformio/commands/remote/client/device_list.py rename to platformio/remote/client/device_list.py index dba1729f..a22911b4 100644 --- a/platformio/commands/remote/client/device_list.py +++ b/platformio/remote/client/device_list.py @@ -16,7 +16,7 @@ import json import click -from platformio.commands.remote.client.base import RemoteClientBase +from platformio.remote.client.base import RemoteClientBase class DeviceListClient(RemoteClientBase): diff --git a/platformio/commands/remote/client/device_monitor.py b/platformio/remote/client/device_monitor.py similarity index 99% rename from platformio/commands/remote/client/device_monitor.py rename to platformio/remote/client/device_monitor.py index b880d270..7ddc0048 100644 --- a/platformio/commands/remote/client/device_monitor.py +++ b/platformio/remote/client/device_monitor.py @@ -19,7 +19,7 @@ import click from twisted.internet import protocol, reactor, task # pylint: disable=import-error from twisted.spread import pb # pylint: disable=import-error -from platformio.commands.remote.client.base import RemoteClientBase +from platformio.remote.client.base import RemoteClientBase class SMBridgeProtocol(protocol.Protocol): # pylint: disable=no-init diff --git a/platformio/commands/remote/client/run_or_test.py b/platformio/remote/client/run_or_test.py similarity index 98% rename from platformio/commands/remote/client/run_or_test.py rename to platformio/remote/client/run_or_test.py index cdc1465d..71065640 100644 --- a/platformio/commands/remote/client/run_or_test.py +++ b/platformio/remote/client/run_or_test.py @@ -21,10 +21,10 @@ from io import BytesIO from twisted.spread import pb # pylint: disable=import-error from platformio import fs -from platformio.commands.remote.client.async_base import AsyncClientBase -from platformio.commands.remote.projectsync import PROJECT_SYNC_STAGE, ProjectSync from platformio.compat import hashlib_encode_data from platformio.project.config import ProjectConfig +from platformio.remote.client.async_base import AsyncClientBase +from platformio.remote.projectsync import PROJECT_SYNC_STAGE, ProjectSync class RunOrTestClient(AsyncClientBase): diff --git a/platformio/commands/remote/client/update_core.py b/platformio/remote/client/update_core.py similarity index 92% rename from platformio/commands/remote/client/update_core.py rename to platformio/remote/client/update_core.py index 49e4488c..4fdec4cc 100644 --- a/platformio/commands/remote/client/update_core.py +++ b/platformio/remote/client/update_core.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from platformio.commands.remote.client.async_base import AsyncClientBase +from platformio.remote.client.async_base import AsyncClientBase class UpdateCoreClient(AsyncClientBase): diff --git a/platformio/commands/remote/factory/__init__.py b/platformio/remote/factory/__init__.py similarity index 100% rename from platformio/commands/remote/factory/__init__.py rename to platformio/remote/factory/__init__.py diff --git a/platformio/commands/remote/factory/client.py b/platformio/remote/factory/client.py similarity index 100% rename from platformio/commands/remote/factory/client.py rename to platformio/remote/factory/client.py diff --git a/platformio/commands/remote/factory/ssl.py b/platformio/remote/factory/ssl.py similarity index 100% rename from platformio/commands/remote/factory/ssl.py rename to platformio/remote/factory/ssl.py diff --git a/platformio/commands/remote/projectsync.py b/platformio/remote/projectsync.py similarity index 100% rename from platformio/commands/remote/projectsync.py rename to platformio/remote/projectsync.py From 756bb07d1a1638da6de56cbb40d292c206f4109d Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 17:14:52 +0300 Subject: [PATCH 57/74] Move "home" command to the root --- platformio/{commands => }/home/__init__.py | 0 .../{commands/home/command.py => home/cli.py} | 4 ++-- platformio/{commands => }/home/helpers.py | 0 platformio/{commands => }/home/rpc/__init__.py | 0 .../{commands => }/home/rpc/handlers/__init__.py | 0 .../{commands => }/home/rpc/handlers/account.py | 0 .../{commands => }/home/rpc/handlers/app.py | 0 .../{commands => }/home/rpc/handlers/ide.py | 0 .../{commands => }/home/rpc/handlers/misc.py | 2 +- .../{commands => }/home/rpc/handlers/os.py | 2 +- .../{commands => }/home/rpc/handlers/piocore.py | 2 +- .../{commands => }/home/rpc/handlers/project.py | 4 ++-- platformio/{commands => }/home/rpc/server.py | 0 platformio/{commands => }/home/run.py | 16 ++++++++-------- 14 files changed, 15 insertions(+), 15 deletions(-) rename platformio/{commands => }/home/__init__.py (100%) rename platformio/{commands/home/command.py => home/cli.py} (96%) rename platformio/{commands => }/home/helpers.py (100%) rename platformio/{commands => }/home/rpc/__init__.py (100%) rename platformio/{commands => }/home/rpc/handlers/__init__.py (100%) rename platformio/{commands => }/home/rpc/handlers/account.py (100%) rename platformio/{commands => }/home/rpc/handlers/app.py (100%) rename platformio/{commands => }/home/rpc/handlers/ide.py (100%) rename platformio/{commands => }/home/rpc/handlers/misc.py (96%) rename platformio/{commands => }/home/rpc/handlers/os.py (99%) rename platformio/{commands => }/home/rpc/handlers/piocore.py (99%) rename platformio/{commands => }/home/rpc/handlers/project.py (98%) rename platformio/{commands => }/home/rpc/server.py (100%) rename platformio/{commands => }/home/run.py (86%) diff --git a/platformio/commands/home/__init__.py b/platformio/home/__init__.py similarity index 100% rename from platformio/commands/home/__init__.py rename to platformio/home/__init__.py diff --git a/platformio/commands/home/command.py b/platformio/home/cli.py similarity index 96% rename from platformio/commands/home/command.py rename to platformio/home/cli.py index b656fc07..2d0ba1ce 100644 --- a/platformio/commands/home/command.py +++ b/platformio/home/cli.py @@ -16,8 +16,8 @@ import mimetypes import click -from platformio.commands.home.helpers import is_port_used -from platformio.commands.home.run import run_server +from platformio.home.helpers import is_port_used +from platformio.home.run import run_server @click.command("home", short_help="GUI to manage PlatformIO") diff --git a/platformio/commands/home/helpers.py b/platformio/home/helpers.py similarity index 100% rename from platformio/commands/home/helpers.py rename to platformio/home/helpers.py diff --git a/platformio/commands/home/rpc/__init__.py b/platformio/home/rpc/__init__.py similarity index 100% rename from platformio/commands/home/rpc/__init__.py rename to platformio/home/rpc/__init__.py diff --git a/platformio/commands/home/rpc/handlers/__init__.py b/platformio/home/rpc/handlers/__init__.py similarity index 100% rename from platformio/commands/home/rpc/handlers/__init__.py rename to platformio/home/rpc/handlers/__init__.py diff --git a/platformio/commands/home/rpc/handlers/account.py b/platformio/home/rpc/handlers/account.py similarity index 100% rename from platformio/commands/home/rpc/handlers/account.py rename to platformio/home/rpc/handlers/account.py diff --git a/platformio/commands/home/rpc/handlers/app.py b/platformio/home/rpc/handlers/app.py similarity index 100% rename from platformio/commands/home/rpc/handlers/app.py rename to platformio/home/rpc/handlers/app.py diff --git a/platformio/commands/home/rpc/handlers/ide.py b/platformio/home/rpc/handlers/ide.py similarity index 100% rename from platformio/commands/home/rpc/handlers/ide.py rename to platformio/home/rpc/handlers/ide.py diff --git a/platformio/commands/home/rpc/handlers/misc.py b/platformio/home/rpc/handlers/misc.py similarity index 96% rename from platformio/commands/home/rpc/handlers/misc.py rename to platformio/home/rpc/handlers/misc.py index 7626456a..c384fea9 100644 --- a/platformio/commands/home/rpc/handlers/misc.py +++ b/platformio/home/rpc/handlers/misc.py @@ -16,8 +16,8 @@ import json import time from platformio.cache import ContentCache -from platformio.commands.home.rpc.handlers.os import OSRPC from platformio.compat import aio_create_task +from platformio.home.rpc.handlers.os import OSRPC class MiscRPC: diff --git a/platformio/commands/home/rpc/handlers/os.py b/platformio/home/rpc/handlers/os.py similarity index 99% rename from platformio/commands/home/rpc/handlers/os.py rename to platformio/home/rpc/handlers/os.py index c6ddcaa6..1aaf6d63 100644 --- a/platformio/commands/home/rpc/handlers/os.py +++ b/platformio/home/rpc/handlers/os.py @@ -24,8 +24,8 @@ import click from platformio import __default_requests_timeout__, fs from platformio.cache import ContentCache -from platformio.commands.home import helpers from platformio.device.list import list_logical_devices +from platformio.home import helpers from platformio.http import ensure_internet_on diff --git a/platformio/commands/home/rpc/handlers/piocore.py b/platformio/home/rpc/handlers/piocore.py similarity index 99% rename from platformio/commands/home/rpc/handlers/piocore.py rename to platformio/home/rpc/handlers/piocore.py index 01bbf90d..add05a31 100644 --- a/platformio/commands/home/rpc/handlers/piocore.py +++ b/platformio/home/rpc/handlers/piocore.py @@ -25,8 +25,8 @@ from ajsonrpc.core import JSONRPC20DispatchException from starlette.concurrency import run_in_threadpool from platformio import __main__, __version__, fs, proc -from platformio.commands.home import helpers from platformio.compat import get_locale_encoding, is_bytes +from platformio.home import helpers class MultiThreadingStdStream(object): diff --git a/platformio/commands/home/rpc/handlers/project.py b/platformio/home/rpc/handlers/project.py similarity index 98% rename from platformio/commands/home/rpc/handlers/project.py rename to platformio/home/rpc/handlers/project.py index 8418fa60..13a9ebbb 100644 --- a/platformio/commands/home/rpc/handlers/project.py +++ b/platformio/home/rpc/handlers/project.py @@ -21,8 +21,8 @@ import time from ajsonrpc.core import JSONRPC20DispatchException from platformio import exception, fs -from platformio.commands.home.rpc.handlers.app import AppRPC -from platformio.commands.home.rpc.handlers.piocore import PIOCoreRPC +from platformio.home.rpc.handlers.app import AppRPC +from platformio.home.rpc.handlers.piocore import PIOCoreRPC from platformio.package.manager.platform import PlatformPackageManager from platformio.project.config import ProjectConfig from platformio.project.exception import ProjectError diff --git a/platformio/commands/home/rpc/server.py b/platformio/home/rpc/server.py similarity index 100% rename from platformio/commands/home/rpc/server.py rename to platformio/home/rpc/server.py diff --git a/platformio/commands/home/run.py b/platformio/home/run.py similarity index 86% rename from platformio/commands/home/run.py rename to platformio/home/run.py index 64d820ea..a5306ad0 100644 --- a/platformio/commands/home/run.py +++ b/platformio/home/run.py @@ -24,16 +24,16 @@ from starlette.routing import Mount, Route, WebSocketRoute from starlette.staticfiles import StaticFiles from starlette.status import HTTP_403_FORBIDDEN -from platformio.commands.home.rpc.handlers.account import AccountRPC -from platformio.commands.home.rpc.handlers.app import AppRPC -from platformio.commands.home.rpc.handlers.ide import IDERPC -from platformio.commands.home.rpc.handlers.misc import MiscRPC -from platformio.commands.home.rpc.handlers.os import OSRPC -from platformio.commands.home.rpc.handlers.piocore import PIOCoreRPC -from platformio.commands.home.rpc.handlers.project import ProjectRPC -from platformio.commands.home.rpc.server import WebSocketJSONRPCServerFactory from platformio.compat import aio_get_running_loop from platformio.exception import PlatformioException +from platformio.home.rpc.handlers.account import AccountRPC +from platformio.home.rpc.handlers.app import AppRPC +from platformio.home.rpc.handlers.ide import IDERPC +from platformio.home.rpc.handlers.misc import MiscRPC +from platformio.home.rpc.handlers.os import OSRPC +from platformio.home.rpc.handlers.piocore import PIOCoreRPC +from platformio.home.rpc.handlers.project import ProjectRPC +from platformio.home.rpc.server import WebSocketJSONRPCServerFactory from platformio.package.manager.core import get_core_package_dir from platformio.proc import force_exit From dcc63da2ef3193488f65689d820ed4fc650f2d7f Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 17:16:55 +0300 Subject: [PATCH 58/74] Move "check" command to the root --- platformio/{commands => }/check/__init__.py | 0 platformio/{commands/check/command.py => check/cli.py} | 4 ++-- platformio/{commands => }/check/defect.py | 0 platformio/{commands => }/check/tools/__init__.py | 6 +++--- platformio/{commands => }/check/tools/base.py | 2 +- platformio/{commands => }/check/tools/clangtidy.py | 4 ++-- platformio/{commands => }/check/tools/cppcheck.py | 4 ++-- platformio/{commands => }/check/tools/pvsstudio.py | 4 ++-- tests/commands/test_check.py | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) rename platformio/{commands => }/check/__init__.py (100%) rename platformio/{commands/check/command.py => check/cli.py} (98%) rename platformio/{commands => }/check/defect.py (100%) rename platformio/{commands => }/check/tools/__init__.py (83%) rename platformio/{commands => }/check/tools/base.py (99%) rename platformio/{commands => }/check/tools/clangtidy.py (96%) rename platformio/{commands => }/check/tools/cppcheck.py (98%) rename platformio/{commands => }/check/tools/pvsstudio.py (98%) diff --git a/platformio/commands/check/__init__.py b/platformio/check/__init__.py similarity index 100% rename from platformio/commands/check/__init__.py rename to platformio/check/__init__.py diff --git a/platformio/commands/check/command.py b/platformio/check/cli.py similarity index 98% rename from platformio/commands/check/command.py rename to platformio/check/cli.py index e24836cc..3033008e 100644 --- a/platformio/commands/check/command.py +++ b/platformio/check/cli.py @@ -26,8 +26,8 @@ import click from tabulate import tabulate from platformio import app, exception, fs, util -from platformio.commands.check.defect import DefectItem -from platformio.commands.check.tools import CheckToolFactory +from platformio.check.defect import DefectItem +from platformio.check.tools import CheckToolFactory from platformio.project.config import ProjectConfig from platformio.project.helpers import find_project_dir_above, get_project_dir diff --git a/platformio/commands/check/defect.py b/platformio/check/defect.py similarity index 100% rename from platformio/commands/check/defect.py rename to platformio/check/defect.py diff --git a/platformio/commands/check/tools/__init__.py b/platformio/check/tools/__init__.py similarity index 83% rename from platformio/commands/check/tools/__init__.py rename to platformio/check/tools/__init__.py index 9c4b1b7e..824df5bb 100644 --- a/platformio/commands/check/tools/__init__.py +++ b/platformio/check/tools/__init__.py @@ -13,9 +13,9 @@ # limitations under the License. from platformio import exception -from platformio.commands.check.tools.clangtidy import ClangtidyCheckTool -from platformio.commands.check.tools.cppcheck import CppcheckCheckTool -from platformio.commands.check.tools.pvsstudio import PvsStudioCheckTool +from platformio.check.tools.clangtidy import ClangtidyCheckTool +from platformio.check.tools.cppcheck import CppcheckCheckTool +from platformio.check.tools.pvsstudio import PvsStudioCheckTool class CheckToolFactory(object): diff --git a/platformio/commands/check/tools/base.py b/platformio/check/tools/base.py similarity index 99% rename from platformio/commands/check/tools/base.py rename to platformio/check/tools/base.py index 07636e1f..90c58bc1 100644 --- a/platformio/commands/check/tools/base.py +++ b/platformio/check/tools/base.py @@ -19,7 +19,7 @@ import tempfile import click from platformio import fs, proc -from platformio.commands.check.defect import DefectItem +from platformio.check.defect import DefectItem from platformio.package.manager.core import get_core_package_dir from platformio.package.meta import PackageSpec from platformio.project.helpers import load_build_metadata diff --git a/platformio/commands/check/tools/clangtidy.py b/platformio/check/tools/clangtidy.py similarity index 96% rename from platformio/commands/check/tools/clangtidy.py rename to platformio/check/tools/clangtidy.py index c357cf4d..682a2cb7 100644 --- a/platformio/commands/check/tools/clangtidy.py +++ b/platformio/check/tools/clangtidy.py @@ -15,8 +15,8 @@ import re from os.path import join -from platformio.commands.check.defect import DefectItem -from platformio.commands.check.tools.base import CheckToolBase +from platformio.check.defect import DefectItem +from platformio.check.tools.base import CheckToolBase class ClangtidyCheckTool(CheckToolBase): diff --git a/platformio/commands/check/tools/cppcheck.py b/platformio/check/tools/cppcheck.py similarity index 98% rename from platformio/commands/check/tools/cppcheck.py rename to platformio/check/tools/cppcheck.py index ec2b96d3..0f8db402 100644 --- a/platformio/commands/check/tools/cppcheck.py +++ b/platformio/check/tools/cppcheck.py @@ -17,8 +17,8 @@ import os import click from platformio import proc -from platformio.commands.check.defect import DefectItem -from platformio.commands.check.tools.base import CheckToolBase +from platformio.check.defect import DefectItem +from platformio.check.tools.base import CheckToolBase class CppcheckCheckTool(CheckToolBase): diff --git a/platformio/commands/check/tools/pvsstudio.py b/platformio/check/tools/pvsstudio.py similarity index 98% rename from platformio/commands/check/tools/pvsstudio.py rename to platformio/check/tools/pvsstudio.py index 0479d91e..ded65d1c 100644 --- a/platformio/commands/check/tools/pvsstudio.py +++ b/platformio/check/tools/pvsstudio.py @@ -20,8 +20,8 @@ from xml.etree.ElementTree import fromstring import click from platformio import proc -from platformio.commands.check.defect import DefectItem -from platformio.commands.check.tools.base import CheckToolBase +from platformio.check.defect import DefectItem +from platformio.check.tools.base import CheckToolBase from platformio.compat import IS_WINDOWS diff --git a/tests/commands/test_check.py b/tests/commands/test_check.py index 746a81dd..be6042ca 100644 --- a/tests/commands/test_check.py +++ b/tests/commands/test_check.py @@ -21,7 +21,7 @@ from os.path import isfile, join import pytest from platformio import fs -from platformio.commands.check.command import cli as cmd_check +from platformio.check.cli import cli as cmd_check DEFAULT_CONFIG = """ [env:native] From 52f8e98eedcc3430def006d28762cc486b84c092 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 17:20:56 +0300 Subject: [PATCH 59/74] Move "run" command to the root --- platformio/commands/ci.py | 2 +- platformio/debug/helpers.py | 4 ++-- platformio/project/helpers.py | 2 +- platformio/remote/cli.py | 2 +- platformio/{commands => }/run/__init__.py | 0 platformio/{commands/run/command.py => run/cli.py} | 4 ++-- platformio/{commands => }/run/helpers.py | 0 platformio/{commands => }/run/processor.py | 0 platformio/test/runners/base.py | 2 +- tests/commands/test_run.py | 2 +- 10 files changed, 9 insertions(+), 9 deletions(-) rename platformio/{commands => }/run/__init__.py (100%) rename platformio/{commands/run/command.py => run/cli.py} (98%) rename platformio/{commands => }/run/helpers.py (100%) rename platformio/{commands => }/run/processor.py (100%) diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index 17880196..f1875b23 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -20,10 +20,10 @@ import tempfile import click from platformio import app, fs -from platformio.commands.run.command import cli as cmd_run from platformio.exception import CIBuildEnvsEmpty from platformio.project.commands.init import project_init_cmd, validate_boards from platformio.project.config import ProjectConfig +from platformio.run.cli import cli as cmd_run def validate_path(ctx, param, value): # pylint: disable=unused-argument diff --git a/platformio/debug/helpers.py b/platformio/debug/helpers.py index 0b15d70d..f156cf45 100644 --- a/platformio/debug/helpers.py +++ b/platformio/debug/helpers.py @@ -21,11 +21,11 @@ from hashlib import sha1 from io import BytesIO from platformio.cli import PlatformioCLI -from platformio.commands.run.command import cli as cmd_run -from platformio.commands.run.command import print_processing_header from platformio.compat import IS_WINDOWS, is_bytes from platformio.debug.exception import DebugInvalidOptionsError from platformio.device.list import list_serial_ports +from platformio.run.cli import cli as cmd_run +from platformio.run.cli import print_processing_header from platformio.test.helpers import list_test_names from platformio.test.result import TestSuite from platformio.test.runners.base import TestRunnerOptions diff --git a/platformio/project/helpers.py b/platformio/project/helpers.py index c7b786c8..2ddc68b3 100644 --- a/platformio/project/helpers.py +++ b/platformio/project/helpers.py @@ -142,7 +142,7 @@ load_project_ide_data = load_build_metadata def _load_build_metadata(project_dir, env_names): # pylint: disable=import-outside-toplevel - from platformio.commands.run.command import cli as cmd_run + from platformio.run.cli import cli as cmd_run args = ["--project-dir", project_dir, "--target", "_idedata"] for name in env_names: diff --git a/platformio/remote/cli.py b/platformio/remote/cli.py index 6df9d8a1..c761f92e 100644 --- a/platformio/remote/cli.py +++ b/platformio/remote/cli.py @@ -24,7 +24,6 @@ from time import sleep import click from platformio import fs, proc -from platformio.commands.run.command import cli as cmd_run from platformio.device.commands.monitor import ( apply_project_monitor_options, device_monitor_cmd, @@ -34,6 +33,7 @@ from platformio.device.commands.monitor import ( from platformio.package.manager.core import inject_contrib_pysite from platformio.project.exception import NotPlatformIOProjectError from platformio.project.options import ProjectOptions +from platformio.run.cli import cli as cmd_run from platformio.test.cli import test_cmd diff --git a/platformio/commands/run/__init__.py b/platformio/run/__init__.py similarity index 100% rename from platformio/commands/run/__init__.py rename to platformio/run/__init__.py diff --git a/platformio/commands/run/command.py b/platformio/run/cli.py similarity index 98% rename from platformio/commands/run/command.py rename to platformio/run/cli.py index 16d71bc9..aa1c0d6c 100644 --- a/platformio/commands/run/command.py +++ b/platformio/run/cli.py @@ -22,11 +22,11 @@ import click from tabulate import tabulate from platformio import app, exception, fs, util -from platformio.commands.run.helpers import clean_build_dir, handle_legacy_libdeps -from platformio.commands.run.processor import EnvironmentProcessor from platformio.device.commands.monitor import device_monitor_cmd from platformio.project.config import ProjectConfig from platformio.project.helpers import find_project_dir_above, load_build_metadata +from platformio.run.helpers import clean_build_dir, handle_legacy_libdeps +from platformio.run.processor import EnvironmentProcessor from platformio.test.runners.base import CTX_META_TEST_IS_RUNNING # pylint: disable=too-many-arguments,too-many-locals,too-many-branches diff --git a/platformio/commands/run/helpers.py b/platformio/run/helpers.py similarity index 100% rename from platformio/commands/run/helpers.py rename to platformio/run/helpers.py diff --git a/platformio/commands/run/processor.py b/platformio/run/processor.py similarity index 100% rename from platformio/commands/run/processor.py rename to platformio/run/processor.py diff --git a/platformio/test/runners/base.py b/platformio/test/runners/base.py index 2e49dc08..2a0e6cec 100644 --- a/platformio/test/runners/base.py +++ b/platformio/test/runners/base.py @@ -174,7 +174,7 @@ class TestRunnerBase: def run_project_targets(self, targets): # pylint: disable=import-outside-toplevel - from platformio.commands.run.command import cli as run_cmd + from platformio.run.cli import cli as run_cmd assert self.cmd_ctx return self.cmd_ctx.invoke( diff --git a/tests/commands/test_run.py b/tests/commands/test_run.py index 504c3b00..5a2617fe 100644 --- a/tests/commands/test_run.py +++ b/tests/commands/test_run.py @@ -14,7 +14,7 @@ from pathlib import Path -from platformio.commands.run.command import cli as cmd_run +from platformio.run.cli import cli as cmd_run def test_build_flags(clirunner, validate_cliresult, tmpdir): From 12fb02db6ee59cbe39365a583479997ec020d317 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 17:30:41 +0300 Subject: [PATCH 60/74] Use "cli" to the top commands --- platformio/debug/cli.py | 5 +---- platformio/remote/cli.py | 2 +- platformio/test/cli.py | 5 +---- tests/commands/test_test.py | 2 +- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/platformio/debug/cli.py b/platformio/debug/cli.py index 489261c9..01e81f36 100644 --- a/platformio/debug/cli.py +++ b/platformio/debug/cli.py @@ -61,7 +61,7 @@ from platformio.project.options import ProjectOptions @click.option("--interface", type=click.Choice(["gdb"])) @click.argument("__unprocessed", nargs=-1, type=click.UNPROCESSED) @click.pass_context -def debug_cmd( +def cli( ctx, project_dir, project_conf, @@ -93,9 +93,6 @@ def debug_cmd( ) -cli = debug_cmd - - def _debug_in_project_dir( ctx, project_dir, diff --git a/platformio/remote/cli.py b/platformio/remote/cli.py index c761f92e..1a32c549 100644 --- a/platformio/remote/cli.py +++ b/platformio/remote/cli.py @@ -34,7 +34,7 @@ from platformio.package.manager.core import inject_contrib_pysite from platformio.project.exception import NotPlatformIOProjectError from platformio.project.options import ProjectOptions from platformio.run.cli import cli as cmd_run -from platformio.test.cli import test_cmd +from platformio.test.cli import cli as test_cmd @click.group("remote", short_help="Remote Development") diff --git a/platformio/test/cli.py b/platformio/test/cli.py index 25279d7d..bfb14fd6 100644 --- a/platformio/test/cli.py +++ b/platformio/test/cli.py @@ -92,7 +92,7 @@ from platformio.test.runners.factory import TestRunnerFactory help="Increase verbosity level, maximum is 3 levels (-vvv), see docs for details", ) @click.pass_context -def test_cmd( # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin +def cli( # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin ctx, environment, ignore, @@ -175,9 +175,6 @@ def test_cmd( # pylint: disable=too-many-arguments,too-many-locals,redefined-bu raise exception.ReturnErrorCode(1) -cli = test_cmd - - def print_suite_header(test_suite): click.echo( "Processing %s in %s environment" diff --git a/tests/commands/test_test.py b/tests/commands/test_test.py index 343dbd05..2a6c7c56 100644 --- a/tests/commands/test_test.py +++ b/tests/commands/test_test.py @@ -21,7 +21,7 @@ import pytest from platformio import proc from platformio.fs import load_json -from platformio.test.cli import test_cmd as pio_test_cmd +from platformio.test.cli import cli as pio_test_cmd def test_calculator_example(tmp_path: Path): From 11a43b26930c87c4b853f4dde756f7217d838c51 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 17:31:21 +0300 Subject: [PATCH 61/74] Regroup "system" commands --- platformio/system/cli.py | 185 ++--------------------- platformio/system/commands/__init__.py | 13 ++ platformio/system/commands/completion.py | 69 +++++++++ platformio/system/commands/info.py | 85 +++++++++++ platformio/system/commands/prune.py | 70 +++++++++ 5 files changed, 250 insertions(+), 172 deletions(-) create mode 100644 platformio/system/commands/__init__.py create mode 100644 platformio/system/commands/completion.py create mode 100644 platformio/system/commands/info.py create mode 100644 platformio/system/commands/prune.py diff --git a/platformio/system/cli.py b/platformio/system/cli.py index 605624c0..dd39ce87 100644 --- a/platformio/system/cli.py +++ b/platformio/system/cli.py @@ -12,180 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json -import platform -import sys - import click -from tabulate import tabulate -from platformio import __version__, compat, fs, proc, util -from platformio.package.manager.library import LibraryPackageManager -from platformio.package.manager.platform import PlatformPackageManager -from platformio.package.manager.tool import ToolPackageManager -from platformio.project.config import ProjectConfig -from platformio.system.completion import ( - ShellType, - get_completion_install_path, - install_completion_code, - uninstall_completion_code, +from platformio.system.commands.completion import system_completion_cmd +from platformio.system.commands.info import system_info_cmd +from platformio.system.commands.prune import system_prune_cmd + + +@click.group( + "system", + commands=[ + system_completion_cmd, + system_info_cmd, + system_prune_cmd, + ], + short_help="Miscellaneous system commands", ) -from platformio.system.prune import ( - prune_cached_data, - prune_core_packages, - prune_platform_packages, -) - - -@click.group("system", short_help="Miscellaneous system commands") def cli(): pass - - -@cli.command("info", short_help="Display system-wide information") -@click.option("--json-output", is_flag=True) -def system_info(json_output): - project_config = ProjectConfig() - data = {} - data["core_version"] = {"title": "PlatformIO Core", "value": __version__} - data["python_version"] = { - "title": "Python", - "value": "{0}.{1}.{2}-{3}.{4}".format(*list(sys.version_info)), - } - data["system"] = {"title": "System Type", "value": util.get_systype()} - data["platform"] = {"title": "Platform", "value": platform.platform(terse=True)} - data["filesystem_encoding"] = { - "title": "File System Encoding", - "value": compat.get_filesystem_encoding(), - } - data["locale_encoding"] = { - "title": "Locale Encoding", - "value": compat.get_locale_encoding(), - } - data["core_dir"] = { - "title": "PlatformIO Core Directory", - "value": project_config.get("platformio", "core_dir"), - } - data["platformio_exe"] = { - "title": "PlatformIO Core Executable", - "value": proc.where_is_program( - "platformio.exe" if compat.IS_WINDOWS else "platformio" - ), - } - data["python_exe"] = { - "title": "Python Executable", - "value": proc.get_pythonexe_path(), - } - data["global_lib_nums"] = { - "title": "Global Libraries", - "value": len(LibraryPackageManager().get_installed()), - } - data["dev_platform_nums"] = { - "title": "Development Platforms", - "value": len(PlatformPackageManager().get_installed()), - } - data["package_tool_nums"] = { - "title": "Tools & Toolchains", - "value": len( - ToolPackageManager( - project_config.get("platformio", "packages_dir") - ).get_installed() - ), - } - - click.echo( - json.dumps(data) - if json_output - else tabulate([(item["title"], item["value"]) for item in data.values()]) - ) - - -@cli.command("prune", short_help="Remove unused data") -@click.option("--force", "-f", is_flag=True, help="Do not prompt for confirmation") -@click.option( - "--dry-run", is_flag=True, help="Do not prune, only show data that will be removed" -) -@click.option("--cache", is_flag=True, help="Prune only cached data") -@click.option( - "--core-packages", is_flag=True, help="Prune only unnecessary core packages" -) -@click.option( - "--platform-packages", - is_flag=True, - help="Prune only unnecessary development platform packages", -) -def system_prune(force, dry_run, cache, core_packages, platform_packages): - if dry_run: - click.secho( - "Dry run mode (do not prune, only show data that will be removed)", - fg="yellow", - ) - click.echo() - - reclaimed_cache = 0 - reclaimed_core_packages = 0 - reclaimed_platform_packages = 0 - prune_all = not any([cache, core_packages, platform_packages]) - - if cache or prune_all: - reclaimed_cache = prune_cached_data(force, dry_run) - click.echo() - - if core_packages or prune_all: - reclaimed_core_packages = prune_core_packages(force, dry_run) - click.echo() - - if platform_packages or prune_all: - reclaimed_platform_packages = prune_platform_packages(force, dry_run) - click.echo() - - click.secho( - "Total reclaimed space: %s" - % fs.humanize_file_size( - reclaimed_cache + reclaimed_core_packages + reclaimed_platform_packages - ), - fg="green", - ) - - -@cli.group("completion", short_help="Shell completion support") -def completion(): - pass - - -@completion.command("install", short_help="Install shell completion files/code") -@click.argument("shell", type=click.Choice([t.value for t in ShellType])) -@click.option( - "--path", - type=click.Path(file_okay=True, dir_okay=False, readable=True, resolve_path=True), - help="Custom installation path of the code to be evaluated by the shell. " - "The standard installation path is used by default.", -) -def completion_install(shell, path): - shell = ShellType(shell) - path = path or get_completion_install_path(shell) - install_completion_code(shell, path) - click.echo( - "PlatformIO CLI completion has been installed for %s shell to %s \n" - "Please restart a current shell session." - % (click.style(shell.name, fg="cyan"), click.style(path, fg="blue")) - ) - - -@completion.command("uninstall", short_help="Uninstall shell completion files/code") -@click.argument("shell", type=click.Choice([t.value for t in ShellType])) -@click.option( - "--path", - type=click.Path(file_okay=True, dir_okay=False, readable=True, resolve_path=True), - help="Custom installation path of the code to be evaluated by the shell. " - "The standard installation path is used by default.", -) -def completion_uninstall(shell, path): - shell = ShellType(shell) - path = path or get_completion_install_path(shell) - uninstall_completion_code(shell, path) - click.echo( - "PlatformIO CLI completion has been uninstalled for %s shell from %s \n" - "Please restart a current shell session." - % (click.style(shell.name, fg="cyan"), click.style(path, fg="blue")) - ) diff --git a/platformio/system/commands/__init__.py b/platformio/system/commands/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/system/commands/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/platformio/system/commands/completion.py b/platformio/system/commands/completion.py new file mode 100644 index 00000000..aac68592 --- /dev/null +++ b/platformio/system/commands/completion.py @@ -0,0 +1,69 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.system.completion import ( + ShellType, + get_completion_install_path, + install_completion_code, + uninstall_completion_code, +) + + +@click.group("completion", short_help="Shell completion support") +def system_completion_cmd(): + pass + + +@system_completion_cmd.command( + "install", short_help="Install shell completion files/code" +) +@click.argument("shell", type=click.Choice([t.value for t in ShellType])) +@click.option( + "--path", + type=click.Path(file_okay=True, dir_okay=False, readable=True, resolve_path=True), + help="Custom installation path of the code to be evaluated by the shell. " + "The standard installation path is used by default.", +) +def system_completion_install(shell, path): + shell = ShellType(shell) + path = path or get_completion_install_path(shell) + install_completion_code(shell, path) + click.echo( + "PlatformIO CLI completion has been installed for %s shell to %s \n" + "Please restart a current shell session." + % (click.style(shell.name, fg="cyan"), click.style(path, fg="blue")) + ) + + +@system_completion_cmd.command( + "uninstall", short_help="Uninstall shell completion files/code" +) +@click.argument("shell", type=click.Choice([t.value for t in ShellType])) +@click.option( + "--path", + type=click.Path(file_okay=True, dir_okay=False, readable=True, resolve_path=True), + help="Custom installation path of the code to be evaluated by the shell. " + "The standard installation path is used by default.", +) +def system_completion_uninstall(shell, path): + shell = ShellType(shell) + path = path or get_completion_install_path(shell) + uninstall_completion_code(shell, path) + click.echo( + "PlatformIO CLI completion has been uninstalled for %s shell from %s \n" + "Please restart a current shell session." + % (click.style(shell.name, fg="cyan"), click.style(path, fg="blue")) + ) diff --git a/platformio/system/commands/info.py b/platformio/system/commands/info.py new file mode 100644 index 00000000..52a06636 --- /dev/null +++ b/platformio/system/commands/info.py @@ -0,0 +1,85 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 json +import platform +import sys + +import click +from tabulate import tabulate + +from platformio import __version__, compat, proc, util +from platformio.package.manager.library import LibraryPackageManager +from platformio.package.manager.platform import PlatformPackageManager +from platformio.package.manager.tool import ToolPackageManager +from platformio.project.config import ProjectConfig + + +@click.command("info", short_help="Display system-wide information") +@click.option("--json-output", is_flag=True) +def system_info_cmd(json_output): + project_config = ProjectConfig() + data = {} + data["core_version"] = {"title": "PlatformIO Core", "value": __version__} + data["python_version"] = { + "title": "Python", + "value": "{0}.{1}.{2}-{3}.{4}".format(*list(sys.version_info)), + } + data["system"] = {"title": "System Type", "value": util.get_systype()} + data["platform"] = {"title": "Platform", "value": platform.platform(terse=True)} + data["filesystem_encoding"] = { + "title": "File System Encoding", + "value": compat.get_filesystem_encoding(), + } + data["locale_encoding"] = { + "title": "Locale Encoding", + "value": compat.get_locale_encoding(), + } + data["core_dir"] = { + "title": "PlatformIO Core Directory", + "value": project_config.get("platformio", "core_dir"), + } + data["platformio_exe"] = { + "title": "PlatformIO Core Executable", + "value": proc.where_is_program( + "platformio.exe" if compat.IS_WINDOWS else "platformio" + ), + } + data["python_exe"] = { + "title": "Python Executable", + "value": proc.get_pythonexe_path(), + } + data["global_lib_nums"] = { + "title": "Global Libraries", + "value": len(LibraryPackageManager().get_installed()), + } + data["dev_platform_nums"] = { + "title": "Development Platforms", + "value": len(PlatformPackageManager().get_installed()), + } + data["package_tool_nums"] = { + "title": "Tools & Toolchains", + "value": len( + ToolPackageManager( + project_config.get("platformio", "packages_dir") + ).get_installed() + ), + } + + click.echo( + json.dumps(data) + if json_output + else tabulate([(item["title"], item["value"]) for item in data.values()]) + ) diff --git a/platformio/system/commands/prune.py b/platformio/system/commands/prune.py new file mode 100644 index 00000000..1559aab3 --- /dev/null +++ b/platformio/system/commands/prune.py @@ -0,0 +1,70 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio import fs +from platformio.system.prune import ( + prune_cached_data, + prune_core_packages, + prune_platform_packages, +) + + +@click.command("prune", short_help="Remove unused data") +@click.option("--force", "-f", is_flag=True, help="Do not prompt for confirmation") +@click.option( + "--dry-run", is_flag=True, help="Do not prune, only show data that will be removed" +) +@click.option("--cache", is_flag=True, help="Prune only cached data") +@click.option( + "--core-packages", is_flag=True, help="Prune only unnecessary core packages" +) +@click.option( + "--platform-packages", + is_flag=True, + help="Prune only unnecessary development platform packages", +) +def system_prune_cmd(force, dry_run, cache, core_packages, platform_packages): + if dry_run: + click.secho( + "Dry run mode (do not prune, only show data that will be removed)", + fg="yellow", + ) + click.echo() + + reclaimed_cache = 0 + reclaimed_core_packages = 0 + reclaimed_platform_packages = 0 + prune_all = not any([cache, core_packages, platform_packages]) + + if cache or prune_all: + reclaimed_cache = prune_cached_data(force, dry_run) + click.echo() + + if core_packages or prune_all: + reclaimed_core_packages = prune_core_packages(force, dry_run) + click.echo() + + if platform_packages or prune_all: + reclaimed_platform_packages = prune_platform_packages(force, dry_run) + click.echo() + + click.secho( + "Total reclaimed space: %s" + % fs.humanize_file_size( + reclaimed_cache + reclaimed_core_packages + reclaimed_platform_packages + ), + fg="green", + ) From 4a95148cd0c81f9f511a157c0baae5d5396c8108 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 20:37:44 +0300 Subject: [PATCH 62/74] Do not modify existing directory --- platformio/package/commands/install.py | 3 ++- platformio/test/runners/unity.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/platformio/package/commands/install.py b/platformio/package/commands/install.py index 472d0ae8..8a6e3017 100644 --- a/platformio/package/commands/install.py +++ b/platformio/package/commands/install.py @@ -264,7 +264,8 @@ def _uninstall_project_unused_libdeps(project_env, options): lm.uninstall(spec) except UnknownPackageError: pass - storage_dir.mkdir(parents=True, exist_ok=True) + if not storage_dir.is_dir(): + storage_dir.mkdir(parents=True) integrity_dat.write_text("\n".join(lib_deps), encoding="utf-8") diff --git a/platformio/test/runners/unity.py b/platformio/test/runners/unity.py index c3604c0c..e048c1ed 100644 --- a/platformio/test/runners/unity.py +++ b/platformio/test/runners/unity.py @@ -237,7 +237,8 @@ void unityOutputComplete(void) { unittest_uart_end(); } def generate_unity_extras(self, dst_dir): dst_dir = Path(dst_dir) - dst_dir.mkdir(parents=True, exist_ok=True) + if not dst_dir.is_dir(): + dst_dir.mkdir(parents=True) unity_h = dst_dir / "unity_config.h" if not unity_h.is_file(): unity_h.write_text( From 472c80159d77314835b1d6cb8fca78567a04df63 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 21:59:24 +0300 Subject: [PATCH 63/74] Do not export common libdeps_dir --- platformio/project/helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/platformio/project/helpers.py b/platformio/project/helpers.py index 2ddc68b3..abe2bf0e 100644 --- a/platformio/project/helpers.py +++ b/platformio/project/helpers.py @@ -50,7 +50,6 @@ def get_project_all_lib_dirs(): result = [ config.get("platformio", "globallib_dir"), config.get("platformio", "lib_dir"), - libdeps_dir, ] if not os.path.isdir(libdeps_dir): return result From db366b31635f997aefb9b066fa55b9fbb3e7e436 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 12:10:57 +0300 Subject: [PATCH 64/74] Fix test --- tests/commands/pkg/test_outdated.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/pkg/test_outdated.py b/tests/commands/pkg/test_outdated.py index da1abda8..91180b42 100644 --- a/tests/commands/pkg/test_outdated.py +++ b/tests/commands/pkg/test_outdated.py @@ -51,7 +51,7 @@ def test_project(clirunner, validate_cliresult, isolated_pio_core, tmp_path): # validate output assert "Checking" in result.output assert re.search( - r"^atmelavr\s+2\.2\.0\s+3\.\d+\.\d+\s+3\.\d+\.\d+\s+Platform\s+devkit", + r"^atmelavr\s+2\.2\.0\s+3\.\d+\.\d+\s+[456789]\.\d+\.\d+\s+Platform\s+devkit", result.output, re.MULTILINE, ) From 32386bec1866ede060c2ac4e6537fef5a4331f76 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 12:11:26 +0300 Subject: [PATCH 65/74] Make "get_project_watch_lib_dirs" public for IDEs --- platformio/project/helpers.py | 7 +++++-- platformio/public.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/platformio/project/helpers.py b/platformio/project/helpers.py index abe2bf0e..d8fb5822 100644 --- a/platformio/project/helpers.py +++ b/platformio/project/helpers.py @@ -43,14 +43,14 @@ def find_project_dir_above(path): return None -def get_project_all_lib_dirs(): +def get_project_watch_lib_dirs(): """Used by platformio-node-helpers.project.observer.fetchLibDirs""" config = ProjectConfig.get_instance() - libdeps_dir = config.get("platformio", "libdeps_dir") result = [ config.get("platformio", "globallib_dir"), config.get("platformio", "lib_dir"), ] + libdeps_dir = config.get("platformio", "libdeps_dir") if not os.path.isdir(libdeps_dir): return result for d in os.listdir(libdeps_dir): @@ -59,6 +59,9 @@ def get_project_all_lib_dirs(): return result +get_project_all_lib_dirs = get_project_watch_lib_dirs + + def get_project_cache_dir(): """Deprecated, use ProjectConfig.get("platformio", "cache_dir") instead""" return ProjectConfig.get_instance().get("platformio", "cache_dir") diff --git a/platformio/public.py b/platformio/public.py index ead093ce..51eecc52 100644 --- a/platformio/public.py +++ b/platformio/public.py @@ -19,7 +19,7 @@ from platformio.device.list import list_serial_ports from platformio.fs import to_unix_path from platformio.platform.base import PlatformBase from platformio.project.config import ProjectConfig -from platformio.project.helpers import load_build_metadata +from platformio.project.helpers import get_project_watch_lib_dirs, load_build_metadata from platformio.test.result import TestCase, TestCaseSource, TestStatus from platformio.test.runners.base import TestRunnerBase from platformio.test.runners.doctest import DoctestTestCaseParser From b104b840c4056ddbb08256428f1d72784b0b061b Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 12:28:41 +0300 Subject: [PATCH 66/74] Move "org" command to the account module --- platformio/account/helpers.py | 4 + platformio/account/org/__init__.py | 13 ++ platformio/account/org/cli.py | 38 +++++ platformio/account/org/commands/__init__.py | 13 ++ platformio/account/org/commands/add.py | 34 ++++ platformio/account/org/commands/create.py | 38 +++++ platformio/account/org/commands/destroy.py | 34 ++++ platformio/account/org/commands/list.py | 48 ++++++ platformio/account/org/commands/remove.py | 34 ++++ platformio/account/org/commands/update.py | 52 ++++++ platformio/commands/org.py | 165 -------------------- tests/commands/test_account_org_team.py | 2 +- 12 files changed, 309 insertions(+), 166 deletions(-) create mode 100644 platformio/account/org/__init__.py create mode 100644 platformio/account/org/cli.py create mode 100644 platformio/account/org/commands/__init__.py create mode 100644 platformio/account/org/commands/add.py create mode 100644 platformio/account/org/commands/create.py create mode 100644 platformio/account/org/commands/destroy.py create mode 100644 platformio/account/org/commands/list.py create mode 100644 platformio/account/org/commands/remove.py create mode 100644 platformio/account/org/commands/update.py delete mode 100644 platformio/commands/org.py diff --git a/platformio/account/helpers.py b/platformio/account/helpers.py index adf9413e..2f469936 100644 --- a/platformio/account/helpers.py +++ b/platformio/account/helpers.py @@ -46,3 +46,7 @@ def validate_password(value): " including a number and a lowercase letter" ) return value + + +def validate_orgname(value): + return validate_username(value, "Organization name") diff --git a/platformio/account/org/__init__.py b/platformio/account/org/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/account/org/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/platformio/account/org/cli.py b/platformio/account/org/cli.py new file mode 100644 index 00000000..5f1f0fc9 --- /dev/null +++ b/platformio/account/org/cli.py @@ -0,0 +1,38 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.org.commands.add import org_add_cmd +from platformio.account.org.commands.create import org_create_cmd +from platformio.account.org.commands.destroy import org_destroy_cmd +from platformio.account.org.commands.list import org_list_cmd +from platformio.account.org.commands.remove import org_remove_cmd +from platformio.account.org.commands.update import org_update_cmd + + +@click.group( + "account", + commands=[ + org_add_cmd, + org_create_cmd, + org_destroy_cmd, + org_list_cmd, + org_remove_cmd, + org_update_cmd, + ], + short_help="Manage organizations", +) +def cli(): + pass diff --git a/platformio/account/org/commands/__init__.py b/platformio/account/org/commands/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/account/org/commands/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/platformio/account/org/commands/add.py b/platformio/account/org/commands/add.py new file mode 100644 index 00000000..55e658e7 --- /dev/null +++ b/platformio/account/org/commands/add.py @@ -0,0 +1,34 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient + + +@click.command("add", short_help="Add a new owner to organization") +@click.argument( + "orgname", +) +@click.argument( + "username", +) +def org_add_cmd(orgname, username): + client = AccountClient() + client.add_org_owner(orgname, username) + return click.secho( + "The new owner `%s` has been successfully added to the `%s` organization." + % (username, orgname), + fg="green", + ) diff --git a/platformio/account/org/commands/create.py b/platformio/account/org/commands/create.py new file mode 100644 index 00000000..f4b7737a --- /dev/null +++ b/platformio/account/org/commands/create.py @@ -0,0 +1,38 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient +from platformio.account.helpers import validate_email, validate_orgname + + +@click.command("create", short_help="Create a new organization") +@click.argument( + "orgname", + callback=lambda _, __, value: validate_orgname(value), +) +@click.option( + "--email", callback=lambda _, __, value: validate_email(value) if value else value +) +@click.option( + "--displayname", +) +def org_create_cmd(orgname, email, displayname): + client = AccountClient() + client.create_org(orgname, email, displayname) + return click.secho( + "The organization `%s` has been successfully created." % orgname, + fg="green", + ) diff --git a/platformio/account/org/commands/destroy.py b/platformio/account/org/commands/destroy.py new file mode 100644 index 00000000..2a202f9a --- /dev/null +++ b/platformio/account/org/commands/destroy.py @@ -0,0 +1,34 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient + + +@click.command("destroy", short_help="Destroy organization") +@click.argument("orgname") +def org_destroy_cmd(orgname): + client = AccountClient() + click.confirm( + "Are you sure you want to delete the `%s` organization account?\n" + "Warning! All linked data will be permanently removed and can not be restored." + % orgname, + abort=True, + ) + client.destroy_org(orgname) + return click.secho( + "Organization `%s` has been destroyed." % orgname, + fg="green", + ) diff --git a/platformio/account/org/commands/list.py b/platformio/account/org/commands/list.py new file mode 100644 index 00000000..471814ea --- /dev/null +++ b/platformio/account/org/commands/list.py @@ -0,0 +1,48 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 json + +import click +from tabulate import tabulate + +from platformio.account.client import AccountClient + + +@click.command("list", short_help="List organizations and their members") +@click.option("--json-output", is_flag=True) +def org_list_cmd(json_output): + client = AccountClient() + orgs = client.list_orgs() + if json_output: + return click.echo(json.dumps(orgs)) + if not orgs: + return click.echo("You do not have any organization") + for org in orgs: + click.echo() + click.secho(org.get("orgname"), fg="cyan") + click.echo("-" * len(org.get("orgname"))) + data = [] + if org.get("displayname"): + data.append(("Display Name:", org.get("displayname"))) + if org.get("email"): + data.append(("Email:", org.get("email"))) + data.append( + ( + "Owners:", + ", ".join((owner.get("username") for owner in org.get("owners"))), + ) + ) + click.echo(tabulate(data, tablefmt="plain")) + return click.echo() diff --git a/platformio/account/org/commands/remove.py b/platformio/account/org/commands/remove.py new file mode 100644 index 00000000..9dee462a --- /dev/null +++ b/platformio/account/org/commands/remove.py @@ -0,0 +1,34 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient + + +@click.command("remove", short_help="Remove an owner from organization") +@click.argument( + "orgname", +) +@click.argument( + "username", +) +def org_remove_cmd(orgname, username): + client = AccountClient() + client.remove_org_owner(orgname, username) + return click.secho( + "The `%s` owner has been successfully removed from the `%s` organization." + % (username, orgname), + fg="green", + ) diff --git a/platformio/account/org/commands/update.py b/platformio/account/org/commands/update.py new file mode 100644 index 00000000..7a7d2b4c --- /dev/null +++ b/platformio/account/org/commands/update.py @@ -0,0 +1,52 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient +from platformio.account.helpers import validate_email, validate_orgname + + +@click.command("update", short_help="Update organization") +@click.argument("cur_orgname") +@click.option( + "--orgname", + callback=lambda _, __, value: validate_orgname(value), + help="A new orgname", +) +@click.option("--email") +@click.option("--displayname") +def org_update_cmd(cur_orgname, **kwargs): + client = AccountClient() + org = client.get_org(cur_orgname) + del org["owners"] + new_org = org.copy() + if not any(kwargs.values()): + for field in org: + new_org[field] = click.prompt( + field.replace("_", " ").capitalize(), default=org[field] + ) + if field == "email": + validate_email(new_org[field]) + if field == "orgname": + validate_orgname(new_org[field]) + else: + new_org.update( + {key.replace("new_", ""): value for key, value in kwargs.items() if value} + ) + client.update_org(cur_orgname, new_org) + return click.secho( + "The organization `%s` has been successfully updated." % cur_orgname, + fg="green", + ) diff --git a/platformio/commands/org.py b/platformio/commands/org.py deleted file mode 100644 index bd569e76..00000000 --- a/platformio/commands/org.py +++ /dev/null @@ -1,165 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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 json - -import click -from tabulate import tabulate - -from platformio.account.client import AccountClient -from platformio.account.helpers import validate_email, validate_username - - -@click.group("org", short_help="Manage organizations") -def cli(): - pass - - -def validate_orgname(value): - return validate_username(value, "Organization name") - - -@cli.command("create", short_help="Create a new organization") -@click.argument( - "orgname", - callback=lambda _, __, value: validate_orgname(value), -) -@click.option( - "--email", callback=lambda _, __, value: validate_email(value) if value else value -) -@click.option( - "--displayname", -) -def org_create(orgname, email, displayname): - client = AccountClient() - client.create_org(orgname, email, displayname) - return click.secho( - "The organization `%s` has been successfully created." % orgname, - fg="green", - ) - - -@cli.command("list", short_help="List organizations and their members") -@click.option("--json-output", is_flag=True) -def org_list(json_output): - client = AccountClient() - orgs = client.list_orgs() - if json_output: - return click.echo(json.dumps(orgs)) - if not orgs: - return click.echo("You do not have any organization") - for org in orgs: - click.echo() - click.secho(org.get("orgname"), fg="cyan") - click.echo("-" * len(org.get("orgname"))) - data = [] - if org.get("displayname"): - data.append(("Display Name:", org.get("displayname"))) - if org.get("email"): - data.append(("Email:", org.get("email"))) - data.append( - ( - "Owners:", - ", ".join((owner.get("username") for owner in org.get("owners"))), - ) - ) - click.echo(tabulate(data, tablefmt="plain")) - return click.echo() - - -@cli.command("update", short_help="Update organization") -@click.argument("cur_orgname") -@click.option( - "--orgname", - callback=lambda _, __, value: validate_orgname(value), - help="A new orgname", -) -@click.option("--email") -@click.option("--displayname") -def org_update(cur_orgname, **kwargs): - client = AccountClient() - org = client.get_org(cur_orgname) - del org["owners"] - new_org = org.copy() - if not any(kwargs.values()): - for field in org: - new_org[field] = click.prompt( - field.replace("_", " ").capitalize(), default=org[field] - ) - if field == "email": - validate_email(new_org[field]) - if field == "orgname": - validate_orgname(new_org[field]) - else: - new_org.update( - {key.replace("new_", ""): value for key, value in kwargs.items() if value} - ) - client.update_org(cur_orgname, new_org) - return click.secho( - "The organization `%s` has been successfully updated." % cur_orgname, - fg="green", - ) - - -@cli.command("destroy", short_help="Destroy organization") -@click.argument("orgname") -def account_destroy(orgname): - client = AccountClient() - click.confirm( - "Are you sure you want to delete the `%s` organization account?\n" - "Warning! All linked data will be permanently removed and can not be restored." - % orgname, - abort=True, - ) - client.destroy_org(orgname) - return click.secho( - "Organization `%s` has been destroyed." % orgname, - fg="green", - ) - - -@cli.command("add", short_help="Add a new owner to organization") -@click.argument( - "orgname", -) -@click.argument( - "username", -) -def org_add_owner(orgname, username): - client = AccountClient() - client.add_org_owner(orgname, username) - return click.secho( - "The new owner `%s` has been successfully added to the `%s` organization." - % (username, orgname), - fg="green", - ) - - -@cli.command("remove", short_help="Remove an owner from organization") -@click.argument( - "orgname", -) -@click.argument( - "username", -) -def org_remove_owner(orgname, username): - client = AccountClient() - client.remove_org_owner(orgname, username) - return click.secho( - "The `%s` owner has been successfully removed from the `%s` organization." - % (username, orgname), - fg="green", - ) diff --git a/tests/commands/test_account_org_team.py b/tests/commands/test_account_org_team.py index 29d85971..eb0c3edf 100644 --- a/tests/commands/test_account_org_team.py +++ b/tests/commands/test_account_org_team.py @@ -22,7 +22,7 @@ import pytest import requests from platformio.account.cli import cli as cmd_account -from platformio.commands.org import cli as cmd_org +from platformio.account.org.cli import cli as cmd_org from platformio.commands.team import cli as cmd_team pytestmark = pytest.mark.skipif( From 7e7856e44cf7febe66cb6263bc02c8d5fbe8030e Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 12:35:55 +0300 Subject: [PATCH 67/74] Restructure "registry" modules --- platformio/commands/access.py | 2 +- platformio/package/commands/publish.py | 2 +- platformio/package/commands/search.py | 2 +- platformio/package/commands/show.py | 2 +- platformio/package/commands/unpublish.py | 2 +- platformio/package/manager/_registry.py | 86 +------------------ platformio/registry/__init__.py | 13 +++ platformio/{package => registry}/client.py | 0 platformio/registry/mirror.py | 99 ++++++++++++++++++++++ tests/commands/test_lib.py | 2 +- tests/misc/test_misc.py | 2 +- 11 files changed, 121 insertions(+), 91 deletions(-) create mode 100644 platformio/registry/__init__.py rename platformio/{package => registry}/client.py (100%) create mode 100644 platformio/registry/mirror.py diff --git a/platformio/commands/access.py b/platformio/commands/access.py index d35d0830..fa726927 100644 --- a/platformio/commands/access.py +++ b/platformio/commands/access.py @@ -22,7 +22,7 @@ from tabulate import tabulate from platformio.account.helpers import validate_username from platformio.commands.team import validate_orgname_teamname -from platformio.package.client import RegistryClient +from platformio.registry.client import RegistryClient def validate_client(value): diff --git a/platformio/package/commands/publish.py b/platformio/package/commands/publish.py index ee7e4a25..c1c12a5a 100644 --- a/platformio/package/commands/publish.py +++ b/platformio/package/commands/publish.py @@ -23,12 +23,12 @@ from tabulate import tabulate from platformio import fs from platformio.account.client import AccountClient from platformio.exception import UserSideException -from platformio.package.client import RegistryClient from platformio.package.manifest.parser import ManifestParserFactory from platformio.package.manifest.schema import ManifestSchema from platformio.package.meta import PackageType from platformio.package.pack import PackagePacker from platformio.package.unpack import FileUnpacker, TARArchiver +from platformio.registry.client import RegistryClient def validate_datetime(ctx, param, value): # pylint: disable=unused-argument diff --git a/platformio/package/commands/search.py b/platformio/package/commands/search.py index d93b9f81..ac71ef4c 100644 --- a/platformio/package/commands/search.py +++ b/platformio/package/commands/search.py @@ -17,7 +17,7 @@ import math import click from platformio import util -from platformio.package.client import RegistryClient +from platformio.registry.client import RegistryClient @click.command("search", short_help="Search for packages") diff --git a/platformio/package/commands/show.py b/platformio/package/commands/show.py index c1ee28bb..82b6e47c 100644 --- a/platformio/package/commands/show.py +++ b/platformio/package/commands/show.py @@ -19,9 +19,9 @@ from tabulate import tabulate from platformio import fs, util from platformio.exception import UserSideException -from platformio.package.client import RegistryClient from platformio.package.manager._registry import PackageManagerRegistryMixin from platformio.package.meta import PackageSpec, PackageType +from platformio.registry.client import RegistryClient @click.command("show", short_help="Show package information") diff --git a/platformio/package/commands/unpublish.py b/platformio/package/commands/unpublish.py index 9c1d067e..7d0e633e 100644 --- a/platformio/package/commands/unpublish.py +++ b/platformio/package/commands/unpublish.py @@ -15,8 +15,8 @@ import click from platformio.account.client import AccountClient -from platformio.package.client import RegistryClient from platformio.package.meta import PackageSpec, PackageType +from platformio.registry.client import RegistryClient @click.command("unpublish", short_help="Remove a pushed package from the registry") diff --git a/platformio/package/manager/_registry.py b/platformio/package/manager/_registry.py index 2fc11e1d..0a9f39c5 100644 --- a/platformio/package/manager/_registry.py +++ b/platformio/package/manager/_registry.py @@ -12,97 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import time -from urllib.parse import urlparse import click -from platformio import __registry_mirror_hosts__ -from platformio.cache import ContentCache -from platformio.http import HTTPClient -from platformio.package.client import RegistryClient from platformio.package.exception import UnknownPackageError from platformio.package.meta import PackageSpec from platformio.package.version import cast_version_to_semver - - -class RegistryFileMirrorIterator(object): - - HTTP_CLIENT_INSTANCES = {} - - def __init__(self, download_url): - self.download_url = download_url - self._url_parts = urlparse(download_url) - self._mirror = "%s://%s" % (self._url_parts.scheme, self._url_parts.netloc) - self._visited_mirrors = [] - - def __iter__(self): # pylint: disable=non-iterator-returned - return self - - def __next__(self): - cache_key = ContentCache.key_from_args( - "head", self.download_url, self._visited_mirrors - ) - with ContentCache("http") as cc: - result = cc.get(cache_key) - if result is not None: - try: - headers = json.loads(result) - return ( - headers["Location"], - headers["X-PIO-Content-SHA256"], - ) - except (ValueError, KeyError): - pass - - http = self.get_http_client() - response = http.send_request( - "head", - self._url_parts.path, - allow_redirects=False, - params=dict(bypass=",".join(self._visited_mirrors)) - if self._visited_mirrors - else None, - x_with_authorization=RegistryClient.allowed_private_packages(), - ) - stop_conditions = [ - response.status_code not in (302, 307), - not response.headers.get("Location"), - not response.headers.get("X-PIO-Mirror"), - response.headers.get("X-PIO-Mirror") in self._visited_mirrors, - ] - if any(stop_conditions): - raise StopIteration - self._visited_mirrors.append(response.headers.get("X-PIO-Mirror")) - cc.set( - cache_key, - json.dumps( - { - "Location": response.headers.get("Location"), - "X-PIO-Content-SHA256": response.headers.get( - "X-PIO-Content-SHA256" - ), - } - ), - "1h", - ) - return ( - response.headers.get("Location"), - response.headers.get("X-PIO-Content-SHA256"), - ) - - def get_http_client(self): - if self._mirror not in RegistryFileMirrorIterator.HTTP_CLIENT_INSTANCES: - endpoints = [self._mirror] - for host in __registry_mirror_hosts__: - endpoint = f"https://dl.{host}" - if endpoint not in endpoints: - endpoints.append(endpoint) - RegistryFileMirrorIterator.HTTP_CLIENT_INSTANCES[self._mirror] = HTTPClient( - endpoints - ) - return RegistryFileMirrorIterator.HTTP_CLIENT_INSTANCES[self._mirror] +from platformio.registry.client import RegistryClient +from platformio.registry.mirror import RegistryFileMirrorIterator class PackageManagerRegistryMixin(object): diff --git a/platformio/registry/__init__.py b/platformio/registry/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/registry/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/platformio/package/client.py b/platformio/registry/client.py similarity index 100% rename from platformio/package/client.py rename to platformio/registry/client.py diff --git a/platformio/registry/mirror.py b/platformio/registry/mirror.py new file mode 100644 index 00000000..d967838e --- /dev/null +++ b/platformio/registry/mirror.py @@ -0,0 +1,99 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 json +from urllib.parse import urlparse + +from platformio import __registry_mirror_hosts__ +from platformio.cache import ContentCache +from platformio.http import HTTPClient +from platformio.registry.client import RegistryClient + + +class RegistryFileMirrorIterator(object): + + HTTP_CLIENT_INSTANCES = {} + + def __init__(self, download_url): + self.download_url = download_url + self._url_parts = urlparse(download_url) + self._mirror = "%s://%s" % (self._url_parts.scheme, self._url_parts.netloc) + self._visited_mirrors = [] + + def __iter__(self): # pylint: disable=non-iterator-returned + return self + + def __next__(self): + cache_key = ContentCache.key_from_args( + "head", self.download_url, self._visited_mirrors + ) + with ContentCache("http") as cc: + result = cc.get(cache_key) + if result is not None: + try: + headers = json.loads(result) + return ( + headers["Location"], + headers["X-PIO-Content-SHA256"], + ) + except (ValueError, KeyError): + pass + + http = self.get_http_client() + response = http.send_request( + "head", + self._url_parts.path, + allow_redirects=False, + params=dict(bypass=",".join(self._visited_mirrors)) + if self._visited_mirrors + else None, + x_with_authorization=RegistryClient.allowed_private_packages(), + ) + stop_conditions = [ + response.status_code not in (302, 307), + not response.headers.get("Location"), + not response.headers.get("X-PIO-Mirror"), + response.headers.get("X-PIO-Mirror") in self._visited_mirrors, + ] + if any(stop_conditions): + raise StopIteration + self._visited_mirrors.append(response.headers.get("X-PIO-Mirror")) + cc.set( + cache_key, + json.dumps( + { + "Location": response.headers.get("Location"), + "X-PIO-Content-SHA256": response.headers.get( + "X-PIO-Content-SHA256" + ), + } + ), + "1h", + ) + return ( + response.headers.get("Location"), + response.headers.get("X-PIO-Content-SHA256"), + ) + + def get_http_client(self): + if self._mirror not in RegistryFileMirrorIterator.HTTP_CLIENT_INSTANCES: + endpoints = [self._mirror] + for host in __registry_mirror_hosts__: + endpoint = f"https://dl.{host}" + if endpoint not in endpoints: + endpoints.append(endpoint) + RegistryFileMirrorIterator.HTTP_CLIENT_INSTANCES[self._mirror] = HTTPClient( + endpoints + ) + return RegistryFileMirrorIterator.HTTP_CLIENT_INSTANCES[self._mirror] diff --git a/tests/commands/test_lib.py b/tests/commands/test_lib.py index dcfcab20..cf1e53b6 100644 --- a/tests/commands/test_lib.py +++ b/tests/commands/test_lib.py @@ -21,10 +21,10 @@ import pytest import semantic_version from platformio.commands.lib.command import cli as cmd_lib -from platformio.package.client import RegistryClient from platformio.package.meta import PackageType from platformio.package.vcsclient import VCSClientFactory from platformio.project.config import ProjectConfig +from platformio.registry.client import RegistryClient def test_saving_deps(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory): diff --git a/tests/misc/test_misc.py b/tests/misc/test_misc.py index a7d776e1..52a6cac4 100644 --- a/tests/misc/test_misc.py +++ b/tests/misc/test_misc.py @@ -18,7 +18,7 @@ import pytest import requests from platformio import __check_internet_hosts__, http, proc -from platformio.package.client import RegistryClient +from platformio.registry.client import RegistryClient def test_platformio_cli(): From 0b8a595288dcc43e3c44922aaf77d931ad527a5b Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 12:46:57 +0300 Subject: [PATCH 68/74] Move "access" command to the registry module --- platformio/commands/access.py | 154 ------------------ platformio/registry/access/__init__.py | 13 ++ platformio/registry/access/cli.py | 36 ++++ .../registry/access/commands/__init__.py | 13 ++ platformio/registry/access/commands/grant.py | 39 +++++ platformio/registry/access/commands/list.py | 58 +++++++ .../registry/access/commands/private.py | 33 ++++ platformio/registry/access/commands/public.py | 33 ++++ platformio/registry/access/commands/revoke.py | 38 +++++ platformio/registry/access/helpers.py | 35 ++++ 10 files changed, 298 insertions(+), 154 deletions(-) delete mode 100644 platformio/commands/access.py create mode 100644 platformio/registry/access/__init__.py create mode 100644 platformio/registry/access/cli.py create mode 100644 platformio/registry/access/commands/__init__.py create mode 100644 platformio/registry/access/commands/grant.py create mode 100644 platformio/registry/access/commands/list.py create mode 100644 platformio/registry/access/commands/private.py create mode 100644 platformio/registry/access/commands/public.py create mode 100644 platformio/registry/access/commands/revoke.py create mode 100644 platformio/registry/access/helpers.py diff --git a/platformio/commands/access.py b/platformio/commands/access.py deleted file mode 100644 index fa726927..00000000 --- a/platformio/commands/access.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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 json -import re - -import click -from tabulate import tabulate - -from platformio.account.helpers import validate_username -from platformio.commands.team import validate_orgname_teamname -from platformio.registry.client import RegistryClient - - -def validate_client(value): - if ":" in value: - validate_orgname_teamname(value) - else: - validate_username(value) - return value - - -@click.group("access", short_help="Manage resource access") -def cli(): - pass - - -def validate_urn(value): - value = str(value).strip() - if not re.match(r"^prn:reg:pkg:(\d+):(\w+)$", value, flags=re.I): - raise click.BadParameter("Invalid URN format.") - return value - - -@cli.command("public", short_help="Make resource public") -@click.argument( - "urn", - callback=lambda _, __, value: validate_urn(value), -) -@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") -def access_public(urn, urn_type): - client = RegistryClient() - client.update_resource(urn=urn, private=0) - return click.secho( - "The resource %s has been successfully updated." % urn, - fg="green", - ) - - -@cli.command("private", short_help="Make resource private") -@click.argument( - "urn", - callback=lambda _, __, value: validate_urn(value), -) -@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") -def access_private(urn, urn_type): - client = RegistryClient() - client.update_resource(urn=urn, private=1) - return click.secho( - "The resource %s has been successfully updated." % urn, - fg="green", - ) - - -@cli.command("grant", short_help="Grant access") -@click.argument("level", type=click.Choice(["admin", "maintainer", "guest"])) -@click.argument( - "client", - metavar="[|]", - callback=lambda _, __, value: validate_client(value), -) -@click.argument( - "urn", - callback=lambda _, __, value: validate_urn(value), -) -@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") -def access_grant(level, client, urn, urn_type): - reg_client = RegistryClient() - reg_client.grant_access_for_resource(urn=urn, client=client, level=level) - return click.secho( - "Access for resource %s has been granted for %s" % (urn, client), - fg="green", - ) - - -@cli.command("revoke", short_help="Revoke access") -@click.argument( - "client", - metavar="[ORGNAME:TEAMNAME|USERNAME]", - callback=lambda _, __, value: validate_client(value), -) -@click.argument( - "urn", - callback=lambda _, __, value: validate_urn(value), -) -@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") -def access_revoke(client, urn, urn_type): - reg_client = RegistryClient() - reg_client.revoke_access_from_resource(urn=urn, client=client) - return click.secho( - "Access for resource %s has been revoked for %s" % (urn, client), - fg="green", - ) - - -@cli.command("list", short_help="List published resources") -@click.argument("owner", required=False) -@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") -@click.option("--json-output", is_flag=True) -def access_list(owner, urn_type, json_output): - reg_client = RegistryClient() - resources = reg_client.list_resources(owner=owner) - if json_output: - return click.echo(json.dumps(resources)) - if not resources: - return click.secho("You do not have any resources.", fg="yellow") - for resource in resources: - click.echo() - click.secho(resource.get("name"), fg="cyan") - click.echo("-" * len(resource.get("name"))) - table_data = [] - table_data.append(("URN:", resource.get("urn"))) - table_data.append(("Owner:", resource.get("owner"))) - table_data.append( - ( - "Access:", - click.style("Private", fg="red") - if resource.get("private", False) - else "Public", - ) - ) - table_data.append( - ( - "Access level(s):", - ", ".join( - (level.capitalize() for level in resource.get("access_levels")) - ), - ) - ) - click.echo(tabulate(table_data, tablefmt="plain")) - return click.echo() diff --git a/platformio/registry/access/__init__.py b/platformio/registry/access/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/registry/access/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/platformio/registry/access/cli.py b/platformio/registry/access/cli.py new file mode 100644 index 00000000..fe585c94 --- /dev/null +++ b/platformio/registry/access/cli.py @@ -0,0 +1,36 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.registry.access.commands.grant import access_grant_cmd +from platformio.registry.access.commands.list import access_list_cmd +from platformio.registry.access.commands.private import access_private_cmd +from platformio.registry.access.commands.public import access_public_cmd +from platformio.registry.access.commands.revoke import access_revoke_cmd + + +@click.group( + "access", + commands=[ + access_grant_cmd, + access_list_cmd, + access_private_cmd, + access_public_cmd, + access_revoke_cmd, + ], + short_help="Manage resource access", +) +def cli(): + pass diff --git a/platformio/registry/access/commands/__init__.py b/platformio/registry/access/commands/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/registry/access/commands/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/platformio/registry/access/commands/grant.py b/platformio/registry/access/commands/grant.py new file mode 100644 index 00000000..52e218ef --- /dev/null +++ b/platformio/registry/access/commands/grant.py @@ -0,0 +1,39 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.registry.access.helpers import validate_client, validate_urn +from platformio.registry.client import RegistryClient + + +@click.command("grant", short_help="Grant access") +@click.argument("level", type=click.Choice(["admin", "maintainer", "guest"])) +@click.argument( + "client", + metavar="[|]", + callback=lambda _, __, value: validate_client(value), +) +@click.argument( + "urn", + callback=lambda _, __, value: validate_urn(value), +) +@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") +def access_grant_cmd(level, client, urn, urn_type): # pylint: disable=unused-argument + reg_client = RegistryClient() + reg_client.grant_access_for_resource(urn=urn, client=client, level=level) + return click.secho( + "Access for resource %s has been granted for %s" % (urn, client), + fg="green", + ) diff --git a/platformio/registry/access/commands/list.py b/platformio/registry/access/commands/list.py new file mode 100644 index 00000000..ce9d8a6a --- /dev/null +++ b/platformio/registry/access/commands/list.py @@ -0,0 +1,58 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 json + +import click +from tabulate import tabulate + +from platformio.registry.client import RegistryClient + + +@click.command("list", short_help="List published resources") +@click.argument("owner", required=False) +@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") +@click.option("--json-output", is_flag=True) +def access_list_cmd(owner, urn_type, json_output): # pylint: disable=unused-argument + reg_client = RegistryClient() + resources = reg_client.list_resources(owner=owner) + if json_output: + return click.echo(json.dumps(resources)) + if not resources: + return click.secho("You do not have any resources.", fg="yellow") + for resource in resources: + click.echo() + click.secho(resource.get("name"), fg="cyan") + click.echo("-" * len(resource.get("name"))) + table_data = [] + table_data.append(("URN:", resource.get("urn"))) + table_data.append(("Owner:", resource.get("owner"))) + table_data.append( + ( + "Access:", + click.style("Private", fg="red") + if resource.get("private", False) + else "Public", + ) + ) + table_data.append( + ( + "Access level(s):", + ", ".join( + (level.capitalize() for level in resource.get("access_levels")) + ), + ) + ) + click.echo(tabulate(table_data, tablefmt="plain")) + return click.echo() diff --git a/platformio/registry/access/commands/private.py b/platformio/registry/access/commands/private.py new file mode 100644 index 00000000..276fb460 --- /dev/null +++ b/platformio/registry/access/commands/private.py @@ -0,0 +1,33 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.registry.access.helpers import validate_urn +from platformio.registry.client import RegistryClient + + +@click.command("private", short_help="Make resource private") +@click.argument( + "urn", + callback=lambda _, __, value: validate_urn(value), +) +@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") +def access_private_cmd(urn, urn_type): # pylint: disable=unused-argument + client = RegistryClient() + client.update_resource(urn=urn, private=1) + return click.secho( + "The resource %s has been successfully updated." % urn, + fg="green", + ) diff --git a/platformio/registry/access/commands/public.py b/platformio/registry/access/commands/public.py new file mode 100644 index 00000000..58a3c26b --- /dev/null +++ b/platformio/registry/access/commands/public.py @@ -0,0 +1,33 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.registry.access.helpers import validate_urn +from platformio.registry.client import RegistryClient + + +@click.command("public", short_help="Make resource public") +@click.argument( + "urn", + callback=lambda _, __, value: validate_urn(value), +) +@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") +def access_public_cmd(urn, urn_type): # pylint: disable=unused-argument + client = RegistryClient() + client.update_resource(urn=urn, private=0) + return click.secho( + "The resource %s has been successfully updated." % urn, + fg="green", + ) diff --git a/platformio/registry/access/commands/revoke.py b/platformio/registry/access/commands/revoke.py new file mode 100644 index 00000000..7768d229 --- /dev/null +++ b/platformio/registry/access/commands/revoke.py @@ -0,0 +1,38 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.registry.access.helpers import validate_client, validate_urn +from platformio.registry.client import RegistryClient + + +@click.command("revoke", short_help="Revoke access") +@click.argument( + "client", + metavar="[ORGNAME:TEAMNAME|USERNAME]", + callback=lambda _, __, value: validate_client(value), +) +@click.argument( + "urn", + callback=lambda _, __, value: validate_urn(value), +) +@click.option("--urn-type", type=click.Choice(["prn:reg:pkg"]), default="prn:reg:pkg") +def access_revoke_cmd(client, urn, urn_type): # pylint: disable=unused-argument + reg_client = RegistryClient() + reg_client.revoke_access_from_resource(urn=urn, client=client) + return click.secho( + "Access for resource %s has been revoked for %s" % (urn, client), + fg="green", + ) diff --git a/platformio/registry/access/helpers.py b/platformio/registry/access/helpers.py new file mode 100644 index 00000000..9a49b81e --- /dev/null +++ b/platformio/registry/access/helpers.py @@ -0,0 +1,35 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 re + +import click + +from platformio.account.helpers import validate_username +from platformio.commands.team import validate_orgname_teamname + + +def validate_urn(value): + value = str(value).strip() + if not re.match(r"^prn:reg:pkg:(\d+):(\w+)$", value, flags=re.I): + raise click.BadParameter("Invalid URN format.") + return value + + +def validate_client(value): + if ":" in value: + validate_orgname_teamname(value) + else: + validate_username(value) + return value From 22860cd4e5a29fc69120418eebd20379d54882fa Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 12:57:23 +0300 Subject: [PATCH 69/74] Rename "helpers" to "validate" --- platformio/registry/access/commands/grant.py | 2 +- platformio/registry/access/commands/private.py | 2 +- platformio/registry/access/commands/public.py | 2 +- platformio/registry/access/commands/revoke.py | 2 +- platformio/registry/access/{helpers.py => validate.py} | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename platformio/registry/access/{helpers.py => validate.py} (94%) diff --git a/platformio/registry/access/commands/grant.py b/platformio/registry/access/commands/grant.py index 52e218ef..f9214476 100644 --- a/platformio/registry/access/commands/grant.py +++ b/platformio/registry/access/commands/grant.py @@ -14,7 +14,7 @@ import click -from platformio.registry.access.helpers import validate_client, validate_urn +from platformio.registry.access.validate import validate_client, validate_urn from platformio.registry.client import RegistryClient diff --git a/platformio/registry/access/commands/private.py b/platformio/registry/access/commands/private.py index 276fb460..5211aeb3 100644 --- a/platformio/registry/access/commands/private.py +++ b/platformio/registry/access/commands/private.py @@ -14,7 +14,7 @@ import click -from platformio.registry.access.helpers import validate_urn +from platformio.registry.access.validate import validate_urn from platformio.registry.client import RegistryClient diff --git a/platformio/registry/access/commands/public.py b/platformio/registry/access/commands/public.py index 58a3c26b..465dcf32 100644 --- a/platformio/registry/access/commands/public.py +++ b/platformio/registry/access/commands/public.py @@ -14,7 +14,7 @@ import click -from platformio.registry.access.helpers import validate_urn +from platformio.registry.access.validate import validate_urn from platformio.registry.client import RegistryClient diff --git a/platformio/registry/access/commands/revoke.py b/platformio/registry/access/commands/revoke.py index 7768d229..b6bb0008 100644 --- a/platformio/registry/access/commands/revoke.py +++ b/platformio/registry/access/commands/revoke.py @@ -14,7 +14,7 @@ import click -from platformio.registry.access.helpers import validate_client, validate_urn +from platformio.registry.access.validate import validate_client, validate_urn from platformio.registry.client import RegistryClient diff --git a/platformio/registry/access/helpers.py b/platformio/registry/access/validate.py similarity index 94% rename from platformio/registry/access/helpers.py rename to platformio/registry/access/validate.py index 9a49b81e..20aae07e 100644 --- a/platformio/registry/access/helpers.py +++ b/platformio/registry/access/validate.py @@ -16,7 +16,7 @@ import re import click -from platformio.account.helpers import validate_username +from platformio.account.validate import validate_username from platformio.commands.team import validate_orgname_teamname From 10da6bf5c63f2826797492339533ccdf6e8542f5 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 13:16:45 +0300 Subject: [PATCH 70/74] Cleanup --- .github/workflows/deployment.yml | 10 ------ platformio/account/helpers.py | 52 -------------------------------- 2 files changed, 62 deletions(-) delete mode 100644 platformio/account/helpers.py diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index ad5d2c3d..0cee3cf6 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -40,13 +40,3 @@ jobs: with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} - - - name: Slack Notification - uses: homoluctus/slatify@master - if: failure() - with: - type: ${{ job.status }} - job_name: '*Core*' - commit: true - url: ${{ secrets.SLACK_BUILD_WEBHOOK }} - token: ${{ secrets.SLACK_GITHUB_TOKEN }} diff --git a/platformio/account/helpers.py b/platformio/account/helpers.py deleted file mode 100644 index 2f469936..00000000 --- a/platformio/account/helpers.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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 re - -import click - - -def validate_username(value, field="username"): - value = str(value).strip() - if not re.match(r"^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,37}$", value, flags=re.I): - raise click.BadParameter( - "Invalid %s format. " - "%s must contain only alphanumeric characters " - "or single hyphens, cannot begin or end with a hyphen, " - "and must not be longer than 38 characters." - % (field.lower(), field.capitalize()) - ) - return value - - -def validate_email(value): - value = str(value).strip() - if not re.match(r"^[a-z\d_.+-]+@[a-z\d\-]+\.[a-z\d\-.]+$", value, flags=re.I): - raise click.BadParameter("Invalid email address") - return value - - -def validate_password(value): - value = str(value).strip() - if not re.match(r"^(?=.*[a-z])(?=.*\d).{8,}$", value): - raise click.BadParameter( - "Invalid password format. " - "Password must contain at least 8 characters" - " including a number and a lowercase letter" - ) - return value - - -def validate_orgname(value): - return validate_username(value, "Organization name") From 206bb38f54b1102905ac42f346648c7def613b34 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 13:16:57 +0300 Subject: [PATCH 71/74] Move "team" command to the "account" module --- platformio/account/commands/register.py | 2 +- platformio/account/commands/update.py | 2 +- platformio/account/org/commands/create.py | 2 +- platformio/account/org/commands/update.py | 2 +- platformio/account/team/__init__.py | 13 ++ platformio/account/team/cli.py | 38 ++++ platformio/account/team/commands/__init__.py | 13 ++ platformio/account/team/commands/add.py | 38 ++++ platformio/account/team/commands/create.py | 39 ++++ platformio/account/team/commands/destroy.py | 40 ++++ platformio/account/team/commands/list.py | 59 ++++++ platformio/account/team/commands/remove.py | 36 ++++ platformio/account/team/commands/update.py | 55 +++++ platformio/account/validate.py | 79 +++++++ platformio/commands/team.py | 212 ------------------- platformio/registry/access/validate.py | 3 +- tests/commands/test_account_org_team.py | 2 +- 17 files changed, 416 insertions(+), 219 deletions(-) create mode 100644 platformio/account/team/__init__.py create mode 100644 platformio/account/team/cli.py create mode 100644 platformio/account/team/commands/__init__.py create mode 100644 platformio/account/team/commands/add.py create mode 100644 platformio/account/team/commands/create.py create mode 100644 platformio/account/team/commands/destroy.py create mode 100644 platformio/account/team/commands/list.py create mode 100644 platformio/account/team/commands/remove.py create mode 100644 platformio/account/team/commands/update.py create mode 100644 platformio/account/validate.py delete mode 100644 platformio/commands/team.py diff --git a/platformio/account/commands/register.py b/platformio/account/commands/register.py index b5ec532d..6fc20ce6 100644 --- a/platformio/account/commands/register.py +++ b/platformio/account/commands/register.py @@ -15,7 +15,7 @@ import click from platformio.account.client import AccountClient -from platformio.account.helpers import ( +from platformio.account.validate import ( validate_email, validate_password, validate_username, diff --git a/platformio/account/commands/update.py b/platformio/account/commands/update.py index e198867d..b3868938 100644 --- a/platformio/account/commands/update.py +++ b/platformio/account/commands/update.py @@ -15,7 +15,7 @@ import click from platformio.account.client import AccountClient, AccountNotAuthorized -from platformio.account.helpers import validate_email, validate_username +from platformio.account.validate import validate_email, validate_username @click.command("update", short_help="Update profile information") diff --git a/platformio/account/org/commands/create.py b/platformio/account/org/commands/create.py index f4b7737a..b0c86bf5 100644 --- a/platformio/account/org/commands/create.py +++ b/platformio/account/org/commands/create.py @@ -15,7 +15,7 @@ import click from platformio.account.client import AccountClient -from platformio.account.helpers import validate_email, validate_orgname +from platformio.account.validate import validate_email, validate_orgname @click.command("create", short_help="Create a new organization") diff --git a/platformio/account/org/commands/update.py b/platformio/account/org/commands/update.py index 7a7d2b4c..9da9564c 100644 --- a/platformio/account/org/commands/update.py +++ b/platformio/account/org/commands/update.py @@ -15,7 +15,7 @@ import click from platformio.account.client import AccountClient -from platformio.account.helpers import validate_email, validate_orgname +from platformio.account.validate import validate_email, validate_orgname @click.command("update", short_help="Update organization") diff --git a/platformio/account/team/__init__.py b/platformio/account/team/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/account/team/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/platformio/account/team/cli.py b/platformio/account/team/cli.py new file mode 100644 index 00000000..feffa7b3 --- /dev/null +++ b/platformio/account/team/cli.py @@ -0,0 +1,38 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.team.commands.add import team_add_cmd +from platformio.account.team.commands.create import team_create_cmd +from platformio.account.team.commands.destroy import team_destroy_cmd +from platformio.account.team.commands.list import team_list_cmd +from platformio.account.team.commands.remove import team_remove_cmd +from platformio.account.team.commands.update import team_update_cmd + + +@click.group( + "team", + commands=[ + team_add_cmd, + team_create_cmd, + team_destroy_cmd, + team_list_cmd, + team_remove_cmd, + team_update_cmd, + ], + short_help="Manage organization teams", +) +def cli(): + pass diff --git a/platformio/account/team/commands/__init__.py b/platformio/account/team/commands/__init__.py new file mode 100644 index 00000000..b0514903 --- /dev/null +++ b/platformio/account/team/commands/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. diff --git a/platformio/account/team/commands/add.py b/platformio/account/team/commands/add.py new file mode 100644 index 00000000..854675e6 --- /dev/null +++ b/platformio/account/team/commands/add.py @@ -0,0 +1,38 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient +from platformio.account.validate import validate_orgname_teamname + + +@click.command("add", short_help="Add a new member to team") +@click.argument( + "orgname_teamname", + metavar="ORGNAME:TEAMNAME", + callback=lambda _, __, value: validate_orgname_teamname(value), +) +@click.argument( + "username", +) +def team_add_cmd(orgname_teamname, username): + orgname, teamname = orgname_teamname.split(":", 1) + client = AccountClient() + client.add_team_member(orgname, teamname, username) + return click.secho( + "The new member %s has been successfully added to the %s team." + % (username, teamname), + fg="green", + ) diff --git a/platformio/account/team/commands/create.py b/platformio/account/team/commands/create.py new file mode 100644 index 00000000..891d33b4 --- /dev/null +++ b/platformio/account/team/commands/create.py @@ -0,0 +1,39 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient +from platformio.account.validate import validate_orgname_teamname + + +@click.command("create", short_help="Create a new team") +@click.argument( + "orgname_teamname", + metavar="ORGNAME:TEAMNAME", + callback=lambda _, __, value: validate_orgname_teamname( + value, teamname_validate=True + ), +) +@click.option( + "--description", +) +def team_create_cmd(orgname_teamname, description): + orgname, teamname = orgname_teamname.split(":", 1) + client = AccountClient() + client.create_team(orgname, teamname, description) + return click.secho( + "The team %s has been successfully created." % teamname, + fg="green", + ) diff --git a/platformio/account/team/commands/destroy.py b/platformio/account/team/commands/destroy.py new file mode 100644 index 00000000..7cd084a1 --- /dev/null +++ b/platformio/account/team/commands/destroy.py @@ -0,0 +1,40 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient +from platformio.account.validate import validate_orgname_teamname + + +@click.command("destroy", short_help="Destroy a team") +@click.argument( + "orgname_teamname", + metavar="ORGNAME:TEAMNAME", + callback=lambda _, __, value: validate_orgname_teamname(value), +) +def team_destroy_cmd(orgname_teamname): + orgname, teamname = orgname_teamname.split(":", 1) + click.confirm( + click.style( + "Are you sure you want to destroy the %s team?" % teamname, fg="yellow" + ), + abort=True, + ) + client = AccountClient() + client.destroy_team(orgname, teamname) + return click.secho( + "The team %s has been successfully destroyed." % teamname, + fg="green", + ) diff --git a/platformio/account/team/commands/list.py b/platformio/account/team/commands/list.py new file mode 100644 index 00000000..e41e872d --- /dev/null +++ b/platformio/account/team/commands/list.py @@ -0,0 +1,59 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 json + +import click +from tabulate import tabulate + +from platformio.account.client import AccountClient + + +@click.command("list", short_help="List teams") +@click.argument("orgname", required=False) +@click.option("--json-output", is_flag=True) +def team_list_cmd(orgname, json_output): + client = AccountClient() + data = {} + if not orgname: + for item in client.list_orgs(): + teams = client.list_teams(item.get("orgname")) + data[item.get("orgname")] = teams + else: + teams = client.list_teams(orgname) + data[orgname] = teams + if json_output: + return click.echo(json.dumps(data[orgname] if orgname else data)) + if not any(data.values()): + return click.secho("You do not have any teams.", fg="yellow") + for org_name in data: + for team in data[org_name]: + click.echo() + click.secho("%s:%s" % (org_name, team.get("name")), fg="cyan") + click.echo("-" * len("%s:%s" % (org_name, team.get("name")))) + table_data = [] + if team.get("description"): + table_data.append(("Description:", team.get("description"))) + table_data.append( + ( + "Members:", + ", ".join( + (member.get("username") for member in team.get("members")) + ) + if team.get("members") + else "-", + ) + ) + click.echo(tabulate(table_data, tablefmt="plain")) + return click.echo() diff --git a/platformio/account/team/commands/remove.py b/platformio/account/team/commands/remove.py new file mode 100644 index 00000000..73c79602 --- /dev/null +++ b/platformio/account/team/commands/remove.py @@ -0,0 +1,36 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient +from platformio.account.validate import validate_orgname_teamname + + +@click.command("remove", short_help="Remove a member from team") +@click.argument( + "orgname_teamname", + metavar="ORGNAME:TEAMNAME", + callback=lambda _, __, value: validate_orgname_teamname(value), +) +@click.argument("username") +def team_remove_cmd(orgname_teamname, username): + orgname, teamname = orgname_teamname.split(":", 1) + client = AccountClient() + client.remove_team_member(orgname, teamname, username) + return click.secho( + "The %s member has been successfully removed from the %s team." + % (username, teamname), + fg="green", + ) diff --git a/platformio/account/team/commands/update.py b/platformio/account/team/commands/update.py new file mode 100644 index 00000000..3ead0fed --- /dev/null +++ b/platformio/account/team/commands/update.py @@ -0,0 +1,55 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 click + +from platformio.account.client import AccountClient +from platformio.account.validate import validate_orgname_teamname, validate_teamname + + +@click.command("update", short_help="Update team") +@click.argument( + "orgname_teamname", + metavar="ORGNAME:TEAMNAME", + callback=lambda _, __, value: validate_orgname_teamname(value), +) +@click.option( + "--name", + callback=lambda _, __, value: validate_teamname(value), + help="A new team name", +) +@click.option( + "--description", +) +def team_update_cmd(orgname_teamname, **kwargs): + orgname, teamname = orgname_teamname.split(":", 1) + client = AccountClient() + team = client.get_team(orgname, teamname) + del team["id"] + del team["members"] + new_team = team.copy() + if not any(kwargs.values()): + for field in team: + new_team[field] = click.prompt( + field.replace("_", " ").capitalize(), default=team[field] + ) + if field == "name": + validate_teamname(new_team[field]) + else: + new_team.update({key: value for key, value in kwargs.items() if value}) + client.update_team(orgname, teamname, new_team) + return click.secho( + "The team %s has been successfully updated." % teamname, + fg="green", + ) diff --git a/platformio/account/validate.py b/platformio/account/validate.py new file mode 100644 index 00000000..69efb753 --- /dev/null +++ b/platformio/account/validate.py @@ -0,0 +1,79 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 re + +import click + + +def validate_username(value, field="username"): + value = str(value).strip() + if not re.match(r"^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,37}$", value, flags=re.I): + raise click.BadParameter( + "Invalid %s format. " + "%s must contain only alphanumeric characters " + "or single hyphens, cannot begin or end with a hyphen, " + "and must not be longer than 38 characters." + % (field.lower(), field.capitalize()) + ) + return value + + +def validate_email(value): + value = str(value).strip() + if not re.match(r"^[a-z\d_.+-]+@[a-z\d\-]+\.[a-z\d\-.]+$", value, flags=re.I): + raise click.BadParameter("Invalid email address") + return value + + +def validate_password(value): + value = str(value).strip() + if not re.match(r"^(?=.*[a-z])(?=.*\d).{8,}$", value): + raise click.BadParameter( + "Invalid password format. " + "Password must contain at least 8 characters" + " including a number and a lowercase letter" + ) + return value + + +def validate_orgname(value): + return validate_username(value, "Organization name") + + +def validate_orgname_teamname(value, teamname_validate=False): + if ":" not in value: + raise click.BadParameter( + "Please specify organization and team name in the next" + " format - orgname:teamname. For example, mycompany:DreamTeam" + ) + teamname = str(value.strip().split(":", 1)[1]) + if teamname_validate: + validate_teamname(teamname) + return value + + +def validate_teamname(value): + if not value: + return value + value = str(value).strip() + if not re.match(r"^[a-z\d](?:[a-z\d]|[\-_ ](?=[a-z\d])){0,19}$", value, flags=re.I): + raise click.BadParameter( + "Invalid team name format. " + "Team name must only contain alphanumeric characters, " + "single hyphens, underscores, spaces. It can not " + "begin or end with a hyphen or a underscore and must" + " not be longer than 20 characters." + ) + return value diff --git a/platformio/commands/team.py b/platformio/commands/team.py deleted file mode 100644 index aac68d1e..00000000 --- a/platformio/commands/team.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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 json -import re - -import click -from tabulate import tabulate - -from platformio.account.client import AccountClient - - -def validate_orgname_teamname(value, teamname_validate=False): - if ":" not in value: - raise click.BadParameter( - "Please specify organization and team name in the next" - " format - orgname:teamname. For example, mycompany:DreamTeam" - ) - teamname = str(value.strip().split(":", 1)[1]) - if teamname_validate: - validate_teamname(teamname) - return value - - -def validate_teamname(value): - if not value: - return value - value = str(value).strip() - if not re.match(r"^[a-z\d](?:[a-z\d]|[\-_ ](?=[a-z\d])){0,19}$", value, flags=re.I): - raise click.BadParameter( - "Invalid team name format. " - "Team name must only contain alphanumeric characters, " - "single hyphens, underscores, spaces. It can not " - "begin or end with a hyphen or a underscore and must" - " not be longer than 20 characters." - ) - return value - - -@click.group("team", short_help="Manage organization teams") -def cli(): - pass - - -@cli.command("create", short_help="Create a new team") -@click.argument( - "orgname_teamname", - metavar="ORGNAME:TEAMNAME", - callback=lambda _, __, value: validate_orgname_teamname( - value, teamname_validate=True - ), -) -@click.option( - "--description", -) -def team_create(orgname_teamname, description): - orgname, teamname = orgname_teamname.split(":", 1) - client = AccountClient() - client.create_team(orgname, teamname, description) - return click.secho( - "The team %s has been successfully created." % teamname, - fg="green", - ) - - -@cli.command("list", short_help="List teams") -@click.argument("orgname", required=False) -@click.option("--json-output", is_flag=True) -def team_list(orgname, json_output): - client = AccountClient() - data = {} - if not orgname: - for item in client.list_orgs(): - teams = client.list_teams(item.get("orgname")) - data[item.get("orgname")] = teams - else: - teams = client.list_teams(orgname) - data[orgname] = teams - if json_output: - return click.echo(json.dumps(data[orgname] if orgname else data)) - if not any(data.values()): - return click.secho("You do not have any teams.", fg="yellow") - for org_name in data: - for team in data[org_name]: - click.echo() - click.secho("%s:%s" % (org_name, team.get("name")), fg="cyan") - click.echo("-" * len("%s:%s" % (org_name, team.get("name")))) - table_data = [] - if team.get("description"): - table_data.append(("Description:", team.get("description"))) - table_data.append( - ( - "Members:", - ", ".join( - (member.get("username") for member in team.get("members")) - ) - if team.get("members") - else "-", - ) - ) - click.echo(tabulate(table_data, tablefmt="plain")) - return click.echo() - - -@cli.command("update", short_help="Update team") -@click.argument( - "orgname_teamname", - metavar="ORGNAME:TEAMNAME", - callback=lambda _, __, value: validate_orgname_teamname(value), -) -@click.option( - "--name", - callback=lambda _, __, value: validate_teamname(value), - help="A new team name", -) -@click.option( - "--description", -) -def team_update(orgname_teamname, **kwargs): - orgname, teamname = orgname_teamname.split(":", 1) - client = AccountClient() - team = client.get_team(orgname, teamname) - del team["id"] - del team["members"] - new_team = team.copy() - if not any(kwargs.values()): - for field in team: - new_team[field] = click.prompt( - field.replace("_", " ").capitalize(), default=team[field] - ) - if field == "name": - validate_teamname(new_team[field]) - else: - new_team.update({key: value for key, value in kwargs.items() if value}) - client.update_team(orgname, teamname, new_team) - return click.secho( - "The team %s has been successfully updated." % teamname, - fg="green", - ) - - -@cli.command("destroy", short_help="Destroy a team") -@click.argument( - "orgname_teamname", - metavar="ORGNAME:TEAMNAME", - callback=lambda _, __, value: validate_orgname_teamname(value), -) -def team_destroy(orgname_teamname): - orgname, teamname = orgname_teamname.split(":", 1) - click.confirm( - click.style( - "Are you sure you want to destroy the %s team?" % teamname, fg="yellow" - ), - abort=True, - ) - client = AccountClient() - client.destroy_team(orgname, teamname) - return click.secho( - "The team %s has been successfully destroyed." % teamname, - fg="green", - ) - - -@cli.command("add", short_help="Add a new member to team") -@click.argument( - "orgname_teamname", - metavar="ORGNAME:TEAMNAME", - callback=lambda _, __, value: validate_orgname_teamname(value), -) -@click.argument( - "username", -) -def team_add_member(orgname_teamname, username): - orgname, teamname = orgname_teamname.split(":", 1) - client = AccountClient() - client.add_team_member(orgname, teamname, username) - return click.secho( - "The new member %s has been successfully added to the %s team." - % (username, teamname), - fg="green", - ) - - -@cli.command("remove", short_help="Remove a member from team") -@click.argument( - "orgname_teamname", - metavar="ORGNAME:TEAMNAME", - callback=lambda _, __, value: validate_orgname_teamname(value), -) -@click.argument("username") -def team_remove_owner(orgname_teamname, username): - orgname, teamname = orgname_teamname.split(":", 1) - client = AccountClient() - client.remove_team_member(orgname, teamname, username) - return click.secho( - "The %s member has been successfully removed from the %s team." - % (username, teamname), - fg="green", - ) diff --git a/platformio/registry/access/validate.py b/platformio/registry/access/validate.py index 20aae07e..71ca5a92 100644 --- a/platformio/registry/access/validate.py +++ b/platformio/registry/access/validate.py @@ -16,8 +16,7 @@ import re import click -from platformio.account.validate import validate_username -from platformio.commands.team import validate_orgname_teamname +from platformio.account.validate import validate_orgname_teamname, validate_username def validate_urn(value): diff --git a/tests/commands/test_account_org_team.py b/tests/commands/test_account_org_team.py index eb0c3edf..4f23ff9b 100644 --- a/tests/commands/test_account_org_team.py +++ b/tests/commands/test_account_org_team.py @@ -23,7 +23,7 @@ import requests from platformio.account.cli import cli as cmd_account from platformio.account.org.cli import cli as cmd_org -from platformio.commands.team import cli as cmd_team +from platformio.account.team.cli import cli as cmd_team pytestmark = pytest.mark.skipif( not all( From cc3ea65faaeaacc00496512fa3570cad77ded8a2 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 13:49:52 +0300 Subject: [PATCH 72/74] Fix release branch name --- .github/workflows/deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 0cee3cf6..d961da8c 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -4,7 +4,7 @@ on: push: branches: - master - - "releases/**" + - "release/**" jobs: deployment: From b02335a294b673622a97377bc66dbfcdfb3e7bbf Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 14:11:10 +0300 Subject: [PATCH 73/74] Allow to implement own "FindInoNodes" method // Resolve #4297 --- platformio/builder/tools/pioino.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/platformio/builder/tools/pioino.py b/platformio/builder/tools/pioino.py index 7a95da9b..0c1f59a2 100644 --- a/platformio/builder/tools/pioino.py +++ b/platformio/builder/tools/pioino.py @@ -225,11 +225,15 @@ class InoToCPPConverter(object): return "\n".join(result) -def ConvertInoToCpp(env): +def FindInoNodes(env): src_dir = glob.escape(env.subst("$PROJECT_SRC_DIR")) - ino_nodes = env.Glob(os.path.join(src_dir, "*.ino")) + env.Glob( + return env.Glob(os.path.join(src_dir, "*.ino")) + env.Glob( os.path.join(src_dir, "*.pde") ) + + +def ConvertInoToCpp(env): + ino_nodes = env.FindInoNodes() if not ino_nodes: return c = InoToCPPConverter(env) @@ -247,6 +251,7 @@ def _delete_file(path): def generate(env): + env.AddMethod(FindInoNodes) env.AddMethod(ConvertInoToCpp) From 4ff591bd7e19e53af95dc924940187ce806d34c3 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 1 Jun 2022 15:55:42 +0300 Subject: [PATCH 74/74] Bump version to 6.0.2 --- HISTORY.rst | 2 +- docs | 2 +- examples | 2 +- platformio/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index b6bb65ca..e37671e5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -13,7 +13,7 @@ PlatformIO Core 6 **A professional collaborative platform for declarative, safety-critical, and test-driven embedded development.** -6.0.2 (2022-??-??) +6.0.2 (2022-06-01) ~~~~~~~~~~~~~~~~~~ * Control |UNITTESTING| verbosity with a new multilevel `pio test -v `__ command option (`issue #4276 `_) diff --git a/docs b/docs index 24524cad..300060ea 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 24524cad974ff59a8e0b59cdbb1c058dea12617e +Subproject commit 300060ea08be494465b03b427186bee66eda1766 diff --git a/examples b/examples index 3ddfa997..6c52fd32 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit 3ddfa9975ccbf1909f88dd8dd04e424a7590a94c +Subproject commit 6c52fd327753f2ca14b575bd8719674b479e1181 diff --git a/platformio/__init__.py b/platformio/__init__.py index 1a12e8df..7fe4bec7 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -14,7 +14,7 @@ import sys -VERSION = (6, 0, "2rc2") +VERSION = (6, 0, 2) __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio"