Replace skip_extra_repos -> with_extra_repos (MR 2480)

Replace the boolean skip_extra_repos with a new with_extra_repos
argument that can be default, enabled or disabled.

This will be used to explicitly enable extra repos in a package search
even if systemd is currently disabled, so we can display a hint when a
package was not found because systemd is disabled in a follow-up patch.
This commit is contained in:
Oliver Smith 2024-11-07 18:04:06 +01:00
parent 444d9e256f
commit 0a0f6ead33
No known key found for this signature in database
GPG key ID: 5AE7F5513E0885CB
6 changed files with 51 additions and 28 deletions

View file

@ -11,13 +11,13 @@ from pmb.meta import Cache
def is_systemd_selected(config: Config) -> bool: def is_systemd_selected(config: Config) -> bool:
if "systemd" not in pmb.config.pmaports.read_config_repos(): if "systemd" not in pmb.config.pmaports.read_config_repos():
return False return False
if pmb.helpers.ui.check_option(config.ui, "pmb:systemd-never", skip_extra_repos=True): if pmb.helpers.ui.check_option(config.ui, "pmb:systemd-never", with_extra_repos="disabled"):
return False return False
if config.systemd == SystemdConfig.ALWAYS: if config.systemd == SystemdConfig.ALWAYS:
return True return True
if config.systemd == SystemdConfig.NEVER: if config.systemd == SystemdConfig.NEVER:
return False return False
return pmb.helpers.ui.check_option(config.ui, "pmb:systemd", skip_extra_repos=True) return pmb.helpers.ui.check_option(config.ui, "pmb:systemd", with_extra_repos="disabled")
def systemd_selected_str(config: Config) -> tuple[str, str]: def systemd_selected_str(config: Config) -> tuple[str, str]:

View file

@ -6,22 +6,30 @@ from collections.abc import Generator
import pmb.config import pmb.config
from pmb.core.context import get_context from pmb.core.context import get_context
from pmb.meta import Cache from pmb.meta import Cache
from pmb.types import WithExtraRepos
@Cache(skip_extras=False) @Cache("with_extra_repos")
def pkgrepo_paths(skip_extras: bool = False) -> list[Path]: def pkgrepo_paths(with_extra_repos: WithExtraRepos = "default") -> list[Path]:
config = get_context().config config = get_context().config
paths = list(map(lambda x: Path(x), config.aports)) paths = list(map(lambda x: Path(x), config.aports))
if not paths: if not paths:
raise RuntimeError("No package repositories specified?") raise RuntimeError("No package repositories specified?")
if skip_extras: with_systemd = False
return paths
match with_extra_repos:
case "disabled":
return paths
case "enabled":
with_systemd = True
case "default":
with_systemd = pmb.config.is_systemd_selected(config)
out_paths = [] out_paths = []
for p in paths: for p in paths:
# This isn't very generic, but we don't plan to add new extra-repos... # This isn't very generic, but we don't plan to add new extra-repos...
if (p / "extra-repos/systemd").is_dir() and pmb.config.is_systemd_selected(config): if (p / "extra-repos/systemd").is_dir() and with_systemd:
out_paths.append(p / "extra-repos/systemd") out_paths.append(p / "extra-repos/systemd")
out_paths.append(p) out_paths.append(p)
@ -30,16 +38,16 @@ def pkgrepo_paths(skip_extras: bool = False) -> list[Path]:
@Cache() @Cache()
def pkgrepo_default_path() -> Path: def pkgrepo_default_path() -> Path:
return pkgrepo_paths(skip_extras=True)[0] return pkgrepo_paths(with_extra_repos="disabled")[0]
def pkgrepo_names(skip_exras: bool = False) -> list[str]: def pkgrepo_names(with_extra_repos: WithExtraRepos = "default") -> list[str]:
""" """
Return a list of all the package repository names. We REQUIRE Return a list of all the package repository names. We REQUIRE
that the last repository is "pmaports", though the directory that the last repository is "pmaports", though the directory
may be named differently. So we hardcode the name here. may be named differently. So we hardcode the name here.
""" """
names = [aports.name for aports in pkgrepo_paths(skip_exras)] names = [aports.name for aports in pkgrepo_paths(with_extra_repos)]
names[-1] = "pmaports" names[-1] = "pmaports"
return names return names
@ -112,14 +120,16 @@ def pkgrepo_iglob(path: str, recursive: bool = False) -> Generator[Path, None, N
yield pdir yield pdir
def pkgrepo_iter_package_dirs(skip_extra_repos: bool = False) -> Generator[Path, None, None]: def pkgrepo_iter_package_dirs(
with_extra_repos: WithExtraRepos = "default",
) -> Generator[Path, None, None]:
""" """
Yield each matching glob over each aports repository. Yield each matching glob over each aports repository.
Detect duplicates within the same aports repository but otherwise Detect duplicates within the same aports repository but otherwise
ignore all but the first. This allows for overriding packages. ignore all but the first. This allows for overriding packages.
""" """
seen: dict[str, list[str]] = dict(map(lambda a: (a, []), pkgrepo_names(skip_extra_repos))) seen: dict[str, list[str]] = dict(map(lambda a: (a, []), pkgrepo_names(with_extra_repos)))
for repo in pkgrepo_paths(skip_extra_repos): for repo in pkgrepo_paths(with_extra_repos):
for g in glob.iglob(os.path.join(repo, "**/*/APKBUILD"), recursive=True): for g in glob.iglob(os.path.join(repo, "**/*/APKBUILD"), recursive=True):
pdir = Path(g).parent pdir = Path(g).parent
# Skip extra-repos when not parsing the extra-repo itself # Skip extra-repos when not parsing the extra-repo itself

View file

@ -19,6 +19,7 @@ def test_pkgrepo_pmaports(pmaports, monkeypatch):
# Disable results caching # Disable results caching
pkgrepo_paths.cache_disable() pkgrepo_paths.cache_disable()
pkgrepo_default_path.cache_disable()
paths = pkgrepo_paths() paths = pkgrepo_paths()
print(f"[master] pkgrepo_paths: {paths}") print(f"[master] pkgrepo_paths: {paths}")
@ -37,7 +38,10 @@ def test_pkgrepo_pmaports(pmaports, monkeypatch):
== 0 == 0
) )
paths = pkgrepo_paths() paths = pkgrepo_paths(with_extra_repos="disabled")
assert len(paths) == 1
paths = pkgrepo_paths(with_extra_repos="enabled")
assert len(paths) == 2 assert len(paths) == 2
# systemd is the first path, since we want packages there to take priority # systemd is the first path, since we want packages there to take priority

View file

@ -13,15 +13,16 @@ from pmb.core.pkgrepo import pkgrepo_iter_package_dirs
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from pmb.types import WithExtraRepos
from pmb.meta import Cache from pmb.meta import Cache
import pmb.parse import pmb.parse
@Cache("skip_extra_repos") @Cache("with_extra_repos")
def _find_apkbuilds(skip_extra_repos: bool = False) -> dict[str, Path]: def _find_apkbuilds(with_extra_repos: WithExtraRepos = "default") -> dict[str, Path]:
apkbuilds = {} apkbuilds = {}
for package in pkgrepo_iter_package_dirs(skip_extra_repos=skip_extra_repos): for package in pkgrepo_iter_package_dirs(with_extra_repos=with_extra_repos):
pkgname = package.name pkgname = package.name
if pkgname in apkbuilds: if pkgname in apkbuilds:
raise RuntimeError( raise RuntimeError(
@ -139,8 +140,8 @@ def _find_package_in_apkbuild(package: str, path: Path) -> bool:
return False return False
@Cache("package", "subpackages", skip_extra_repos=False) @Cache("package", "subpackages", "with_extra_repos")
def find(package, must_exist=True, subpackages=True, skip_extra_repos=False): def find(package, must_exist=True, subpackages=True, with_extra_repos="default"):
"""Find the directory in pmaports that provides a package or subpackage. """Find the directory in pmaports that provides a package or subpackage.
If you want the parsed APKBUILD instead, use pmb.helpers.pmaports.get(). If you want the parsed APKBUILD instead, use pmb.helpers.pmaports.get().
@ -161,7 +162,7 @@ def find(package, must_exist=True, subpackages=True, skip_extra_repos=False):
raise RuntimeError("Invalid pkgname: " + package) raise RuntimeError("Invalid pkgname: " + package)
# Try to find an APKBUILD with the exact pkgname we are looking for # Try to find an APKBUILD with the exact pkgname we are looking for
path = _find_apkbuilds(skip_extra_repos).get(package) path = _find_apkbuilds(with_extra_repos).get(package)
if path: if path:
logging.verbose(f"{package}: found apkbuild: {path}") logging.verbose(f"{package}: found apkbuild: {path}")
ret = path.parent ret = path.parent
@ -203,9 +204,12 @@ def find_optional(package: str) -> Path | None:
# The only caller with subpackages=False is ui.check_option() # The only caller with subpackages=False is ui.check_option()
@Cache("pkgname", subpackages=True) @Cache("pkgname", "with_extra_repos", subpackages=True)
def get_with_path( def get_with_path(
pkgname: str, must_exist: bool = True, subpackages: bool = True, skip_extra_repos: bool = False pkgname: str,
must_exist: bool = True,
subpackages: bool = True,
with_extra_repos: WithExtraRepos = "default",
) -> tuple[Path | None, dict[str, Any] | None]: ) -> tuple[Path | None, dict[str, Any] | None]:
"""Find and parse an APKBUILD file. """Find and parse an APKBUILD file.
@ -216,7 +220,7 @@ def get_with_path(
:param must_exist: raise an exception when it can't be found :param must_exist: raise an exception when it can't be found
:param subpackages: also search for subpackages with the specified :param subpackages: also search for subpackages with the specified
names (slow! might need to parse all APKBUILDs to find it) names (slow! might need to parse all APKBUILDs to find it)
:param skip_extra_repos: skip extra repositories (e.g. systemd) when :param with_extra_repos: use extra repositories (e.g. systemd) when
searching for the package searching for the package
:returns: relevant variables from the APKBUILD as dictionary, e.g.: :returns: relevant variables from the APKBUILD as dictionary, e.g.:
@ -228,16 +232,19 @@ def get_with_path(
... } ... }
""" """
pkgname = pmb.helpers.package.remove_operators(pkgname) pkgname = pmb.helpers.package.remove_operators(pkgname)
pmaport = find(pkgname, must_exist, subpackages, skip_extra_repos) pmaport = find(pkgname, must_exist, subpackages, with_extra_repos)
if pmaport: if pmaport:
return pmaport, pmb.parse.apkbuild(pmaport / "APKBUILD") return pmaport, pmb.parse.apkbuild(pmaport / "APKBUILD")
return None, None return None, None
def get( def get(
pkgname: str, must_exist: bool = True, subpackages: bool = True, skip_extra_repos: bool = False pkgname: str,
must_exist: bool = True,
subpackages: bool = True,
with_extra_repos: WithExtraRepos = "default",
) -> dict[str, Any]: ) -> dict[str, Any]:
return get_with_path(pkgname, must_exist, subpackages, skip_extra_repos)[1] return get_with_path(pkgname, must_exist, subpackages, with_extra_repos)[1]
def find_providers(provide: str, default: list[str]) -> list[tuple[Any, Any]]: def find_providers(provide: str, default: list[str]) -> list[tuple[Any, Any]]:

View file

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import os import os
from pmb.core.pkgrepo import pkgrepo_iglob from pmb.core.pkgrepo import pkgrepo_iglob
from pmb.types import WithExtraRepos
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.helpers.package import pmb.helpers.package
import pmb.parse import pmb.parse
@ -29,7 +30,7 @@ def list_ui(arch):
return ret return ret
def check_option(ui, option, skip_extra_repos=False): def check_option(ui: str, option: str, with_extra_repos: WithExtraRepos = "default") -> bool:
""" """
Check if an option, such as pmb:systemd, is inside an UI's APKBUILD. Check if an option, such as pmb:systemd, is inside an UI's APKBUILD.
""" """
@ -40,6 +41,6 @@ def check_option(ui, option, skip_extra_repos=False):
pkgname = f"postmarketos-ui-{ui}" pkgname = f"postmarketos-ui-{ui}"
apkbuild = pmb.helpers.pmaports.get( apkbuild = pmb.helpers.pmaports.get(
pkgname, subpackages=False, skip_extra_repos=skip_extra_repos pkgname, subpackages=False, with_extra_repos=with_extra_repos
) )
return option in apkbuild["options"] return option in apkbuild["options"]

View file

@ -16,6 +16,7 @@ RunReturnType = str | int | subprocess.Popen
PathString = Path | str PathString = Path | str
Env = dict[str, PathString] Env = dict[str, PathString]
Apkbuild = dict[str, Any] Apkbuild = dict[str, Any]
WithExtraRepos = Literal["default", "enabled", "disabled"]
# These types are not definitive / API, they exist to describe the current # These types are not definitive / API, they exist to describe the current
# state of things so that we can improve our type hinting coverage and make # state of things so that we can improve our type hinting coverage and make