fix(install): install a repository once, by the first hook that supports it
The pipeline used to skip a second installer only when it advertised capabilities the first had already provided, so a hook with a different capability still ran on the very same project. file-dedupe was installed through pip and then again through `make install`, whose first line upgrades pip; PEP 668 refuses that, and the run went red on a repository that was already installed correctly. One hook now installs a repository and the pipeline stops there. The order of INSTALLERS is the preference: `make install` leads, because it is the hook a repository writes for itself, while the others infer an installation from a manifest. A hook that fails installed nothing, so the rule is one *successful* installation rather than one attempt: _run_installer returns a bool instead of re-raising, and the caller moves on to the next hook. To keep that fallback from turning a broken repository into a silent pass, a repository whose hooks all fail now exits with the list of what was tried. The capability bookkeeping the old skip rule needed is gone from the pipeline; discover_capabilities remains on BaseInstaller and is now unused there. Verified: 14 targeted unit and integration tests, and a full `pkgmgr update --all` sweep in the arch image with 0 installation failures. Delta measured at the consumer: repositories left without a CLI entry point, 16 before and 16 after, diff identical. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
"""
|
||||
Integration tests for recursive capability resolution and installer shadowing.
|
||||
|
||||
These tests verify that, given different repository layouts (Makefile, pyproject,
|
||||
flake.nix, PKGBUILD), only the expected installers are executed based on the
|
||||
capabilities provided by higher layers.
|
||||
Integration tests for installer selection across repository layouts.
|
||||
|
||||
Layer order (strongest → weakest):
|
||||
|
||||
@@ -129,10 +125,8 @@ class TestRecursiveCapabilitiesIntegration(unittest.TestCase):
|
||||
"With only a Makefile, the MakefileInstaller should run exactly once.",
|
||||
)
|
||||
|
||||
def test_python_and_makefile_both_run_when_caps_disjoint(self) -> None:
|
||||
"""
|
||||
If Python and Makefile have disjoint capabilities, both installers run.
|
||||
"""
|
||||
def test_only_the_first_supported_hook_runs(self) -> None:
|
||||
"""A repository with two usable hooks is installed by the first one."""
|
||||
repo_dir = self._new_repo()
|
||||
|
||||
# pyproject.toml without any explicit "make install" hint
|
||||
@@ -153,9 +147,8 @@ class TestRecursiveCapabilitiesIntegration(unittest.TestCase):
|
||||
|
||||
self.assertEqual(
|
||||
called,
|
||||
["python", "makefile"],
|
||||
"PythonInstaller and MakefileInstaller should both run when their "
|
||||
"capabilities are disjoint.",
|
||||
["python"],
|
||||
"Only the first supported hook may install the repository.",
|
||||
)
|
||||
|
||||
def test_python_shadows_makefile_when_pyproject_mentions_make_install(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user