diff --git a/custom_components/hacs/validate/license.py b/custom_components/hacs/validate/license.py new file mode 100644 index 00000000000..b035041be25 --- /dev/null +++ b/custom_components/hacs/validate/license.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .base import ActionValidationBase, ValidationException + +if TYPE_CHECKING: + from ..repositories.base import HacsRepository + + +async def async_setup_validator(repository: HacsRepository) -> Validator: + """Set up this validator.""" + return Validator(repository=repository) + + +class Validator(ActionValidationBase): + """Validate the repository.""" + + more_info = "https://hacs.xyz/docs/publish/include#check-repository" + allow_fork = False + + async def async_validate(self) -> None: + """Validate the repository.""" + if (license_info := self.repository.repository_object.attributes.get("license")) is None: + raise ValidationException("The repository has no license") + if license_info.get("key") == "other": + raise ValidationException( + "The repository has no recognized license " + f"(license name is '{license_info.get('name', 'unknown')}')" + ) + self.repository.logger.debug( + "The repository has a valid license: %s", license_info.get("name") + ) diff --git a/tests/action/test_hacs_action_integration.py b/tests/action/test_hacs_action_integration.py index 3ab9d6328a7..355c7496717 100644 --- a/tests/action/test_hacs_action_integration.py +++ b/tests/action/test_hacs_action_integration.py @@ -110,7 +110,7 @@ async def test_hacs_action_integration( await preflight() assert ( - "All (8) checks passed" if test_case["succeed"] else "1/8 checks failed") in caplog.text + "All (9) checks passed" if test_case["succeed"] else "1/9 checks failed") in caplog.text splitlines = [f"<{line.rsplit(' <')[1]}" for line in caplog.text.split( "\n") if " <" in line] diff --git a/tests/snapshots/action/test_hacs_action_integration/bad_documentation.log b/tests/snapshots/action/test_hacs_action_integration/bad_documentation.log index cf1bdadbd25..e20a2b2732b 100644 --- a/tests/snapshots/action/test_hacs_action_integration/bad_documentation.log +++ b/tests/snapshots/action/test_hacs_action_integration/bad_documentation.log @@ -10,8 +10,9 @@ completed failed: invalid url for dictionary value @ data['documentation']. Got None (More info: https://hacs.xyz/docs/publish/include#check-manifest ) completed + completed completed - 1/8 checks failed + 1/9 checks failed Validation completed ::group::data { diff --git a/tests/snapshots/action/test_hacs_action_integration/bad_issue_tracker.log b/tests/snapshots/action/test_hacs_action_integration/bad_issue_tracker.log index 982759ef7fb..e2a3458a234 100644 --- a/tests/snapshots/action/test_hacs_action_integration/bad_issue_tracker.log +++ b/tests/snapshots/action/test_hacs_action_integration/bad_issue_tracker.log @@ -10,8 +10,9 @@ completed failed: invalid url for dictionary value @ data['issue_tracker']. Got None (More info: https://hacs.xyz/docs/publish/include#check-manifest ) completed + completed completed - 1/8 checks failed + 1/9 checks failed Validation completed ::group::data { diff --git a/tests/snapshots/action/test_hacs_action_integration/no_releases.log b/tests/snapshots/action/test_hacs_action_integration/no_releases.log index 3da59bf0192..c65f466cb27 100644 --- a/tests/snapshots/action/test_hacs_action_integration/no_releases.log +++ b/tests/snapshots/action/test_hacs_action_integration/no_releases.log @@ -10,8 +10,9 @@ completed completed completed + completed completed - All (8) checks passed + All (9) checks passed Validation completed ::group::data { diff --git a/tests/snapshots/action/test_hacs_action_integration/releases_without_assets.log b/tests/snapshots/action/test_hacs_action_integration/releases_without_assets.log index 0d9730ccee5..2ed0b2175f7 100644 --- a/tests/snapshots/action/test_hacs_action_integration/releases_without_assets.log +++ b/tests/snapshots/action/test_hacs_action_integration/releases_without_assets.log @@ -10,8 +10,9 @@ completed completed completed + completed completed - All (8) checks passed + All (9) checks passed Validation completed ::group::data { diff --git a/tests/snapshots/action/test_hacs_action_integration/valid_manifest.log b/tests/snapshots/action/test_hacs_action_integration/valid_manifest.log index f074c070bb7..3647ca91f23 100644 --- a/tests/snapshots/action/test_hacs_action_integration/valid_manifest.log +++ b/tests/snapshots/action/test_hacs_action_integration/valid_manifest.log @@ -10,8 +10,9 @@ completed completed completed + completed completed - All (8) checks passed + All (9) checks passed Validation completed ::group::data { diff --git a/tests/snapshots/api-usage/tests/validate/test_repository_license_checktest-repository-no-license.json b/tests/snapshots/api-usage/tests/validate/test_repository_license_checktest-repository-no-license.json new file mode 100644 index 00000000000..d01d012a081 --- /dev/null +++ b/tests/snapshots/api-usage/tests/validate/test_repository_license_checktest-repository-no-license.json @@ -0,0 +1,9 @@ +{ + "tests/validate/test_repository_license_check.py::test_repository_no_license": { + "https://api.github.com/repos/hacs/integration": 1, + "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, + "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, + "https://api.github.com/repos/hacs/integration/git/trees/main": 1, + "https://api.github.com/repos/hacs/integration/releases": 1 + } +} \ No newline at end of file diff --git a/tests/snapshots/api-usage/tests/validate/test_repository_license_checktest-repository-unrecognized-license.json b/tests/snapshots/api-usage/tests/validate/test_repository_license_checktest-repository-unrecognized-license.json new file mode 100644 index 00000000000..02c2ca6b36c --- /dev/null +++ b/tests/snapshots/api-usage/tests/validate/test_repository_license_checktest-repository-unrecognized-license.json @@ -0,0 +1,9 @@ +{ + "tests/validate/test_repository_license_check.py::test_repository_unrecognized_license": { + "https://api.github.com/repos/hacs/integration": 1, + "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, + "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, + "https://api.github.com/repos/hacs/integration/git/trees/main": 1, + "https://api.github.com/repos/hacs/integration/releases": 1 + } +} \ No newline at end of file diff --git a/tests/snapshots/api-usage/tests/validate/test_repository_license_checktest-repository-valid-license.json b/tests/snapshots/api-usage/tests/validate/test_repository_license_checktest-repository-valid-license.json new file mode 100644 index 00000000000..819dcdf4d17 --- /dev/null +++ b/tests/snapshots/api-usage/tests/validate/test_repository_license_checktest-repository-valid-license.json @@ -0,0 +1,9 @@ +{ + "tests/validate/test_repository_license_check.py::test_repository_valid_license": { + "https://api.github.com/repos/hacs/integration": 1, + "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, + "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, + "https://api.github.com/repos/hacs/integration/git/trees/main": 1, + "https://api.github.com/repos/hacs/integration/releases": 1 + } +} \ No newline at end of file diff --git a/tests/validate/test_repository_license_check.py b/tests/validate/test_repository_license_check.py new file mode 100644 index 00000000000..9d7be7160b9 --- /dev/null +++ b/tests/validate/test_repository_license_check.py @@ -0,0 +1,31 @@ +from unittest.mock import MagicMock + +from custom_components.hacs.validate.license import Validator + + +async def test_repository_no_license(repository): + repository.repository_object = MagicMock() + repository.repository_object.attributes = {"license": None} + check = Validator(repository) + await check.execute_validation() + assert check.failed + + +async def test_repository_unrecognized_license(repository): + repository.repository_object = MagicMock() + repository.repository_object.attributes = { + "license": {"key": "other", "name": "Other", "spdx_id": "NOASSERTION"}, + } + check = Validator(repository) + await check.execute_validation() + assert check.failed + + +async def test_repository_valid_license(repository): + repository.repository_object = MagicMock() + repository.repository_object.attributes = { + "license": {"key": "mit", "name": "MIT License", "spdx_id": "MIT"}, + } + check = Validator(repository) + await check.execute_validation() + assert not check.failed