From e8d6f2aaf24fe865616045da763b59f44ef27ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Fri, 19 Jul 2024 18:02:59 +0200 Subject: [PATCH] Add handler to use raw URL for manifest.json in validation (#3903) --- .../hacs/repositories/integration.py | 33 +++++++++++++++++-- .../hacs/validate/integration_manifest.py | 2 +- tests/action/test_hacs_action_integration.py | 8 ++--- .../bad_documentation.log | 1 + .../bad_issue_tracker.log | 1 + .../valid_manifest1.log | 1 + ...ion-bad-documentation-manifest0-false.json | 5 +-- ...ion-bad-issue-tracker-manifest1-false.json | 5 +-- ...ration-valid-manifest1-manifest2-true.json | 5 +-- .../test_integration_manifest_check.py | 8 ++--- 10 files changed, 49 insertions(+), 20 deletions(-) diff --git a/custom_components/hacs/repositories/integration.py b/custom_components/hacs/repositories/integration.py index 0d75fc8b255..8456ce697d9 100644 --- a/custom_components/hacs/repositories/integration.py +++ b/custom_components/hacs/repositories/integration.py @@ -86,7 +86,8 @@ async def validate_repository(self): ): raise AddonRepositoryException() raise HacsException( - f"{self.string} Repository structure for {self.ref.replace('tags/','')} is not compliant" + f"{self.string} Repository structure for { + self.ref.replace('tags/', '')} is not compliant" ) self.content.path.remote = f"custom_components/{name}" @@ -101,7 +102,8 @@ async def validate_repository(self): except KeyError as exception: self.validate.errors.append( - f"Missing expected key '{exception}' in { RepositoryFile.MAINIFEST_JSON}" + f"Missing expected key '{exception}' in { + RepositoryFile.MAINIFEST_JSON}" ) self.hacs.log.error( "Missing expected key '%s' in '%s'", exception, RepositoryFile.MAINIFEST_JSON @@ -141,7 +143,8 @@ async def update_repository(self, ignore_issues=False, force=False): except KeyError as exception: self.validate.errors.append( - f"Missing expected key '{exception}' in { RepositoryFile.MAINIFEST_JSON}" + f"Missing expected key '{exception}' in { + RepositoryFile.MAINIFEST_JSON}" ) self.hacs.log.error( "Missing expected key '%s' in '%s'", exception, RepositoryFile.MAINIFEST_JSON @@ -188,3 +191,27 @@ async def async_get_integration_manifest(self, ref: str = None) -> dict[str, Any ) if response: return json_loads(decode_content(response.data.content)) + + async def get_integration_manifest(self, *, version: str, **kwargs) -> dict[str, Any] | None: + """Get the content of the manifest.json file.""" + manifest_path = ( + "manifest.json" + if self.repository_manifest.content_in_root + else f"{self.content.path.remote}/{RepositoryFile.MAINIFEST_JSON}" + ) + + if manifest_path not in (x.full_path for x in self.tree): + raise HacsException(f"No {RepositoryFile.MAINIFEST_JSON} file found '{manifest_path}'") + + self.logger.debug("%s Getting manifest.json for version=%s", self.string, version) + try: + result = await self.hacs.async_download_file( + f"https://raw.githubusercontent.com/{ + self.data.full_name}/{version}/{manifest_path}", + nolog=True, + ) + if result is None: + return None + return json_loads(result) + except Exception: # pylint: disable=broad-except + return None diff --git a/custom_components/hacs/validate/integration_manifest.py b/custom_components/hacs/validate/integration_manifest.py index f8bd7366b15..edd54b105b1 100644 --- a/custom_components/hacs/validate/integration_manifest.py +++ b/custom_components/hacs/validate/integration_manifest.py @@ -32,7 +32,7 @@ async def async_validate(self) -> None: f"The repository has no '{RepositoryFile.MAINIFEST_JSON}' file" ) - content = await self.repository.async_get_integration_manifest(self.repository.ref) + content = await self.repository.get_integration_manifest(version=self.repository.ref) try: INTEGRATION_MANIFEST_JSON_SCHEMA(content) except Invalid as exception: diff --git a/tests/action/test_hacs_action_integration.py b/tests/action/test_hacs_action_integration.py index a54cdd94d54..a0da654247e 100644 --- a/tests/action/test_hacs_action_integration.py +++ b/tests/action/test_hacs_action_integration.py @@ -46,14 +46,10 @@ async def test_hacs_action_integration( response=MockedResponse(status=200, content={"custom": ["example"]}), ) response_mocker.add( - "https://api.github.com/repos/hacs-test-org/integration-basic/contents/custom_components/example/manifest.json", + "https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json", response=MockedResponse( status=200, - content={ - "content": base64.b64encode( - json.dumps({**basemanifest, **manifest}).encode("ascii"), - ).decode("ascii"), - }, + content=json.dumps({**basemanifest, **manifest}), keep=True, ), ) 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 8bf908c3e2c..dceb43d407d 100644 --- a/tests/snapshots/action/test_hacs_action_integration/bad_documentation.log +++ b/tests/snapshots/action/test_hacs_action_integration/bad_documentation.log @@ -1,5 +1,6 @@ Checking repository. Running checks against main + Getting manifest.json for version=main completed completed completed 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 89a53e77c72..af45c46f0c3 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 @@ -1,5 +1,6 @@ Checking repository. Running checks against main + Getting manifest.json for version=main completed completed completed diff --git a/tests/snapshots/action/test_hacs_action_integration/valid_manifest1.log b/tests/snapshots/action/test_hacs_action_integration/valid_manifest1.log index 7b813f6428a..cef67a4ffed 100644 --- a/tests/snapshots/action/test_hacs_action_integration/valid_manifest1.log +++ b/tests/snapshots/action/test_hacs_action_integration/valid_manifest1.log @@ -1,5 +1,6 @@ Checking repository. Running checks against main + Getting manifest.json for version=main completed completed completed diff --git a/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-bad-documentation-manifest0-false.json b/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-bad-documentation-manifest0-false.json index 9e087da6471..cdc0d4b8d50 100644 --- a/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-bad-documentation-manifest0-false.json +++ b/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-bad-documentation-manifest0-false.json @@ -1,10 +1,11 @@ { "tests/action/test_hacs_action_integration.py::test_hacs_action_integration[bad_documentation-manifest0-False]": { "https://api.github.com/repos/hacs-test-org/integration-basic": 2, - "https://api.github.com/repos/hacs-test-org/integration-basic/contents/custom_components/example/manifest.json": 2, + "https://api.github.com/repos/hacs-test-org/integration-basic/contents/custom_components/example/manifest.json": 1, "https://api.github.com/repos/hacs-test-org/integration-basic/contents/hacs.json": 2, "https://api.github.com/repos/hacs-test-org/integration-basic/git/trees/main": 1, "https://api.github.com/repos/hacs-test-org/integration-basic/releases": 1, - "https://brands.home-assistant.io/domains.json": 1 + "https://brands.home-assistant.io/domains.json": 1, + "https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json": 1 } } \ No newline at end of file diff --git a/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-bad-issue-tracker-manifest1-false.json b/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-bad-issue-tracker-manifest1-false.json index 36361818284..da786f3712e 100644 --- a/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-bad-issue-tracker-manifest1-false.json +++ b/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-bad-issue-tracker-manifest1-false.json @@ -1,10 +1,11 @@ { "tests/action/test_hacs_action_integration.py::test_hacs_action_integration[bad_issue_tracker-manifest1-False]": { "https://api.github.com/repos/hacs-test-org/integration-basic": 2, - "https://api.github.com/repos/hacs-test-org/integration-basic/contents/custom_components/example/manifest.json": 2, + "https://api.github.com/repos/hacs-test-org/integration-basic/contents/custom_components/example/manifest.json": 1, "https://api.github.com/repos/hacs-test-org/integration-basic/contents/hacs.json": 2, "https://api.github.com/repos/hacs-test-org/integration-basic/git/trees/main": 1, "https://api.github.com/repos/hacs-test-org/integration-basic/releases": 1, - "https://brands.home-assistant.io/domains.json": 1 + "https://brands.home-assistant.io/domains.json": 1, + "https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json": 1 } } \ No newline at end of file diff --git a/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-valid-manifest1-manifest2-true.json b/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-valid-manifest1-manifest2-true.json index 78150cceb34..4ec19eb0e2f 100644 --- a/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-valid-manifest1-manifest2-true.json +++ b/tests/snapshots/api-usage/tests/action/test_hacs_action_integrationtest-hacs-action-integration-valid-manifest1-manifest2-true.json @@ -1,10 +1,11 @@ { "tests/action/test_hacs_action_integration.py::test_hacs_action_integration[valid_manifest1-manifest2-True]": { "https://api.github.com/repos/hacs-test-org/integration-basic": 2, - "https://api.github.com/repos/hacs-test-org/integration-basic/contents/custom_components/example/manifest.json": 2, + "https://api.github.com/repos/hacs-test-org/integration-basic/contents/custom_components/example/manifest.json": 1, "https://api.github.com/repos/hacs-test-org/integration-basic/contents/hacs.json": 2, "https://api.github.com/repos/hacs-test-org/integration-basic/git/trees/main": 1, "https://api.github.com/repos/hacs-test-org/integration-basic/releases": 1, - "https://brands.home-assistant.io/domains.json": 1 + "https://brands.home-assistant.io/domains.json": 1, + "https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json": 1 } } \ No newline at end of file diff --git a/tests/validate/test_integration_manifest_check.py b/tests/validate/test_integration_manifest_check.py index 20e78d804b1..d881972bc7c 100644 --- a/tests/validate/test_integration_manifest_check.py +++ b/tests/validate/test_integration_manifest_check.py @@ -16,7 +16,7 @@ async def test_integration_manifest_with_valid_manifest(repository_integration): ), ] - async def _async_get_integration_manifest(_): + async def _async_get_integration_manifest(**__): return { "domain": "test", "documentation": "https://hacs.xyz", @@ -26,7 +26,7 @@ async def _async_get_integration_manifest(_): "version": "1.0.0", } - repository_integration.async_get_integration_manifest = _async_get_integration_manifest + repository_integration.get_integration_manifest = _async_get_integration_manifest check = Validator(repository_integration) await check.execute_validation() @@ -40,10 +40,10 @@ async def test_hacs_manifest_with_invalid_manifest(repository_integration): ), ] - async def _async_get_integration_manifest(_): + async def _async_get_integration_manifest(**__): return {"not": "valid"} - repository_integration.async_get_integration_manifest = _async_get_integration_manifest + repository_integration.get_integration_manifest = _async_get_integration_manifest check = Validator(repository_integration) await check.execute_validation() assert check.failed