mirror of
https://github.com/kiviktnm/decman.git
synced 2026-09-19 12:08:28 +00:00
Implement PKGBUILD parsing for CustomPackages
This commit is contained in:
@@ -17,6 +17,7 @@ decman = "decman.app:main"
|
|||||||
[project.entry-points."decman.plugins"]
|
[project.entry-points."decman.plugins"]
|
||||||
systemd = "decman.plugins.systemd:Systemd"
|
systemd = "decman.plugins.systemd:Systemd"
|
||||||
pacman = "decman.plugins.pacman:Pacman"
|
pacman = "decman.plugins.pacman:Pacman"
|
||||||
|
aur = "decman.plugins.pacman:AUR"
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
|
|||||||
@@ -25,3 +25,5 @@ quiet_output: bool = False
|
|||||||
color_output: bool = True
|
color_output: bool = True
|
||||||
|
|
||||||
module_on_disable_scripts_dir: str = "/var/lib/decman/scripts/"
|
module_on_disable_scripts_dir: str = "/var/lib/decman/scripts/"
|
||||||
|
|
||||||
|
arch: str = "x86_64"
|
||||||
|
|||||||
@@ -48,17 +48,47 @@ def custom_packages(fn):
|
|||||||
return fn
|
return fn
|
||||||
|
|
||||||
|
|
||||||
|
class AUR(plugins.Plugin):
|
||||||
|
"""
|
||||||
|
Plugin that manages additional pacman packages installed outside the pacman repos.
|
||||||
|
|
||||||
|
AUR packages are added directly to ``packages`` or declared by modules via ``@aur_packages``.
|
||||||
|
|
||||||
|
Custom packages are added directly to ``custom_packages`` or declared by modules via
|
||||||
|
``@custom_packages``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
NAME = "aur"
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.packages: set[str] = set()
|
||||||
|
self.custom_packages: set[CustomPackage] = set()
|
||||||
|
self.commands = PacmanCommands()
|
||||||
|
|
||||||
|
def available(self) -> bool:
|
||||||
|
return shutil.which("pacman") is not None
|
||||||
|
|
||||||
|
def process_modules(self, store: _store.Store, modules: set[module.Module]):
|
||||||
|
# This is used to track changes in modules.
|
||||||
|
store.ensure("aur_packages_for_module", {})
|
||||||
|
store.ensure("custom_packages_for_module", {})
|
||||||
|
|
||||||
|
def apply(
|
||||||
|
self, store: _store.Store, dry_run: bool = False, params: list[str] | None = None
|
||||||
|
) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Pacman(plugins.Plugin):
|
class Pacman(plugins.Plugin):
|
||||||
"""
|
"""
|
||||||
Plugin that manages pacman packages added directly to ``packages`` or declared by modules via
|
Plugin that manages pacman packages added directly to ``packages`` or declared by modules via
|
||||||
@packages.
|
``@packages``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NAME = "pacman"
|
NAME = "pacman"
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.packages: set[str] = set()
|
self.packages: set[str] = set()
|
||||||
self.aur_packages: set[str] = set()
|
|
||||||
self.commands = PacmanCommands()
|
self.commands = PacmanCommands()
|
||||||
|
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
|
|||||||
@@ -173,6 +173,13 @@ class PacmanCommands:
|
|||||||
|
|
||||||
return makechrootpkg_cmd
|
return makechrootpkg_cmd
|
||||||
|
|
||||||
|
def print_srcinfo(self) -> list[str]:
|
||||||
|
"""
|
||||||
|
Running this command prints SRCINFO generated from the package in the current
|
||||||
|
working directory.
|
||||||
|
"""
|
||||||
|
return ["makepkg", "--printsrcinfo"]
|
||||||
|
|
||||||
|
|
||||||
class PacmanInterface:
|
class PacmanInterface:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
import tempfile
|
||||||
|
|
||||||
import requests # type: ignore
|
import requests # type: ignore
|
||||||
|
|
||||||
|
import decman.config as config
|
||||||
|
import decman.core.command as command
|
||||||
|
import decman.core.error as errors
|
||||||
import decman.core.output as output
|
import decman.core.output as output
|
||||||
from decman.plugins.pacman.commands import PacmanInterface
|
from decman.plugins.pacman.commands import PacmanCommands, PacmanInterface
|
||||||
from decman.plugins.pacman.error import AurRPCError
|
from decman.plugins.pacman.error import AurRPCError, PKGBUILDParseError
|
||||||
|
|
||||||
|
|
||||||
def strip_dependency(dep: str) -> str:
|
def strip_dependency(dep: str) -> str:
|
||||||
@@ -138,9 +144,14 @@ class CustomPackage:
|
|||||||
"""
|
"""
|
||||||
Custom package installed from some other location than the official repos or the AUR.
|
Custom package installed from some other location than the official repos or the AUR.
|
||||||
|
|
||||||
|
``pkgname`` is required because the PKGBUILD might be for split packages.
|
||||||
|
|
||||||
Exactly one of ``git_url`` or ``pkgbuild_directory`` must be provided.
|
Exactly one of ``git_url`` or ``pkgbuild_directory`` must be provided.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
``pkgname``:
|
||||||
|
Name of the package.
|
||||||
|
|
||||||
``git_url``:
|
``git_url``:
|
||||||
URL to a git repository containing the PKGBUILD.
|
URL to a git repository containing the PKGBUILD.
|
||||||
|
|
||||||
@@ -148,23 +159,237 @@ class CustomPackage:
|
|||||||
Path to the directory containing the PKGBUILD.
|
Path to the directory containing the PKGBUILD.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, git_url: str | None, pkgbuild_directory: str | None) -> None:
|
def __init__(self, pkgname: str, git_url: str | None, pkgbuild_directory: str | None) -> None:
|
||||||
if git_url is None and pkgbuild_directory is None:
|
if git_url is None and pkgbuild_directory is None:
|
||||||
raise ValueError("Both git_url and pkgbuild_directory cannot be None.")
|
raise ValueError("Both git_url and pkgbuild_directory cannot be None.")
|
||||||
|
|
||||||
if git_url is not None and pkgbuild_directory is not None:
|
if git_url is not None and pkgbuild_directory is not None:
|
||||||
raise ValueError("Both git_url and pkgbuild_directory cannot be set.")
|
raise ValueError("Both git_url and pkgbuild_directory cannot be set.")
|
||||||
|
|
||||||
|
self.pkgname = pkgname
|
||||||
self.git_url = git_url
|
self.git_url = git_url
|
||||||
self.pkgbuild_directory = pkgbuild_directory
|
self.pkgbuild_directory = pkgbuild_directory
|
||||||
|
|
||||||
def parse(self) -> PackageInfo:
|
def parse(self, commands: PacmanCommands) -> PackageInfo:
|
||||||
"""
|
"""
|
||||||
Parses this package's PKGBUILD to ``PackageInfo``.
|
Parses this package's PKGBUILD to ``PackageInfo``.
|
||||||
|
|
||||||
If this fails, raises a ``PKGBUILDParseError``.
|
If this fails, raises a ``PKGBUILDParseError``.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
if self.pkgbuild_directory is not None:
|
||||||
|
srcinfo = self._srcinfo_from_pkgbuild_directory(commands)
|
||||||
|
else:
|
||||||
|
srcinfo = self._srcinfo_from_git(commands)
|
||||||
|
|
||||||
|
return self._parse_srcinfo(srcinfo)
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if not isinstance(other, CustomPackage):
|
||||||
|
return False
|
||||||
|
return (
|
||||||
|
self.git_url == other.git_url
|
||||||
|
and self.pkgbuild_directory == other.pkgbuild_directory
|
||||||
|
and self.pkgname == other.pkgname
|
||||||
|
)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((self.pkgname, self.git_url, self.pkgbuild_directory))
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
if self.git_url is not None:
|
||||||
|
return f"CustomPackage(pkgname={self.pkgname}, git_url={self.git_url})"
|
||||||
|
return (
|
||||||
|
f"CustomPackage(pkgname={self.pkgname}, pkgbuild_directory={self.pkgbuild_directory})"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _srcinfo_from_pkgbuild_directory(self, commands: PacmanCommands) -> str:
|
||||||
|
assert self.pkgbuild_directory is not None, (
|
||||||
|
"This will not get called if pkgbuild_directory is unset."
|
||||||
|
)
|
||||||
|
|
||||||
|
path = pathlib.Path(self.pkgbuild_directory)
|
||||||
|
if not path.is_dir():
|
||||||
|
raise PKGBUILDParseError(
|
||||||
|
self.git_url,
|
||||||
|
self.pkgbuild_directory,
|
||||||
|
f"pkgbuild_directory '{path}' does not exist or is not a directory.",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not (path / "PKGBUILD").exists():
|
||||||
|
raise PKGBUILDParseError(
|
||||||
|
self.git_url, self.pkgbuild_directory, f"No PKGBUILD found in '{path}'."
|
||||||
|
)
|
||||||
|
|
||||||
|
return self._run_makepkg_printsrcinfo(path, commands)
|
||||||
|
|
||||||
|
def _srcinfo_from_git(self, commands: PacmanCommands) -> str:
|
||||||
|
assert self.git_url is not None, "This will not get called if git_url is unset."
|
||||||
|
with tempfile.TemporaryDirectory(prefix="decman-pkgbuild-") as tmpdir:
|
||||||
|
tmp_path = pathlib.Path(tmpdir)
|
||||||
|
try:
|
||||||
|
cmd = commands.git_clone(self.git_url, tmpdir)
|
||||||
|
command.check_run_result(cmd, command.run(cmd))
|
||||||
|
except errors.CommandFailedError as error:
|
||||||
|
raise PKGBUILDParseError(
|
||||||
|
self.git_url, self.pkgbuild_directory, "Failed to clone PKGBUILD repository."
|
||||||
|
) from error
|
||||||
|
|
||||||
|
if not (tmp_path / "PKGBUILD").exists():
|
||||||
|
raise PKGBUILDParseError(
|
||||||
|
self.git_url,
|
||||||
|
self.pkgbuild_directory,
|
||||||
|
f"Cloned repository '{self.git_url}' does not contain a PKGBUILD.",
|
||||||
|
)
|
||||||
|
|
||||||
|
return self._run_makepkg_printsrcinfo(tmp_path, commands)
|
||||||
|
|
||||||
|
def _run_makepkg_printsrcinfo(self, path: pathlib.Path, commands: PacmanCommands) -> str:
|
||||||
|
orig_wd = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir(path)
|
||||||
|
cmd = commands.print_srcinfo()
|
||||||
|
_, srcinfo = command.check_run_result(cmd, command.run(cmd))
|
||||||
|
except errors.CommandFailedError as error:
|
||||||
|
raise PKGBUILDParseError(
|
||||||
|
self.git_url, self.pkgbuild_directory, "Failed to generate SRCINFO using makepkg."
|
||||||
|
) from error
|
||||||
|
finally:
|
||||||
|
os.chdir(orig_wd)
|
||||||
|
|
||||||
|
return srcinfo
|
||||||
|
|
||||||
|
def _parse_srcinfo(self, srcinfo: str) -> PackageInfo:
|
||||||
|
pkgbase: str | None = None
|
||||||
|
pkgver: str | None = None
|
||||||
|
pkgrel: str | None = None
|
||||||
|
epoch: str | None = None
|
||||||
|
provides: list[str] = []
|
||||||
|
|
||||||
|
# I'm not sure if split packages can have dependencies listed in the base.
|
||||||
|
# Easy to handle regardless
|
||||||
|
base_depends: list[str] = []
|
||||||
|
base_makedepends: list[str] = []
|
||||||
|
base_checkdepends: list[str] = []
|
||||||
|
|
||||||
|
pkg_depends: list[str] = []
|
||||||
|
pkg_makedepends: list[str] = []
|
||||||
|
pkg_checkdepends: list[str] = []
|
||||||
|
|
||||||
|
current_pkg: str | None = None
|
||||||
|
found_pkgnames = set()
|
||||||
|
|
||||||
|
for raw in srcinfo.splitlines():
|
||||||
|
line = raw.strip()
|
||||||
|
if not line or line.startswith("#") or "=" not in line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
key, value = (part.strip() for part in line.split("=", 1))
|
||||||
|
|
||||||
|
is_base = current_pkg is None
|
||||||
|
is_target_pkg = current_pkg == self.pkgname
|
||||||
|
|
||||||
|
match key:
|
||||||
|
case "pkgbase":
|
||||||
|
pkgbase = value
|
||||||
|
current_pkg = None
|
||||||
|
|
||||||
|
case "pkgname":
|
||||||
|
current_pkg = value
|
||||||
|
found_pkgnames.add(value)
|
||||||
|
|
||||||
|
case "pkgver":
|
||||||
|
if pkgver is None or current_pkg == self.pkgname:
|
||||||
|
pkgver = value
|
||||||
|
|
||||||
|
case "pkgrel":
|
||||||
|
if pkgrel is None or current_pkg == self.pkgname:
|
||||||
|
pkgrel = value
|
||||||
|
|
||||||
|
case "epoch":
|
||||||
|
if epoch is None or current_pkg == self.pkgname:
|
||||||
|
epoch = value
|
||||||
|
|
||||||
|
case "provides":
|
||||||
|
if is_target_pkg:
|
||||||
|
provides.append(value)
|
||||||
|
|
||||||
|
case "depends":
|
||||||
|
if is_base:
|
||||||
|
base_depends.append(value)
|
||||||
|
elif is_target_pkg:
|
||||||
|
pkg_depends.append(value)
|
||||||
|
|
||||||
|
case "makedepends":
|
||||||
|
if is_base:
|
||||||
|
base_makedepends.append(value)
|
||||||
|
elif is_target_pkg:
|
||||||
|
pkg_makedepends.append(value)
|
||||||
|
|
||||||
|
case "checkdepends":
|
||||||
|
if is_base:
|
||||||
|
base_checkdepends.append(value)
|
||||||
|
elif is_target_pkg:
|
||||||
|
pkg_checkdepends.append(value)
|
||||||
|
|
||||||
|
case _ if key.startswith("depends") and key.removeprefix("depends_") == config.arch:
|
||||||
|
if is_base:
|
||||||
|
base_depends.append(value)
|
||||||
|
elif is_target_pkg:
|
||||||
|
pkg_depends.append(value)
|
||||||
|
|
||||||
|
case _ if (
|
||||||
|
key.startswith("makedepends")
|
||||||
|
and key.removeprefix("makedepends_") == config.arch
|
||||||
|
):
|
||||||
|
if is_base:
|
||||||
|
base_makedepends.append(value)
|
||||||
|
elif is_target_pkg:
|
||||||
|
pkg_makedepends.append(value)
|
||||||
|
|
||||||
|
case _ if (
|
||||||
|
key.startswith("checkdepends")
|
||||||
|
and key.removeprefix("checkdepends_") == config.arch
|
||||||
|
):
|
||||||
|
if is_base:
|
||||||
|
base_checkdepends.append(value)
|
||||||
|
elif is_target_pkg:
|
||||||
|
pkg_checkdepends.append(value)
|
||||||
|
|
||||||
|
if pkgbase is None or pkgver is None:
|
||||||
|
raise PKGBUILDParseError(
|
||||||
|
self.git_url,
|
||||||
|
self.pkgbuild_directory,
|
||||||
|
"Missing required fields (pkgbase/pkgver) in SRCINFO.",
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.pkgname not in found_pkgnames:
|
||||||
|
raise PKGBUILDParseError(
|
||||||
|
self.git_url,
|
||||||
|
self.pkgbuild_directory,
|
||||||
|
f"Package {self.pkgname} not found in SRCINFO.\
|
||||||
|
Packages present: {' '.join(found_pkgnames)}.",
|
||||||
|
)
|
||||||
|
|
||||||
|
version_core = pkgver
|
||||||
|
if pkgrel is not None:
|
||||||
|
version_core = f"{version_core}-{pkgrel}"
|
||||||
|
|
||||||
|
if epoch is not None:
|
||||||
|
version = f"{epoch}:{version_core}"
|
||||||
|
else:
|
||||||
|
version = version_core
|
||||||
|
|
||||||
|
return PackageInfo(
|
||||||
|
pkgname=self.pkgname,
|
||||||
|
pkgbase=pkgbase,
|
||||||
|
version=version,
|
||||||
|
git_url=self.git_url,
|
||||||
|
pkgbuild_directory=self.pkgbuild_directory,
|
||||||
|
provides=tuple(provides),
|
||||||
|
dependencies=tuple(base_depends + pkg_depends),
|
||||||
|
make_dependencies=tuple(base_makedepends + pkg_makedepends),
|
||||||
|
check_dependencies=tuple(base_checkdepends + pkg_checkdepends),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PackageSearch:
|
class PackageSearch:
|
||||||
|
|||||||
+246
-3
@@ -1,8 +1,9 @@
|
|||||||
|
import pathlib
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from decman.plugins.pacman import package as pkg_mod
|
from decman.plugins.pacman import package as pkg_mod
|
||||||
from decman.plugins.pacman.error import AurRPCError
|
from decman.plugins.pacman.error import AurRPCError, PKGBUILDParseError
|
||||||
from decman.plugins.pacman.package import (
|
from decman.plugins.pacman.package import (
|
||||||
CustomPackage,
|
CustomPackage,
|
||||||
PackageInfo,
|
PackageInfo,
|
||||||
@@ -102,10 +103,252 @@ def test_packageinfo_foreign_and_native_dependencies_are_split_and_stripped():
|
|||||||
|
|
||||||
def test_custompackage_requires_exactly_one_source():
|
def test_custompackage_requires_exactly_one_source():
|
||||||
with pytest.raises(ValueError, match="cannot be None"):
|
with pytest.raises(ValueError, match="cannot be None"):
|
||||||
CustomPackage(git_url=None, pkgbuild_directory=None)
|
CustomPackage("pkg", git_url=None, pkgbuild_directory=None)
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="cannot be set"):
|
with pytest.raises(ValueError, match="cannot be set"):
|
||||||
CustomPackage(git_url="git://example", pkgbuild_directory="/tmp")
|
CustomPackage("pkg", git_url="git://example", pkgbuild_directory="/tmp")
|
||||||
|
|
||||||
|
|
||||||
|
class DummyCommands:
|
||||||
|
"""Minimal stub; only here so type checks pass where needed."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"srcinfo, expected_version",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"""
|
||||||
|
pkgbase = foo
|
||||||
|
pkgver = 1.2.3
|
||||||
|
pkgrel = 4
|
||||||
|
pkgname = foo
|
||||||
|
""",
|
||||||
|
"1.2.3-4",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"""
|
||||||
|
pkgbase = foo
|
||||||
|
pkgver = 1.2.3
|
||||||
|
pkgrel = 4
|
||||||
|
epoch = 2
|
||||||
|
pkgname = foo
|
||||||
|
""",
|
||||||
|
"2:1.2.3-4",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"""
|
||||||
|
pkgbase = foo
|
||||||
|
pkgver = 1.2.3
|
||||||
|
pkgname = foo
|
||||||
|
""",
|
||||||
|
"1.2.3",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_parse_srcinfo_version_handling(srcinfo: str, expected_version: str) -> None:
|
||||||
|
pkg = CustomPackage(pkgname="foo", git_url=None, pkgbuild_directory="/dummy")
|
||||||
|
|
||||||
|
info = pkg._parse_srcinfo(srcinfo)
|
||||||
|
|
||||||
|
assert info.pkgname == "foo"
|
||||||
|
assert info.pkgbase == "foo"
|
||||||
|
assert info.version == expected_version
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_srcinfo_single_package_dependencies() -> None:
|
||||||
|
srcinfo = """
|
||||||
|
pkgbase = foo
|
||||||
|
pkgver = 1.2.3
|
||||||
|
pkgrel = 1
|
||||||
|
depends = bar>=1.0
|
||||||
|
makedepends = baz
|
||||||
|
checkdepends = qux
|
||||||
|
|
||||||
|
pkgname = foo
|
||||||
|
"""
|
||||||
|
|
||||||
|
pkg = CustomPackage(pkgname="foo", git_url=None, pkgbuild_directory="/dummy")
|
||||||
|
|
||||||
|
info = pkg._parse_srcinfo(srcinfo)
|
||||||
|
|
||||||
|
assert info.dependencies == ("bar>=1.0",)
|
||||||
|
assert info.make_dependencies == ("baz",)
|
||||||
|
assert info.check_dependencies == ("qux",)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_srcinfo_split_package_uses_only_target_pkg_dependencies(monkeypatch) -> None:
|
||||||
|
# Ensure arch-specific keys match
|
||||||
|
monkeypatch.setattr(pkg_mod.config, "arch", "x86_64", raising=False)
|
||||||
|
|
||||||
|
srcinfo = """
|
||||||
|
pkgbase = clion
|
||||||
|
pkgver = 2025.3
|
||||||
|
pkgrel = 1
|
||||||
|
makedepends = rsync
|
||||||
|
depends = base-dep
|
||||||
|
depends_x86_64 = base-arch-dep
|
||||||
|
|
||||||
|
pkgname = clion
|
||||||
|
depends = libdbusmenu-glib
|
||||||
|
depends_x86_64 = clion-arch-dep
|
||||||
|
checkdepends = clion-check
|
||||||
|
|
||||||
|
pkgname = clion-jre
|
||||||
|
depends = jre-dep
|
||||||
|
makedepends = jre-make
|
||||||
|
|
||||||
|
pkgname = clion-cmake
|
||||||
|
depends = cmake-dep
|
||||||
|
"""
|
||||||
|
|
||||||
|
pkg = CustomPackage(pkgname="clion", git_url=None, pkgbuild_directory="/dummy")
|
||||||
|
|
||||||
|
info = pkg._parse_srcinfo(srcinfo)
|
||||||
|
|
||||||
|
# version
|
||||||
|
assert info.pkgbase == "clion"
|
||||||
|
assert info.version == "2025.3-1"
|
||||||
|
|
||||||
|
# base deps + target pkg deps (including arch-specific)
|
||||||
|
assert info.dependencies == (
|
||||||
|
"base-dep",
|
||||||
|
"base-arch-dep",
|
||||||
|
"libdbusmenu-glib",
|
||||||
|
"clion-arch-dep",
|
||||||
|
)
|
||||||
|
|
||||||
|
# only base and target pkg makedepends
|
||||||
|
assert info.make_dependencies == ("rsync",)
|
||||||
|
|
||||||
|
# base + target pkg checkdepends
|
||||||
|
assert info.check_dependencies == ("clion-check",)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_srcinfo_arch_specific_ignored_for_other_arch(monkeypatch) -> None:
|
||||||
|
# Different arch → *_x86_64 keys should be ignored
|
||||||
|
monkeypatch.setattr(pkg_mod.config, "arch", "aarch64", raising=False)
|
||||||
|
|
||||||
|
srcinfo = """
|
||||||
|
pkgbase = foo
|
||||||
|
pkgver = 1.0
|
||||||
|
pkgrel = 1
|
||||||
|
depends_x86_64 = base-arch-dep
|
||||||
|
|
||||||
|
pkgname = foo
|
||||||
|
depends = common-dep
|
||||||
|
depends_x86_64 = pkg-arch-dep
|
||||||
|
"""
|
||||||
|
|
||||||
|
pkg = CustomPackage(pkgname="foo", git_url=None, pkgbuild_directory="/dummy")
|
||||||
|
|
||||||
|
info = pkg._parse_srcinfo(srcinfo)
|
||||||
|
|
||||||
|
# Only common deps, no *_x86_64 because arch != x86_64
|
||||||
|
assert info.dependencies == ("common-dep",)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_srcinfo_missing_required_fields_raises() -> None:
|
||||||
|
# Missing pkgbase
|
||||||
|
srcinfo_no_pkgbase = """
|
||||||
|
pkgver = 1.0
|
||||||
|
pkgrel = 1
|
||||||
|
pkgname = foo
|
||||||
|
"""
|
||||||
|
pkg = CustomPackage(pkgname="foo", git_url=None, pkgbuild_directory="/dummy")
|
||||||
|
|
||||||
|
with pytest.raises(PKGBUILDParseError) as excinfo:
|
||||||
|
pkg._parse_srcinfo(srcinfo_no_pkgbase)
|
||||||
|
assert "pkgbase/pkgver" in str(excinfo.value)
|
||||||
|
|
||||||
|
# Missing pkgver
|
||||||
|
srcinfo_no_pkgver = """
|
||||||
|
pkgbase = foo
|
||||||
|
pkgname = foo
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(PKGBUILDParseError) as excinfo2:
|
||||||
|
pkg._parse_srcinfo(srcinfo_no_pkgver)
|
||||||
|
assert "pkgbase/pkgver" in str(excinfo2.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_srcinfo_missing_target_pkg_raises() -> None:
|
||||||
|
srcinfo = """
|
||||||
|
pkgbase = foo
|
||||||
|
pkgver = 1.0
|
||||||
|
pkgrel = 1
|
||||||
|
pkgname = other
|
||||||
|
"""
|
||||||
|
|
||||||
|
pkg = CustomPackage(pkgname="foo", git_url=None, pkgbuild_directory="/dummy")
|
||||||
|
|
||||||
|
with pytest.raises(PKGBUILDParseError) as excinfo:
|
||||||
|
pkg._parse_srcinfo(srcinfo)
|
||||||
|
|
||||||
|
msg = str(excinfo.value)
|
||||||
|
assert "Package foo not found in SRCINFO" in msg
|
||||||
|
assert "other" in msg # listed in present packages
|
||||||
|
|
||||||
|
|
||||||
|
def test_srcinfo_from_pkgbuild_directory_missing_dir_raises(tmp_path: pathlib.Path) -> None:
|
||||||
|
missing = tmp_path / "does-not-exist"
|
||||||
|
|
||||||
|
pkg = CustomPackage(pkgname="foo", git_url=None, pkgbuild_directory=str(missing))
|
||||||
|
|
||||||
|
with pytest.raises(PKGBUILDParseError) as excinfo:
|
||||||
|
pkg._srcinfo_from_pkgbuild_directory(DummyCommands())
|
||||||
|
|
||||||
|
msg = str(excinfo.value)
|
||||||
|
assert "does not exist or is not a directory" in msg
|
||||||
|
|
||||||
|
|
||||||
|
def test_srcinfo_from_pkgbuild_directory_missing_pkgbuild_raises(tmp_path: pathlib.Path) -> None:
|
||||||
|
path = tmp_path / "pkgdir"
|
||||||
|
path.mkdir()
|
||||||
|
|
||||||
|
pkg = CustomPackage(pkgname="foo", git_url=None, pkgbuild_directory=str(path))
|
||||||
|
|
||||||
|
with pytest.raises(PKGBUILDParseError) as excinfo:
|
||||||
|
pkg._srcinfo_from_pkgbuild_directory(DummyCommands())
|
||||||
|
|
||||||
|
msg = str(excinfo.value)
|
||||||
|
assert "No PKGBUILD found" in msg
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_package_equality_and_hash() -> None:
|
||||||
|
a1 = CustomPackage(
|
||||||
|
pkgname="foo", git_url="https://example.com/repo.git", pkgbuild_directory=None
|
||||||
|
)
|
||||||
|
a2 = CustomPackage(
|
||||||
|
pkgname="foo", git_url="https://example.com/repo.git", pkgbuild_directory=None
|
||||||
|
)
|
||||||
|
b = CustomPackage(pkgname="foo", git_url=None, pkgbuild_directory="/some/path")
|
||||||
|
|
||||||
|
assert a1 == a2
|
||||||
|
assert hash(a1) == hash(a2)
|
||||||
|
|
||||||
|
assert a1 != b
|
||||||
|
assert hash(a1) != hash(b)
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_package_str_git_and_directory() -> None:
|
||||||
|
git_pkg = CustomPackage(
|
||||||
|
pkgname="foo",
|
||||||
|
git_url="https://example.com/repo.git",
|
||||||
|
pkgbuild_directory=None,
|
||||||
|
)
|
||||||
|
dir_pkg = CustomPackage(
|
||||||
|
pkgname="foo",
|
||||||
|
git_url=None,
|
||||||
|
pkgbuild_directory="/some/path",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "pkgname=foo" in str(git_pkg)
|
||||||
|
assert "git_url=https://example.com/repo.git" in str(git_pkg)
|
||||||
|
|
||||||
|
assert "pkgname=foo" in str(dir_pkg)
|
||||||
|
assert "pkgbuild_directory=/some/path" in str(dir_pkg)
|
||||||
|
|
||||||
|
|
||||||
# --- PackageSearch: caching ------------------------------------------------
|
# --- PackageSearch: caching ------------------------------------------------
|
||||||
Reference in New Issue
Block a user