1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 00:31:58 +00:00
This commit is contained in:
J. Nick Koston
2026-02-06 14:20:55 +01:00
parent 024c87a80b
commit 136606a435
3 changed files with 22 additions and 17 deletions

View File

@@ -966,7 +966,7 @@ def command_clean(args: ArgsProtocol, config: ConfigType) -> int | None:
def command_bundle(args: ArgsProtocol, config: ConfigType) -> int | None:
from esphome.bundle import ConfigBundleCreator
from esphome.bundle import BUNDLE_EXTENSION, ConfigBundleCreator
creator = ConfigBundleCreator(config)
@@ -983,7 +983,7 @@ def command_bundle(args: ArgsProtocol, config: ConfigType) -> int | None:
output_path = Path(args.output)
else:
stem = CORE.config_path.stem
output_path = CORE.config_dir / f"{stem}.bundle.tar.gz"
output_path = CORE.config_dir / f"{stem}{BUNDLE_EXTENSION}"
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_bytes(result.data)
@@ -1674,7 +1674,7 @@ def run_esphome(argv):
_LOGGER.warning("Skipping secrets file %s", conf_path)
return 0
# Bundle support: if the configuration is a .tar.gz bundle, extract it
# Bundle support: if the configuration is a .esphomebundle, extract it
# and rewrite conf_path to the extracted YAML config.
from esphome.bundle import is_bundle_path, prepare_bundle_for_compile

View File

@@ -28,6 +28,10 @@ MANIFEST_FILENAME = "manifest.json"
CURRENT_MANIFEST_VERSION = 1
MAX_DECOMPRESSED_SIZE = 500 * 1024 * 1024 # 500 MB
# Directories preserved across bundle extractions (build caches)
_PRESERVE_DIRS = (".esphome", ".pioenvs", ".pio")
_BUNDLE_STAGING_DIR = ".bundle_staging"
# String prefixes that are never local file paths
_NON_PATH_PREFIXES = ("http://", "https://", "ftp://", "mdi:", "<")
@@ -374,7 +378,7 @@ class ConfigBundleCreator:
continue
filtered = {k: v for k, v in all_secrets.items() if k in used_keys}
if filtered:
data = yaml_util.dump(filtered).encode("utf-8")
data = yaml_util.dump(filtered, show_secrets=True).encode("utf-8")
result[rel_path] = data
return result
@@ -552,10 +556,12 @@ def _validate_tar_members(tar: tarfile.TarFile, target_dir: Path) -> None:
)
BUNDLE_EXTENSION = ".esphomebundle.tar.gz"
def is_bundle_path(path: Path) -> bool:
"""Check if a path looks like a bundle file."""
name = path.name.lower()
return name.endswith(".tar.gz") or name.endswith(".tgz")
return path.name.lower().endswith(BUNDLE_EXTENSION)
def _add_bytes_to_tar(tar: tarfile.TarFile, name: str, data: bytes) -> None:
@@ -581,9 +587,10 @@ def _resolve_include_path(include_path: Any) -> Path | None:
def _default_target_dir(bundle_path: Path) -> Path:
"""Compute the default extraction directory for a bundle."""
return bundle_path.parent / bundle_path.name.removesuffix(".tar.gz").removesuffix(
".gz"
)
name = bundle_path.name
if name.lower().endswith(BUNDLE_EXTENSION):
name = name[: -len(BUNDLE_EXTENSION)]
return bundle_path.parent / name
def _restore_preserved_dirs(preserved: dict[str, Path], target_dir: Path) -> None:
@@ -622,13 +629,11 @@ def prepare_bundle_for_compile(
target_dir = target_dir.resolve()
target_dir.mkdir(parents=True, exist_ok=True)
# Identify directories to preserve (build caches)
preserve_dirs = [".esphome", ".pioenvs", ".pio"]
preserved: dict[str, Path] = {}
# Temporarily move preserved dirs out of the way
staging = target_dir / ".bundle_staging"
for dirname in preserve_dirs:
staging = target_dir / _BUNDLE_STAGING_DIR
for dirname in _PRESERVE_DIRS:
src = target_dir / dirname
if src.is_dir():
dst = staging / dirname
@@ -639,7 +644,7 @@ def prepare_bundle_for_compile(
try:
# Clean non-preserved content and extract fresh
for item in target_dir.iterdir():
if item.name == ".bundle_staging":
if item.name == _BUNDLE_STAGING_DIR:
continue
if item.is_dir():
shutil.rmtree(item)

View File

@@ -435,10 +435,10 @@ def _load_yaml_internal(fname: Path) -> Any:
raise EsphomeError(f"Error reading file {fname}: {err}") from err
def parse_yaml(
file_name: Path, file_handle: TextIOWrapper, yaml_loader=_load_yaml_internal
) -> Any:
def parse_yaml(file_name: Path, file_handle: TextIOWrapper, yaml_loader=None) -> Any:
"""Parse a YAML file."""
if yaml_loader is None:
yaml_loader = _load_yaml_internal
try:
return _load_yaml_internal_with_type(
ESPHomeLoader, file_name, file_handle, yaml_loader