mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-29 01:37:17 +00:00
Compare commits
2 Commits
43c553a3db
...
e58ccdc8ad
Author | SHA1 | Date | |
---|---|---|---|
e58ccdc8ad | |||
2a07356d24 |
@ -13,11 +13,14 @@ ENV AHRIMAN_REPOSITORY_ROOT="/var/lib/ahriman/ahriman"
|
|||||||
ENV AHRIMAN_USER="ahriman"
|
ENV AHRIMAN_USER="ahriman"
|
||||||
|
|
||||||
# install environment
|
# install environment
|
||||||
|
## update pacman.conf with multilib
|
||||||
|
RUN echo "[multilib]" >> "/etc/pacman.conf" && \
|
||||||
|
echo "Include = /etc/pacman.d/mirrorlist" >> "/etc/pacman.conf"
|
||||||
## install minimal required packages
|
## install minimal required packages
|
||||||
RUN pacman --noconfirm -Syu binutils fakeroot git make sudo
|
RUN pacman --noconfirm -Syu binutils fakeroot git make sudo
|
||||||
## create build user
|
## create build user
|
||||||
RUN useradd -m -d /home/build -s /usr/bin/nologin build && \
|
RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build && \
|
||||||
echo "build ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/build
|
echo "build ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/build"
|
||||||
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
|
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
|
||||||
## install package dependencies
|
## install package dependencies
|
||||||
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
|
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
|
||||||
|
@ -26,9 +26,11 @@ Base configuration settings.
|
|||||||
|
|
||||||
libalpm and AUR related configuration.
|
libalpm and AUR related configuration.
|
||||||
|
|
||||||
* ``database`` - path to pacman local database cache, string, required.
|
* ``database`` - path to pacman system database cache, string, required.
|
||||||
|
* ``mirror`` - package database mirror used by pacman for syncronization, string, required. This option supports standard pacman substitutions with ``$arch`` and ``$repo``. Note that the mentioned mirror should contain all repositories which are set by ``alpm.repositories`` option.
|
||||||
* ``repositories`` - list of pacman repositories, space separated list of strings, required.
|
* ``repositories`` - list of pacman repositories, space separated list of strings, required.
|
||||||
* ``root`` - root for alpm library, string, required.
|
* ``root`` - root for alpm library, string, required.
|
||||||
|
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands).
|
||||||
|
|
||||||
``auth`` group
|
``auth`` group
|
||||||
--------------
|
--------------
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
Description=ArcH linux ReposItory MANager (%I architecture)
|
Description=ArcH linux ReposItory MANager (%I architecture)
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/bin/ahriman --architecture %i update
|
ExecStart=/usr/bin/ahriman --architecture %i repo-update --refresh
|
||||||
User=ahriman
|
User=ahriman
|
||||||
Group=ahriman
|
Group=ahriman
|
@ -5,8 +5,10 @@ database = /var/lib/ahriman/ahriman.db
|
|||||||
|
|
||||||
[alpm]
|
[alpm]
|
||||||
database = /var/lib/pacman
|
database = /var/lib/pacman
|
||||||
|
mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
|
||||||
repositories = core extra community multilib
|
repositories = core extra community multilib
|
||||||
root = /
|
root = /
|
||||||
|
use_ahriman_cache = yes
|
||||||
|
|
||||||
[auth]
|
[auth]
|
||||||
target = disabled
|
target = disabled
|
||||||
|
@ -160,6 +160,9 @@ def _set_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
parser.add_argument("--no-local", help="do not check local packages for updates", action="store_true")
|
parser.add_argument("--no-local", help="do not check local packages for updates", action="store_true")
|
||||||
parser.add_argument("--no-manual", help="do not include manual updates", action="store_true")
|
parser.add_argument("--no-manual", help="do not include manual updates", action="store_true")
|
||||||
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
|
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
|
||||||
|
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||||
|
"-yy to force refresh even if up to date",
|
||||||
|
action="count", default=0)
|
||||||
parser.set_defaults(handler=handlers.Daemon, dry_run=False, exit_code=False, package=[])
|
parser.set_defaults(handler=handlers.Daemon, dry_run=False, exit_code=False, package=[])
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@ -251,6 +254,9 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
|
parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
|
||||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||||
parser.add_argument("-n", "--now", help="run update function after", action="store_true")
|
parser.add_argument("-n", "--now", help="run update function after", action="store_true")
|
||||||
|
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||||
|
"-yy to force refresh even if up to date",
|
||||||
|
action="count", default=0)
|
||||||
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
|
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
|
||||||
type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
|
type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
|
||||||
parser.add_argument("--without-dependencies", help="do not add dependencies", action="store_true")
|
parser.add_argument("--without-dependencies", help="do not add dependencies", action="store_true")
|
||||||
@ -467,6 +473,9 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
parser.add_argument("package", help="filter check by package base", nargs="*")
|
parser.add_argument("package", help="filter check by package base", nargs="*")
|
||||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||||
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
|
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
|
||||||
|
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||||
|
"-yy to force refresh even if up to date",
|
||||||
|
action="count", default=0)
|
||||||
parser.set_defaults(handler=handlers.Update, dry_run=True, no_aur=False, no_local=False, no_manual=True)
|
parser.set_defaults(handler=handlers.Update, dry_run=True, no_aur=False, no_local=False, no_manual=True)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@ -717,6 +726,9 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
parser.add_argument("--no-local", help="do not check local packages for updates", action="store_true")
|
parser.add_argument("--no-local", help="do not check local packages for updates", action="store_true")
|
||||||
parser.add_argument("--no-manual", help="do not include manual updates", action="store_true")
|
parser.add_argument("--no-manual", help="do not include manual updates", action="store_true")
|
||||||
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
|
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
|
||||||
|
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||||
|
"-yy to force refresh even if up to date",
|
||||||
|
action="count", default=0)
|
||||||
parser.set_defaults(handler=handlers.Update)
|
parser.set_defaults(handler=handlers.Update)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class Application(ApplicationPackages, ApplicationRepository):
|
|||||||
for package, properties in base.packages.items():
|
for package, properties in base.packages.items():
|
||||||
known_packages.add(package)
|
known_packages.add(package)
|
||||||
known_packages.update(properties.provides)
|
known_packages.update(properties.provides)
|
||||||
known_packages.update(self.repository.pacman.all_packages())
|
known_packages.update(self.repository.pacman.packages())
|
||||||
return known_packages
|
return known_packages
|
||||||
|
|
||||||
def on_result(self, result: Result) -> None:
|
def on_result(self, result: Result) -> None:
|
||||||
|
@ -34,7 +34,8 @@ class ApplicationProperties(LazyLogging):
|
|||||||
repository(Repository): repository instance
|
repository(Repository): repository instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, architecture: str, configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
def __init__(self, architecture: str, configuration: Configuration,
|
||||||
|
no_report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> None:
|
||||||
"""
|
"""
|
||||||
default constructor
|
default constructor
|
||||||
|
|
||||||
@ -43,8 +44,10 @@ class ApplicationProperties(LazyLogging):
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
no_report(bool): force disable reporting
|
no_report(bool): force disable reporting
|
||||||
unsafe(bool): if set no user check will be performed before path creation
|
unsafe(bool): if set no user check will be performed before path creation
|
||||||
|
refresh_pacman_database(int): pacman database syncronization level, ``0`` is disabled
|
||||||
"""
|
"""
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
self.architecture = architecture
|
self.architecture = architecture
|
||||||
self.database = SQLite.load(configuration)
|
self.database = SQLite.load(configuration)
|
||||||
self.repository = Repository(architecture, configuration, self.database, no_report, unsafe)
|
self.repository = Repository(architecture, configuration, self.database,
|
||||||
|
no_report, unsafe, refresh_pacman_database)
|
||||||
|
@ -44,7 +44,7 @@ class Add(Handler):
|
|||||||
no_report(bool): force disable reporting
|
no_report(bool): force disable reporting
|
||||||
unsafe(bool): if set no user check will be performed before path creation
|
unsafe(bool): if set no user check will be performed before path creation
|
||||||
"""
|
"""
|
||||||
application = Application(architecture, configuration, no_report, unsafe)
|
application = Application(architecture, configuration, no_report, unsafe, args.refresh)
|
||||||
application.on_start()
|
application.on_start()
|
||||||
application.add(args.package, args.source, args.without_dependencies)
|
application.add(args.package, args.source, args.without_dependencies)
|
||||||
if not args.now:
|
if not args.now:
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from pwd import getpwuid
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from ahriman.application.application import Application
|
from ahriman.application.application import Application
|
||||||
@ -173,7 +174,9 @@ class Setup(Handler):
|
|||||||
packager(str): packager identifier (e.g. name, email)
|
packager(str): packager identifier (e.g. name, email)
|
||||||
paths(RepositoryPaths): repository paths instance
|
paths(RepositoryPaths): repository paths instance
|
||||||
"""
|
"""
|
||||||
(paths.root / ".makepkg.conf").write_text(f"PACKAGER='{packager}'\n", encoding="utf8")
|
uid, _ = paths.root_owner
|
||||||
|
home_dir = Path(getpwuid(uid).pw_dir)
|
||||||
|
(home_dir / ".makepkg.conf").write_text(f"PACKAGER='{packager}'\n", encoding="utf8")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def configuration_create_sudo(paths: RepositoryPaths, prefix: str, architecture: str) -> None:
|
def configuration_create_sudo(paths: RepositoryPaths, prefix: str, architecture: str) -> None:
|
||||||
|
@ -44,7 +44,7 @@ class Update(Handler):
|
|||||||
no_report(bool): force disable reporting
|
no_report(bool): force disable reporting
|
||||||
unsafe(bool): if set no user check will be performed before path creation
|
unsafe(bool): if set no user check will be performed before path creation
|
||||||
"""
|
"""
|
||||||
application = Application(architecture, configuration, no_report, unsafe)
|
application = Application(architecture, configuration, no_report, unsafe, args.refresh)
|
||||||
application.on_start()
|
application.on_start()
|
||||||
packages = application.updates(args.package, args.no_aur, args.no_local, args.no_manual, args.no_vcs,
|
packages = application.updates(args.package, args.no_aur, args.no_local, args.no_manual, args.no_vcs,
|
||||||
Update.log_fn(application, args.dry_run))
|
Update.log_fn(application, args.dry_run))
|
||||||
|
@ -17,13 +17,18 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from pyalpm import Handle, Package, SIG_PACKAGE # type: ignore
|
import shutil
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore
|
||||||
from typing import Generator, Set
|
from typing import Generator, Set
|
||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.lazy_logging import LazyLogging
|
||||||
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
|
|
||||||
class Pacman:
|
class Pacman(LazyLogging):
|
||||||
"""
|
"""
|
||||||
alpm wrapper
|
alpm wrapper
|
||||||
|
|
||||||
@ -31,35 +36,96 @@ class Pacman:
|
|||||||
handle(Handle): pyalpm root ``Handle``
|
handle(Handle): pyalpm root ``Handle``
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, configuration: Configuration) -> None:
|
def __init__(self, architecture: str, configuration: Configuration, *, refresh_database: int) -> None:
|
||||||
"""
|
"""
|
||||||
default constructor
|
default constructor
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
architecture(str): repository architecture
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
|
refresh_database(int): synchronize local cache to remote. If set to ``0``, no syncronization will be
|
||||||
|
enabled, if set to ``1`` - normal syncronization, if set to ``2`` - force syncronization
|
||||||
"""
|
"""
|
||||||
root = configuration.get("alpm", "root")
|
root = configuration.getpath("alpm", "root")
|
||||||
pacman_root = configuration.getpath("alpm", "database")
|
pacman_root = configuration.getpath("alpm", "database")
|
||||||
self.handle = Handle(root, str(pacman_root))
|
use_ahriman_cache = configuration.getboolean("alpm", "use_ahriman_cache")
|
||||||
for repository in configuration.getlist("alpm", "repositories"):
|
mirror = configuration.get("alpm", "mirror")
|
||||||
self.handle.register_syncdb(repository, SIG_PACKAGE)
|
paths = configuration.repository_paths
|
||||||
|
database_path = paths.pacman if use_ahriman_cache else pacman_root
|
||||||
|
|
||||||
def all_packages(self) -> Set[str]:
|
self.handle = Handle(str(root), str(database_path))
|
||||||
|
for repository in configuration.getlist("alpm", "repositories"):
|
||||||
|
database = self.database_init(repository, mirror, architecture)
|
||||||
|
self.database_copy(database, pacman_root, paths, use_ahriman_cache=use_ahriman_cache)
|
||||||
|
|
||||||
|
if use_ahriman_cache and refresh_database:
|
||||||
|
self.database_sync(refresh_database > 1)
|
||||||
|
|
||||||
|
def database_copy(self, database: DB, pacman_root: Path, paths: RepositoryPaths, *,
|
||||||
|
use_ahriman_cache: bool) -> None:
|
||||||
"""
|
"""
|
||||||
get list of packages known for alpm
|
copy database from the operating system root to the ahriman local home
|
||||||
|
|
||||||
|
Args:
|
||||||
|
database(DB): pacman database instance to be copied
|
||||||
|
pacman_root(Path): operating system pacman's root
|
||||||
|
paths(RepositoryPaths): repository paths instance
|
||||||
|
use_ahriman_cache(bool): use local ahriman cache instead of system one
|
||||||
|
"""
|
||||||
|
def repository_database(root: Path) -> Path:
|
||||||
|
return root / "sync" / f"{database.name}.db"
|
||||||
|
|
||||||
|
if not use_ahriman_cache:
|
||||||
|
return
|
||||||
|
# copy root database if no local copy found
|
||||||
|
pacman_db_path = Path(self.handle.dbpath)
|
||||||
|
if not pacman_db_path.is_dir():
|
||||||
|
return # root directory does not exist yet
|
||||||
|
dst = repository_database(pacman_db_path)
|
||||||
|
if dst.is_file():
|
||||||
|
return # file already exists, do not copy
|
||||||
|
src = repository_database(pacman_root)
|
||||||
|
if not src.is_file():
|
||||||
|
self.logger.warning("repository %s is set to be used, however, no working copy was found", database.name)
|
||||||
|
return # database for some reasons deos not exist
|
||||||
|
self.logger.info("copy pacman database from operating system root to ahriman's home")
|
||||||
|
shutil.copy(src, dst)
|
||||||
|
paths.chown(dst)
|
||||||
|
|
||||||
|
def database_init(self, repository: str, mirror: str, architecture: str) -> DB:
|
||||||
|
"""
|
||||||
|
create database instance from pacman handler and set its properties
|
||||||
|
|
||||||
|
Args:
|
||||||
|
repository(str): pacman repository name (e.g. core)
|
||||||
|
mirror(str): arch linux mirror url
|
||||||
|
architecture(str): repository architecture
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Set[str]: list of package names
|
DB: loaded pacman database instance
|
||||||
"""
|
"""
|
||||||
result: Set[str] = set()
|
database: DB = self.handle.register_syncdb(repository, SIG_PACKAGE)
|
||||||
|
# replace variables in mirror address
|
||||||
|
database.servers = [mirror.replace("$repo", repository).replace("$arch", architecture)]
|
||||||
|
return database
|
||||||
|
|
||||||
|
def database_sync(self, force: bool) -> None:
|
||||||
|
"""
|
||||||
|
sync local database
|
||||||
|
|
||||||
|
Args:
|
||||||
|
force(bool): force database syncronization (same as ``pacman -Syy``)
|
||||||
|
"""
|
||||||
|
self.logger.info("refresh ahriman's home pacman database (force refresh %s)", force)
|
||||||
|
transaction = self.handle.init_transaction()
|
||||||
for database in self.handle.get_syncdbs():
|
for database in self.handle.get_syncdbs():
|
||||||
for package in database.pkgcache:
|
try:
|
||||||
result.add(package.name) # package itself
|
database.update(force)
|
||||||
result.update(package.provides) # provides list for meta-packages
|
except PyalpmError:
|
||||||
|
self.logger.exception("exception during update %s", database.name)
|
||||||
|
transaction.release()
|
||||||
|
|
||||||
return result
|
def package_get(self, package_name: str) -> Generator[Package, None, None]:
|
||||||
|
|
||||||
def get(self, package_name: str) -> Generator[Package, None, None]:
|
|
||||||
"""
|
"""
|
||||||
retrieve list of the packages from the repository by name
|
retrieve list of the packages from the repository by name
|
||||||
|
|
||||||
@ -74,3 +140,18 @@ class Pacman:
|
|||||||
if package is None:
|
if package is None:
|
||||||
continue
|
continue
|
||||||
yield package
|
yield package
|
||||||
|
|
||||||
|
def packages(self) -> Set[str]:
|
||||||
|
"""
|
||||||
|
get list of packages known for alpm
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Set[str]: list of package names
|
||||||
|
"""
|
||||||
|
result: Set[str] = set()
|
||||||
|
for database in self.handle.get_syncdbs():
|
||||||
|
for package in database.pkgcache:
|
||||||
|
result.add(package.name) # package itself
|
||||||
|
result.update(package.provides) # provides list for meta-packages
|
||||||
|
|
||||||
|
return result
|
||||||
|
@ -48,4 +48,4 @@ class OfficialSyncdb(Official):
|
|||||||
Returns:
|
Returns:
|
||||||
AURPackage: package which match the package name
|
AURPackage: package which match the package name
|
||||||
"""
|
"""
|
||||||
return next(AURPackage.from_pacman(package) for package in pacman.get(package_name))
|
return next(AURPackage.from_pacman(package) for package in pacman.package_get(package_name))
|
||||||
|
@ -225,14 +225,14 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
|
|
||||||
# pylint and mypy are too stupid to find these methods
|
# pylint and mypy are too stupid to find these methods
|
||||||
# pylint: disable=missing-function-docstring,multiple-statements,unused-argument
|
# pylint: disable=missing-function-docstring,multiple-statements,unused-argument
|
||||||
def getlist(self, *args: Any, **kwargs: Any) -> List[str]: ...
|
def getlist(self, *args: Any, **kwargs: Any) -> List[str]: ... # type: ignore
|
||||||
|
|
||||||
def getpath(self, *args: Any, **kwargs: Any) -> Path: ...
|
def getpath(self, *args: Any, **kwargs: Any) -> Path: ... # type: ignore
|
||||||
|
|
||||||
def gettype(self, section: str, architecture: str) -> Tuple[str, str]:
|
def gettype(self, section: str, architecture: str) -> Tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
get type variable with fallback to old logic
|
get type variable with fallback to old logic. Despite the fact that it has same semantics as other get* methods,
|
||||||
Despite the fact that it has same semantics as other get* methods, but it has different argument list
|
but it has different argument list
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
section(str): section name
|
section(str): section name
|
||||||
|
@ -48,7 +48,7 @@ class RepositoryProperties(LazyLogging):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, architecture: str, configuration: Configuration, database: SQLite,
|
def __init__(self, architecture: str, configuration: Configuration, database: SQLite,
|
||||||
no_report: bool, unsafe: bool) -> None:
|
no_report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> None:
|
||||||
"""
|
"""
|
||||||
default constructor
|
default constructor
|
||||||
|
|
||||||
@ -58,6 +58,7 @@ class RepositoryProperties(LazyLogging):
|
|||||||
database(SQLite): database instance
|
database(SQLite): database instance
|
||||||
no_report(bool): force disable reporting
|
no_report(bool): force disable reporting
|
||||||
unsafe(bool): if set no user check will be performed before path creation
|
unsafe(bool): if set no user check will be performed before path creation
|
||||||
|
refresh_pacman_database(int): pacman database syncronization level, ``0`` is disabled
|
||||||
"""
|
"""
|
||||||
self.architecture = architecture
|
self.architecture = architecture
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
@ -73,7 +74,7 @@ class RepositoryProperties(LazyLogging):
|
|||||||
self.logger.warning("root owner differs from the current user, skipping tree creation")
|
self.logger.warning("root owner differs from the current user, skipping tree creation")
|
||||||
|
|
||||||
self.ignore_list = configuration.getlist("build", "ignore_packages", fallback=[])
|
self.ignore_list = configuration.getlist("build", "ignore_packages", fallback=[])
|
||||||
self.pacman = Pacman(configuration)
|
self.pacman = Pacman(architecture, configuration, refresh_database=refresh_pacman_database)
|
||||||
self.sign = GPG(architecture, configuration)
|
self.sign = GPG(architecture, configuration)
|
||||||
self.repo = Repo(self.name, self.paths, self.sign.repository_sign_args)
|
self.repo = Repo(self.name, self.paths, self.sign.repository_sign_args)
|
||||||
self.reporter = Client() if no_report else Client.load(configuration)
|
self.reporter = Client() if no_report else Client.load(configuration)
|
||||||
|
@ -67,8 +67,10 @@ class AURPackage:
|
|||||||
>>>
|
>>>
|
||||||
>>>
|
>>>
|
||||||
>>> from ahriman.core.alpm.pacman import Pacman
|
>>> from ahriman.core.alpm.pacman import Pacman
|
||||||
|
>>> from ahriman.core.configuration import Configuration
|
||||||
>>>
|
>>>
|
||||||
>>> pacman = Pacman(configuration)
|
>>> configuration = Configuration()
|
||||||
|
>>> pacman = Pacman("x86_64", configuration)
|
||||||
>>> metadata = pacman.get("pacman")
|
>>> metadata = pacman.get("pacman")
|
||||||
>>> package = AURPackage.from_pacman(next(metadata)) # load package from pyalpm wrapper
|
>>> package = AURPackage.from_pacman(next(metadata)) # load package from pyalpm wrapper
|
||||||
"""
|
"""
|
||||||
|
@ -57,7 +57,7 @@ class PackageDescription:
|
|||||||
>>> from ahriman.core.configuration import Configuration
|
>>> from ahriman.core.configuration import Configuration
|
||||||
>>>
|
>>>
|
||||||
>>> configuration = Configuration()
|
>>> configuration = Configuration()
|
||||||
>>> pacman = Pacman(configuration)
|
>>> pacman = Pacman("x86_64", configuration)
|
||||||
>>> pyalpm_description = next(package for package in pacman.get("pacman"))
|
>>> pyalpm_description = next(package for package in pacman.get("pacman"))
|
||||||
>>> description = PackageDescription.from_package(
|
>>> description = PackageDescription.from_package(
|
||||||
>>> pyalpm_description, Path("/var/cache/pacman/pkg/pacman-6.0.1-4-x86_64.pkg.tar.zst"))
|
>>> pyalpm_description, Path("/var/cache/pacman/pkg/pacman-6.0.1-4-x86_64.pkg.tar.zst"))
|
||||||
|
@ -87,6 +87,16 @@ class RepositoryPaths:
|
|||||||
"""
|
"""
|
||||||
return self.root / "packages" / self.architecture
|
return self.root / "packages" / self.architecture
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pacman(self) -> Path:
|
||||||
|
"""
|
||||||
|
get directory for pacman local package cache
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path: full path to pacman local database cache
|
||||||
|
"""
|
||||||
|
return self.root / "pacman" / self.architecture
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def repository(self) -> Path:
|
def repository(self) -> Path:
|
||||||
"""
|
"""
|
||||||
@ -194,6 +204,7 @@ class RepositoryPaths:
|
|||||||
self.cache,
|
self.cache,
|
||||||
self.chroot,
|
self.chroot,
|
||||||
self.packages,
|
self.packages,
|
||||||
|
self.pacman / "sync", # we need sync directory in order to be able to copy databases
|
||||||
self.repository,
|
self.repository,
|
||||||
):
|
):
|
||||||
directory.mkdir(mode=0o755, parents=True, exist_ok=True)
|
directory.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||||
|
@ -21,8 +21,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from dataclasses import dataclass, replace
|
from dataclasses import dataclass, replace
|
||||||
from typing import Optional, Type
|
from typing import Optional, Type
|
||||||
from passlib.pwd import genword as generate_password # type: ignore
|
from passlib.pwd import genword as generate_password
|
||||||
from passlib.handlers.sha2_crypt import sha512_crypt # type: ignore
|
from passlib.handlers.sha2_crypt import sha512_crypt
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
|
|
||||||
|
17
tests/ahriman/application/handlers/conftest.py
Normal file
17
tests/ahriman/application/handlers/conftest.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
|
_passwd = namedtuple("passwd", ["pw_dir"])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def passwd() -> _passwd:
|
||||||
|
"""
|
||||||
|
get passwd structure for the user
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
_passwd: passwd structure test instance
|
||||||
|
"""
|
||||||
|
return _passwd("home")
|
@ -23,6 +23,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
|||||||
args.package = []
|
args.package = []
|
||||||
args.exit_code = False
|
args.exit_code = False
|
||||||
args.now = False
|
args.now = False
|
||||||
|
args.refresh = 0
|
||||||
args.source = PackageSource.Auto
|
args.source = PackageSource.Auto
|
||||||
args.without_dependencies = False
|
args.without_dependencies = False
|
||||||
return args
|
return args
|
||||||
|
@ -3,6 +3,7 @@ import pytest
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
from typing import Any
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from ahriman.application.handlers import Setup
|
from ahriman.application.handlers import Setup
|
||||||
@ -130,15 +131,17 @@ def test_configuration_create_devtools_no_multilib(args: argparse.Namespace, rep
|
|||||||
|
|
||||||
|
|
||||||
def test_configuration_create_makepkg(args: argparse.Namespace, repository_paths: RepositoryPaths,
|
def test_configuration_create_makepkg(args: argparse.Namespace, repository_paths: RepositoryPaths,
|
||||||
mocker: MockerFixture) -> None:
|
passwd: Any, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must create makepkg configuration
|
must create makepkg configuration
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
write_text_mock = mocker.patch("pathlib.Path.write_text")
|
mocker.patch("ahriman.application.handlers.setup.getpwuid", return_value=passwd)
|
||||||
|
write_text_mock = mocker.patch("pathlib.Path.write_text", autospec=True)
|
||||||
|
|
||||||
Setup.configuration_create_makepkg(args.packager, repository_paths)
|
Setup.configuration_create_makepkg(args.packager, repository_paths)
|
||||||
write_text_mock.assert_called_once_with(pytest.helpers.anyvar(str, True), encoding="utf8")
|
write_text_mock.assert_called_once_with(
|
||||||
|
Path("home") / ".makepkg.conf", pytest.helpers.anyvar(str, True), encoding="utf8")
|
||||||
|
|
||||||
|
|
||||||
def test_configuration_create_sudo(args: argparse.Namespace, repository_paths: RepositoryPaths,
|
def test_configuration_create_sudo(args: argparse.Namespace, repository_paths: RepositoryPaths,
|
||||||
|
@ -28,6 +28,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
|||||||
args.no_local = False
|
args.no_local = False
|
||||||
args.no_manual = False
|
args.no_manual = False
|
||||||
args.no_vcs = False
|
args.no_vcs = False
|
||||||
|
args.refresh = 0
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,6 +75,18 @@ def test_subparsers_daemon(parser: argparse.ArgumentParser) -> None:
|
|||||||
assert args.package == []
|
assert args.package == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_subparsers_daemon_option_refresh(parser: argparse.ArgumentParser) -> None:
|
||||||
|
"""
|
||||||
|
daemon command must count refresh options
|
||||||
|
"""
|
||||||
|
args = parser.parse_args(["daemon"])
|
||||||
|
assert args.refresh == 0
|
||||||
|
args = parser.parse_args(["daemon", "-y"])
|
||||||
|
assert args.refresh == 1
|
||||||
|
args = parser.parse_args(["daemon", "-yy"])
|
||||||
|
assert args.refresh == 2
|
||||||
|
|
||||||
|
|
||||||
def test_subparsers_daemon_option_interval(parser: argparse.ArgumentParser) -> None:
|
def test_subparsers_daemon_option_interval(parser: argparse.ArgumentParser) -> None:
|
||||||
"""
|
"""
|
||||||
daemon command must convert interval option to int instance
|
daemon command must convert interval option to int instance
|
||||||
@ -155,6 +167,18 @@ def test_subparsers_package_add_architecture(parser: argparse.ArgumentParser) ->
|
|||||||
assert args.architecture == ["x86_64"]
|
assert args.architecture == ["x86_64"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_subparsers_package_add_option_refresh(parser: argparse.ArgumentParser) -> None:
|
||||||
|
"""
|
||||||
|
package-add command must count refresh options
|
||||||
|
"""
|
||||||
|
args = parser.parse_args(["package-add", "ahriman"])
|
||||||
|
assert args.refresh == 0
|
||||||
|
args = parser.parse_args(["package-add", "ahriman", "-y"])
|
||||||
|
assert args.refresh == 1
|
||||||
|
args = parser.parse_args(["package-add", "ahriman", "-yy"])
|
||||||
|
assert args.refresh == 2
|
||||||
|
|
||||||
|
|
||||||
def test_subparsers_package_remove_architecture(parser: argparse.ArgumentParser) -> None:
|
def test_subparsers_package_remove_architecture(parser: argparse.ArgumentParser) -> None:
|
||||||
"""
|
"""
|
||||||
package-remove command must correctly parse architecture list
|
package-remove command must correctly parse architecture list
|
||||||
@ -345,6 +369,18 @@ def test_subparsers_repo_check_architecture(parser: argparse.ArgumentParser) ->
|
|||||||
assert args.architecture == ["x86_64"]
|
assert args.architecture == ["x86_64"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_subparsers_repo_check_option_refresh(parser: argparse.ArgumentParser) -> None:
|
||||||
|
"""
|
||||||
|
repo-check command must count refresh options
|
||||||
|
"""
|
||||||
|
args = parser.parse_args(["repo-check"])
|
||||||
|
assert args.refresh == 0
|
||||||
|
args = parser.parse_args(["repo-check", "-y"])
|
||||||
|
assert args.refresh == 1
|
||||||
|
args = parser.parse_args(["repo-check", "-yy"])
|
||||||
|
assert args.refresh == 2
|
||||||
|
|
||||||
|
|
||||||
def test_subparsers_repo_clean(parser: argparse.ArgumentParser) -> None:
|
def test_subparsers_repo_clean(parser: argparse.ArgumentParser) -> None:
|
||||||
"""
|
"""
|
||||||
repo-clean command must imply quiet and unsafe
|
repo-clean command must imply quiet and unsafe
|
||||||
@ -540,6 +576,18 @@ def test_subparsers_repo_update_architecture(parser: argparse.ArgumentParser) ->
|
|||||||
assert args.architecture == ["x86_64"]
|
assert args.architecture == ["x86_64"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_subparsers_repo_update_option_refresh(parser: argparse.ArgumentParser) -> None:
|
||||||
|
"""
|
||||||
|
repo-update command must count refresh options
|
||||||
|
"""
|
||||||
|
args = parser.parse_args(["repo-update"])
|
||||||
|
assert args.refresh == 0
|
||||||
|
args = parser.parse_args(["repo-update", "-y"])
|
||||||
|
assert args.refresh == 1
|
||||||
|
args = parser.parse_args(["repo-update", "-yy"])
|
||||||
|
assert args.refresh == 2
|
||||||
|
|
||||||
|
|
||||||
def test_subparsers_shell(parser: argparse.ArgumentParser) -> None:
|
def test_subparsers_shell(parser: argparse.ArgumentParser) -> None:
|
||||||
"""
|
"""
|
||||||
shell command must imply lock and no-report
|
shell command must imply lock and no-report
|
||||||
|
@ -360,7 +360,7 @@ def pacman(configuration: Configuration) -> Pacman:
|
|||||||
Returns:
|
Returns:
|
||||||
Pacman: pacman wrapper test instance
|
Pacman: pacman wrapper test instance
|
||||||
"""
|
"""
|
||||||
return Pacman(configuration)
|
return Pacman("x86_64", configuration, refresh_database=0)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -11,7 +11,7 @@ def test_package_info(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURP
|
|||||||
must return package info from the database
|
must return package info from the database
|
||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.models.aur_package.AURPackage.from_pacman", return_value=aur_package_akonadi)
|
mocker.patch("ahriman.models.aur_package.AURPackage.from_pacman", return_value=aur_package_akonadi)
|
||||||
get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.get", return_value=[aur_package_akonadi])
|
get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.package_get", return_value=[aur_package_akonadi])
|
||||||
|
|
||||||
package = official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)
|
package = official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)
|
||||||
get_mock.assert_called_once_with(aur_package_akonadi.name)
|
get_mock.assert_called_once_with(aur_package_akonadi.name)
|
||||||
|
@ -1,31 +1,205 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from pyalpm import error as PyalpmError
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
from ahriman.core.alpm.pacman import Pacman
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
|
|
||||||
def test_all_packages(pacman: Pacman) -> None:
|
def test_init_with_local_cache(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must sync repositories at the start if set
|
||||||
|
"""
|
||||||
|
mocker.patch("ahriman.core.alpm.pacman.Pacman.database_copy")
|
||||||
|
sync_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.database_sync")
|
||||||
|
configuration.set_option("alpm", "use_ahriman_cache", "yes")
|
||||||
|
|
||||||
|
# pyalpm.Handle is trying to reach the directory we've asked, thus we need to patch it a bit
|
||||||
|
with TemporaryDirectory(ignore_cleanup_errors=True) as pacman_root:
|
||||||
|
mocker.patch.object(RepositoryPaths, "pacman", Path(pacman_root))
|
||||||
|
# during the creation pyalpm.Handle will create also version file which we would like to remove later
|
||||||
|
Pacman("x86_64", configuration, refresh_database=1)
|
||||||
|
sync_mock.assert_called_once_with(False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_with_local_cache_forced(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must sync repositories at the start if set with force flag
|
||||||
|
"""
|
||||||
|
mocker.patch("ahriman.core.alpm.pacman.Pacman.database_copy")
|
||||||
|
sync_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.database_sync")
|
||||||
|
configuration.set_option("alpm", "use_ahriman_cache", "yes")
|
||||||
|
|
||||||
|
# pyalpm.Handle is trying to reach the directory we've asked, thus we need to patch it a bit
|
||||||
|
with TemporaryDirectory(ignore_cleanup_errors=True) as pacman_root:
|
||||||
|
mocker.patch.object(RepositoryPaths, "pacman", Path(pacman_root))
|
||||||
|
# during the creation pyalpm.Handle will create also version file which we would like to remove later
|
||||||
|
Pacman("x86_64", configuration, refresh_database=2)
|
||||||
|
sync_mock.assert_called_once_with(True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_copy(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must copy database from root
|
||||||
|
"""
|
||||||
|
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
|
||||||
|
path = Path("randomname")
|
||||||
|
dst_path = Path("/var/lib/pacman/sync/core.db")
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
|
# root database exists, local database does not
|
||||||
|
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: True if p.is_relative_to(path) else False)
|
||||||
|
copy_mock = mocker.patch("shutil.copy")
|
||||||
|
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
|
||||||
|
|
||||||
|
pacman.database_copy(database, path, repository_paths, use_ahriman_cache=True)
|
||||||
|
copy_mock.assert_called_once_with(path / "sync" / "core.db", dst_path)
|
||||||
|
chown_mock.assert_called_once_with(dst_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_copy_skip(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must do not copy database from root if local cache is disabled
|
||||||
|
"""
|
||||||
|
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
|
||||||
|
path = Path("randomname")
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
|
# root database exists, local database does not
|
||||||
|
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: True if p.is_relative_to(path) else False)
|
||||||
|
copy_mock = mocker.patch("shutil.copy")
|
||||||
|
|
||||||
|
pacman.database_copy(database, path, repository_paths, use_ahriman_cache=False)
|
||||||
|
copy_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_copy_no_directory(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must do not copy database if local cache already exists
|
||||||
|
"""
|
||||||
|
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
|
||||||
|
path = Path("randomname")
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=False)
|
||||||
|
# root database exists, local database does not
|
||||||
|
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: True if p.is_relative_to(path) else False)
|
||||||
|
copy_mock = mocker.patch("shutil.copy")
|
||||||
|
|
||||||
|
pacman.database_copy(database, path, repository_paths, use_ahriman_cache=True)
|
||||||
|
copy_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_copy_no_root_file(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must do not copy database if no repository file exists in filesystem
|
||||||
|
"""
|
||||||
|
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
|
||||||
|
path = Path("randomname")
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
|
# root database does not exist, local database does not either
|
||||||
|
mocker.patch("pathlib.Path.is_file", return_value=False)
|
||||||
|
copy_mock = mocker.patch("shutil.copy")
|
||||||
|
|
||||||
|
pacman.database_copy(database, path, repository_paths, use_ahriman_cache=True)
|
||||||
|
copy_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_copy_database_exist(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must do not copy database if local cache already exists
|
||||||
|
"""
|
||||||
|
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
|
||||||
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
|
# root database exists, local database does either
|
||||||
|
mocker.patch("pathlib.Path.is_file", return_value=True)
|
||||||
|
copy_mock = mocker.patch("shutil.copy")
|
||||||
|
|
||||||
|
pacman.database_copy(database, Path("root"), repository_paths, use_ahriman_cache=True)
|
||||||
|
copy_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_init(pacman: Pacman, configuration: Configuration) -> None:
|
||||||
|
"""
|
||||||
|
must init database with settings
|
||||||
|
"""
|
||||||
|
mirror = configuration.get("alpm", "mirror")
|
||||||
|
database = pacman.database_init("test", mirror, "x86_64")
|
||||||
|
assert len(database.servers) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_sync(pacman: Pacman) -> None:
|
||||||
|
"""
|
||||||
|
must sync databases
|
||||||
|
"""
|
||||||
|
handle_mock = MagicMock()
|
||||||
|
core_mock = MagicMock()
|
||||||
|
extra_mock = MagicMock()
|
||||||
|
transaction_mock = MagicMock()
|
||||||
|
handle_mock.get_syncdbs.return_value = [core_mock, extra_mock]
|
||||||
|
handle_mock.init_transaction.return_value = transaction_mock
|
||||||
|
pacman.handle = handle_mock
|
||||||
|
|
||||||
|
pacman.database_sync(False)
|
||||||
|
handle_mock.init_transaction.assert_called_once_with()
|
||||||
|
core_mock.update.assert_called_once_with(False)
|
||||||
|
extra_mock.update.assert_called_once_with(False)
|
||||||
|
transaction_mock.release.assert_called_once_with()
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_sync_failed(pacman: Pacman) -> None:
|
||||||
|
"""
|
||||||
|
must sync databases even if there was exception
|
||||||
|
"""
|
||||||
|
handle_mock = MagicMock()
|
||||||
|
core_mock = MagicMock()
|
||||||
|
core_mock.update.side_effect = PyalpmError()
|
||||||
|
extra_mock = MagicMock()
|
||||||
|
handle_mock.get_syncdbs.return_value = [core_mock, extra_mock]
|
||||||
|
pacman.handle = handle_mock
|
||||||
|
|
||||||
|
pacman.database_sync(False)
|
||||||
|
extra_mock.update.assert_called_once_with(False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_sync_forced(pacman: Pacman) -> None:
|
||||||
|
"""
|
||||||
|
must sync databases with force flag
|
||||||
|
"""
|
||||||
|
handle_mock = MagicMock()
|
||||||
|
core_mock = MagicMock()
|
||||||
|
handle_mock.get_syncdbs.return_value = [core_mock]
|
||||||
|
pacman.handle = handle_mock
|
||||||
|
|
||||||
|
pacman.database_sync(True)
|
||||||
|
handle_mock.init_transaction.assert_called_once_with()
|
||||||
|
core_mock.update.assert_called_once_with(True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_get(pacman: Pacman) -> None:
|
||||||
|
"""
|
||||||
|
must retrieve package
|
||||||
|
"""
|
||||||
|
assert list(pacman.package_get("pacman"))
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_get_empty(pacman: Pacman) -> None:
|
||||||
|
"""
|
||||||
|
must return empty packages list without exception
|
||||||
|
"""
|
||||||
|
assert not list(pacman.package_get("some-random-name"))
|
||||||
|
|
||||||
|
|
||||||
|
def test_packages(pacman: Pacman) -> None:
|
||||||
"""
|
"""
|
||||||
package list must not be empty
|
package list must not be empty
|
||||||
"""
|
"""
|
||||||
packages = pacman.all_packages()
|
packages = pacman.packages()
|
||||||
assert packages
|
assert packages
|
||||||
assert "pacman" in packages
|
assert "pacman" in packages
|
||||||
|
|
||||||
|
|
||||||
def test_all_packages_with_provides(pacman: Pacman) -> None:
|
def test_packages_with_provides(pacman: Pacman) -> None:
|
||||||
"""
|
"""
|
||||||
package list must contain provides packages
|
package list must contain provides packages
|
||||||
"""
|
"""
|
||||||
assert "sh" in pacman.all_packages()
|
assert "sh" in pacman.packages()
|
||||||
|
|
||||||
|
|
||||||
def test_get(pacman: Pacman) -> None:
|
|
||||||
"""
|
|
||||||
must retrieve package
|
|
||||||
"""
|
|
||||||
assert list(pacman.get("pacman"))
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_empty(pacman: Pacman) -> None:
|
|
||||||
"""
|
|
||||||
must return empty packages list without exception
|
|
||||||
"""
|
|
||||||
assert not list(pacman.get("some-random-name"))
|
|
||||||
|
@ -154,9 +154,5 @@ def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -
|
|||||||
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
|
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
|
||||||
|
|
||||||
repository_paths.tree_create()
|
repository_paths.tree_create()
|
||||||
mkdir_mock.assert_has_calls(
|
mkdir_mock.assert_has_calls([mock.call(mode=0o755, parents=True, exist_ok=True) for _ in paths], any_order=True)
|
||||||
[
|
chown_mock.assert_has_calls([mock.call(pytest.helpers.anyvar(int)) for _ in paths], any_order=True)
|
||||||
mock.call(mode=0o755, parents=True, exist_ok=True)
|
|
||||||
for _ in paths
|
|
||||||
], any_order=True)
|
|
||||||
chown_mock.assert_has_calls([mock.call(getattr(repository_paths, path)) for path in paths], any_order=True)
|
|
||||||
|
@ -5,8 +5,10 @@ database = ../../../ahriman-test.db
|
|||||||
|
|
||||||
[alpm]
|
[alpm]
|
||||||
database = /var/lib/pacman
|
database = /var/lib/pacman
|
||||||
|
mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
|
||||||
repositories = core extra community multilib
|
repositories = core extra community multilib
|
||||||
root = /
|
root = /
|
||||||
|
use_ahriman_cache = no
|
||||||
|
|
||||||
[auth]
|
[auth]
|
||||||
client_id = client_id
|
client_id = client_id
|
||||||
|
Loading…
Reference in New Issue
Block a user