feat: enhance repository configuration to support url_to_replace patterns for disabling existing entries

Signed-off-by: d9pouces <github@19pouces.net>
This commit is contained in:
d9pouces
2026-09-19 23:40:35 +02:00
parent b2dcae0daf
commit cea45c97ff
8 changed files with 757 additions and 66 deletions
+163 -12
View File
@@ -70,7 +70,9 @@ def test_is_relevant_true_with_apt_dir(tmp_path) -> None:
assert AptModule().is_relevant(context) is True
def test_apply_disables_existing_sources_and_adds_configured_ones(tmp_path) -> None:
def test_apply_copies_existing_sources_unchanged_without_uri_to_replace(
tmp_path,
) -> None:
src = tmp_path / "src"
apt_dir = src / "etc/apt"
sources_list_d = apt_dir / "sources.list.d"
@@ -97,24 +99,24 @@ def test_apply_disables_existing_sources_and_adds_configured_ones(tmp_path) -> N
AptModule().apply(context)
disabled_top_level = (dst / "etc/apt/sources.list").read_text(encoding="utf-8")
# No configured source declares a uri_to_replace, so none of the existing
# entries are disabled: they are copied through unchanged.
top_level = (dst / "etc/apt/sources.list").read_text(encoding="utf-8")
assert (
disabled_top_level
== "# already disabled\n# deb http://example.invalid/legacy stable main\n\n"
top_level
== "# already disabled\ndeb http://example.invalid/legacy stable main\n\n"
)
disabled_list = (dst / "etc/apt/sources.list.d/extra.list").read_text(
encoding="utf-8"
)
assert disabled_list == "# deb http://example.invalid/extra stable main\n"
extra_list = (dst / "etc/apt/sources.list.d/extra.list").read_text(encoding="utf-8")
assert extra_list == "deb http://example.invalid/extra stable main\n"
disabled_sources = (dst / "etc/apt/sources.list.d/docker.sources").read_text(
docker_sources = (dst / "etc/apt/sources.list.d/docker.sources").read_text(
encoding="utf-8"
)
assert disabled_sources == (
"Types: deb\nURIs: http://example.invalid/docker\nSuites: stable\nEnabled: no"
assert docker_sources == (
"Types: deb\nURIs: http://example.invalid/docker\nSuites: stable\nEnabled: yes"
"\n\n"
"Types: deb\nURIs: http://example.invalid/docker2\nSuites: stable\nEnabled: no\n"
"Types: deb\nURIs: http://example.invalid/docker2\nSuites: stable\n"
)
assert not (dst / "etc/apt/sources.list.d/ignored.txt").exists()
@@ -133,6 +135,55 @@ def test_apply_disables_existing_sources_and_adds_configured_ones(tmp_path) -> N
)
def test_apply_disables_only_existing_sources_matching_uri_to_replace(
tmp_path,
) -> None:
src = tmp_path / "src"
apt_dir = src / "etc/apt"
sources_list_d = apt_dir / "sources.list.d"
sources_list_d.mkdir(parents=True)
(apt_dir / "sources.list").write_text(
"deb http://example.invalid/legacy stable main\n", encoding="utf-8"
)
(sources_list_d / "extra.list").write_text(
"deb http://example.invalid/extra stable main\n", encoding="utf-8"
)
(sources_list_d / "docker.sources").write_text(
"Types: deb\nURIs: http://example.invalid/extra\nSuites: stable\nEnabled: yes\n"
"\n"
"Types: deb\nURIs: http://example.invalid/docker2\nSuites: stable\n"
"\n\n",
encoding="utf-8",
)
dst = tmp_path / "dst"
context = Context(
config=make_config_with({"uri-to-replace": "http://example.invalid/extra"}),
dst=dst,
src=src,
)
AptModule().apply(context)
# The legacy source doesn't match the pattern, so it stays enabled...
top_level = (dst / "etc/apt/sources.list").read_text(encoding="utf-8")
assert top_level == "deb http://example.invalid/legacy stable main\n"
# ...but every entry whose URI matches is disabled, regardless of format.
extra_list = (dst / "etc/apt/sources.list.d/extra.list").read_text(encoding="utf-8")
assert extra_list == "# deb http://example.invalid/extra stable main\n"
docker_sources = (dst / "etc/apt/sources.list.d/docker.sources").read_text(
encoding="utf-8"
)
assert docker_sources == (
"Types: deb\nURIs: http://example.invalid/extra\nSuites: stable\nEnabled: no"
"\n\n"
"Types: deb\nURIs: http://example.invalid/docker2\nSuites: stable\n"
)
def test_apply_without_existing_top_level_sources_list(tmp_path) -> None:
src = tmp_path / "src"
(src / "etc/apt").mkdir(parents=True)
@@ -200,6 +251,106 @@ def test_apply_without_signed_by_writes_no_field(tmp_path) -> None:
)
def test_apply_skips_source_when_uri_to_replace_matches_nothing(tmp_path) -> None:
src = tmp_path / "src"
apt_dir = src / "etc/apt"
apt_dir.mkdir(parents=True)
(apt_dir / "sources.list").write_text(
"deb http://example.invalid/legacy stable main\n", encoding="utf-8"
)
dst = tmp_path / "dst"
context = Context(
config=make_config_with({"uri-to-replace": "http://other.invalid/*"}),
dst=dst,
src=src,
)
AptModule().apply(context)
assert not (dst / "etc/apt/sources.list.d/interdiode.sources").exists()
def test_apply_writes_source_when_uri_to_replace_matches_existing_uri(
tmp_path,
) -> None:
src = tmp_path / "src"
apt_dir = src / "etc/apt"
apt_dir.mkdir(parents=True)
(apt_dir / "sources.list").write_text(
"deb http://legacy.invalid/debian stable main\n", encoding="utf-8"
)
dst = tmp_path / "dst"
context = Context(
config=make_config_with({"uri-to-replace": "http://legacy.invalid/*"}),
dst=dst,
src=src,
)
AptModule().apply(context)
assert (dst / "etc/apt/sources.list.d/interdiode.sources").exists()
def test_apply_skips_source_when_uri_to_replace_set_without_src(tmp_path) -> None:
dst = tmp_path / "dst"
context = Context(
config=make_config_with({"uri-to-replace": "http://*"}), dst=dst, src=None
)
AptModule().apply(context)
assert not (dst / "etc/apt/sources.list.d/interdiode.sources").exists()
def test_apply_matches_uri_to_replace_across_list_line_variants(tmp_path) -> None:
src = tmp_path / "src"
apt_dir = src / "etc/apt"
sources_list_d = apt_dir / "sources.list.d"
sources_list_d.mkdir(parents=True)
(apt_dir / "sources.list").write_text(
"# a comment\n"
"\n"
"garbage line here\n"
"deb [unterminated bracket with no closing marker\n"
"deb [arch=amd64 trusted=yes] http://example.invalid/extra stable main\n",
encoding="utf-8",
)
(sources_list_d / "ignored.txt").write_text("not a source file\n", encoding="utf-8")
data = {
"uuid": "12345678-1234-5678-1234-567812345678",
"organisation": "InterDiode",
"configurations": {
"apt": {
"sources": [
{
"name": "first",
"uris": ["http://example.invalid/first"],
"suites": ["stable"],
"uri-to-replace": "http://example.invalid/extra",
},
{
"name": "second",
"uris": ["http://example.invalid/second"],
"suites": ["stable"],
"uri-to-replace": "http://example.invalid/extra",
},
],
}
},
}
dst = tmp_path / "dst"
context = Context(config=Configuration.model_validate(data), dst=dst, src=src)
AptModule().apply(context)
assert (dst / "etc/apt/sources.list.d/first.sources").exists()
assert (dst / "etc/apt/sources.list.d/second.sources").exists()
def test_apply_sanitizes_source_name_with_slash(tmp_path) -> None:
dst = tmp_path / "dst"
context = Context(