Skip to content

Commit 33e8da3

Browse files
author
rosch100
committed
Add supported_languages field to hacs.json schema and validation
- Add supported_languages field to HacsManifest class - Add _supported_languages_validator to validate ISO 639-1 language codes - Add supported_languages to HACS_MANIFEST_JSON_SCHEMA - Add validation in hacsjson.py to check if declared README files exist - Add runtime check in async_get_info_file_contents_with_language to only use declared languages - Fixes review comments from ludeeus
1 parent daa7e8d commit 33e8da3

2 files changed

Lines changed: 92 additions & 44 deletions

File tree

custom_components/hacs/repositories/base.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ class HacsManifest:
228228
name: str = None
229229
persistent_directory: str = None
230230
render_readme: bool = False
231+
supported_languages: list[str] = [] # Supported README languages (e.g., ["de", "fr", "es"])
231232
zip_release: bool = False
232233

233234
def to_dict(self):
@@ -250,6 +251,8 @@ def from_dict(manifest: dict):
250251
for key, value in manifest_data.manifest.items():
251252
if key == "country" and isinstance(value, str):
252253
setattr(manifest_data, key, [value])
254+
elif key == "supported_languages" and isinstance(value, str):
255+
setattr(manifest_data, key, [value])
253256
elif key in manifest_data.__dict__:
254257
setattr(manifest_data, key, value)
255258
return manifest_data
@@ -265,6 +268,11 @@ def update_data(self, data: dict) -> None:
265268
setattr(self, key, [value])
266269
else:
267270
setattr(self, key, value)
271+
elif key == "supported_languages":
272+
if isinstance(value, str):
273+
setattr(self, key, [value])
274+
else:
275+
setattr(self, key, value)
268276
else:
269277
setattr(self, key, value)
270278

@@ -771,6 +779,19 @@ async def async_get_info_file_contents_with_language(
771779
language = None
772780
else:
773781
language = language.lower()
782+
783+
# Check if language is declared in supported_languages
784+
if (
785+
self.repository_manifest.supported_languages
786+
and language not in self.repository_manifest.supported_languages
787+
):
788+
self.logger.debug(
789+
"%s Language '%s' not in supported_languages %s, using README.md",
790+
self.string,
791+
language,
792+
self.repository_manifest.supported_languages,
793+
)
794+
language = None
774795

775796
# If no language or English, use standard README
776797
if not language or language == "en":
Lines changed: 71 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,71 @@
1-
from __future__ import annotations
2-
3-
from voluptuous.error import Invalid
4-
from voluptuous.humanize import humanize_error
5-
6-
from ..enums import HacsCategory, RepositoryFile
7-
from ..repositories.base import HacsManifest, HacsRepository
8-
from ..utils.validate import HACS_MANIFEST_JSON_SCHEMA
9-
from .base import ActionValidationBase, ValidationException
10-
11-
12-
async def async_setup_validator(repository: HacsRepository) -> Validator:
13-
"""Set up this validator."""
14-
return Validator(repository=repository)
15-
16-
17-
class Validator(ActionValidationBase):
18-
"""Validate the repository."""
19-
20-
more_info = "https://hacs.xyz/docs/publish/include#check-hacs-manifest"
21-
22-
async def async_validate(self) -> None:
23-
"""Validate the repository."""
24-
if RepositoryFile.HACS_JSON not in [x.filename for x in self.repository.tree]:
25-
raise ValidationException(f"The repository has no '{RepositoryFile.HACS_JSON}' file")
26-
27-
rawhacsjson = await self.repository.get_hacs_json_raw(version=self.repository.ref)
28-
if rawhacsjson is None:
29-
raise ValidationException(
30-
f"The repository has an invalid '{RepositoryFile.HACS_JSON}' file"
31-
)
32-
33-
try:
34-
hacsjson = HacsManifest.from_dict(HACS_MANIFEST_JSON_SCHEMA(rawhacsjson))
35-
except Invalid as exception:
36-
self.repository.logger.warning(
37-
"HACS JSON validation failed for: %s",
38-
rawhacsjson,
39-
)
40-
raise ValidationException(humanize_error(rawhacsjson, exception)) from exception
41-
42-
if self.repository.data.category == HacsCategory.INTEGRATION:
43-
if hacsjson.zip_release and not hacsjson.filename:
44-
raise ValidationException("zip_release is True, but filename is not set")
1+
from __future__ import annotations
2+
3+
from voluptuous.error import Invalid
4+
from voluptuous.humanize import humanize_error
5+
6+
from ..enums import HacsCategory, RepositoryFile
7+
from ..repositories.base import HacsManifest, HacsRepository
8+
from ..utils.validate import HACS_MANIFEST_JSON_SCHEMA
9+
from .base import ActionValidationBase, ValidationException
10+
11+
12+
async def async_setup_validator(repository: HacsRepository) -> Validator:
13+
"""Set up this validator."""
14+
return Validator(repository=repository)
15+
16+
17+
class Validator(ActionValidationBase):
18+
"""Validate the repository."""
19+
20+
more_info = "https://hacs.xyz/docs/publish/include#check-hacs-manifest"
21+
22+
async def async_validate(self) -> None:
23+
"""Validate the repository."""
24+
if RepositoryFile.HACS_JSON not in [x.filename for x in self.repository.tree]:
25+
raise ValidationException(f"The repository has no '{RepositoryFile.HACS_JSON}' file")
26+
27+
rawhacsjson = await self.repository.get_hacs_json_raw(version=self.repository.ref)
28+
if rawhacsjson is None:
29+
raise ValidationException(
30+
f"The repository has an invalid '{RepositoryFile.HACS_JSON}' file"
31+
)
32+
33+
try:
34+
hacsjson = HacsManifest.from_dict(HACS_MANIFEST_JSON_SCHEMA(rawhacsjson))
35+
except Invalid as exception:
36+
self.repository.logger.warning(
37+
"HACS JSON validation failed for: %s",
38+
rawhacsjson,
39+
)
40+
raise ValidationException(humanize_error(rawhacsjson, exception)) from exception
41+
42+
if self.repository.data.category == HacsCategory.INTEGRATION:
43+
if hacsjson.zip_release and not hacsjson.filename:
44+
raise ValidationException("zip_release is True, but filename is not set")
45+
46+
# Validate supported_languages if provided
47+
if hacsjson.supported_languages:
48+
# Check if README files for declared languages exist
49+
tree_files = [x.filename for x in self.repository.tree]
50+
missing_readmes = []
51+
for lang in hacsjson.supported_languages:
52+
readme_path = f"README.{lang}.md"
53+
# Check various case combinations
54+
found = False
55+
for possible_path in [
56+
readme_path,
57+
f"README.{lang.upper()}.md",
58+
f"readme.{lang}.md",
59+
f"readme.{lang.upper()}.md",
60+
]:
61+
if possible_path in tree_files:
62+
found = True
63+
break
64+
if not found:
65+
missing_readmes.append(lang)
66+
67+
if missing_readmes:
68+
raise ValidationException(
69+
f"supported_languages declares languages {missing_readmes}, "
70+
f"but corresponding README files (README.{{lang}}.md) were not found in the repository."
71+
)

0 commit comments

Comments
 (0)