fix(install): copy app subdirectories so dependency hooks ship
install_from() only copied top-level files, so an app's scripts/ folder — where provider on_install/on_start dependency hooks live — never reached /var/lib/furtka/apps/<app>/. Every hooked dependency therefore failed at reconcile with "hook ... missing in provider". Copy subdirectories too (rmtree+copytree so a reinstall drops files removed upstream). Found during the first real end-to-end run of the mosquitto+zigbee2mqtt dependency pair on a test VM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1155f1d4ba
commit
0615d92c8d
2 changed files with 35 additions and 1 deletions
|
|
@ -171,13 +171,22 @@ def install_from(src: Path, settings: dict[str, str] | None = None) -> Path:
|
|||
target.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for item in src.iterdir():
|
||||
dest = target / item.name
|
||||
# Subdirectories (e.g. scripts/ holding a provider's on_install/on_start
|
||||
# dependency hooks) must come along too — copy the whole tree, replacing
|
||||
# any stale copy from a previous install so removed files don't linger.
|
||||
if item.is_dir():
|
||||
if dest.exists():
|
||||
shutil.rmtree(dest)
|
||||
shutil.copytree(item, dest)
|
||||
continue
|
||||
if not item.is_file():
|
||||
continue
|
||||
# Never overwrite an existing user .env — either settings-driven write
|
||||
# or previous manual edit has authority.
|
||||
if item.name == ".env" and (target / ".env").exists():
|
||||
continue
|
||||
shutil.copy2(item, target / item.name)
|
||||
shutil.copy2(item, dest)
|
||||
|
||||
env = target / ".env"
|
||||
env_example = target / ".env.example"
|
||||
|
|
|
|||
|
|
@ -73,6 +73,31 @@ def test_install_from_copies_files(tmp_path, fake_dirs):
|
|||
assert (target / ".env").read_text() == "A=1"
|
||||
|
||||
|
||||
def test_install_from_copies_subdirectories(tmp_path, fake_dirs):
|
||||
# Dependency hooks live under scripts/ — the whole subtree must be copied,
|
||||
# not just top-level files, or providers ship without their hooks.
|
||||
src = _write_app_source(tmp_path, "mosquitto", VALID_MANIFEST, env_example="A=1")
|
||||
scripts = src / "scripts"
|
||||
scripts.mkdir()
|
||||
(scripts / "provision-client.sh").write_text("#!/bin/sh\necho hi\n")
|
||||
target = installer.install_from(src)
|
||||
assert (target / "scripts" / "provision-client.sh").read_text() == "#!/bin/sh\necho hi\n"
|
||||
|
||||
|
||||
def test_install_from_reinstall_drops_stale_subdir_files(tmp_path, fake_dirs):
|
||||
# A reinstall whose source renamed/removed a hook must not leave the old one.
|
||||
src = _write_app_source(tmp_path, "mosquitto", VALID_MANIFEST, env_example="A=1")
|
||||
scripts = src / "scripts"
|
||||
scripts.mkdir()
|
||||
(scripts / "old-hook.sh").write_text("old\n")
|
||||
installer.install_from(src)
|
||||
(scripts / "old-hook.sh").unlink()
|
||||
(scripts / "new-hook.sh").write_text("new\n")
|
||||
target = installer.install_from(src)
|
||||
assert not (target / "scripts" / "old-hook.sh").exists()
|
||||
assert (target / "scripts" / "new-hook.sh").read_text() == "new\n"
|
||||
|
||||
|
||||
def test_install_from_preserves_existing_env(tmp_path, fake_dirs):
|
||||
src = _write_app_source(tmp_path, "fileshare", VALID_MANIFEST, env_example="A=new")
|
||||
target = apps_dir() / "fileshare"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue