mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 04:37:06 +00:00
Validate quality scale tiers against the tier declared in the integration manifest (#131286)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
This commit is contained in:
parent
0626b005e2
commit
96e67373db
@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import IntEnum, StrEnum, auto
|
||||
from enum import StrEnum, auto
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
@ -20,7 +20,7 @@ from voluptuous.humanize import humanize_error
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
from .model import Config, Integration
|
||||
from .model import Config, Integration, ScaledQualityScaleTiers
|
||||
|
||||
DOCUMENTATION_URL_SCHEMA = "https"
|
||||
DOCUMENTATION_URL_HOST = "www.home-assistant.io"
|
||||
@ -28,15 +28,6 @@ DOCUMENTATION_URL_PATH_PREFIX = "/integrations/"
|
||||
DOCUMENTATION_URL_EXCEPTIONS = {"https://www.home-assistant.io/hassio"}
|
||||
|
||||
|
||||
class ScaledQualityScaleTiers(IntEnum):
|
||||
"""Supported manifest quality scales."""
|
||||
|
||||
BRONZE = 1
|
||||
SILVER = 2
|
||||
GOLD = 3
|
||||
PLATINUM = 4
|
||||
|
||||
|
||||
class NonScaledQualityScaleTiers(StrEnum):
|
||||
"""Supported manifest quality scales."""
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import IntEnum
|
||||
import json
|
||||
import pathlib
|
||||
from typing import Any, Literal
|
||||
@ -230,3 +231,12 @@ class Integration:
|
||||
|
||||
self._manifest = manifest
|
||||
self.manifest_path = manifest_path
|
||||
|
||||
|
||||
class ScaledQualityScaleTiers(IntEnum):
|
||||
"""Supported manifest quality scales."""
|
||||
|
||||
BRONZE = 1
|
||||
SILVER = 2
|
||||
GOLD = 3
|
||||
PLATINUM = 4
|
||||
|
@ -9,62 +9,73 @@ from homeassistant.const import Platform
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.util.yaml import load_yaml_dict
|
||||
|
||||
from .model import Config, Integration
|
||||
from .model import Config, Integration, ScaledQualityScaleTiers
|
||||
|
||||
RULES = [
|
||||
"action-exceptions",
|
||||
"action-setup",
|
||||
"appropriate-polling",
|
||||
"async-dependency",
|
||||
"brands",
|
||||
"common-modules",
|
||||
"config-entry-unloading",
|
||||
"config-flow",
|
||||
"config-flow-test-coverage",
|
||||
"dependency-transparency",
|
||||
"devices",
|
||||
"diagnostics",
|
||||
"discovery",
|
||||
"discovery-update-info",
|
||||
"docs-actions",
|
||||
"docs-configuration-parameters",
|
||||
"docs-data-update",
|
||||
"docs-examples",
|
||||
"docs-high-level-description",
|
||||
"docs-installation-instructions",
|
||||
"docs-installation-parameters",
|
||||
"docs-known-limitations",
|
||||
"docs-removal-instructions",
|
||||
"docs-supported-devices",
|
||||
"docs-supported-functions",
|
||||
"docs-troubleshooting",
|
||||
"docs-use-cases",
|
||||
"dynamic-devices",
|
||||
"entity-category",
|
||||
"entity-device-class",
|
||||
"entity-disabled-by-default",
|
||||
"entity-event-setup",
|
||||
"entity-translations",
|
||||
"entity-unavailable",
|
||||
"entity-unique-id",
|
||||
"exception-translations",
|
||||
"has-entity-name",
|
||||
"icon-translations",
|
||||
"inject-websession",
|
||||
"integration-owner",
|
||||
"log-when-unavailable",
|
||||
"parallel-updates",
|
||||
"reauthentication-flow",
|
||||
"reconfiguration-flow",
|
||||
"repair-issues",
|
||||
"runtime-data",
|
||||
"stale-devices",
|
||||
"strict-typing",
|
||||
"test-before-configure",
|
||||
"test-before-setup",
|
||||
"test-coverage",
|
||||
"unique-config-entry",
|
||||
]
|
||||
QUALITY_SCALE_TIERS = {value.name.lower(): value for value in ScaledQualityScaleTiers}
|
||||
|
||||
RULES = {
|
||||
ScaledQualityScaleTiers.BRONZE: [
|
||||
"action-setup",
|
||||
"appropriate-polling",
|
||||
"brands",
|
||||
"common-modules",
|
||||
"config-flow",
|
||||
"config-flow-test-coverage",
|
||||
"dependency-transparency",
|
||||
"docs-actions",
|
||||
"docs-high-level-description",
|
||||
"docs-installation-parameters",
|
||||
"docs-installation-instructions",
|
||||
"docs-removal-instructions",
|
||||
"entity-event-setup",
|
||||
"entity-unique-id",
|
||||
"has-entity-name",
|
||||
"runtime-data",
|
||||
"test-before-configure",
|
||||
"test-before-setup",
|
||||
"unique-config-entry",
|
||||
],
|
||||
ScaledQualityScaleTiers.SILVER: [
|
||||
"action-exceptions",
|
||||
"config-entry-unloading",
|
||||
"docs-configuration-parameters",
|
||||
"docs-installation-parameters",
|
||||
"entity-unavailable",
|
||||
"integration-owner",
|
||||
"log-when-unavailable",
|
||||
"parallel-updates",
|
||||
"reauthentication-flow",
|
||||
"test-coverage",
|
||||
],
|
||||
ScaledQualityScaleTiers.GOLD: [
|
||||
"devices",
|
||||
"diagnostics",
|
||||
"discovery",
|
||||
"discovery-update-info",
|
||||
"docs-data-update",
|
||||
"docs-examples",
|
||||
"docs-known-limitations",
|
||||
"docs-supported-devices",
|
||||
"docs-supported-functions",
|
||||
"docs-troubleshooting",
|
||||
"docs-use-cases",
|
||||
"dynamic-devices",
|
||||
"entity-category",
|
||||
"entity-device-class",
|
||||
"entity-disabled-by-default",
|
||||
"entity-translations",
|
||||
"exception-translations",
|
||||
"icon-translations",
|
||||
"reconfiguration-flow",
|
||||
"repair-issues",
|
||||
"stale-devices",
|
||||
],
|
||||
ScaledQualityScaleTiers.PLATINUM: [
|
||||
"async-dependency",
|
||||
"inject-websession",
|
||||
"strict-typing",
|
||||
],
|
||||
}
|
||||
|
||||
INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [
|
||||
"abode",
|
||||
@ -1264,7 +1275,8 @@ SCHEMA = vol.Schema(
|
||||
}
|
||||
),
|
||||
)
|
||||
for rule in RULES
|
||||
for tier_list in RULES.values()
|
||||
for rule in tier_list
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -1275,6 +1287,9 @@ def validate_iqs_file(config: Config, integration: Integration) -> None:
|
||||
"""Validate quality scale file for integration."""
|
||||
if not integration.core:
|
||||
return
|
||||
|
||||
declared_quality_scale = QUALITY_SCALE_TIERS.get(integration.quality_scale)
|
||||
|
||||
iqs_file = integration.path / "quality_scale.yaml"
|
||||
has_file = iqs_file.is_file()
|
||||
if not has_file:
|
||||
@ -1288,6 +1303,12 @@ def validate_iqs_file(config: Config, integration: Integration) -> None:
|
||||
"Quality scale definition not found. New integrations are required to at least reach the Bronze tier.",
|
||||
)
|
||||
return
|
||||
if declared_quality_scale is not None:
|
||||
integration.add_error(
|
||||
"quality_scale",
|
||||
"Quality scale definition not found. Integrations that set a manifest quality scale must have a quality scale definition.",
|
||||
)
|
||||
return
|
||||
return
|
||||
if integration.integration_type == "virtual":
|
||||
integration.add_error(
|
||||
@ -1322,6 +1343,28 @@ def validate_iqs_file(config: Config, integration: Integration) -> None:
|
||||
"quality_scale", f"Invalid {name}: {humanize_error(data, err)}"
|
||||
)
|
||||
|
||||
if declared_quality_scale is None:
|
||||
return
|
||||
|
||||
rules_met = set()
|
||||
for rule_name, rule_value in data.get("rules", {}).items():
|
||||
status = rule_value["status"] if isinstance(rule_value, dict) else rule_value
|
||||
if status in {"done", "exempt"}:
|
||||
rules_met.add(rule_name)
|
||||
|
||||
# An integration must have all the necessary rules for the declared
|
||||
# quality scale, and all the rules below.
|
||||
for scale in ScaledQualityScaleTiers:
|
||||
if scale > declared_quality_scale:
|
||||
break
|
||||
required_rules = set(RULES[scale])
|
||||
if missing_rules := (required_rules - rules_met):
|
||||
friendly_rule_str = "\n".join(f" {rule}: todo" for rule in missing_rules)
|
||||
integration.add_error(
|
||||
"quality_scale",
|
||||
f"Quality scale tier {scale.name.lower()} requires quality scale rules to be met:\n{friendly_rule_str}",
|
||||
)
|
||||
|
||||
|
||||
def validate(integrations: dict[str, Integration], config: Config) -> None:
|
||||
"""Handle YAML files inside integrations."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user