From 7c547785b8a540b78e1610147e318b5cd33c7593 Mon Sep 17 00:00:00 2001 From: per1234 Date: Wed, 31 Mar 2021 23:52:49 -0700 Subject: [PATCH] Support new Arduino CLI JSON output keys A breaking change was made to the key names of Arduino CLI's JSON output in its 0.18.0 release. This action was dependent on some of the keys that changed. Because the action has a `cli-version` input that allows it to be used with any Arduino CLI version, it is necessary to support both interface styles. Even though there is not currently extensive use of the interface, it seemed best to set up a translation system to support all usage of the interface rather than just doing a spot fix at the site of the current breakage. --- compilesketches/compilesketches.py | 98 ++++++++++++++++++- compilesketches/requirements.txt | 1 + compilesketches/tests/test_compilesketches.py | 15 ++- 3 files changed, 108 insertions(+), 6 deletions(-) diff --git a/compilesketches/compilesketches.py b/compilesketches/compilesketches.py index 678ae7b..5b8a81b 100644 --- a/compilesketches/compilesketches.py +++ b/compilesketches/compilesketches.py @@ -15,6 +15,7 @@ import git import gitdb.exc import github +import semver import yaml import yaml.parser @@ -527,13 +528,15 @@ def __init__(self): command_data = self.run_arduino_cli_command(command=["core", "list", "--format", "json"]) installed_platform_list = json.loads(command_data.stdout) for installed_platform in installed_platform_list: - if installed_platform["ID"] == platform[self.dependency_name_key]: + if installed_platform[self.cli_json_key("core list", "ID")] == platform[self.dependency_name_key]: # The platform has been installed via Board Manager, so do an overwrite platform_installation_path.path = ( - self.board_manager_platforms_path.joinpath(platform_vendor, - "hardware", - platform_architecture, - installed_platform["Installed"]) + self.board_manager_platforms_path.joinpath( + platform_vendor, + "hardware", + platform_architecture, + installed_platform[self.cli_json_key("core list", "Installed")] + ) ) platform_installation_path.is_overwrite = True @@ -1413,6 +1416,91 @@ def create_sketches_report_file(self, sketches_report): encoding="utf-8") as report_file: json.dump(obj=sketches_report, fp=report_file, indent=2) + def cli_json_key(self, command, original_key_name): + """Return the appropriate JSON output key name for the Arduino CLI version in use. + + Keyword arguments: + command -- Arduino CLI command (e.g., "core list") + original_key_name -- key name used by the original Arduino CLI JSON interface + """ + final_original_interface_version = "0.17.0" # Interface was changed in the next Arduino CLI release + + key_translation = { + "board details": { + "identification_pref": "identification_prefs", + "usbID": "usb_id", + "PID": "pid", + "VID": "vid", + "websiteURL": "website_url", + "archiveFileName": "archive_filename", + "propertiesId": "properties_id", + "toolsDependencies": "tools_dependencies" + }, + "board list": { + "FQBN": "fqbn", + "VID": "vid", + "PID": "pid" + }, + "board listall": { + "FQBN": "fqbn", + "Email": "email", + "ID": "id", + "Installed": "installed", + "Latest": "latest", + "Name": "name", + "Maintainer": "maintainer", + "Website": "website" + }, + "board search": { + "FQBN": "fqbn", + "Email": "email", + "ID": "id", + "Installed": "installed", + "Latest": "latest", + "Name": "name", + "Maintainer": "maintainer", + "Website": "website" + }, + "core list": { + "Boards": "boards", + "Email": "email", + "ID": "id", + "Installed": "installed", + "Latest": "latest", + "Maintainer": "maintainer", + "Name": "name", + "Website": "website" + }, + "core search": { + "Boards": "boards", + "Email": "email", + "ID": "id", + "Latest": "latest", + "Maintainer": "maintainer", + "Name": "name", + "Website": "website" + }, + "lib deps": { + "versionRequired": "version_required", + "versionInstalled": "version_installed" + }, + "lib search": { + "archivefilename": "archive_filename", + "cachepath": "cache_path" + } + } + + if ( + ( + not semver.VersionInfo.isvalid(version=self.cli_version) + or semver.compare(ver1=self.cli_version, ver2=final_original_interface_version) > 0 + ) + and (command in key_translation and original_key_name in key_translation[command]) + ): + return key_translation[command][original_key_name] + + return original_key_name + def parse_list_input(list_input): """Parse a space separated list and return the equivalent Python list diff --git a/compilesketches/requirements.txt b/compilesketches/requirements.txt index f3a619c..6b8da46 100644 --- a/compilesketches/requirements.txt +++ b/compilesketches/requirements.txt @@ -1,3 +1,4 @@ GitPython==3.1.2 PyGithub==1.51 PyYAML==5.3.1 +semver==2.13.0 diff --git a/compilesketches/tests/test_compilesketches.py b/compilesketches/tests/test_compilesketches.py index f9bebf7..e19b8e3 100644 --- a/compilesketches/tests/test_compilesketches.py +++ b/compilesketches/tests/test_compilesketches.py @@ -20,7 +20,7 @@ def get_compilesketches_object( - cli_version=unittest.mock.sentinel.cli_version, + cli_version="0.12.3", fqbn_arg="foo fqbn_arg", platforms="- name: FooVendor:BarArchitecture", libraries="foo libraries", @@ -2578,6 +2578,19 @@ def test_create_sketches_report_file(monkeypatch, tmp_path): assert json.load(sketch_report_file) == sketches_report +@pytest.mark.parametrize("cli_version, command, original_key, expected_key", + [("latest", "core list", "ID", "id"), # Non-semver + ("1.0.0", "core list", "ID", "id"), # > + ("0.17.0", "core list", "ID", "ID"), # == + ("0.14.0-rc2", "core list", "ID", "ID"), # < + ("1.0.0", "foo", "ID", "ID"), # Command has no translation + ("1.0.0", "core list", "foo", "foo")]) # Key has no translation +def test_cli_json_key(cli_version, command, original_key, expected_key): + compile_sketches = get_compilesketches_object(cli_version=cli_version) + + assert compile_sketches.cli_json_key(command, original_key) == expected_key + + @pytest.mark.parametrize("verbose", ["true", "false"]) def test_verbose_print(capsys, verbose): string_print_argument = "foo string argument"