Skip to content

Commit

Permalink
Merge pull request #285 from per1234/cli-1-format
Browse files Browse the repository at this point in the history
Handle Arduino CLI 1.x `core list` command output data format
  • Loading branch information
per1234 authored Jun 13, 2024
2 parents b35a113 + 26705b3 commit 1bc7c20
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 75 deletions.
122 changes: 57 additions & 65 deletions compilesketches/compilesketches.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,15 +558,15 @@ def __init__(self):
self.run_arduino_cli_command(command=["core", "update-index"])
# Use Arduino CLI to get the list of installed platforms
command_data = self.run_arduino_cli_command(command=["core", "list", "--format", "json"])
installed_platform_list = json.loads(command_data.stdout)
installed_platform_list = self.cli_core_list_platform_list(json.loads(command_data.stdout))
for installed_platform in installed_platform_list:
if installed_platform[self.cli_json_key("core list", "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[self.cli_json_key("core list", "Installed")],
installed_platform[self.cli_json_key("core list", "installed_version")],
)
platform_installation_path.is_overwrite = True

Expand Down Expand Up @@ -1437,77 +1437,69 @@ def create_sketches_report_file(self, sketches_report):
) as report_file:
json.dump(obj=sketches_report, fp=report_file, indent=2)

def cli_json_key(self, command, original_key_name):
def cli_core_list_platform_list(self, data):
"""Extract the list of platform data from the `arduino-cli core list` command output according to the Arduino
CLI version in use.
Keyword arguments:
data -- Arduino CLI command output data
"""
# Interface was changed at this Arduino CLI release:
# https://arduino.github.io/arduino-cli/dev/UPGRADING/#cli-changed-json-output-for-some-lib-core-config-board-and-sketch-commands
first_new_interface_version = "1.0.0"

if (
not semver.VersionInfo.is_valid(version=self.cli_version)
or semver.Version.parse(version=self.cli_version).compare(other=first_new_interface_version) >= 0
):
# cli_version is either "latest" (which will now always be >=1.0.0) or an explicit version >=1.0.0
return data["platforms"]

return data

def cli_json_key(self, command, 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
key_name -- key name used by the current 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",
},
key_translations = {
"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"},
"id": [
{"constraints": [">=0.0.0", "<=0.17.0"], "name": "ID"},
# https://arduino.github.io/arduino-cli/dev/UPGRADING/#arduino-cli-json-output-breaking-changes
{"constraints": [">0.17.0"], "name": "id"},
],
"installed_version": [
{"constraints": [">=0.0.0", "<=0.17.0"], "name": "Installed"},
# https://arduino.github.io/arduino-cli/dev/UPGRADING/#arduino-cli-json-output-breaking-changes
{"constraints": [">0.17.0", "<1.0.0"], "name": "installed"},
# https://arduino.github.io/arduino-cli/dev/UPGRADING/#cli-core-list-and-core-search-changed-json-output
{"constraints": [">=1.0.0"], "name": "installed_version"},
],
}
}

if (
not semver.VersionInfo.is_valid(version=self.cli_version)
or semver.Version.parse(version=self.cli_version).compare(other=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]
if not semver.VersionInfo.is_valid(version=self.cli_version):
# cli_version is "latest", so use the current key name
return key_name

for translation in key_translations[command][key_name]:
match = True
for constraint in translation["constraints"]:
if not semver.Version.parse(version=self.cli_version).match(match_expr=constraint):
# The Arduino CLI version does not match the translation's version constraints
match = False
break

if match:
# The Arduino CLI version matches the translation's version constraints
return translation["name"]

return original_key_name
raise RuntimeError(
f"Translation not implemented for `{key_name}` key of `arduino-cli {command}` for version {self.cli_version}"
) # pragma: no cover


def parse_list_input(list_input):
Expand Down
33 changes: 23 additions & 10 deletions compilesketches/tests/test_compilesketches.py
Original file line number Diff line number Diff line change
Expand Up @@ -2884,20 +2884,33 @@ def test_create_sketches_report_file(monkeypatch, tmp_path):


@pytest.mark.parametrize(
"cli_version, command, original_key, expected_key",
"cli_version, data, assertion",
[
("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"),
("latest", {"platforms": [unittest.mock.sentinel.list_item]}, [unittest.mock.sentinel.list_item]), # Non-semver
("2.0.0", {"platforms": [unittest.mock.sentinel.list_item]}, [unittest.mock.sentinel.list_item]), # >
("1.0.0", {"platforms": [unittest.mock.sentinel.list_item]}, [unittest.mock.sentinel.list_item]), # ==
("0.1.2", [unittest.mock.sentinel.list_item], [unittest.mock.sentinel.list_item]), # <
],
) # Key has no translation
def test_cli_json_key(cli_version, command, original_key, expected_key):
)
def test_cli_core_list_platform_list(cli_version, data, assertion):
compile_sketches = get_compilesketches_object(cli_version=cli_version)

assert compile_sketches.cli_core_list_platform_list(data) == assertion


@pytest.mark.parametrize(
"cli_version, command, key_name, expected_key",
[
("latest", "core list", "installed_version", "installed_version"), # Non-semver
("0.1.2", "core list", "installed_version", "Installed"),
("0.17.1", "core list", "installed_version", "installed"),
("1.2.3", "core list", "installed_version", "installed_version"),
],
)
def test_cli_json_key(cli_version, command, key_name, expected_key):
compile_sketches = get_compilesketches_object(cli_version=cli_version)

assert compile_sketches.cli_json_key(command, original_key) == expected_key
assert compile_sketches.cli_json_key(command, key_name) == expected_key


@pytest.mark.parametrize("verbose", ["true", "false"])
Expand Down

0 comments on commit 1bc7c20

Please sign in to comment.