mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 07:17:17 +00:00
patch support (#35)
This commit is contained in:
parent
e897e2cde2
commit
3e0058620b
@ -23,7 +23,7 @@ import shutil
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, Iterable, List, Set
|
from typing import Callable, Iterable, List, Set
|
||||||
|
|
||||||
from ahriman.core.build_tools.task import Task
|
from ahriman.core.build_tools.sources import Sources
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.repository.repository import Repository
|
from ahriman.core.repository.repository import Repository
|
||||||
from ahriman.core.tree import Tree
|
from ahriman.core.tree import Tree
|
||||||
@ -112,9 +112,9 @@ class Application:
|
|||||||
|
|
||||||
def add_manual(src: str) -> Path:
|
def add_manual(src: str) -> Path:
|
||||||
package = Package.load(src, self.repository.pacman, self.configuration.get("alpm", "aur_url"))
|
package = Package.load(src, self.repository.pacman, self.configuration.get("alpm", "aur_url"))
|
||||||
path = self.repository.paths.manual / package.base
|
Sources.load(self.repository.paths.manual_for(package.base), package.git_url,
|
||||||
Task.fetch(path, package.git_url)
|
self.repository.paths.patches_for(package.base))
|
||||||
return path
|
return self.repository.paths.manual_for(package.base)
|
||||||
|
|
||||||
def add_archive(src: Path) -> None:
|
def add_archive(src: Path) -> None:
|
||||||
dst = self.repository.paths.packages / src.name
|
dst = self.repository.paths.packages / src.name
|
||||||
@ -238,7 +238,7 @@ class Application:
|
|||||||
process_update(packages)
|
process_update(packages)
|
||||||
|
|
||||||
# process manual packages
|
# process manual packages
|
||||||
tree = Tree.load(updates)
|
tree = Tree.load(updates, self.repository.paths)
|
||||||
for num, level in enumerate(tree.levels()):
|
for num, level in enumerate(tree.levels()):
|
||||||
self.logger.info("processing level #%i %s", num, [package.base for package in level])
|
self.logger.info("processing level #%i %s", num, [package.base for package in level])
|
||||||
packages = self.repository.process_build(level)
|
packages = self.repository.process_build(level)
|
||||||
|
83
src/ahriman/core/build_tools/sources.py
Normal file
83
src/ahriman/core/build_tools/sources.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ahriman.core.util import check_output
|
||||||
|
|
||||||
|
|
||||||
|
class Sources:
|
||||||
|
"""
|
||||||
|
helper to download package sources (PKGBUILD etc)
|
||||||
|
"""
|
||||||
|
|
||||||
|
_check_output = check_output
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fetch(local_path: Path, remote: str, branch: str = "master") -> None:
|
||||||
|
"""
|
||||||
|
either clone repository or update it to origin/`branch`
|
||||||
|
:param local_path: local path to fetch
|
||||||
|
:param remote: remote target (from where to fetch)
|
||||||
|
:param branch: branch name to checkout, master by default
|
||||||
|
"""
|
||||||
|
logger = logging.getLogger("build_details")
|
||||||
|
# local directory exists and there is .git directory
|
||||||
|
if (local_path / ".git").is_dir():
|
||||||
|
logger.info("update HEAD to remote to %s", local_path)
|
||||||
|
Sources._check_output("git", "fetch", "origin", branch, exception=None, cwd=local_path, logger=logger)
|
||||||
|
else:
|
||||||
|
logger.info("clone remote %s to %s", remote, local_path)
|
||||||
|
Sources._check_output("git", "clone", remote, str(local_path), exception=None, logger=logger)
|
||||||
|
# and now force reset to our branch
|
||||||
|
Sources._check_output("git", "checkout", "--force", branch, exception=None, cwd=local_path, logger=logger)
|
||||||
|
Sources._check_output("git", "reset", "--hard", f"origin/{branch}",
|
||||||
|
exception=None, cwd=local_path, logger=logger)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load(local_path: Path, remote: str, patch_path: Path) -> None:
|
||||||
|
"""
|
||||||
|
fetch sources from remote and apply patches
|
||||||
|
:param local_path: local path to fetch
|
||||||
|
:param remote: remote target (from where to fetch)
|
||||||
|
:param patch_path: path to directory with package patches
|
||||||
|
"""
|
||||||
|
Sources.fetch(local_path, remote)
|
||||||
|
Sources.patch(local_path, patch_path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def patch(local_path: Path, patch_path: Path) -> None:
|
||||||
|
"""
|
||||||
|
apply patches if any
|
||||||
|
:param local_path: local path to directory with git sources
|
||||||
|
:param patch_path: path to directory with package patches
|
||||||
|
"""
|
||||||
|
# check if even there are patches
|
||||||
|
if not patch_path.is_dir():
|
||||||
|
return # no patches provided
|
||||||
|
logger = logging.getLogger("build_details")
|
||||||
|
# find everything that looks like patch and sort it
|
||||||
|
patches = sorted(patch_path.glob("*.patch"))
|
||||||
|
logger.info("found %s patches", patches)
|
||||||
|
for patch in patches:
|
||||||
|
logger.info("apply patch %s", patch.name)
|
||||||
|
Sources._check_output("git", "apply", "--ignore-space-change", "--ignore-whitespace", str(patch),
|
||||||
|
exception=None, cwd=local_path, logger=logger)
|
@ -23,6 +23,7 @@ import shutil
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from ahriman.core.build_tools.sources import Sources
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.exceptions import BuildFailed
|
from ahriman.core.exceptions import BuildFailed
|
||||||
from ahriman.core.util import check_output
|
from ahriman.core.util import check_output
|
||||||
@ -58,38 +59,6 @@ class Task:
|
|||||||
self.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[])
|
self.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[])
|
||||||
self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[])
|
self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[])
|
||||||
|
|
||||||
@property
|
|
||||||
def cache_path(self) -> Path:
|
|
||||||
"""
|
|
||||||
:return: path to cached packages
|
|
||||||
"""
|
|
||||||
return self.paths.cache / self.package.base
|
|
||||||
|
|
||||||
@property
|
|
||||||
def git_path(self) -> Path:
|
|
||||||
"""
|
|
||||||
:return: path to clone package from git
|
|
||||||
"""
|
|
||||||
return self.paths.sources / self.package.base
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def fetch(local: Path, remote: str, branch: str = "master") -> None:
|
|
||||||
"""
|
|
||||||
either clone repository or update it to origin/`branch`
|
|
||||||
:param local: local path to fetch
|
|
||||||
:param remote: remote target (from where to fetch)
|
|
||||||
:param branch: branch name to checkout, master by default
|
|
||||||
"""
|
|
||||||
logger = logging.getLogger("build_details")
|
|
||||||
# local directory exists and there is .git directory
|
|
||||||
if (local / ".git").is_dir():
|
|
||||||
Task._check_output("git", "fetch", "origin", branch, exception=None, cwd=local, logger=logger)
|
|
||||||
else:
|
|
||||||
Task._check_output("git", "clone", remote, str(local), exception=None, logger=logger)
|
|
||||||
# and now force reset to our branch
|
|
||||||
Task._check_output("git", "checkout", "--force", branch, exception=None, cwd=local, logger=logger)
|
|
||||||
Task._check_output("git", "reset", "--hard", f"origin/{branch}", exception=None, cwd=local, logger=logger)
|
|
||||||
|
|
||||||
def build(self) -> List[Path]:
|
def build(self) -> List[Path]:
|
||||||
"""
|
"""
|
||||||
run package build
|
run package build
|
||||||
@ -104,13 +73,13 @@ class Task:
|
|||||||
Task._check_output(
|
Task._check_output(
|
||||||
*command,
|
*command,
|
||||||
exception=BuildFailed(self.package.base),
|
exception=BuildFailed(self.package.base),
|
||||||
cwd=self.git_path,
|
cwd=self.paths.sources_for(self.package.base),
|
||||||
logger=self.build_logger)
|
logger=self.build_logger)
|
||||||
|
|
||||||
# well it is not actually correct, but we can deal with it
|
# well it is not actually correct, but we can deal with it
|
||||||
packages = Task._check_output("makepkg", "--packagelist",
|
packages = Task._check_output("makepkg", "--packagelist",
|
||||||
exception=BuildFailed(self.package.base),
|
exception=BuildFailed(self.package.base),
|
||||||
cwd=self.git_path,
|
cwd=self.paths.sources_for(self.package.base),
|
||||||
logger=self.build_logger).splitlines()
|
logger=self.build_logger).splitlines()
|
||||||
return [Path(package) for package in packages]
|
return [Path(package) for package in packages]
|
||||||
|
|
||||||
@ -119,8 +88,8 @@ class Task:
|
|||||||
fetch package from git
|
fetch package from git
|
||||||
:param path: optional local path to fetch. If not set default path will be used
|
:param path: optional local path to fetch. If not set default path will be used
|
||||||
"""
|
"""
|
||||||
git_path = path or self.git_path
|
git_path = path or self.paths.sources_for(self.package.base)
|
||||||
if self.cache_path.is_dir():
|
if self.paths.cache_for(self.package.base).is_dir():
|
||||||
# no need to clone whole repository, just copy from cache first
|
# no need to clone whole repository, just copy from cache first
|
||||||
shutil.copytree(self.cache_path, git_path)
|
shutil.copytree(self.paths.cache_for(self.package.base), git_path)
|
||||||
return self.fetch(git_path, self.package.git_url)
|
Sources.load(git_path, self.package.git_url, self.paths.patches_for(self.package.base))
|
||||||
|
@ -25,8 +25,9 @@ import tempfile
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable, List, Set, Type
|
from typing import Iterable, List, Set, Type
|
||||||
|
|
||||||
from ahriman.core.build_tools.task import Task
|
from ahriman.core.build_tools.sources import Sources
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
|
|
||||||
class Leaf:
|
class Leaf:
|
||||||
@ -53,15 +54,16 @@ class Leaf:
|
|||||||
return self.package.packages.keys()
|
return self.package.packages.keys()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls: Type[Leaf], package: Package) -> Leaf:
|
def load(cls: Type[Leaf], package: Package, paths: RepositoryPaths) -> Leaf:
|
||||||
"""
|
"""
|
||||||
load leaf from package with dependencies
|
load leaf from package with dependencies
|
||||||
:param package: package properties
|
:param package: package properties
|
||||||
|
:param paths: repository paths instance
|
||||||
:return: loaded class
|
:return: loaded class
|
||||||
"""
|
"""
|
||||||
clone_dir = Path(tempfile.mkdtemp())
|
clone_dir = Path(tempfile.mkdtemp())
|
||||||
try:
|
try:
|
||||||
Task.fetch(clone_dir, package.git_url)
|
Sources.load(clone_dir, package.git_url, paths.patches_for(package.base))
|
||||||
dependencies = Package.dependencies(clone_dir)
|
dependencies = Package.dependencies(clone_dir)
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(clone_dir, ignore_errors=True)
|
shutil.rmtree(clone_dir, ignore_errors=True)
|
||||||
@ -93,13 +95,14 @@ class Tree:
|
|||||||
self.leaves = leaves
|
self.leaves = leaves
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls: Type[Tree], packages: Iterable[Package]) -> Tree:
|
def load(cls: Type[Tree], packages: Iterable[Package], paths: RepositoryPaths) -> Tree:
|
||||||
"""
|
"""
|
||||||
load tree from packages
|
load tree from packages
|
||||||
:param packages: packages list
|
:param packages: packages list
|
||||||
|
:param paths: repository paths instance
|
||||||
:return: loaded class
|
:return: loaded class
|
||||||
"""
|
"""
|
||||||
return cls([Leaf.load(package) for package in packages])
|
return cls([Leaf.load(package, paths) for package in packages])
|
||||||
|
|
||||||
def levels(self) -> List[List[Package]]:
|
def levels(self) -> List[List[Package]]:
|
||||||
"""
|
"""
|
||||||
|
@ -231,22 +231,18 @@ class Package:
|
|||||||
if not self.is_vcs:
|
if not self.is_vcs:
|
||||||
return self.version
|
return self.version
|
||||||
|
|
||||||
from ahriman.core.build_tools.task import Task
|
from ahriman.core.build_tools.sources import Sources
|
||||||
|
|
||||||
clone_dir = paths.cache / self.base
|
|
||||||
logger = logging.getLogger("build_details")
|
logger = logging.getLogger("build_details")
|
||||||
Task.fetch(clone_dir, self.git_url)
|
Sources.load(paths.cache_for(self.base), self.git_url, paths.patches_for(self.base))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# update pkgver first
|
# update pkgver first
|
||||||
Package._check_output("makepkg", "--nodeps", "--nobuild", exception=None, cwd=clone_dir, logger=logger)
|
Package._check_output("makepkg", "--nodeps", "--nobuild",
|
||||||
|
exception=None, cwd=paths.cache_for(self.base), logger=logger)
|
||||||
# generate new .SRCINFO and put it to parser
|
# generate new .SRCINFO and put it to parser
|
||||||
srcinfo_source = Package._check_output(
|
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo",
|
||||||
"makepkg",
|
exception=None, cwd=paths.cache_for(self.base), logger=logger)
|
||||||
"--printsrcinfo",
|
|
||||||
exception=None,
|
|
||||||
cwd=clone_dir,
|
|
||||||
logger=logger)
|
|
||||||
srcinfo, errors = parse_srcinfo(srcinfo_source)
|
srcinfo, errors = parse_srcinfo(srcinfo_source)
|
||||||
if errors:
|
if errors:
|
||||||
raise InvalidPackageInfo(errors)
|
raise InvalidPackageInfo(errors)
|
||||||
|
@ -64,6 +64,13 @@ class RepositoryPaths:
|
|||||||
"""
|
"""
|
||||||
return self.root / "packages" / self.architecture
|
return self.root / "packages" / self.architecture
|
||||||
|
|
||||||
|
@property
|
||||||
|
def patches(self) -> Path:
|
||||||
|
"""
|
||||||
|
:return: directory for source patches
|
||||||
|
"""
|
||||||
|
return self.root / "patches"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def repository(self) -> Path:
|
def repository(self) -> Path:
|
||||||
"""
|
"""
|
||||||
@ -92,6 +99,14 @@ class RepositoryPaths:
|
|||||||
if path.is_dir()
|
if path.is_dir()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def cache_for(self, package_base: str) -> Path:
|
||||||
|
"""
|
||||||
|
get cache path for specific package base
|
||||||
|
:param package_base: package base name
|
||||||
|
:return: full path to directory for specified package base cache
|
||||||
|
"""
|
||||||
|
return self.cache / package_base
|
||||||
|
|
||||||
def create_tree(self) -> None:
|
def create_tree(self) -> None:
|
||||||
"""
|
"""
|
||||||
create ahriman working tree
|
create ahriman working tree
|
||||||
@ -100,5 +115,30 @@ class RepositoryPaths:
|
|||||||
self.chroot.mkdir(mode=0o755, parents=True, exist_ok=True)
|
self.chroot.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||||
self.manual.mkdir(mode=0o755, parents=True, exist_ok=True)
|
self.manual.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||||
self.packages.mkdir(mode=0o755, parents=True, exist_ok=True)
|
self.packages.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||||
|
self.patches.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||||
self.repository.mkdir(mode=0o755, parents=True, exist_ok=True)
|
self.repository.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||||
self.sources.mkdir(mode=0o755, parents=True, exist_ok=True)
|
self.sources.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
def manual_for(self, package_base: str) -> Path:
|
||||||
|
"""
|
||||||
|
get manual path for specific package base
|
||||||
|
:param package_base: package base name
|
||||||
|
:return: full path to directory for specified package base manual updates
|
||||||
|
"""
|
||||||
|
return self.manual / package_base
|
||||||
|
|
||||||
|
def patches_for(self, package_base: str) -> Path:
|
||||||
|
"""
|
||||||
|
get patches path for specific package base
|
||||||
|
:param package_base: package base name
|
||||||
|
:return: full path to directory for specified package base patches
|
||||||
|
"""
|
||||||
|
return self.patches / package_base
|
||||||
|
|
||||||
|
def sources_for(self, package_base: str) -> Path:
|
||||||
|
"""
|
||||||
|
get sources path for specific package base
|
||||||
|
:param package_base: package base name
|
||||||
|
:return: full path to directory for specified package base sources
|
||||||
|
"""
|
||||||
|
return self.sources / package_base
|
||||||
|
@ -124,10 +124,10 @@ def test_add_manual(application: Application, package_ahriman: Package, mocker:
|
|||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
|
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
|
||||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||||
fetch_mock = mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load")
|
||||||
|
|
||||||
application.add([package_ahriman.base], PackageSource.AUR, True)
|
application.add([package_ahriman.base], PackageSource.AUR, True)
|
||||||
fetch_mock.assert_called_once()
|
load_mock.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
def test_add_manual_with_dependencies(application: Application, package_ahriman: Package,
|
def test_add_manual_with_dependencies(application: Application, package_ahriman: Package,
|
||||||
@ -137,7 +137,7 @@ def test_add_manual_with_dependencies(application: Application, package_ahriman:
|
|||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
|
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
|
||||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
|
||||||
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies")
|
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies")
|
||||||
|
|
||||||
application.add([package_ahriman.base], PackageSource.AUR, False)
|
application.add([package_ahriman.base], PackageSource.AUR, False)
|
||||||
|
107
tests/ahriman/core/build_tools/test_sources.py
Normal file
107
tests/ahriman/core/build_tools/test_sources.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from ahriman.core.build_tools.sources import Sources
|
||||||
|
|
||||||
|
|
||||||
|
def test_fetch_existing(mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must fetch new package via clone command
|
||||||
|
"""
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
|
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||||
|
|
||||||
|
local = Path("local")
|
||||||
|
Sources.fetch(local, "remote", "master")
|
||||||
|
check_output_mock.assert_has_calls([
|
||||||
|
mock.call("git", "fetch", "origin", "master",
|
||||||
|
exception=pytest.helpers.anyvar(int),
|
||||||
|
cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||||
|
mock.call("git", "checkout", "--force", "master",
|
||||||
|
exception=pytest.helpers.anyvar(int),
|
||||||
|
cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||||
|
mock.call("git", "reset", "--hard", "origin/master",
|
||||||
|
exception=pytest.helpers.anyvar(int),
|
||||||
|
cwd=local, logger=pytest.helpers.anyvar(int))
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_fetch_new(mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must fetch new package via clone command
|
||||||
|
"""
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=False)
|
||||||
|
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||||
|
|
||||||
|
local = Path("local")
|
||||||
|
Sources.fetch(local, "remote", "master")
|
||||||
|
check_output_mock.assert_has_calls([
|
||||||
|
mock.call("git", "clone", "remote", str(local),
|
||||||
|
exception=pytest.helpers.anyvar(int),
|
||||||
|
logger=pytest.helpers.anyvar(int)),
|
||||||
|
mock.call("git", "checkout", "--force", "master",
|
||||||
|
exception=pytest.helpers.anyvar(int),
|
||||||
|
cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||||
|
mock.call("git", "reset", "--hard", "origin/master",
|
||||||
|
exception=pytest.helpers.anyvar(int),
|
||||||
|
cwd=local, logger=pytest.helpers.anyvar(int))
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_load(mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must load packages sources correctly
|
||||||
|
"""
|
||||||
|
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||||
|
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch")
|
||||||
|
|
||||||
|
Sources.load(Path("local"), "remote", Path("patches"))
|
||||||
|
fetch_mock.assert_called_with(Path("local"), "remote")
|
||||||
|
patch_mock.assert_called_with(Path("local"), Path("patches"))
|
||||||
|
|
||||||
|
|
||||||
|
def test_patches(mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must apply patches if any
|
||||||
|
"""
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
|
glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path("01.patch"), Path("02.patch")])
|
||||||
|
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||||
|
|
||||||
|
local = Path("local")
|
||||||
|
Sources.patch(local, Path("patches"))
|
||||||
|
glob_mock.assert_called_once()
|
||||||
|
check_output_mock.assert_has_calls([
|
||||||
|
mock.call("git", "apply", "--ignore-space-change", "--ignore-whitespace", "01.patch",
|
||||||
|
exception=pytest.helpers.anyvar(int),
|
||||||
|
cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||||
|
mock.call("git", "apply", "--ignore-space-change", "--ignore-whitespace", "02.patch",
|
||||||
|
exception=pytest.helpers.anyvar(int),
|
||||||
|
cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_patches_no_dir(mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must not fail if no patches directory exists
|
||||||
|
"""
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=False)
|
||||||
|
glob_mock = mocker.patch("pathlib.Path.glob")
|
||||||
|
|
||||||
|
Sources.patch(Path("local"), Path("patches"))
|
||||||
|
glob_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_patches_no_patches(mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must not fail if no patches exist
|
||||||
|
"""
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
|
mocker.patch("pathlib.Path.glob", return_value=[])
|
||||||
|
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||||
|
|
||||||
|
Sources.patch(Path("local"), Path("patches"))
|
||||||
|
check_output_mock.assert_not_called()
|
@ -1,56 +1,8 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from ahriman.core.build_tools.task import Task
|
from ahriman.core.build_tools.task import Task
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_existing(mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must fetch new package via clone command
|
|
||||||
"""
|
|
||||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
|
||||||
check_output_mock = mocker.patch("ahriman.core.build_tools.task.Task._check_output")
|
|
||||||
|
|
||||||
local = Path("local")
|
|
||||||
Task.fetch(local, "remote", "master")
|
|
||||||
check_output_mock.assert_has_calls([
|
|
||||||
mock.call("git", "fetch", "origin", "master",
|
|
||||||
exception=pytest.helpers.anyvar(int),
|
|
||||||
cwd=local, logger=pytest.helpers.anyvar(int)),
|
|
||||||
mock.call("git", "checkout", "--force", "master",
|
|
||||||
exception=pytest.helpers.anyvar(int),
|
|
||||||
cwd=local, logger=pytest.helpers.anyvar(int)),
|
|
||||||
mock.call("git", "reset", "--hard", "origin/master",
|
|
||||||
exception=pytest.helpers.anyvar(int),
|
|
||||||
cwd=local, logger=pytest.helpers.anyvar(int))
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_new(mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must fetch new package via clone command
|
|
||||||
"""
|
|
||||||
mocker.patch("pathlib.Path.is_dir", return_value=False)
|
|
||||||
check_output_mock = mocker.patch("ahriman.core.build_tools.task.Task._check_output")
|
|
||||||
|
|
||||||
local = Path("local")
|
|
||||||
Task.fetch(local, "remote", "master")
|
|
||||||
check_output_mock.assert_has_calls([
|
|
||||||
mock.call("git", "clone", "remote", str(local),
|
|
||||||
exception=pytest.helpers.anyvar(int),
|
|
||||||
logger=pytest.helpers.anyvar(int)),
|
|
||||||
mock.call("git", "checkout", "--force", "master",
|
|
||||||
exception=pytest.helpers.anyvar(int),
|
|
||||||
cwd=local, logger=pytest.helpers.anyvar(int)),
|
|
||||||
mock.call("git", "reset", "--hard", "origin/master",
|
|
||||||
exception=pytest.helpers.anyvar(int),
|
|
||||||
cwd=local, logger=pytest.helpers.anyvar(int))
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def test_build(task_ahriman: Task, mocker: MockerFixture) -> None:
|
def test_build(task_ahriman: Task, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must build package
|
must build package
|
||||||
@ -65,7 +17,7 @@ def test_init_with_cache(task_ahriman: Task, mocker: MockerFixture) -> None:
|
|||||||
must copy tree instead of fetch
|
must copy tree instead of fetch
|
||||||
"""
|
"""
|
||||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
|
||||||
copytree_mock = mocker.patch("shutil.copytree")
|
copytree_mock = mocker.patch("shutil.copytree")
|
||||||
|
|
||||||
task_ahriman.init(None)
|
task_ahriman.init(None)
|
||||||
|
@ -2,6 +2,7 @@ from pytest_mock import MockerFixture
|
|||||||
|
|
||||||
from ahriman.core.tree import Leaf, Tree
|
from ahriman.core.tree import Leaf, Tree
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
|
|
||||||
def test_leaf_is_root_empty(leaf_ahriman: Leaf) -> None:
|
def test_leaf_is_root_empty(leaf_ahriman: Leaf) -> None:
|
||||||
@ -34,25 +35,25 @@ def test_leaf_is_root_true(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> No
|
|||||||
assert not leaf_ahriman.is_root([leaf_python_schedule])
|
assert not leaf_ahriman.is_root([leaf_python_schedule])
|
||||||
|
|
||||||
|
|
||||||
def test_leaf_load(package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_leaf_load(package_ahriman: Package, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must load with dependencies
|
must load with dependencies
|
||||||
"""
|
"""
|
||||||
tempdir_mock = mocker.patch("tempfile.mkdtemp")
|
tempdir_mock = mocker.patch("tempfile.mkdtemp")
|
||||||
fetch_mock = mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load")
|
||||||
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies", return_value={"ahriman-dependency"})
|
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies", return_value={"ahriman-dependency"})
|
||||||
rmtree_mock = mocker.patch("shutil.rmtree")
|
rmtree_mock = mocker.patch("shutil.rmtree")
|
||||||
|
|
||||||
leaf = Leaf.load(package_ahriman)
|
leaf = Leaf.load(package_ahriman, repository_paths)
|
||||||
assert leaf.package == package_ahriman
|
assert leaf.package == package_ahriman
|
||||||
assert leaf.dependencies == {"ahriman-dependency"}
|
assert leaf.dependencies == {"ahriman-dependency"}
|
||||||
tempdir_mock.assert_called_once()
|
tempdir_mock.assert_called_once()
|
||||||
fetch_mock.assert_called_once()
|
load_mock.assert_called_once()
|
||||||
dependencies_mock.assert_called_once()
|
dependencies_mock.assert_called_once()
|
||||||
rmtree_mock.assert_called_once()
|
rmtree_mock.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf, mocker: MockerFixture) -> None:
|
def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> None:
|
||||||
"""
|
"""
|
||||||
must generate correct levels in the simples case
|
must generate correct levels in the simples case
|
||||||
"""
|
"""
|
||||||
@ -65,14 +66,15 @@ def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf, mocker: Moc
|
|||||||
assert second == [leaf_ahriman.package]
|
assert second == [leaf_ahriman.package]
|
||||||
|
|
||||||
|
|
||||||
def test_tree_load(package_ahriman: Package, package_python_schedule: Package, mocker: MockerFixture) -> None:
|
def test_tree_load(package_ahriman: Package, package_python_schedule: Package,
|
||||||
|
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must package list
|
must package list
|
||||||
"""
|
"""
|
||||||
mocker.patch("tempfile.mkdtemp")
|
mocker.patch("tempfile.mkdtemp")
|
||||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
|
||||||
mocker.patch("ahriman.models.package.Package.dependencies")
|
mocker.patch("ahriman.models.package.Package.dependencies")
|
||||||
mocker.patch("shutil.rmtree")
|
mocker.patch("shutil.rmtree")
|
||||||
|
|
||||||
tree = Tree.load([package_ahriman, package_python_schedule])
|
tree = Tree.load([package_ahriman, package_python_schedule], repository_paths)
|
||||||
assert len(tree.leaves) == 2
|
assert len(tree.leaves) == 2
|
||||||
|
@ -244,7 +244,7 @@ def test_actual_version_vcs(package_tpacpi_bat_git: Package, repository_paths: R
|
|||||||
"""
|
"""
|
||||||
srcinfo = (resource_path_root / "models" / "package_tpacpi-bat-git_srcinfo").read_text()
|
srcinfo = (resource_path_root / "models" / "package_tpacpi-bat-git_srcinfo").read_text()
|
||||||
mocker.patch("ahriman.models.package.Package._check_output", return_value=srcinfo)
|
mocker.patch("ahriman.models.package.Package._check_output", return_value=srcinfo)
|
||||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
|
||||||
|
|
||||||
assert package_tpacpi_bat_git.actual_version(repository_paths) == "3.1.r13.g4959b52-1"
|
assert package_tpacpi_bat_git.actual_version(repository_paths) == "3.1.r13.g4959b52-1"
|
||||||
|
|
||||||
@ -255,7 +255,7 @@ def test_actual_version_srcinfo_failed(package_tpacpi_bat_git: Package, reposito
|
|||||||
must return same version in case if exception occurred
|
must return same version in case if exception occurred
|
||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.models.package.Package._check_output", side_effect=Exception())
|
mocker.patch("ahriman.models.package.Package._check_output", side_effect=Exception())
|
||||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
|
||||||
|
|
||||||
assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version
|
assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version
|
||||||
|
|
||||||
@ -268,7 +268,7 @@ def test_actual_version_vcs_failed(package_tpacpi_bat_git: Package, repository_p
|
|||||||
mocker.patch("pathlib.Path.read_text", return_value="")
|
mocker.patch("pathlib.Path.read_text", return_value="")
|
||||||
mocker.patch("ahriman.models.package.parse_srcinfo", return_value=({"packages": {}}, ["an error"]))
|
mocker.patch("ahriman.models.package.parse_srcinfo", return_value=({"packages": {}}, ["an error"]))
|
||||||
mocker.patch("ahriman.models.package.Package._check_output")
|
mocker.patch("ahriman.models.package.Package._check_output")
|
||||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
|
||||||
|
|
||||||
assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version
|
assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.repository_paths import RepositoryPaths
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
|
|
||||||
@ -13,6 +14,15 @@ def test_known_architectures(repository_paths: RepositoryPaths, mocker: MockerFi
|
|||||||
iterdir_mock.assert_called_once()
|
iterdir_mock.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_cache_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must return correct path for cache directory
|
||||||
|
"""
|
||||||
|
path = repository_paths.cache_for(package_ahriman.base)
|
||||||
|
assert path.name == package_ahriman.base
|
||||||
|
assert path.parent == repository_paths.cache
|
||||||
|
|
||||||
|
|
||||||
def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must create whole tree
|
must create whole tree
|
||||||
@ -20,7 +30,9 @@ def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -
|
|||||||
paths = {
|
paths = {
|
||||||
prop
|
prop
|
||||||
for prop in dir(repository_paths)
|
for prop in dir(repository_paths)
|
||||||
if not prop.startswith("_") and prop not in ("architecture", "create_tree", "known_architectures", "root")
|
if not prop.startswith("_")
|
||||||
|
and not prop.endswith("_for")
|
||||||
|
and prop not in ("architecture", "create_tree", "known_architectures", "root")
|
||||||
}
|
}
|
||||||
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
|
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
|
||||||
|
|
||||||
@ -30,3 +42,30 @@ def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -
|
|||||||
mock.call(mode=0o755, parents=True, exist_ok=True)
|
mock.call(mode=0o755, parents=True, exist_ok=True)
|
||||||
for _ in paths
|
for _ in paths
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_manual_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must return correct path for manual directory
|
||||||
|
"""
|
||||||
|
path = repository_paths.manual_for(package_ahriman.base)
|
||||||
|
assert path.name == package_ahriman.base
|
||||||
|
assert path.parent == repository_paths.manual
|
||||||
|
|
||||||
|
|
||||||
|
def test_patches_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must return correct path for patches directory
|
||||||
|
"""
|
||||||
|
path = repository_paths.patches_for(package_ahriman.base)
|
||||||
|
assert path.name == package_ahriman.base
|
||||||
|
assert path.parent == repository_paths.patches
|
||||||
|
|
||||||
|
|
||||||
|
def test_sources_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must return correct path for sources directory
|
||||||
|
"""
|
||||||
|
path = repository_paths.sources_for(package_ahriman.base)
|
||||||
|
assert path.name == package_ahriman.base
|
||||||
|
assert path.parent == repository_paths.sources
|
||||||
|
Loading…
Reference in New Issue
Block a user