mirror of
https://github.com/esphome/esphome.git
synced 2024-11-21 11:37:27 +01:00
Add min_version to esphome config (#3866)
This commit is contained in:
parent
263b603188
commit
c3a8972550
@ -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,
|
||||
|
@ -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}
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user