Add min_version to esphome config (#3866)

This commit is contained in:
Jesse Hills 2022-10-05 16:30:56 +13:00 committed by GitHub
parent 263b603188
commit c3a8972550
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 77 additions and 18 deletions

View file

@ -98,7 +98,7 @@ async def to_code(config):
def _process_git_config(config: dict, refresh) -> str:
repo_dir = git.clone_or_update(
repo_dir, _ = git.clone_or_update(
url=config[CONF_URL],
ref=config.get(CONF_REF),
refresh=refresh,

View file

@ -5,14 +5,17 @@ from esphome.config_helpers import merge_config
from esphome import git, yaml_util
from esphome.const import (
CONF_ESPHOME,
CONF_FILE,
CONF_FILES,
CONF_MIN_VERSION,
CONF_PACKAGES,
CONF_REF,
CONF_REFRESH,
CONF_URL,
CONF_USERNAME,
CONF_PASSWORD,
__version__ as ESPHOME_VERSION,
)
import esphome.config_validation as cv
@ -104,7 +107,7 @@ CONFIG_SCHEMA = cv.All(
def _process_base_package(config: dict) -> dict:
repo_dir = git.clone_or_update(
repo_dir, revert = git.clone_or_update(
url=config[CONF_URL],
ref=config.get(CONF_REF),
refresh=config[CONF_REFRESH],
@ -112,21 +115,51 @@ def _process_base_package(config: dict) -> dict:
username=config.get(CONF_USERNAME),
password=config.get(CONF_PASSWORD),
)
files: str = config[CONF_FILES]
files: list[str] = config[CONF_FILES]
def get_packages(files) -> dict:
packages = {}
for file in files:
yaml_file: Path = repo_dir / file
if not yaml_file.is_file():
raise cv.Invalid(
f"{file} does not exist in repository", path=[CONF_FILES]
)
try:
new_yaml = yaml_util.load_yaml(yaml_file)
if (
CONF_ESPHOME in new_yaml
and CONF_MIN_VERSION in new_yaml[CONF_ESPHOME]
):
min_version = new_yaml[CONF_ESPHOME][CONF_MIN_VERSION]
if cv.Version.parse(min_version) > cv.Version.parse(
ESPHOME_VERSION
):
raise cv.Invalid(
f"Current ESPHome Version is too old to use this package: {ESPHOME_VERSION} < {min_version}"
)
packages[file] = new_yaml
except EsphomeError as e:
raise cv.Invalid(
f"{file} is not a valid YAML file. Please check the file contents."
) from e
return packages
packages = {}
for file in files:
yaml_file: Path = repo_dir / file
if not yaml_file.is_file():
raise cv.Invalid(f"{file} does not exist in repository", path=[CONF_FILES])
try:
packages = get_packages(files)
except cv.Invalid:
if revert is not None:
revert()
packages = get_packages(files)
finally:
if packages is None:
raise cv.Invalid("Failed to load packages")
try:
packages[file] = yaml_util.load_yaml(yaml_file)
except EsphomeError as e:
raise cv.Invalid(
f"{file} is not a valid YAML file. Please check the file contents."
) from e
return {"packages": packages}

View file

@ -1689,7 +1689,7 @@ class Version:
@classmethod
def parse(cls, value: str) -> "Version":
match = re.match(r"(\d+).(\d+).(\d+)", value)
match = re.match(r"^(\d+).(\d+).(\d+)-?\w*$", value)
if match is None:
raise ValueError(f"Not a valid version number {value}")
major = int(match[1])
@ -1703,7 +1703,7 @@ def version_number(value):
try:
return str(Version.parse(value))
except ValueError as e:
raise Invalid("Not a version number") from e
raise Invalid("Not a valid version number") from e
def platformio_version_constraint(value):

View file

@ -396,6 +396,7 @@ CONF_MIN_POWER = "min_power"
CONF_MIN_RANGE = "min_range"
CONF_MIN_TEMPERATURE = "min_temperature"
CONF_MIN_VALUE = "min_value"
CONF_MIN_VERSION = "min_version"
CONF_MINUTE = "minute"
CONF_MINUTES = "minutes"
CONF_MISO_PIN = "miso_pin"

View file

@ -15,6 +15,7 @@ from esphome.const import (
CONF_FRAMEWORK,
CONF_INCLUDES,
CONF_LIBRARIES,
CONF_MIN_VERSION,
CONF_NAME,
CONF_ON_BOOT,
CONF_ON_LOOP,
@ -30,6 +31,7 @@ from esphome.const import (
KEY_CORE,
TARGET_PLATFORMS,
PLATFORM_ESP8266,
__version__ as ESPHOME_VERSION,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.helpers import copy_file_if_changed, walk_files
@ -96,6 +98,16 @@ def valid_project_name(value: str):
return value
def validate_version(value: str):
min_version = cv.Version.parse(value)
current_version = cv.Version.parse(ESPHOME_VERSION)
if current_version < min_version:
raise cv.Invalid(
f"Your ESPHome version is too old. Please update to at least {min_version}"
)
return value
CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash"
CONFIG_SCHEMA = cv.All(
cv.Schema(
@ -136,6 +148,9 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_VERSION): cv.string_strict,
}
),
cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All(
cv.version_number, validate_version
),
}
),
validate_hostname,

View file

@ -2,6 +2,7 @@ from pathlib import Path
import subprocess
import hashlib
import logging
from typing import Callable, Optional
import urllib.parse
from datetime import datetime
@ -12,7 +13,7 @@ import esphome.config_validation as cv
_LOGGER = logging.getLogger(__name__)
def run_git_command(cmd, cwd=None):
def run_git_command(cmd, cwd=None) -> str:
try:
ret = subprocess.run(cmd, cwd=cwd, capture_output=True, check=False)
except FileNotFoundError as err:
@ -28,6 +29,8 @@ def run_git_command(cmd, cwd=None):
raise cv.Invalid(lines[-1][len("fatal: ") :])
raise cv.Invalid(err_str)
return ret.stdout.decode("utf-8").strip()
def _compute_destination_path(key: str, domain: str) -> Path:
base_dir = Path(CORE.config_dir) / ".esphome" / domain
@ -44,7 +47,7 @@ def clone_or_update(
domain: str,
username: str = None,
password: str = None,
) -> Path:
) -> tuple[Path, Optional[Callable[[], None]]]:
key = f"{url}@{ref}"
if username is not None and password is not None:
@ -78,6 +81,7 @@ def clone_or_update(
file_timestamp = Path(repo_dir / ".git" / "HEAD")
age = datetime.now() - datetime.fromtimestamp(file_timestamp.stat().st_mtime)
if age.total_seconds() > refresh.total_seconds:
old_sha = run_git_command(["git", "rev-parse", "HEAD"], str(repo_dir))
_LOGGER.info("Updating %s", key)
_LOGGER.debug("Location: %s", repo_dir)
# Stash local changes (if any)
@ -92,4 +96,10 @@ def clone_or_update(
# Hard reset to FETCH_HEAD (short-lived git ref corresponding to most recent fetch)
run_git_command(["git", "reset", "--hard", "FETCH_HEAD"], str(repo_dir))
return repo_dir
def revert():
_LOGGER.info("Reverting changes to %s -> %s", key, old_sha)
run_git_command(["git", "reset", "--hard", old_sha], str(repo_dir))
return repo_dir, revert
return repo_dir, None