mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 07:17:17 +00:00
Complete official repository support (#59)
This commit is contained in:
parent
5030395025
commit
571f720ae2
@ -22,7 +22,7 @@ This package contains application (aka executable) related classes and everythin
|
||||
|
||||
This package contains everything which is required for any time of application run and separated to several packages:
|
||||
|
||||
* `ahriman.core.alpm` package controls pacman related functions. It provides wrappers for `pyalpm` library and safe calls for repository tools (`repo-add` and `repo-remove`).
|
||||
* `ahriman.core.alpm` package controls pacman related functions. It provides wrappers for `pyalpm` library and safe calls for repository tools (`repo-add` and `repo-remove`). Also this package contains `ahriman.core.alpm.remote` package which provides wrapper for remote sources (e.g. AUR RPC and official repositories RPC).
|
||||
* `ahriman.core.auth` package provides classes for authorization methods used by web mostly. Base class is `ahriman.core.auth.auth.Auth` which must be called by `load` method.
|
||||
* `ahriman.core.build_tools` is a package which provides wrapper for `devtools` commands.
|
||||
* `ahriman.core.database` is everything including data and schema migrations for database.
|
||||
|
@ -23,7 +23,6 @@ Base configuration settings.
|
||||
|
||||
libalpm and AUR related configuration.
|
||||
|
||||
* `aur_url` - base url for AUR, string, required.
|
||||
* `database` - path to pacman local database cache, string, required.
|
||||
* `repositories` - list of pacman repositories, space separated list of strings, required.
|
||||
* `root` - root for alpm library, string, required.
|
||||
|
@ -113,6 +113,14 @@ Well it is supported also.
|
||||
|
||||
The last command will calculate diff from current tree to the `HEAD` and will store it locally. Patches will be applied on any package actions (e.g. it can be used for dependency management).
|
||||
|
||||
### Hey, I would like to rebuild the official repository package
|
||||
|
||||
So it is the same as adding any other package, but due to restrictions you must specify source explicitly, e.g.:
|
||||
|
||||
```shell
|
||||
sudo -u ahriman ahriman package-add pacmann -s repository
|
||||
```
|
||||
|
||||
### Package build fails because it cannot validate PGP signature of source files
|
||||
|
||||
TL;DR
|
||||
|
@ -4,7 +4,6 @@ logging = ahriman.ini.d/logging.ini
|
||||
database = /var/lib/ahriman/ahriman.db
|
||||
|
||||
[alpm]
|
||||
aur_url = https://aur.archlinux.org
|
||||
database = /var/lib/pacman
|
||||
repositories = core extra community multilib
|
||||
root = /
|
||||
|
@ -74,7 +74,7 @@
|
||||
{% for package in packages %}
|
||||
<tr data-package-base="{{ package.base }}">
|
||||
<td data-checkbox="true"></td>
|
||||
<td><a href="{{ package.web_url }}" title="{{ package.base }}">{{ package.base }}</a></td>
|
||||
<td>{% if package.web_url is not none %}<a href="{{ package.web_url }}" title="{{ package.base }}">{{ package.base }}</a>{% else %}{{ package.base }}{% endif %}</td>
|
||||
<td>{{ package.version }}</td>
|
||||
<td>{{ package.packages|join("<br>"|safe) }}</td>
|
||||
<td>{{ package.groups|join("<br>"|safe) }}</td>
|
||||
|
@ -80,12 +80,13 @@ class ApplicationPackages(ApplicationProperties):
|
||||
known_packages(Set[str]): list of packages which are known by the service
|
||||
without_dependencies(bool): if set, dependency check will be disabled
|
||||
"""
|
||||
package = Package.load(source, PackageSource.AUR, self.repository.pacman, self.repository.aur_url)
|
||||
package = Package.from_aur(source, self.repository.pacman)
|
||||
|
||||
self.database.build_queue_insert(package)
|
||||
self.database.remote_update(package)
|
||||
|
||||
with tmpdir() as local_path:
|
||||
Sources.load(local_path, package.git_url, self.database.patches_get(package.base))
|
||||
Sources.load(local_path, package.remote, self.database.patches_get(package.base))
|
||||
self._process_dependencies(local_path, known_packages, without_dependencies)
|
||||
|
||||
def _add_directory(self, source: str, *_: Any) -> None:
|
||||
@ -108,9 +109,10 @@ class ApplicationPackages(ApplicationProperties):
|
||||
known_packages(Set[str]): list of packages which are known by the service
|
||||
without_dependencies(bool): if set, dependency check will be disabled
|
||||
"""
|
||||
package = Package.load(source, PackageSource.Local, self.repository.pacman, self.repository.aur_url)
|
||||
source_dir = Path(source)
|
||||
package = Package.from_build(source_dir)
|
||||
cache_dir = self.repository.paths.cache_for(package.base)
|
||||
shutil.copytree(Path(source), cache_dir) # copy package to store in caches
|
||||
shutil.copytree(source_dir, cache_dir) # copy package to store in caches
|
||||
Sources.init(cache_dir) # we need to run init command in directory where we do have permissions
|
||||
|
||||
self.database.build_queue_insert(package)
|
||||
@ -132,6 +134,18 @@ class ApplicationPackages(ApplicationProperties):
|
||||
for chunk in response.iter_content(chunk_size=1024):
|
||||
local_file.write(chunk)
|
||||
|
||||
def _add_repository(self, source: str, *_: Any) -> None:
|
||||
"""
|
||||
add package from official repository
|
||||
|
||||
Args:
|
||||
source(str): package base name
|
||||
"""
|
||||
package = Package.from_official(source, self.repository.pacman)
|
||||
self.database.build_queue_insert(package)
|
||||
self.database.remote_update(package)
|
||||
# repository packages must not depend on unknown packages, thus we are not going to process dependencies
|
||||
|
||||
def _process_dependencies(self, local_path: Path, known_packages: Set[str], without_dependencies: bool) -> None:
|
||||
"""
|
||||
process package dependencies
|
||||
|
@ -128,14 +128,14 @@ class ApplicationRepository(ApplicationProperties):
|
||||
packages: List[str] = []
|
||||
for single in probe.packages:
|
||||
try:
|
||||
_ = Package.from_aur(single, probe.aur_url)
|
||||
_ = Package.from_aur(single, self.repository.pacman)
|
||||
except Exception:
|
||||
packages.append(single)
|
||||
return packages
|
||||
|
||||
def unknown_local(probe: Package) -> List[str]:
|
||||
cache_dir = self.repository.paths.cache_for(probe.base)
|
||||
local = Package.from_build(cache_dir, probe.aur_url)
|
||||
local = Package.from_build(cache_dir)
|
||||
packages = set(probe.packages.keys()).difference(local.packages.keys())
|
||||
return list(packages)
|
||||
|
||||
|
@ -29,7 +29,6 @@ from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.formatters.string_printer import StringPrinter
|
||||
from ahriman.models.action import Action
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_source import PackageSource
|
||||
|
||||
|
||||
class Patch(Handler):
|
||||
@ -57,21 +56,20 @@ class Patch(Handler):
|
||||
elif args.action == Action.Remove:
|
||||
Patch.patch_set_remove(application, args.package)
|
||||
elif args.action == Action.Update:
|
||||
Patch.patch_set_create(application, args.package, args.track)
|
||||
Patch.patch_set_create(application, Path(args.package), args.track)
|
||||
|
||||
@staticmethod
|
||||
def patch_set_create(application: Application, sources_dir: str, track: List[str]) -> None:
|
||||
def patch_set_create(application: Application, sources_dir: Path, track: List[str]) -> None:
|
||||
"""
|
||||
create patch set for the package base
|
||||
|
||||
Args:
|
||||
application(Application): application instance
|
||||
sources_dir(str): path to directory with the package sources
|
||||
sources_dir(Path): path to directory with the package sources
|
||||
track(List[str]): track files which match the glob before creating the patch
|
||||
"""
|
||||
package = Package.load(sources_dir, PackageSource.Local, application.repository.pacman,
|
||||
application.repository.aur_url)
|
||||
patch = Sources.patch_create(Path(sources_dir), *track)
|
||||
package = Package.from_build(sources_dir)
|
||||
patch = Sources.patch_create(sources_dir, *track)
|
||||
application.database.patches_insert(package.base, patch)
|
||||
|
||||
@staticmethod
|
||||
|
@ -22,6 +22,7 @@ import argparse
|
||||
from dataclasses import fields
|
||||
from typing import Callable, Iterable, List, Tuple, Type
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers.handler import Handler
|
||||
from ahriman.core.alpm.remote.aur import AUR
|
||||
from ahriman.core.alpm.remote.official import Official
|
||||
@ -55,8 +56,10 @@ class Search(Handler):
|
||||
no_report(bool): force disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
official_packages_list = Official.multisearch(*args.search)
|
||||
aur_packages_list = AUR.multisearch(*args.search)
|
||||
application = Application(architecture, configuration, no_report, unsafe)
|
||||
|
||||
official_packages_list = Official.multisearch(*args.search, pacman=application.repository.pacman)
|
||||
aur_packages_list = AUR.multisearch(*args.search, pacman=application.repository.pacman)
|
||||
Search.check_if_empty(args.exit_code, not official_packages_list and not aur_packages_list)
|
||||
|
||||
for packages_list in (official_packages_list, aur_packages_list):
|
||||
|
@ -17,8 +17,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from pyalpm import Handle # type: ignore
|
||||
from typing import Set
|
||||
from pyalpm import Handle, Package, SIG_PACKAGE # type: ignore
|
||||
from typing import Generator, Set
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
@ -42,7 +42,7 @@ class Pacman:
|
||||
pacman_root = configuration.getpath("alpm", "database")
|
||||
self.handle = Handle(root, str(pacman_root))
|
||||
for repository in configuration.getlist("alpm", "repositories"):
|
||||
self.handle.register_syncdb(repository, 0) # 0 is pgp_level
|
||||
self.handle.register_syncdb(repository, SIG_PACKAGE)
|
||||
|
||||
def all_packages(self) -> Set[str]:
|
||||
"""
|
||||
@ -58,3 +58,19 @@ class Pacman:
|
||||
result.update(package.provides) # provides list for meta-packages
|
||||
|
||||
return result
|
||||
|
||||
def get(self, package_name: str) -> Generator[Package, None, None]:
|
||||
"""
|
||||
retrieve list of the packages from the repository by name
|
||||
|
||||
Args:
|
||||
package_name(str): package name to search
|
||||
|
||||
Yields:
|
||||
Package: list of packages which were returned by the query
|
||||
"""
|
||||
for database in self.handle.get_syncdbs():
|
||||
package = database.get_pkg(package_name)
|
||||
if package is None:
|
||||
continue
|
||||
yield package
|
||||
|
@ -19,8 +19,9 @@
|
||||
#
|
||||
import requests
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.remote import Remote
|
||||
from ahriman.core.exceptions import InvalidPackageInfo
|
||||
from ahriman.core.util import exception_response_text
|
||||
@ -32,27 +33,15 @@ class AUR(Remote):
|
||||
AUR RPC wrapper
|
||||
|
||||
Attributes:
|
||||
DEFAULT_AUR_URL(str): (class attribute) default AUR url
|
||||
DEFAULT_RPC_URL(str): (class attribute) default AUR RPC url
|
||||
DEFAULT_RPC_VERSION(str): (class attribute) default AUR RPC version
|
||||
rpc_url(str): AUR RPC url
|
||||
rpc_version(str): AUR RPC version
|
||||
"""
|
||||
|
||||
DEFAULT_RPC_URL = "https://aur.archlinux.org/rpc"
|
||||
DEFAULT_AUR_URL = "https://aur.archlinux.org"
|
||||
DEFAULT_RPC_URL = f"{DEFAULT_AUR_URL}/rpc"
|
||||
DEFAULT_RPC_VERSION = "5"
|
||||
|
||||
def __init__(self, rpc_url: Optional[str] = None, rpc_version: Optional[str] = None) -> None:
|
||||
"""
|
||||
default constructor
|
||||
|
||||
Args:
|
||||
rpc_url(Optional[str], optional): AUR RPC url (Default value = None)
|
||||
rpc_version(Optional[str], optional): AUR RPC version (Default value = None)
|
||||
"""
|
||||
Remote.__init__(self)
|
||||
self.rpc_url = rpc_url or self.DEFAULT_RPC_URL
|
||||
self.rpc_version = rpc_version or self.DEFAULT_RPC_VERSION
|
||||
|
||||
@staticmethod
|
||||
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
|
||||
"""
|
||||
@ -73,6 +62,33 @@ class AUR(Remote):
|
||||
raise InvalidPackageInfo(error_details)
|
||||
return [AURPackage.from_json(package) for package in response["results"]]
|
||||
|
||||
@staticmethod
|
||||
def remote_git_url(package_base: str, repository: str) -> str:
|
||||
"""
|
||||
generate remote git url from the package base
|
||||
|
||||
Args
|
||||
package_base(str): package base
|
||||
repository(str): repository name
|
||||
|
||||
Returns:
|
||||
str: git url for the specific base
|
||||
"""
|
||||
return f"{AUR.DEFAULT_AUR_URL}/{package_base}.git"
|
||||
|
||||
@staticmethod
|
||||
def remote_web_url(package_base: str) -> str:
|
||||
"""
|
||||
generate remote web url from the package base
|
||||
|
||||
Args
|
||||
package_base(str): package base
|
||||
|
||||
Returns:
|
||||
str: web url for the specific base
|
||||
"""
|
||||
return f"{AUR.DEFAULT_AUR_URL}/packages/{package_base}"
|
||||
|
||||
def make_request(self, request_type: str, *args: str, **kwargs: str) -> List[AURPackage]:
|
||||
"""
|
||||
perform request to AUR RPC
|
||||
@ -87,7 +103,7 @@ class AUR(Remote):
|
||||
"""
|
||||
query: Dict[str, Any] = {
|
||||
"type": request_type,
|
||||
"v": self.rpc_version
|
||||
"v": self.DEFAULT_RPC_VERSION
|
||||
}
|
||||
|
||||
arg_query = "arg[]" if len(args) > 1 else "arg"
|
||||
@ -97,7 +113,7 @@ class AUR(Remote):
|
||||
query[key] = value
|
||||
|
||||
try:
|
||||
response = requests.get(self.rpc_url, params=query)
|
||||
response = requests.get(self.DEFAULT_RPC_URL, params=query)
|
||||
response.raise_for_status()
|
||||
return self.parse_response(response.json())
|
||||
except requests.HTTPError as e:
|
||||
@ -110,12 +126,13 @@ class AUR(Remote):
|
||||
self.logger.exception("could not perform request by using type %s", request_type)
|
||||
raise
|
||||
|
||||
def package_info(self, package_name: str) -> AURPackage:
|
||||
def package_info(self, package_name: str, *, pacman: Pacman) -> AURPackage:
|
||||
"""
|
||||
get package info by its name
|
||||
|
||||
Args:
|
||||
package_name(str): package name to search
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
AURPackage: package which match the package name
|
||||
@ -123,12 +140,13 @@ class AUR(Remote):
|
||||
packages = self.make_request("info", package_name)
|
||||
return next(package for package in packages if package.name == package_name)
|
||||
|
||||
def package_search(self, *keywords: str) -> List[AURPackage]:
|
||||
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
|
||||
"""
|
||||
search package in AUR web
|
||||
|
||||
Args:
|
||||
*keywords(str): keywords to search
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
List[AURPackage]: list of packages which match the criteria
|
||||
|
@ -19,8 +19,9 @@
|
||||
#
|
||||
import requests
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.remote import Remote
|
||||
from ahriman.core.exceptions import InvalidPackageInfo
|
||||
from ahriman.core.util import exception_response_text
|
||||
@ -32,22 +33,15 @@ class Official(Remote):
|
||||
official repository RPC wrapper
|
||||
|
||||
Attributes:
|
||||
DEFAULT_RPC_URL(str): (class attribute) default AUR RPC url
|
||||
rpc_url(str): AUR RPC url
|
||||
DEFAULT_ARCHLINUX_URL(str): (class attribute) default archlinux url
|
||||
DEFAULT_SEARCH_REPOSITORIES(List[str]): (class attribute) default list of repositories to search
|
||||
DEFAULT_RPC_URL(str): (class attribute) default archlinux repositories RPC url
|
||||
"""
|
||||
|
||||
DEFAULT_ARCHLINUX_URL = "https://archlinux.org"
|
||||
DEFAULT_SEARCH_REPOSITORIES = ["Core", "Extra", "Multilib", "Community"]
|
||||
DEFAULT_RPC_URL = "https://archlinux.org/packages/search/json"
|
||||
|
||||
def __init__(self, rpc_url: Optional[str] = None) -> None:
|
||||
"""
|
||||
default constructor
|
||||
|
||||
Args:
|
||||
rpc_url(Optional[str], optional): AUR RPC url (Default value = None)
|
||||
"""
|
||||
Remote.__init__(self)
|
||||
self.rpc_url = rpc_url or self.DEFAULT_RPC_URL
|
||||
|
||||
@staticmethod
|
||||
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
|
||||
"""
|
||||
@ -66,6 +60,35 @@ class Official(Remote):
|
||||
raise InvalidPackageInfo("API validation error")
|
||||
return [AURPackage.from_repo(package) for package in response["results"]]
|
||||
|
||||
@staticmethod
|
||||
def remote_git_url(package_base: str, repository: str) -> str:
|
||||
"""
|
||||
generate remote git url from the package base
|
||||
|
||||
Args
|
||||
package_base(str): package base
|
||||
repository(str): repository name
|
||||
|
||||
Returns:
|
||||
str: git url for the specific base
|
||||
"""
|
||||
if repository.lower() in ("core", "extra", "testing", "kde-unstable"):
|
||||
return "https://github.com/archlinux/svntogit-packages.git" # hardcoded, ok
|
||||
return "https://github.com/archlinux/svntogit-community.git"
|
||||
|
||||
@staticmethod
|
||||
def remote_web_url(package_base: str) -> str:
|
||||
"""
|
||||
generate remote web url from the package base
|
||||
|
||||
Args
|
||||
package_base(str): package base
|
||||
|
||||
Returns:
|
||||
str: web url for the specific base
|
||||
"""
|
||||
return f"{Official.DEFAULT_ARCHLINUX_URL}/packages/{package_base}"
|
||||
|
||||
def make_request(self, *args: str, by: str) -> List[AURPackage]:
|
||||
"""
|
||||
perform request to official repositories RPC
|
||||
@ -78,7 +101,7 @@ class Official(Remote):
|
||||
List[AURPackage]: response parsed to package list
|
||||
"""
|
||||
try:
|
||||
response = requests.get(self.rpc_url, params={by: args})
|
||||
response = requests.get(self.DEFAULT_RPC_URL, params={by: args, "repo": self.DEFAULT_SEARCH_REPOSITORIES})
|
||||
response.raise_for_status()
|
||||
return self.parse_response(response.json())
|
||||
except requests.HTTPError as e:
|
||||
@ -88,12 +111,13 @@ class Official(Remote):
|
||||
self.logger.exception("could not perform request")
|
||||
raise
|
||||
|
||||
def package_info(self, package_name: str) -> AURPackage:
|
||||
def package_info(self, package_name: str, *, pacman: Pacman) -> AURPackage:
|
||||
"""
|
||||
get package info by its name
|
||||
|
||||
Args:
|
||||
package_name(str): package name to search
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
AURPackage: package which match the package name
|
||||
@ -101,12 +125,13 @@ class Official(Remote):
|
||||
packages = self.make_request(package_name, by="name")
|
||||
return next(package for package in packages if package.name == package_name)
|
||||
|
||||
def package_search(self, *keywords: str) -> List[AURPackage]:
|
||||
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
|
||||
"""
|
||||
search package in AUR web
|
||||
|
||||
Args:
|
||||
*keywords(str): keywords to search
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
List[AURPackage]: list of packages which match the criteria
|
||||
|
51
src/ahriman/core/alpm/remote/official_syncdb.py
Normal file
51
src/ahriman/core/alpm/remote/official_syncdb.py
Normal file
@ -0,0 +1,51 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 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/>.
|
||||
#
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.official import Official
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
|
||||
|
||||
class OfficialSyncdb(Official):
|
||||
"""
|
||||
official repository wrapper based on synchronized databases.
|
||||
|
||||
Despite the fact that official repository provides an API for the interaction according to the comment in issue
|
||||
https://github.com/arcan1s/ahriman/pull/59#issuecomment-1106412297 we might face rate limits while requesting
|
||||
updates.
|
||||
|
||||
This approach also has limitations, because we don't require superuser rights (neither going to download database
|
||||
separately), the database file might be outdated and must be handled manually (or kind of). This behaviour might be
|
||||
changed in the future.
|
||||
|
||||
Still we leave search function based on the official repositories RPC.
|
||||
"""
|
||||
|
||||
def package_info(self, package_name: str, *, pacman: Pacman) -> AURPackage:
|
||||
"""
|
||||
get package info by its name
|
||||
|
||||
Args:
|
||||
package_name(str): package name to search
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
AURPackage: package which match the package name
|
||||
"""
|
||||
return next(AURPackage.from_pacman(package) for package in pacman.get(package_name))
|
@ -23,6 +23,7 @@ import logging
|
||||
|
||||
from typing import Dict, List, Type
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
|
||||
|
||||
@ -41,26 +42,28 @@ class Remote:
|
||||
self.logger = logging.getLogger("build_details")
|
||||
|
||||
@classmethod
|
||||
def info(cls: Type[Remote], package_name: str) -> AURPackage:
|
||||
def info(cls: Type[Remote], package_name: str, *, pacman: Pacman) -> AURPackage:
|
||||
"""
|
||||
get package info by its name
|
||||
|
||||
Args:
|
||||
package_name(str): package name to search
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
AURPackage: package which match the package name
|
||||
"""
|
||||
return cls().package_info(package_name)
|
||||
return cls().package_info(package_name, pacman=pacman)
|
||||
|
||||
@classmethod
|
||||
def multisearch(cls: Type[Remote], *keywords: str) -> List[AURPackage]:
|
||||
def multisearch(cls: Type[Remote], *keywords: str, pacman: Pacman) -> List[AURPackage]:
|
||||
"""
|
||||
search in remote repository by using API with multiple words. This method is required in order to handle
|
||||
https://bugs.archlinux.org/task/49133. In addition, short words will be dropped
|
||||
|
||||
Args:
|
||||
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
List[AURPackage]: list of packages each of them matches all search terms
|
||||
@ -68,7 +71,7 @@ class Remote:
|
||||
instance = cls()
|
||||
packages: Dict[str, AURPackage] = {}
|
||||
for term in filter(lambda word: len(word) > 3, keywords):
|
||||
portion = instance.search(term)
|
||||
portion = instance.search(term, pacman=pacman)
|
||||
packages = {
|
||||
package.name: package # not mistake to group them by name
|
||||
for package in portion
|
||||
@ -76,25 +79,60 @@ class Remote:
|
||||
}
|
||||
return list(packages.values())
|
||||
|
||||
@staticmethod
|
||||
def remote_git_url(package_base: str, repository: str) -> str:
|
||||
"""
|
||||
generate remote git url from the package base
|
||||
|
||||
Args
|
||||
package_base(str): package base
|
||||
repository(str): repository name
|
||||
|
||||
Returns:
|
||||
str: git url for the specific base
|
||||
|
||||
Raises:
|
||||
NotImplementedError: not implemented method
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def remote_web_url(package_base: str) -> str:
|
||||
"""
|
||||
generate remote web url from the package base
|
||||
|
||||
Args
|
||||
package_base(str): package base
|
||||
|
||||
Returns:
|
||||
str: web url for the specific base
|
||||
|
||||
Raises:
|
||||
NotImplementedError: not implemented method
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def search(cls: Type[Remote], *keywords: str) -> List[AURPackage]:
|
||||
def search(cls: Type[Remote], *keywords: str, pacman: Pacman) -> List[AURPackage]:
|
||||
"""
|
||||
search package in AUR web
|
||||
|
||||
Args:
|
||||
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
List[AURPackage]: list of packages which match the criteria
|
||||
"""
|
||||
return cls().package_search(*keywords)
|
||||
return cls().package_search(*keywords, pacman=pacman)
|
||||
|
||||
def package_info(self, package_name: str) -> AURPackage:
|
||||
def package_info(self, package_name: str, *, pacman: Pacman) -> AURPackage:
|
||||
"""
|
||||
get package info by its name
|
||||
|
||||
Args:
|
||||
package_name(str): package name to search
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
AURPackage: package which match the package name
|
||||
@ -104,12 +142,13 @@ class Remote:
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def package_search(self, *keywords: str) -> List[AURPackage]:
|
||||
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
|
||||
"""
|
||||
search package in AUR web
|
||||
|
||||
Args:
|
||||
*keywords(str): keywords to search
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
List[AURPackage]: list of packages which match the criteria
|
||||
|
@ -18,11 +18,13 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import logging
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from ahriman.core.util import check_output
|
||||
from ahriman.core.util import check_output, walk
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
|
||||
|
||||
class Sources:
|
||||
@ -30,12 +32,14 @@ class Sources:
|
||||
helper to download package sources (PKGBUILD etc)
|
||||
|
||||
Attributes:
|
||||
DEFAULT_BRANCH(str): (class attribute) default branch to process git repositories.
|
||||
Must be used only for local stored repositories, use RemoteSource descriptor instead for real packages
|
||||
logger(logging.Logger): (class attribute) class logger
|
||||
"""
|
||||
|
||||
DEFAULT_BRANCH = "master" # default fallback branch
|
||||
logger = logging.getLogger("build_details")
|
||||
|
||||
_branch = "master" # in case if BLM would like to change it
|
||||
_check_output = check_output
|
||||
|
||||
@staticmethod
|
||||
@ -73,13 +77,13 @@ class Sources:
|
||||
return Sources._check_output("git", "diff", exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
|
||||
@staticmethod
|
||||
def fetch(sources_dir: Path, remote: Optional[str]) -> None:
|
||||
def fetch(sources_dir: Path, remote: Optional[RemoteSource]) -> None:
|
||||
"""
|
||||
either clone repository or update it to origin/`branch`
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to fetch
|
||||
remote(Optional[str]): remote target (from where to fetch)
|
||||
remote(Optional[RemoteSource]): remote target (from where to fetch)
|
||||
"""
|
||||
# local directory exists and there is .git directory
|
||||
is_initialized_git = (sources_dir / ".git").is_dir()
|
||||
@ -88,22 +92,30 @@ class Sources:
|
||||
Sources.logger.info("skip update at %s because there are no branches configured", sources_dir)
|
||||
return
|
||||
|
||||
branch = remote.branch if remote is not None else Sources.DEFAULT_BRANCH
|
||||
if is_initialized_git:
|
||||
Sources.logger.info("update HEAD to remote at %s", sources_dir)
|
||||
Sources._check_output("git", "fetch", "origin", Sources._branch,
|
||||
Sources.logger.info("update HEAD to remote at %s using branch %s", sources_dir, branch)
|
||||
Sources._check_output("git", "fetch", "origin", branch,
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
elif remote is not None:
|
||||
Sources.logger.info("clone remote %s to %s using branch %s", remote.git_url, sources_dir, branch)
|
||||
Sources._check_output("git", "clone", "--branch", branch, "--single-branch",
|
||||
remote.git_url, str(sources_dir),
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
elif remote is None:
|
||||
Sources.logger.warning("%s is not initialized, but no remote provided", sources_dir)
|
||||
else:
|
||||
Sources.logger.info("clone remote %s to %s", remote, sources_dir)
|
||||
Sources._check_output("git", "clone", remote, str(sources_dir),
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
Sources.logger.warning("%s is not initialized, but no remote provided", sources_dir)
|
||||
|
||||
# and now force reset to our branch
|
||||
Sources._check_output("git", "checkout", "--force", Sources._branch,
|
||||
Sources._check_output("git", "checkout", "--force", branch,
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
Sources._check_output("git", "reset", "--hard", f"origin/{Sources._branch}",
|
||||
Sources._check_output("git", "reset", "--hard", f"origin/{branch}",
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
|
||||
# move content if required
|
||||
# we are using full path to source directory in order to make append possible
|
||||
pkgbuild_dir = remote.pkgbuild_dir if remote is not None else sources_dir.resolve()
|
||||
Sources.move((sources_dir / pkgbuild_dir).resolve(), sources_dir)
|
||||
|
||||
@staticmethod
|
||||
def has_remotes(sources_dir: Path) -> bool:
|
||||
"""
|
||||
@ -126,17 +138,17 @@ class Sources:
|
||||
Args:
|
||||
sources_dir(Path): local path to sources
|
||||
"""
|
||||
Sources._check_output("git", "init", "--initial-branch", Sources._branch,
|
||||
Sources._check_output("git", "init", "--initial-branch", Sources.DEFAULT_BRANCH,
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
|
||||
@staticmethod
|
||||
def load(sources_dir: Path, remote: str, patch: Optional[str]) -> None:
|
||||
def load(sources_dir: Path, remote: Optional[RemoteSource], patch: Optional[str]) -> None:
|
||||
"""
|
||||
fetch sources from remote and apply patches
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to fetch
|
||||
remote(str): remote target (from where to fetch)
|
||||
remote(Optional[RemoteSource]): remote target (from where to fetch)
|
||||
patch(Optional[str]): optional patch to be applied
|
||||
"""
|
||||
Sources.fetch(sources_dir, remote)
|
||||
@ -145,6 +157,21 @@ class Sources:
|
||||
return
|
||||
Sources.patch_apply(sources_dir, patch)
|
||||
|
||||
@staticmethod
|
||||
def move(pkgbuild_dir: Path, sources_dir: Path) -> None:
|
||||
"""
|
||||
move content from pkgbuild_dir to sources_dir
|
||||
|
||||
Args:
|
||||
pkgbuild_dir(Path): path to directory with pkgbuild from which need to move
|
||||
sources_dir(Path): path to target directory
|
||||
"""
|
||||
if pkgbuild_dir == sources_dir:
|
||||
return # directories are the same, no need to move
|
||||
for src in walk(pkgbuild_dir):
|
||||
dst = sources_dir / src.relative_to(pkgbuild_dir)
|
||||
shutil.move(src, dst)
|
||||
|
||||
@staticmethod
|
||||
def patch_apply(sources_dir: Path, patch: str) -> None:
|
||||
"""
|
||||
|
@ -107,4 +107,4 @@ class Task:
|
||||
if self.paths.cache_for(self.package.base).is_dir():
|
||||
# no need to clone whole repository, just copy from cache first
|
||||
shutil.copytree(self.paths.cache_for(self.package.base), path, dirs_exist_ok=True)
|
||||
Sources.load(path, self.package.git_url, database.patches_get(self.package.base))
|
||||
Sources.load(path, self.package.remote, database.patches_get(self.package.base))
|
||||
|
@ -20,6 +20,7 @@
|
||||
from sqlite3 import Connection
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database.data.package_remotes import migrate_package_remotes
|
||||
from ahriman.core.database.data.package_statuses import migrate_package_statuses
|
||||
from ahriman.core.database.data.patches import migrate_patches
|
||||
from ahriman.core.database.data.users import migrate_users_data
|
||||
@ -43,3 +44,5 @@ def migrate_data(
|
||||
migrate_package_statuses(connection, repository_paths)
|
||||
migrate_patches(connection, repository_paths)
|
||||
migrate_users_data(connection, configuration)
|
||||
if result.old_version <= 1:
|
||||
migrate_package_remotes(connection, repository_paths)
|
||||
|
61
src/ahriman/core/database/data/package_remotes.py
Normal file
61
src/ahriman/core/database/data/package_remotes.py
Normal file
@ -0,0 +1,61 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 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/>.
|
||||
#
|
||||
from sqlite3 import Connection
|
||||
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
# pylint: disable=protected-access
|
||||
def migrate_package_remotes(connection: Connection, paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
perform migration for package remote sources
|
||||
|
||||
Args:
|
||||
connection(Connection): database connection
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
"""
|
||||
from ahriman.core.database.operations.package_operations import PackageOperations
|
||||
|
||||
def insert_remote(base: str, remote: RemoteSource) -> None:
|
||||
connection.execute(
|
||||
"""
|
||||
update package_bases set
|
||||
branch = :branch, git_url = :git_url, path = :path,
|
||||
web_url = :web_url, source = :source
|
||||
where package_base = :package_base
|
||||
""",
|
||||
dict(
|
||||
package_base=base,
|
||||
branch=remote.branch, git_url=remote.git_url, path=remote.path,
|
||||
web_url=remote.web_url, source=remote.source
|
||||
)
|
||||
)
|
||||
|
||||
packages = PackageOperations._packages_get_select_package_bases(connection)
|
||||
for package_base, package in packages.items():
|
||||
local_cache = paths.cache_for(package_base)
|
||||
if local_cache.exists() and not package.is_vcs:
|
||||
continue # skip packages which are not VCS and with local cache
|
||||
remote_source = RemoteSource.from_remote(PackageSource.AUR, package_base, "aur")
|
||||
if remote_source is None:
|
||||
continue # should never happen
|
||||
insert_remote(package_base, remote_source)
|
@ -42,7 +42,7 @@ def migrate_package_statuses(connection: Connection, paths: RepositoryPaths) ->
|
||||
values
|
||||
(:package_base, :version, :aur_url)
|
||||
""",
|
||||
dict(package_base=metadata.base, version=metadata.version, aur_url=metadata.aur_url))
|
||||
dict(package_base=metadata.base, version=metadata.version, aur_url=""))
|
||||
connection.execute(
|
||||
"""
|
||||
insert into package_statuses
|
||||
|
40
src/ahriman/core/database/migrations/m001_package_source.py
Normal file
40
src/ahriman/core/database/migrations/m001_package_source.py
Normal file
@ -0,0 +1,40 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 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/>.
|
||||
#
|
||||
|
||||
steps = [
|
||||
"""
|
||||
alter table package_bases add column branch text
|
||||
""",
|
||||
"""
|
||||
alter table package_bases add column git_url text
|
||||
""",
|
||||
"""
|
||||
alter table package_bases add column path text
|
||||
""",
|
||||
"""
|
||||
alter table package_bases add column web_url text
|
||||
""",
|
||||
"""
|
||||
alter table package_bases add column source text
|
||||
""",
|
||||
"""
|
||||
alter table package_bases drop column aur_url
|
||||
""",
|
||||
]
|
@ -24,6 +24,7 @@ from ahriman.core.database.operations.operations import Operations
|
||||
from ahriman.models.build_status import BuildStatus
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_description import PackageDescription
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
|
||||
|
||||
class PackageOperations(Operations):
|
||||
@ -75,13 +76,22 @@ class PackageOperations(Operations):
|
||||
connection.execute(
|
||||
"""
|
||||
insert into package_bases
|
||||
(package_base, version, aur_url)
|
||||
(package_base, version, source, branch, git_url, path, web_url)
|
||||
values
|
||||
(:package_base, :version, :aur_url)
|
||||
(:package_base, :version, :source, :branch, :git_url, :path, :web_url)
|
||||
on conflict (package_base) do update set
|
||||
version = :version, aur_url = :aur_url
|
||||
version = :version, branch = :branch, git_url = :git_url, path = :path, web_url = :web_url, source = :source
|
||||
""",
|
||||
dict(package_base=package.base, version=package.version, aur_url=package.aur_url))
|
||||
dict(
|
||||
package_base=package.base,
|
||||
version=package.version,
|
||||
branch=package.remote.branch if package.remote is not None else None,
|
||||
git_url=package.remote.git_url if package.remote is not None else None,
|
||||
path=package.remote.path if package.remote is not None else None,
|
||||
web_url=package.remote.web_url if package.remote is not None else None,
|
||||
source=package.remote.source.value if package.remote is not None else None,
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _package_update_insert_packages(connection: Connection, package: Package) -> None:
|
||||
@ -144,7 +154,7 @@ class PackageOperations(Operations):
|
||||
Dict[str, Package]: map of the package base to its descriptor (without packages themselves)
|
||||
"""
|
||||
return {
|
||||
row["package_base"]: Package(row["package_base"], row["version"], row["aur_url"], {})
|
||||
row["package_base"]: Package(row["package_base"], row["version"], RemoteSource.from_json(row), {})
|
||||
for row in connection.execute("""select * from package_bases""")
|
||||
}
|
||||
|
||||
@ -225,3 +235,28 @@ class PackageOperations(Operations):
|
||||
yield package, statuses.get(package_base, BuildStatus())
|
||||
|
||||
return self.with_connection(lambda connection: list(run(connection)))
|
||||
|
||||
def remote_update(self, package: Package) -> None:
|
||||
"""
|
||||
update package remote source
|
||||
|
||||
Args:
|
||||
package(Package): package properties
|
||||
"""
|
||||
return self.with_connection(
|
||||
lambda connection: self._package_update_insert_base(connection, package),
|
||||
commit=True)
|
||||
|
||||
def remotes_get(self) -> Dict[str, RemoteSource]:
|
||||
"""
|
||||
get packages remotes based on current settings
|
||||
|
||||
Returns:
|
||||
Dict[str, RemoteSource]: map of package base to its remote sources
|
||||
"""
|
||||
packages = self.with_connection(self._packages_get_select_package_bases)
|
||||
return {
|
||||
package_base: package.remote
|
||||
for package_base, package in packages.items()
|
||||
if package.remote is not None
|
||||
}
|
||||
|
@ -81,5 +81,5 @@ class SQLite(AuthOperations, BuildOperations, PackageOperations, PatchOperations
|
||||
|
||||
paths = configuration.repository_paths
|
||||
|
||||
self.with_connection(lambda conn: Migrations.migrate(conn, configuration))
|
||||
self.with_connection(lambda connection: Migrations.migrate(connection, configuration))
|
||||
paths.chown(self.path)
|
||||
|
@ -24,7 +24,6 @@ from ahriman.core.repository.executor import Executor
|
||||
from ahriman.core.repository.update_handler import UpdateHandler
|
||||
from ahriman.core.util import package_like
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_source import PackageSource
|
||||
|
||||
|
||||
class Repository(Executor, UpdateHandler):
|
||||
@ -42,11 +41,15 @@ class Repository(Executor, UpdateHandler):
|
||||
Returns:
|
||||
List[Package]: list of read packages
|
||||
"""
|
||||
sources = self.database.remotes_get()
|
||||
|
||||
result: Dict[str, Package] = {}
|
||||
# we are iterating over bases, not single packages
|
||||
for full_path in packages:
|
||||
try:
|
||||
local = Package.load(str(full_path), PackageSource.Archive, self.pacman, self.aur_url)
|
||||
local = Package.from_archive(full_path, self.pacman, None)
|
||||
local.remote = sources.get(local.base)
|
||||
|
||||
current = result.setdefault(local.base, local)
|
||||
if current.version != local.version:
|
||||
# force version to max of them
|
||||
@ -95,5 +98,5 @@ class Repository(Executor, UpdateHandler):
|
||||
return [
|
||||
package
|
||||
for package in packages
|
||||
if depends_on is None or depends_on.intersection(package.full_depends(self.pacman, packages))
|
||||
if depends_on.intersection(package.full_depends(self.pacman, packages))
|
||||
]
|
||||
|
@ -35,7 +35,6 @@ class RepositoryProperties:
|
||||
|
||||
Attributes:
|
||||
architecture(str): repository architecture
|
||||
aur_url(str): base AUR url
|
||||
configuration(Configuration): configuration instance
|
||||
database(SQLite): database instance
|
||||
ignore_list(List[str]): package bases which will be ignored during auto updates
|
||||
@ -65,7 +64,6 @@ class RepositoryProperties:
|
||||
self.configuration = configuration
|
||||
self.database = database
|
||||
|
||||
self.aur_url = configuration.get("alpm", "aur_url")
|
||||
self.name = configuration.get("repository", "name")
|
||||
|
||||
self.paths = configuration.repository_paths
|
||||
|
@ -62,13 +62,18 @@ class UpdateHandler(Cleaner):
|
||||
continue
|
||||
if filter_packages and local.base not in filter_packages:
|
||||
continue
|
||||
source = local.remote.source if local.remote is not None else None
|
||||
|
||||
try:
|
||||
remote = Package.load(local.base, PackageSource.AUR, self.pacman, self.aur_url)
|
||||
if source == PackageSource.Repository:
|
||||
remote = Package.from_official(local.base, self.pacman)
|
||||
else:
|
||||
remote = Package.from_aur(local.base, self.pacman)
|
||||
if local.is_outdated(remote, self.paths):
|
||||
self.reporter.set_pending(local.base)
|
||||
result.append(remote)
|
||||
self.reporter.set_success(local)
|
||||
else:
|
||||
self.reporter.set_success(local)
|
||||
except Exception:
|
||||
self.reporter.set_failed(local.base)
|
||||
self.logger.exception("could not load remote package %s", local.base)
|
||||
@ -89,7 +94,7 @@ class UpdateHandler(Cleaner):
|
||||
for dirname in self.paths.cache.iterdir():
|
||||
try:
|
||||
Sources.fetch(dirname, remote=None)
|
||||
remote = Package.load(str(dirname), PackageSource.Local, self.pacman, self.aur_url)
|
||||
remote = Package.from_build(dirname)
|
||||
|
||||
local = packages.get(remote.base)
|
||||
if local is None:
|
||||
|
@ -70,7 +70,7 @@ class Leaf:
|
||||
Leaf: loaded class
|
||||
"""
|
||||
with tmpdir() as clone_dir:
|
||||
Sources.load(clone_dir, package.git_url, database.patches_get(package.base))
|
||||
Sources.load(clone_dir, package.remote, database.patches_get(package.base))
|
||||
dependencies = Package.dependencies(clone_dir)
|
||||
return cls(package, dependencies)
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Action(Enum):
|
||||
class Action(str, Enum):
|
||||
"""
|
||||
base action enumeration
|
||||
|
||||
|
@ -23,6 +23,7 @@ import datetime
|
||||
import inflection
|
||||
|
||||
from dataclasses import dataclass, field, fields
|
||||
from pyalpm import Package # type: ignore
|
||||
from typing import Any, Callable, Dict, List, Optional, Type
|
||||
|
||||
from ahriman.core.util import filter_json, full_version
|
||||
@ -47,6 +48,7 @@ class AURPackage:
|
||||
first_submitted(datetime.datetime): timestamp of the first package submission
|
||||
last_modified(datetime.datetime): timestamp of the last package submission
|
||||
url_path(str): AUR package path
|
||||
repository(str): repository name of the package
|
||||
depends(List[str]): list of package dependencies
|
||||
make_depends(List[str]): list of package make dependencies
|
||||
opt_depends(List[str]): list of package optional dependencies
|
||||
@ -70,6 +72,7 @@ class AURPackage:
|
||||
url: Optional[str] = None
|
||||
out_of_date: Optional[datetime.datetime] = None
|
||||
maintainer: Optional[str] = None
|
||||
repository: str = "aur"
|
||||
depends: List[str] = field(default_factory=list)
|
||||
make_depends: List[str] = field(default_factory=list)
|
||||
opt_depends: List[str] = field(default_factory=list)
|
||||
@ -94,6 +97,42 @@ class AURPackage:
|
||||
properties = cls.convert(dump)
|
||||
return cls(**filter_json(properties, known_fields))
|
||||
|
||||
@classmethod
|
||||
def from_pacman(cls: Type[AURPackage], package: Package) -> AURPackage:
|
||||
"""
|
||||
construct package descriptor from official repository wrapper
|
||||
|
||||
Args:
|
||||
package(Package): pyalpm package descriptor
|
||||
|
||||
Returns:
|
||||
AURPackage: AUR package descriptor
|
||||
"""
|
||||
return cls(
|
||||
id=0,
|
||||
name=package.name,
|
||||
package_base_id=0,
|
||||
package_base=package.base,
|
||||
version=package.version,
|
||||
description=package.desc,
|
||||
num_votes=0,
|
||||
popularity=0.0,
|
||||
first_submitted=datetime.datetime.utcfromtimestamp(0),
|
||||
last_modified=datetime.datetime.utcfromtimestamp(package.builddate),
|
||||
url_path="",
|
||||
url=package.url,
|
||||
out_of_date=None,
|
||||
maintainer=None,
|
||||
repository=package.db.name,
|
||||
depends=package.depends,
|
||||
make_depends=package.makedepends,
|
||||
opt_depends=package.optdepends,
|
||||
conflicts=package.conflicts,
|
||||
provides=package.provides,
|
||||
license=package.licenses,
|
||||
keywords=[],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_repo(cls: Type[AURPackage], dump: Dict[str, Any]) -> AURPackage:
|
||||
"""
|
||||
@ -122,6 +161,7 @@ class AURPackage:
|
||||
dump["flag_date"],
|
||||
"%Y-%m-%dT%H:%M:%S.%fZ") if dump["flag_date"] is not None else None,
|
||||
maintainer=next(iter(dump["maintainers"]), None),
|
||||
repository=dump["repo"],
|
||||
depends=dump["depends"],
|
||||
make_depends=dump["makedepends"],
|
||||
opt_depends=dump["optdepends"],
|
||||
|
@ -23,7 +23,7 @@ from enum import Enum
|
||||
from typing import Type
|
||||
|
||||
|
||||
class AuthSettings(Enum):
|
||||
class AuthSettings(str, Enum):
|
||||
"""
|
||||
web authorization type
|
||||
|
||||
|
@ -28,7 +28,7 @@ from typing import Any, Dict, Type
|
||||
from ahriman.core.util import filter_json, pretty_datetime
|
||||
|
||||
|
||||
class BuildStatusEnum(Enum):
|
||||
class BuildStatusEnum(str, Enum):
|
||||
"""
|
||||
build status enumeration
|
||||
|
||||
|
@ -26,15 +26,17 @@ from dataclasses import asdict, dataclass
|
||||
from pathlib import Path
|
||||
from pyalpm import vercmp # type: ignore
|
||||
from srcinfo.parse import parse_srcinfo # type: ignore
|
||||
from typing import Any, Dict, Iterable, List, Set, Type
|
||||
from typing import Any, Dict, Iterable, List, Optional, Set, Type
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.aur import AUR
|
||||
from ahriman.core.alpm.remote.official import Official
|
||||
from ahriman.core.alpm.remote.official_syncdb import OfficialSyncdb
|
||||
from ahriman.core.exceptions import InvalidPackageInfo
|
||||
from ahriman.core.util import check_output, full_version
|
||||
from ahriman.models.package_description import PackageDescription
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
@ -44,15 +46,16 @@ class Package:
|
||||
package properties representation
|
||||
|
||||
Attributes:
|
||||
aur_url(str): AUR root url
|
||||
base(str): package base name
|
||||
packages(Dict[str, PackageDescription): map of package names to their properties. Filled only on load from archive
|
||||
packages(Dict[str, PackageDescription): map of package names to their properties.
|
||||
Filled only on load from archive
|
||||
remote(Optional[RemoteSource]): package remote source if applicable
|
||||
version(str): package full version
|
||||
"""
|
||||
|
||||
base: str
|
||||
version: str
|
||||
aur_url: str
|
||||
remote: Optional[RemoteSource]
|
||||
packages: Dict[str, PackageDescription]
|
||||
|
||||
_check_output = check_output
|
||||
@ -67,16 +70,6 @@ class Package:
|
||||
"""
|
||||
return sorted(set(sum([package.depends for package in self.packages.values()], start=[])))
|
||||
|
||||
@property
|
||||
def git_url(self) -> str:
|
||||
"""
|
||||
get git clone url
|
||||
|
||||
Returns:
|
||||
str: package git url to clone
|
||||
"""
|
||||
return f"{self.aur_url}/{self.base}.git"
|
||||
|
||||
@property
|
||||
def groups(self) -> List[str]:
|
||||
"""
|
||||
@ -122,56 +115,46 @@ class Package:
|
||||
"""
|
||||
return sorted(set(sum([package.licenses for package in self.packages.values()], start=[])))
|
||||
|
||||
@property
|
||||
def web_url(self) -> str:
|
||||
"""
|
||||
get package url which can be used to see package in web
|
||||
|
||||
Returns:
|
||||
str: package AUR url
|
||||
"""
|
||||
return f"{self.aur_url}/packages/{self.base}"
|
||||
|
||||
@classmethod
|
||||
def from_archive(cls: Type[Package], path: Path, pacman: Pacman, aur_url: str) -> Package:
|
||||
def from_archive(cls: Type[Package], path: Path, pacman: Pacman, remote: Optional[RemoteSource]) -> Package:
|
||||
"""
|
||||
construct package properties from package archive
|
||||
|
||||
Args:
|
||||
path(Path): path to package archive
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
aur_url(str): AUR root url
|
||||
remote(RemoteSource): package remote source if applicable
|
||||
|
||||
Returns:
|
||||
Package: package properties
|
||||
"""
|
||||
package = pacman.handle.load_pkg(str(path))
|
||||
return cls(package.base, package.version, aur_url,
|
||||
{package.name: PackageDescription.from_package(package, path)})
|
||||
description = PackageDescription.from_package(package, path)
|
||||
return cls(package.base, package.version, remote, {package.name: description})
|
||||
|
||||
@classmethod
|
||||
def from_aur(cls: Type[Package], name: str, aur_url: str) -> Package:
|
||||
def from_aur(cls: Type[Package], name: str, pacman: Pacman) -> Package:
|
||||
"""
|
||||
construct package properties from AUR page
|
||||
|
||||
Args:
|
||||
name(str): package name (either base or normal name)
|
||||
aur_url(str): AUR root url
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
|
||||
Returns:
|
||||
Package: package properties
|
||||
"""
|
||||
package = AUR.info(name)
|
||||
return cls(package.package_base, package.version, aur_url, {package.name: PackageDescription()})
|
||||
package = AUR.info(name, pacman=pacman)
|
||||
remote = RemoteSource.from_remote(PackageSource.AUR, package.package_base, package.repository)
|
||||
return cls(package.package_base, package.version, remote, {package.name: PackageDescription()})
|
||||
|
||||
@classmethod
|
||||
def from_build(cls: Type[Package], path: Path, aur_url: str) -> Package:
|
||||
def from_build(cls: Type[Package], path: Path) -> Package:
|
||||
"""
|
||||
construct package properties from sources directory
|
||||
|
||||
Args:
|
||||
path(Path): path to package sources directory
|
||||
aur_url(str): AUR root url
|
||||
|
||||
Returns:
|
||||
Package: package properties
|
||||
@ -179,13 +162,14 @@ class Package:
|
||||
Raises:
|
||||
InvalidPackageInfo: if there are parsing errors
|
||||
"""
|
||||
srcinfo, errors = parse_srcinfo((path / ".SRCINFO").read_text())
|
||||
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo", exception=None, cwd=path)
|
||||
srcinfo, errors = parse_srcinfo(srcinfo_source)
|
||||
if errors:
|
||||
raise InvalidPackageInfo(errors)
|
||||
packages = {key: PackageDescription() for key in srcinfo["packages"]}
|
||||
version = full_version(srcinfo.get("epoch"), srcinfo["pkgver"], srcinfo["pkgrel"])
|
||||
|
||||
return cls(srcinfo["pkgbase"], version, aur_url, packages)
|
||||
return cls(srcinfo["pkgbase"], version, None, packages)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls: Type[Package], dump: Dict[str, Any]) -> Package:
|
||||
@ -202,59 +186,29 @@ class Package:
|
||||
key: PackageDescription.from_json(value)
|
||||
for key, value in dump.get("packages", {}).items()
|
||||
}
|
||||
return Package(
|
||||
remote = dump.get("remote", {})
|
||||
return cls(
|
||||
base=dump["base"],
|
||||
version=dump["version"],
|
||||
aur_url=dump["aur_url"],
|
||||
remote=RemoteSource.from_json(remote),
|
||||
packages=packages)
|
||||
|
||||
@classmethod
|
||||
def from_official(cls: Type[Package], name: str, aur_url: str) -> Package:
|
||||
def from_official(cls: Type[Package], name: str, pacman: Pacman, use_syncdb: bool = True) -> Package:
|
||||
"""
|
||||
construct package properties from official repository page
|
||||
|
||||
Args:
|
||||
name(str): package name (either base or normal name)
|
||||
aur_url(str): AUR root url
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
use_syncdb(bool): use pacman databases instead of official repositories RPC (Default value = True)
|
||||
|
||||
Returns:
|
||||
Package: package properties
|
||||
"""
|
||||
package = Official.info(name)
|
||||
return cls(package.package_base, package.version, aur_url, {package.name: PackageDescription()})
|
||||
|
||||
@classmethod
|
||||
def load(cls: Type[Package], package: str, source: PackageSource, pacman: Pacman, aur_url: str) -> Package:
|
||||
"""
|
||||
package constructor from available sources
|
||||
|
||||
Args:
|
||||
package(str): one of path to sources directory, path to archive or package name/base
|
||||
source(PackageSource): source of the package required to define the load method
|
||||
pacman(Pacman): alpm wrapper instance (required to load from archive)
|
||||
aur_url(str): AUR root url
|
||||
|
||||
Returns:
|
||||
Package: package properties
|
||||
|
||||
Raises:
|
||||
InvalidPackageInfo: if supplied package source is not valid
|
||||
"""
|
||||
try:
|
||||
resolved_source = source.resolve(package)
|
||||
if resolved_source == PackageSource.Archive:
|
||||
return cls.from_archive(Path(package), pacman, aur_url)
|
||||
if resolved_source == PackageSource.AUR:
|
||||
return cls.from_aur(package, aur_url)
|
||||
if resolved_source == PackageSource.Local:
|
||||
return cls.from_build(Path(package), aur_url)
|
||||
if resolved_source == PackageSource.Repository:
|
||||
return cls.from_official(package, aur_url)
|
||||
raise InvalidPackageInfo(f"Unsupported local package source {resolved_source}")
|
||||
except InvalidPackageInfo:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise InvalidPackageInfo(str(e))
|
||||
package = OfficialSyncdb.info(name, pacman=pacman) if use_syncdb else Official.info(name, pacman=pacman)
|
||||
remote = RemoteSource.from_remote(PackageSource.Repository, package.package_base, package.repository)
|
||||
return cls(package.package_base, package.version, remote, {package.name: PackageDescription()})
|
||||
|
||||
@staticmethod
|
||||
def dependencies(path: Path) -> Set[str]:
|
||||
@ -279,7 +233,8 @@ class Package:
|
||||
package_name = package_name.split(symbol)[0]
|
||||
return package_name
|
||||
|
||||
srcinfo, errors = parse_srcinfo((path / ".SRCINFO").read_text())
|
||||
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo", exception=None, cwd=path)
|
||||
srcinfo, errors = parse_srcinfo(srcinfo_source)
|
||||
if errors:
|
||||
raise InvalidPackageInfo(errors)
|
||||
makedepends = extract_packages(srcinfo.get("makedepends", []))
|
||||
@ -310,7 +265,7 @@ class Package:
|
||||
from ahriman.core.build_tools.sources import Sources
|
||||
|
||||
logger = logging.getLogger("build_details")
|
||||
Sources.load(paths.cache_for(self.base), self.git_url, None)
|
||||
Sources.load(paths.cache_for(self.base), self.remote, None)
|
||||
|
||||
try:
|
||||
# update pkgver first
|
||||
|
@ -26,7 +26,7 @@ from urllib.parse import urlparse
|
||||
from ahriman.core.util import package_like
|
||||
|
||||
|
||||
class PackageSource(Enum):
|
||||
class PackageSource(str, Enum):
|
||||
"""
|
||||
package source for addition enumeration
|
||||
|
||||
|
124
src/ahriman/models/remote_source.py
Normal file
124
src/ahriman/models/remote_source.py
Normal file
@ -0,0 +1,124 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 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/>.
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict, dataclass, fields
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional, Type
|
||||
|
||||
from ahriman.core.util import filter_json
|
||||
from ahriman.models.package_source import PackageSource
|
||||
|
||||
|
||||
@dataclass
|
||||
class RemoteSource:
|
||||
"""
|
||||
remote package source properties
|
||||
|
||||
Attributes:
|
||||
branch(str): branch of the git repository
|
||||
git_url(str): url of the git repository
|
||||
path(str): path to directory with PKGBUILD inside the git repository
|
||||
source(PackageSource): package source pointer used by some parsers
|
||||
web_url(str): url of the package in the web interface
|
||||
"""
|
||||
|
||||
git_url: str
|
||||
web_url: str
|
||||
path: str
|
||||
branch: str
|
||||
source: PackageSource
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""
|
||||
convert source to enum type
|
||||
"""
|
||||
self.source = PackageSource(self.source)
|
||||
|
||||
@property
|
||||
def pkgbuild_dir(self) -> Path:
|
||||
"""
|
||||
get path to directory with package sources (PKGBUILD etc)
|
||||
|
||||
Returns:
|
||||
Path: path to directory with package sources based on settings
|
||||
"""
|
||||
return Path(self.path)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls: Type[RemoteSource], dump: Dict[str, Any]) -> Optional[RemoteSource]:
|
||||
"""
|
||||
construct remote source from the json dump (or database row)
|
||||
|
||||
Args:
|
||||
dump(Dict[str, Any]): json dump body
|
||||
|
||||
Returns:
|
||||
Optional[RemoteSource]: remote source
|
||||
"""
|
||||
# filter to only known fields
|
||||
known_fields = [pair.name for pair in fields(cls)]
|
||||
dump = filter_json(dump, known_fields)
|
||||
if dump:
|
||||
return cls(**dump)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def from_remote(cls: Type[RemoteSource], source: PackageSource, package_base: str,
|
||||
repository: str) -> Optional[RemoteSource]:
|
||||
"""
|
||||
generate remote source from the package base
|
||||
|
||||
Args:
|
||||
source(PackageSource): source of the package
|
||||
package_base(str): package base
|
||||
repository(str): repository name
|
||||
|
||||
Returns:
|
||||
Optional[RemoteSource]: generated remote source if any, None otherwise
|
||||
"""
|
||||
if source == PackageSource.AUR:
|
||||
from ahriman.core.alpm.remote.aur import AUR
|
||||
return RemoteSource(
|
||||
git_url=AUR.remote_git_url(package_base, repository),
|
||||
web_url=AUR.remote_web_url(package_base),
|
||||
path=".",
|
||||
branch="master",
|
||||
source=source,
|
||||
)
|
||||
if source == PackageSource.Repository:
|
||||
from ahriman.core.alpm.remote.official import Official
|
||||
return RemoteSource(
|
||||
git_url=Official.remote_git_url(package_base, repository),
|
||||
web_url=Official.remote_web_url(package_base),
|
||||
path="trunk",
|
||||
branch=f"packages/{package_base}",
|
||||
source=source,
|
||||
)
|
||||
return None
|
||||
|
||||
def view(self) -> Dict[str, Any]:
|
||||
"""
|
||||
generate json package remote view
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: json-friendly dictionary
|
||||
"""
|
||||
return asdict(self)
|
@ -23,7 +23,7 @@ from enum import Enum
|
||||
from typing import Type
|
||||
|
||||
|
||||
class ReportSettings(Enum):
|
||||
class ReportSettings(str, Enum):
|
||||
"""
|
||||
report targets enumeration
|
||||
|
||||
|
@ -23,7 +23,7 @@ from enum import Enum
|
||||
from typing import Type
|
||||
|
||||
|
||||
class SignSettings(Enum):
|
||||
class SignSettings(str, Enum):
|
||||
"""
|
||||
sign targets enumeration
|
||||
|
||||
|
@ -23,7 +23,7 @@ from enum import Enum
|
||||
from typing import Type
|
||||
|
||||
|
||||
class SmtpSSLSettings(Enum):
|
||||
class SmtpSSLSettings(str, Enum):
|
||||
"""
|
||||
SMTP SSL mode enumeration
|
||||
|
||||
|
@ -23,7 +23,7 @@ from enum import Enum
|
||||
from typing import Type
|
||||
|
||||
|
||||
class UploadSettings(Enum):
|
||||
class UploadSettings(str, Enum):
|
||||
"""
|
||||
remote synchronization targets enumeration
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class UserAccess(Enum):
|
||||
class UserAccess(str, Enum):
|
||||
"""
|
||||
web user access enumeration
|
||||
|
||||
|
@ -86,7 +86,7 @@ class IndexView(BaseView):
|
||||
"status_color": status.status.bootstrap_color(),
|
||||
"timestamp": pretty_datetime(status.timestamp),
|
||||
"version": package.version,
|
||||
"web_url": package.web_url,
|
||||
"web_url": package.remote.web_url if package.remote is not None else None,
|
||||
} for package, status in sorted(self.service.packages, key=lambda item: item[0].base)
|
||||
]
|
||||
service = {
|
||||
|
@ -50,7 +50,7 @@ class SearchView(BaseView):
|
||||
HTTPNotFound: if no packages found
|
||||
"""
|
||||
search: List[str] = self.request.query.getall("for", default=[])
|
||||
packages = AUR.multisearch(*search)
|
||||
packages = AUR.multisearch(*search, pacman=self.service.repository.pacman)
|
||||
if not packages:
|
||||
raise HTTPNotFound(reason=f"No packages found for terms: {search}")
|
||||
|
||||
|
@ -44,19 +44,21 @@ def test_add_aur(application_packages: ApplicationPackages, package_ahriman: Pac
|
||||
"""
|
||||
must add package from AUR
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
|
||||
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load")
|
||||
dependencies_mock = mocker.patch(
|
||||
"ahriman.application.application.application_packages.ApplicationPackages._process_dependencies")
|
||||
build_queue_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.build_queue_insert")
|
||||
update_remote_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.remote_update")
|
||||
|
||||
application_packages._add_aur(package_ahriman.base, set(), False)
|
||||
load_mock.assert_called_once_with(
|
||||
pytest.helpers.anyvar(int),
|
||||
package_ahriman.git_url,
|
||||
package_ahriman.remote,
|
||||
pytest.helpers.anyvar(int))
|
||||
dependencies_mock.assert_called_once_with(pytest.helpers.anyvar(int), set(), False)
|
||||
build_queue_mock.assert_called_once_with(package_ahriman)
|
||||
update_remote_mock.assert_called_once_with(package_ahriman)
|
||||
|
||||
|
||||
def test_add_directory(
|
||||
@ -80,7 +82,7 @@ def test_add_local(application_packages: ApplicationPackages, package_ahriman: P
|
||||
"""
|
||||
must add package from local sources
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||
init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init")
|
||||
copytree_mock = mocker.patch("shutil.copytree")
|
||||
dependencies_mock = mocker.patch(
|
||||
@ -112,6 +114,20 @@ def test_add_remote(application_packages: ApplicationPackages, package_descripti
|
||||
response_mock.raise_for_status.assert_called_once_with()
|
||||
|
||||
|
||||
def test_add_repository(application_packages: ApplicationPackages, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must add package from official repository
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package.from_official", return_value=package_ahriman)
|
||||
build_queue_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.build_queue_insert")
|
||||
update_remote_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.remote_update")
|
||||
|
||||
application_packages._add_repository(package_ahriman.base)
|
||||
build_queue_mock.assert_called_once_with(package_ahriman)
|
||||
update_remote_mock.assert_called_once_with(package_ahriman)
|
||||
|
||||
|
||||
def test_process_dependencies(application_packages: ApplicationPackages, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process dependencies addition
|
||||
|
@ -14,7 +14,7 @@ def test_finalize(application_repository: ApplicationRepository) -> None:
|
||||
must raise NotImplemented for missing finalize method
|
||||
"""
|
||||
with pytest.raises(NotImplementedError):
|
||||
application_repository._finalize([])
|
||||
application_repository._finalize(Result())
|
||||
|
||||
|
||||
def test_clean_cache(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
|
||||
@ -58,8 +58,8 @@ def test_report(application_repository: ApplicationRepository, mocker: MockerFix
|
||||
must generate report
|
||||
"""
|
||||
executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_report")
|
||||
application_repository.report(["a"], [])
|
||||
executor_mock.assert_called_once_with(["a"], [])
|
||||
application_repository.report(["a"], Result())
|
||||
executor_mock.assert_called_once_with(["a"], Result())
|
||||
|
||||
|
||||
def test_sign(application_repository: ApplicationRepository, package_ahriman: Package, package_python_schedule: Package,
|
||||
@ -179,7 +179,6 @@ def test_update(application_repository: ApplicationRepository, package_ahriman:
|
||||
|
||||
mocker.patch("ahriman.core.tree.Tree.load", return_value=tree)
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=paths)
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
build_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_build", return_value=result)
|
||||
update_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_update", return_value=result)
|
||||
finalize_mock = mocker.patch(
|
||||
@ -201,7 +200,6 @@ def test_update_empty(application_repository: ApplicationRepository, package_ahr
|
||||
|
||||
mocker.patch("ahriman.core.tree.Tree.load", return_value=tree)
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=[])
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.process_build")
|
||||
update_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_update")
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import argparse
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.application import Application
|
||||
@ -37,7 +38,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_create")
|
||||
|
||||
Patch.run(args, "x86_64", configuration, True, False)
|
||||
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, args.track)
|
||||
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), Path(args.package), args.track)
|
||||
|
||||
|
||||
def test_run_list(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
@ -96,11 +97,11 @@ def test_patch_set_create(application: Application, package_ahriman: Package, mo
|
||||
must create patch set for the package
|
||||
"""
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.patch_create", return_value="patch")
|
||||
create_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.patches_insert")
|
||||
|
||||
Patch.patch_set_create(application, "path", ["*.patch"])
|
||||
Patch.patch_set_create(application, Path("path"), ["*.patch"])
|
||||
create_mock.assert_called_once_with(package_ahriman.base, "patch")
|
||||
|
||||
|
||||
|
@ -34,6 +34,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, aur_package
|
||||
must run command
|
||||
"""
|
||||
args = _default_args(args)
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
aur_search_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[aur_package_ahriman])
|
||||
official_search_mock = mocker.patch("ahriman.core.alpm.remote.official.Official.multisearch",
|
||||
return_value=[aur_package_ahriman])
|
||||
@ -41,8 +42,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, aur_package
|
||||
print_mock = mocker.patch("ahriman.core.formatters.printer.Printer.print")
|
||||
|
||||
Search.run(args, "x86_64", configuration, True, False)
|
||||
aur_search_mock.assert_called_once_with("ahriman")
|
||||
official_search_mock.assert_called_once_with("ahriman")
|
||||
aur_search_mock.assert_called_once_with("ahriman", pacman=pytest.helpers.anyvar(int))
|
||||
official_search_mock.assert_called_once_with("ahriman", pacman=pytest.helpers.anyvar(int))
|
||||
check_mock.assert_called_once_with(False, False)
|
||||
print_mock.assert_has_calls([mock.call(False), mock.call(False)])
|
||||
|
||||
@ -56,6 +57,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
|
||||
mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[])
|
||||
mocker.patch("ahriman.core.alpm.remote.official.Official.multisearch", return_value=[])
|
||||
mocker.patch("ahriman.core.formatters.printer.Printer.print")
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_if_empty")
|
||||
|
||||
Search.run(args, "x86_64", configuration, True, False)
|
||||
@ -70,6 +72,7 @@ def test_run_sort(args: argparse.Namespace, configuration: Configuration, aur_pa
|
||||
args = _default_args(args)
|
||||
mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[aur_package_ahriman])
|
||||
mocker.patch("ahriman.core.alpm.remote.official.Official.multisearch", return_value=[])
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
|
||||
|
||||
Search.run(args, "x86_64", configuration, True, False)
|
||||
@ -88,6 +91,7 @@ def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, aur
|
||||
args.sort_by = "field"
|
||||
mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[aur_package_ahriman])
|
||||
mocker.patch("ahriman.core.alpm.remote.official.Official.multisearch", return_value=[])
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
|
||||
|
||||
Search.run(args, "x86_64", configuration, True, False)
|
||||
|
@ -6,6 +6,7 @@ from pytest_mock import MockerFixture
|
||||
from typing import Any, Dict, Type, TypeVar
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.auth.auth import Auth
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database.sqlite import SQLite
|
||||
@ -15,6 +16,8 @@ from ahriman.models.aur_package import AURPackage
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_description import PackageDescription
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
from ahriman.models.result import Result
|
||||
from ahriman.models.user import User
|
||||
@ -101,8 +104,8 @@ def aur_package_ahriman() -> AURPackage:
|
||||
description="ArcH Linux ReposItory MANager",
|
||||
num_votes=0,
|
||||
popularity=0,
|
||||
first_submitted=datetime.datetime(2021, 4, 9, 22, 44, 45),
|
||||
last_modified=datetime.datetime(2021, 12, 25, 23, 11, 11),
|
||||
first_submitted=datetime.datetime.utcfromtimestamp(1618008285),
|
||||
last_modified=datetime.datetime.utcfromtimestamp(1640473871),
|
||||
url_path="/cgit/aur.git/snapshot/ahriman.tar.gz",
|
||||
url="https://github.com/arcan1s/ahriman",
|
||||
out_of_date=None,
|
||||
@ -155,13 +158,14 @@ def aur_package_akonadi() -> AURPackage:
|
||||
version="21.12.3-2",
|
||||
description="PIM layer, which provides an asynchronous API to access all kind of PIM data",
|
||||
num_votes=0,
|
||||
popularity=0,
|
||||
first_submitted=datetime.datetime(1970, 1, 1, 0, 0, 0),
|
||||
last_modified=datetime.datetime(2022, 3, 6, 8, 39, 50, 610000),
|
||||
popularity=0.0,
|
||||
first_submitted=datetime.datetime.utcfromtimestamp(0),
|
||||
last_modified=datetime.datetime.utcfromtimestamp(1646555990.610),
|
||||
url_path="",
|
||||
url="https://kontact.kde.org",
|
||||
out_of_date=None,
|
||||
maintainer="felixonmars",
|
||||
repository="extra",
|
||||
depends=[
|
||||
"libakonadi",
|
||||
"mariadb",
|
||||
@ -245,7 +249,7 @@ def package_ahriman(package_description_ahriman: PackageDescription) -> Package:
|
||||
return Package(
|
||||
base="ahriman",
|
||||
version="1.7.0-1",
|
||||
aur_url="https://aur.archlinux.org",
|
||||
remote=RemoteSource.from_remote(PackageSource.AUR, "ahriman", "aur"),
|
||||
packages=packages)
|
||||
|
||||
|
||||
@ -270,7 +274,7 @@ def package_python_schedule(
|
||||
return Package(
|
||||
base="python-schedule",
|
||||
version="1.0.0-2",
|
||||
aur_url="https://aur.archlinux.org",
|
||||
remote=RemoteSource.from_remote(PackageSource.AUR, "python-schedule", "aur"),
|
||||
packages=packages)
|
||||
|
||||
|
||||
@ -344,6 +348,34 @@ def package_description_python2_schedule() -> PackageDescription:
|
||||
url="https://github.com/dbader/schedule")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pacman(configuration: Configuration) -> Pacman:
|
||||
"""
|
||||
fixture for pacman wrapper
|
||||
|
||||
Args:
|
||||
configuration(Configuration): configuration fixture
|
||||
|
||||
Returns:
|
||||
Pacman: pacman wrapper test instance
|
||||
"""
|
||||
return Pacman(configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def remote_source(package_ahriman: Package) -> RemoteSource:
|
||||
"""
|
||||
remote source fixture
|
||||
|
||||
Args:
|
||||
package_ahriman(Package): package fixture
|
||||
|
||||
Returns:
|
||||
RemoteSource: remote source test instance
|
||||
"""
|
||||
return RemoteSource.from_remote(PackageSource.AUR, "ahriman", "aur")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def repository_paths(configuration: Configuration) -> RepositoryPaths:
|
||||
"""
|
||||
|
@ -2,6 +2,7 @@ import pytest
|
||||
|
||||
from ahriman.core.alpm.remote.aur import AUR
|
||||
from ahriman.core.alpm.remote.official import Official
|
||||
from ahriman.core.alpm.remote.official_syncdb import OfficialSyncdb
|
||||
from ahriman.core.alpm.remote.remote import Remote
|
||||
|
||||
|
||||
@ -27,6 +28,17 @@ def official() -> Official:
|
||||
return Official()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def official_syncdb() -> OfficialSyncdb:
|
||||
"""
|
||||
official repository fixture with database processing
|
||||
|
||||
Returns:
|
||||
OfficialSyncdb: official repository with database processing helper instance
|
||||
"""
|
||||
return OfficialSyncdb()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def remote() -> Remote:
|
||||
"""
|
||||
|
@ -6,6 +6,7 @@ from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.aur import AUR
|
||||
from ahriman.core.exceptions import InvalidPackageInfo
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
@ -49,6 +50,23 @@ def test_parse_response_unknown_error() -> None:
|
||||
AUR.parse_response({"type": "error"})
|
||||
|
||||
|
||||
def test_remote_git_url(aur_package_ahriman: AURPackage) -> None:
|
||||
"""
|
||||
must generate package git url
|
||||
"""
|
||||
git_url = AUR.remote_git_url(aur_package_ahriman.package_base, aur_package_ahriman.repository)
|
||||
assert git_url.endswith(".git")
|
||||
assert git_url.startswith(AUR.DEFAULT_AUR_URL)
|
||||
|
||||
|
||||
def test_remote_web_url(aur_package_ahriman: AURPackage) -> None:
|
||||
"""
|
||||
must generate package git url
|
||||
"""
|
||||
web_url = AUR.remote_web_url(aur_package_ahriman.package_base)
|
||||
assert web_url.startswith(AUR.DEFAULT_AUR_URL)
|
||||
|
||||
|
||||
def test_make_request(aur: AUR, aur_package_ahriman: AURPackage,
|
||||
mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
@ -109,19 +127,19 @@ def test_make_request_failed_http_error(aur: AUR, mocker: MockerFixture) -> None
|
||||
aur.make_request("info", "ahriman")
|
||||
|
||||
|
||||
def test_package_info(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
||||
def test_package_info(aur: AUR, aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must make request for info
|
||||
"""
|
||||
request_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.make_request", return_value=[aur_package_ahriman])
|
||||
assert aur.package_info(aur_package_ahriman.name) == aur_package_ahriman
|
||||
assert aur.package_info(aur_package_ahriman.name, pacman=pacman) == aur_package_ahriman
|
||||
request_mock.assert_called_once_with("info", aur_package_ahriman.name)
|
||||
|
||||
|
||||
def test_package_search(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
||||
def test_package_search(aur: AUR, aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must make request for search
|
||||
"""
|
||||
request_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.make_request", return_value=[aur_package_ahriman])
|
||||
assert aur.package_search(aur_package_ahriman.name) == [aur_package_ahriman]
|
||||
assert aur.package_search(aur_package_ahriman.name, pacman=pacman) == [aur_package_ahriman]
|
||||
request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="name-desc")
|
||||
|
@ -6,6 +6,7 @@ from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.official import Official
|
||||
from ahriman.core.exceptions import InvalidPackageInfo
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
@ -41,6 +42,38 @@ def test_parse_response_unknown_error(resource_path_root: Path) -> None:
|
||||
Official.parse_response(json.loads(response))
|
||||
|
||||
|
||||
def test_remote_git_url(aur_package_akonadi: AURPackage) -> None:
|
||||
"""
|
||||
must generate package git url for core packages
|
||||
"""
|
||||
git_urls = [
|
||||
Official.remote_git_url(aur_package_akonadi.package_base, repository)
|
||||
for repository in ("core", "extra", "Core", "Extra")
|
||||
]
|
||||
assert all(git_url.endswith("svntogit-packages.git") for git_url in git_urls)
|
||||
assert len(set(git_urls)) == 1
|
||||
|
||||
|
||||
def test_remote_git_url_community(aur_package_akonadi: AURPackage) -> None:
|
||||
"""
|
||||
must generate package git url for core packages
|
||||
"""
|
||||
git_urls = [
|
||||
Official.remote_git_url(aur_package_akonadi.package_base, repository)
|
||||
for repository in ("community", "multilib", "Community", "Multilib")
|
||||
]
|
||||
assert all(git_url.endswith("svntogit-community.git") for git_url in git_urls)
|
||||
assert len(set(git_urls)) == 1
|
||||
|
||||
|
||||
def test_remote_web_url(aur_package_akonadi: AURPackage) -> None:
|
||||
"""
|
||||
must generate package git url
|
||||
"""
|
||||
web_url = Official.remote_web_url(aur_package_akonadi.package_base)
|
||||
assert web_url.startswith(Official.DEFAULT_ARCHLINUX_URL)
|
||||
|
||||
|
||||
def test_make_request(official: Official, aur_package_akonadi: AURPackage,
|
||||
mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
@ -51,7 +84,8 @@ def test_make_request(official: Official, aur_package_akonadi: AURPackage,
|
||||
request_mock = mocker.patch("requests.get", return_value=response_mock)
|
||||
|
||||
assert official.make_request("akonadi", by="q") == [aur_package_akonadi]
|
||||
request_mock.assert_called_once_with("https://archlinux.org/packages/search/json", params={"q": ("akonadi",)})
|
||||
request_mock.assert_called_once_with("https://archlinux.org/packages/search/json",
|
||||
params={"q": ("akonadi",), "repo": Official.DEFAULT_SEARCH_REPOSITORIES})
|
||||
|
||||
|
||||
def test_make_request_failed(official: Official, mocker: MockerFixture) -> None:
|
||||
@ -72,21 +106,23 @@ def test_make_request_failed_http_error(official: Official, mocker: MockerFixtur
|
||||
official.make_request("akonadi", by="q")
|
||||
|
||||
|
||||
def test_package_info(official: Official, aur_package_akonadi: AURPackage, mocker: MockerFixture) -> None:
|
||||
def test_package_info(official: Official, aur_package_akonadi: AURPackage, pacman: Pacman,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must make request for info
|
||||
"""
|
||||
request_mock = mocker.patch("ahriman.core.alpm.remote.official.Official.make_request",
|
||||
return_value=[aur_package_akonadi])
|
||||
assert official.package_info(aur_package_akonadi.name) == aur_package_akonadi
|
||||
assert official.package_info(aur_package_akonadi.name, pacman=pacman) == aur_package_akonadi
|
||||
request_mock.assert_called_once_with(aur_package_akonadi.name, by="name")
|
||||
|
||||
|
||||
def test_package_search(official: Official, aur_package_akonadi: AURPackage, mocker: MockerFixture) -> None:
|
||||
def test_package_search(official: Official, aur_package_akonadi: AURPackage, pacman: Pacman,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must make request for search
|
||||
"""
|
||||
request_mock = mocker.patch("ahriman.core.alpm.remote.official.Official.make_request",
|
||||
return_value=[aur_package_akonadi])
|
||||
assert official.package_search(aur_package_akonadi.name) == [aur_package_akonadi]
|
||||
assert official.package_search(aur_package_akonadi.name, pacman=pacman) == [aur_package_akonadi]
|
||||
request_mock.assert_called_once_with(aur_package_akonadi.name, by="q")
|
||||
|
18
tests/ahriman/core/alpm/remote/test_official_syncdb.py
Normal file
18
tests/ahriman/core/alpm/remote/test_official_syncdb.py
Normal file
@ -0,0 +1,18 @@
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.official_syncdb import OfficialSyncdb
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
|
||||
|
||||
def test_package_info(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage,
|
||||
pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return package info from the database
|
||||
"""
|
||||
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])
|
||||
|
||||
package = official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)
|
||||
get_mock.assert_called_once_with(aur_package_akonadi.name)
|
||||
assert package == aur_package_akonadi
|
@ -3,70 +3,87 @@ import pytest
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.remote import Remote
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
|
||||
|
||||
def test_info(mocker: MockerFixture) -> None:
|
||||
def test_info(pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call info method
|
||||
"""
|
||||
info_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.package_info")
|
||||
Remote.info("ahriman")
|
||||
info_mock.assert_called_once_with("ahriman")
|
||||
Remote.info("ahriman", pacman=pacman)
|
||||
info_mock.assert_called_once_with("ahriman", pacman=pacman)
|
||||
|
||||
|
||||
def test_multisearch(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
||||
def test_multisearch(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must search in AUR with multiple words
|
||||
"""
|
||||
terms = ["ahriman", "is", "cool"]
|
||||
search_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.search", return_value=[aur_package_ahriman])
|
||||
|
||||
assert Remote.multisearch(*terms) == [aur_package_ahriman]
|
||||
search_mock.assert_has_calls([mock.call("ahriman"), mock.call("cool")])
|
||||
assert Remote.multisearch(*terms, pacman=pacman) == [aur_package_ahriman]
|
||||
search_mock.assert_has_calls([mock.call("ahriman", pacman=pacman), mock.call("cool", pacman=pacman)])
|
||||
|
||||
|
||||
def test_multisearch_empty(mocker: MockerFixture) -> None:
|
||||
def test_multisearch_empty(pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return empty list if no long terms supplied
|
||||
"""
|
||||
terms = ["it", "is"]
|
||||
search_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.search")
|
||||
|
||||
assert Remote.multisearch(*terms) == []
|
||||
assert Remote.multisearch(*terms, pacman=pacman) == []
|
||||
search_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_multisearch_single(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
||||
def test_multisearch_single(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must search in AUR with one word
|
||||
"""
|
||||
search_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.search", return_value=[aur_package_ahriman])
|
||||
assert Remote.multisearch("ahriman") == [aur_package_ahriman]
|
||||
search_mock.assert_called_once_with("ahriman")
|
||||
assert Remote.multisearch("ahriman", pacman=pacman) == [aur_package_ahriman]
|
||||
search_mock.assert_called_once_with("ahriman", pacman=pacman)
|
||||
|
||||
|
||||
def test_search(mocker: MockerFixture) -> None:
|
||||
def test_remote_git_url(remote: Remote, pacman: Pacman) -> None:
|
||||
"""
|
||||
must raise NotImplemented for missing remote git url
|
||||
"""
|
||||
with pytest.raises(NotImplementedError):
|
||||
remote.remote_git_url("package", "repositorys")
|
||||
|
||||
|
||||
def test_remote_web_url(remote: Remote, pacman: Pacman) -> None:
|
||||
"""
|
||||
must raise NotImplemented for missing remote web url
|
||||
"""
|
||||
with pytest.raises(NotImplementedError):
|
||||
remote.remote_web_url("package")
|
||||
|
||||
|
||||
def test_search(pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call search method
|
||||
"""
|
||||
search_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.package_search")
|
||||
Remote.search("ahriman")
|
||||
search_mock.assert_called_once_with("ahriman")
|
||||
Remote.search("ahriman", pacman=pacman)
|
||||
search_mock.assert_called_once_with("ahriman", pacman=pacman)
|
||||
|
||||
|
||||
def test_package_info(remote: Remote) -> None:
|
||||
def test_package_info(remote: Remote, pacman: Pacman) -> None:
|
||||
"""
|
||||
must raise NotImplemented for missing package info method
|
||||
"""
|
||||
with pytest.raises(NotImplementedError):
|
||||
remote.package_info("package")
|
||||
remote.package_info("package", pacman=pacman)
|
||||
|
||||
|
||||
def test_package_search(remote: Remote) -> None:
|
||||
def test_package_search(remote: Remote, pacman: Pacman) -> None:
|
||||
"""
|
||||
must raise NotImplemented for missing package search method
|
||||
"""
|
||||
with pytest.raises(NotImplementedError):
|
||||
remote.package_search("package")
|
||||
remote.package_search("package", pacman=pacman)
|
||||
|
@ -15,3 +15,17 @@ def test_all_packages_with_provides(pacman: Pacman) -> None:
|
||||
package list must contain provides packages
|
||||
"""
|
||||
assert "sh" in pacman.all_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"))
|
||||
|
@ -5,6 +5,7 @@ from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.core.build_tools.sources import Sources
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
|
||||
|
||||
def test_add(mocker: MockerFixture) -> None:
|
||||
@ -45,7 +46,7 @@ def test_diff(mocker: MockerFixture) -> None:
|
||||
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
|
||||
|
||||
|
||||
def test_fetch_empty(mocker: MockerFixture) -> None:
|
||||
def test_fetch_empty(remote_source: RemoteSource, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must do nothing in case if no branches available
|
||||
"""
|
||||
@ -53,46 +54,51 @@ def test_fetch_empty(mocker: MockerFixture) -> None:
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=False)
|
||||
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||
|
||||
Sources.fetch(Path("local"), "remote")
|
||||
Sources.fetch(Path("local"), remote_source)
|
||||
check_output_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_fetch_existing(mocker: MockerFixture) -> None:
|
||||
def test_fetch_existing(remote_source: RemoteSource, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must fetch new package via fetch command
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=True)
|
||||
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
|
||||
|
||||
local = Path("local")
|
||||
Sources.fetch(local, "remote")
|
||||
Sources.fetch(local, remote_source)
|
||||
check_output_mock.assert_has_calls([
|
||||
mock.call("git", "fetch", "origin", Sources._branch,
|
||||
mock.call("git", "fetch", "origin", remote_source.branch,
|
||||
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "checkout", "--force", Sources._branch,
|
||||
mock.call("git", "checkout", "--force", remote_source.branch,
|
||||
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "reset", "--hard", f"origin/{Sources._branch}",
|
||||
mock.call("git", "reset", "--hard", f"origin/{remote_source.branch}",
|
||||
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
|
||||
])
|
||||
move_mock.assert_called_once_with(local.resolve(), local)
|
||||
|
||||
|
||||
def test_fetch_new(mocker: MockerFixture) -> None:
|
||||
def test_fetch_new(remote_source: RemoteSource, 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")
|
||||
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
|
||||
|
||||
local = Path("local")
|
||||
Sources.fetch(local, "remote")
|
||||
Sources.fetch(local, remote_source)
|
||||
check_output_mock.assert_has_calls([
|
||||
mock.call("git", "clone", "remote", str(local), exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "checkout", "--force", Sources._branch,
|
||||
mock.call("git", "clone", "--branch", remote_source.branch, "--single-branch",
|
||||
remote_source.git_url, str(local), exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "checkout", "--force", remote_source.branch,
|
||||
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "reset", "--hard", f"origin/{Sources._branch}",
|
||||
mock.call("git", "reset", "--hard", f"origin/{remote_source.branch}",
|
||||
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
|
||||
])
|
||||
move_mock.assert_called_once_with(local.resolve(), local)
|
||||
|
||||
|
||||
def test_fetch_new_without_remote(mocker: MockerFixture) -> None:
|
||||
@ -101,15 +107,28 @@ def test_fetch_new_without_remote(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=False)
|
||||
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
|
||||
|
||||
local = Path("local")
|
||||
Sources.fetch(local, None)
|
||||
check_output_mock.assert_has_calls([
|
||||
mock.call("git", "checkout", "--force", Sources._branch,
|
||||
mock.call("git", "checkout", "--force", Sources.DEFAULT_BRANCH,
|
||||
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "reset", "--hard", f"origin/{Sources._branch}",
|
||||
mock.call("git", "reset", "--hard", f"origin/{Sources.DEFAULT_BRANCH}",
|
||||
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
|
||||
])
|
||||
move_mock.assert_called_once_with(local.resolve(), local)
|
||||
|
||||
|
||||
def test_fetch_relative(remote_source: RemoteSource, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process move correctly on relative directory
|
||||
"""
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
|
||||
|
||||
Sources.fetch(Path("path"), remote_source)
|
||||
move_mock.assert_called_once_with(Path("path").resolve(), Path("path"))
|
||||
|
||||
|
||||
def test_has_remotes(mocker: MockerFixture) -> None:
|
||||
@ -140,33 +159,53 @@ def test_init(mocker: MockerFixture) -> None:
|
||||
|
||||
local = Path("local")
|
||||
Sources.init(local)
|
||||
check_output_mock.assert_called_once_with("git", "init", "--initial-branch", Sources._branch,
|
||||
check_output_mock.assert_called_once_with("git", "init", "--initial-branch", Sources.DEFAULT_BRANCH,
|
||||
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
|
||||
|
||||
|
||||
def test_load(mocker: MockerFixture) -> None:
|
||||
def test_load(remote_source: RemoteSource, 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_apply")
|
||||
|
||||
Sources.load(Path("local"), "remote", "patch")
|
||||
fetch_mock.assert_called_once_with(Path("local"), "remote")
|
||||
Sources.load(Path("local"), remote_source, "patch")
|
||||
fetch_mock.assert_called_once_with(Path("local"), remote_source)
|
||||
patch_mock.assert_called_once_with(Path("local"), "patch")
|
||||
|
||||
|
||||
def test_load_no_patch(mocker: MockerFixture) -> None:
|
||||
def test_load_no_patch(remote_source: RemoteSource, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must load packages sources correctly without patches
|
||||
"""
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch_apply")
|
||||
|
||||
Sources.load(Path("local"), "remote", None)
|
||||
Sources.load(Path("local"), remote_source, None)
|
||||
patch_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_move(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must move content between directories
|
||||
"""
|
||||
mocker.patch("ahriman.core.build_tools.sources.walk", return_value=[Path("/source/path")])
|
||||
move_mock = mocker.patch("shutil.move")
|
||||
|
||||
Sources.move(Path("/source"), Path("/destination"))
|
||||
move_mock.assert_called_once_with(Path("/source/path"), Path("/destination/path"))
|
||||
|
||||
|
||||
def test_move_same(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not do anything in case if directories are the same
|
||||
"""
|
||||
walk_mock = mocker.patch("ahriman.core.build_tools.sources.walk")
|
||||
Sources.move(Path("/same"), Path("/same"))
|
||||
walk_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_patch_apply(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must apply patches if any
|
||||
|
@ -1,6 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.repo import Repo
|
||||
from ahriman.core.build_tools.task import Task
|
||||
from ahriman.core.configuration import Configuration
|
||||
@ -37,20 +36,6 @@ def leaf_python_schedule(package_python_schedule: Package) -> Leaf:
|
||||
return Leaf(package_python_schedule, set())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pacman(configuration: Configuration) -> Pacman:
|
||||
"""
|
||||
fixture for pacman wrapper
|
||||
|
||||
Args:
|
||||
configuration(Configuration): configuration fixture
|
||||
|
||||
Returns:
|
||||
Pacman: pacman wrapper test instance
|
||||
"""
|
||||
return Pacman(configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def repo(configuration: Configuration, repository_paths: RepositoryPaths) -> Repo:
|
||||
"""
|
||||
|
@ -15,11 +15,24 @@ def test_migrate_data_initial(connection: Connection, configuration: Configurati
|
||||
packages = mocker.patch("ahriman.core.database.data.migrate_package_statuses")
|
||||
patches = mocker.patch("ahriman.core.database.data.migrate_patches")
|
||||
users = mocker.patch("ahriman.core.database.data.migrate_users_data")
|
||||
remotes = mocker.patch("ahriman.core.database.data.migrate_package_remotes")
|
||||
|
||||
migrate_data(MigrationResult(old_version=0, new_version=900), connection, configuration)
|
||||
packages.assert_called_once_with(connection, repository_paths)
|
||||
patches.assert_called_once_with(connection, repository_paths)
|
||||
users.assert_called_once_with(connection, configuration)
|
||||
remotes.assert_called_once_with(connection, repository_paths)
|
||||
|
||||
|
||||
def test_migrate_data_remotes(connection: Connection, configuration: Configuration,
|
||||
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must perform initial migration
|
||||
"""
|
||||
remotes = mocker.patch("ahriman.core.database.data.migrate_package_remotes")
|
||||
|
||||
migrate_data(MigrationResult(old_version=1, new_version=900), connection, configuration)
|
||||
remotes.assert_called_once_with(connection, repository_paths)
|
||||
|
||||
|
||||
def test_migrate_data_skip(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
|
66
tests/ahriman/core/database/data/test_package_remotes.py
Normal file
66
tests/ahriman/core/database/data/test_package_remotes.py
Normal file
@ -0,0 +1,66 @@
|
||||
import pytest
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
from sqlite3 import Connection
|
||||
|
||||
from ahriman.core.database.data import migrate_package_remotes
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
def test_migrate_package_remotes(package_ahriman: Package, connection: Connection, repository_paths: RepositoryPaths,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must put package remotes to database
|
||||
"""
|
||||
mocker.patch(
|
||||
"ahriman.core.database.operations.package_operations.PackageOperations._packages_get_select_package_bases",
|
||||
return_value={package_ahriman.base: package_ahriman})
|
||||
mocker.patch("pathlib.Path.exists", return_value=False)
|
||||
|
||||
migrate_package_remotes(connection, repository_paths)
|
||||
connection.execute.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
|
||||
|
||||
|
||||
def test_migrate_package_remotes_has_local(package_ahriman: Package, connection: Connection,
|
||||
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip processing for packages which have local cache
|
||||
"""
|
||||
mocker.patch(
|
||||
"ahriman.core.database.operations.package_operations.PackageOperations._packages_get_select_package_bases",
|
||||
return_value={package_ahriman.base: package_ahriman})
|
||||
mocker.patch("pathlib.Path.exists", return_value=True)
|
||||
|
||||
migrate_package_remotes(connection, repository_paths)
|
||||
connection.execute.assert_not_called()
|
||||
|
||||
|
||||
def test_migrate_package_remotes_vcs(package_ahriman: Package, connection: Connection,
|
||||
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process VCS packages with local cache
|
||||
"""
|
||||
mocker.patch(
|
||||
"ahriman.core.database.operations.package_operations.PackageOperations._packages_get_select_package_bases",
|
||||
return_value={package_ahriman.base: package_ahriman})
|
||||
mocker.patch("pathlib.Path.exists", return_value=True)
|
||||
mocker.patch.object(Package, "is_vcs", True)
|
||||
|
||||
migrate_package_remotes(connection, repository_paths)
|
||||
connection.execute.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
|
||||
|
||||
|
||||
def test_migrate_package_remotes_no_remotes(package_ahriman: Package, connection: Connection,
|
||||
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip processing in case if no remotes generated (should never happen)
|
||||
"""
|
||||
mocker.patch(
|
||||
"ahriman.core.database.operations.package_operations.PackageOperations._packages_get_select_package_bases",
|
||||
return_value={package_ahriman.base: package_ahriman})
|
||||
mocker.patch("pathlib.Path.exists", return_value=False)
|
||||
mocker.patch("ahriman.models.remote_source.RemoteSource.from_remote", return_value=None)
|
||||
|
||||
migrate_package_remotes(connection, repository_paths)
|
||||
connection.execute.assert_not_called()
|
@ -9,7 +9,7 @@ from ahriman.core.database.data import migrate_users_data
|
||||
|
||||
def test_migrate_users_data(connection: Connection, configuration: Configuration) -> None:
|
||||
"""
|
||||
must users to database
|
||||
must put users to database
|
||||
"""
|
||||
configuration.set_option("auth:read", "user1", "password1")
|
||||
configuration.set_option("auth:write", "user2", "password2")
|
||||
|
@ -0,0 +1,8 @@
|
||||
from ahriman.core.database.migrations.m001_package_source import steps
|
||||
|
||||
|
||||
def test_migration_package_source() -> None:
|
||||
"""
|
||||
migration must not be empty
|
||||
"""
|
||||
assert steps
|
@ -7,6 +7,8 @@ from unittest import mock
|
||||
from ahriman.core.database.sqlite import SQLite
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
|
||||
|
||||
def test_package_remove_package_base(database: SQLite, connection: Connection) -> None:
|
||||
@ -166,3 +168,23 @@ def test_package_update_update(database: SQLite, package_ahriman: Package) -> No
|
||||
assert next(db_status.status
|
||||
for db_package, db_status in database.packages_get()
|
||||
if db_package.base == package_ahriman.base) == BuildStatusEnum.Failed
|
||||
|
||||
|
||||
def test_remote_update_get(database: SQLite, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must insert and retrieve package remote
|
||||
"""
|
||||
database.remote_update(package_ahriman)
|
||||
assert database.remotes_get()[package_ahriman.base] == package_ahriman.remote
|
||||
|
||||
|
||||
def test_remote_update_update(database: SQLite, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must perform package remote update for existing package
|
||||
"""
|
||||
database.remote_update(package_ahriman)
|
||||
remote_source = RemoteSource.from_remote(PackageSource.Repository, package_ahriman.base, "community")
|
||||
package_ahriman.remote = remote_source
|
||||
|
||||
database.remote_update(package_ahriman)
|
||||
assert database.remotes_get()[package_ahriman.base] == remote_source
|
||||
|
@ -15,11 +15,11 @@ def test_load_archives(package_ahriman: Package, package_python_schedule: Packag
|
||||
single_packages = [
|
||||
Package(base=package_python_schedule.base,
|
||||
version=package_python_schedule.version,
|
||||
aur_url=package_python_schedule.aur_url,
|
||||
remote=package_python_schedule.remote,
|
||||
packages={package: props})
|
||||
for package, props in package_python_schedule.packages.items()
|
||||
] + [package_ahriman]
|
||||
mocker.patch("ahriman.models.package.Package.load", side_effect=single_packages)
|
||||
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=single_packages)
|
||||
|
||||
packages = repository.load_archives([Path("a.pkg.tar.xz"), Path("b.pkg.tar.xz"), Path("c.pkg.tar.xz")])
|
||||
assert len(packages) == 2
|
||||
@ -36,7 +36,7 @@ def test_load_archives_failed(repository: Repository, mocker: MockerFixture) ->
|
||||
"""
|
||||
must skip packages which cannot be loaded
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package.load", side_effect=Exception())
|
||||
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=Exception())
|
||||
assert not repository.load_archives([Path("a.pkg.tar.xz")])
|
||||
|
||||
|
||||
@ -55,12 +55,12 @@ def test_load_archives_different_version(repository: Repository, package_python_
|
||||
single_packages = [
|
||||
Package(base=package_python_schedule.base,
|
||||
version=package_python_schedule.version,
|
||||
aur_url=package_python_schedule.aur_url,
|
||||
remote=package_python_schedule.remote,
|
||||
packages={package: props})
|
||||
for package, props in package_python_schedule.packages.items()
|
||||
]
|
||||
single_packages[0].version = "0.0.1-1"
|
||||
mocker.patch("ahriman.models.package.Package.load", side_effect=single_packages)
|
||||
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=single_packages)
|
||||
|
||||
packages = repository.load_archives([Path("a.pkg.tar.xz"), Path("b.pkg.tar.xz")])
|
||||
assert len(packages) == 1
|
||||
|
@ -5,6 +5,7 @@ from pytest_mock import MockerFixture
|
||||
from ahriman.core.repository.update_handler import UpdateHandler
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
|
||||
|
||||
def test_packages(update_handler: UpdateHandler) -> None:
|
||||
@ -22,7 +23,22 @@ def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
|
||||
|
||||
assert update_handler.updates_aur([], False) == [package_ahriman]
|
||||
status_client_mock.assert_called_once_with(package_ahriman.base)
|
||||
|
||||
|
||||
def test_updates_aur_official(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must provide updates based on repository data
|
||||
"""
|
||||
package_ahriman.remote = RemoteSource.from_remote(PackageSource.Repository, package_ahriman.base, "community")
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.models.package.Package.from_official", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
|
||||
|
||||
assert update_handler.updates_aur([], False) == [package_ahriman]
|
||||
@ -36,7 +52,7 @@ def test_updates_aur_success(update_handler: UpdateHandler, package_ahriman: Pac
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=False)
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_success")
|
||||
|
||||
assert not update_handler.updates_aur([], False)
|
||||
@ -49,7 +65,7 @@ def test_updates_aur_failed(update_handler: UpdateHandler, package_ahriman: Pack
|
||||
must update status via client for failed load
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.load", side_effect=Exception())
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=Exception())
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_failed")
|
||||
|
||||
update_handler.updates_aur([], False)
|
||||
@ -64,11 +80,10 @@ def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Pack
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
|
||||
return_value=[package_ahriman, package_python_schedule])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
|
||||
|
||||
assert update_handler.updates_aur([package_ahriman.base], False) == [package_ahriman]
|
||||
package_load_mock.assert_called_once_with(package_ahriman.base, PackageSource.AUR,
|
||||
update_handler.pacman, update_handler.aur_url)
|
||||
package_load_mock.assert_called_once_with(package_ahriman.base, update_handler.pacman)
|
||||
|
||||
|
||||
def test_updates_aur_ignore(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
@ -78,7 +93,7 @@ def test_updates_aur_ignore(update_handler: UpdateHandler, package_ahriman: Pack
|
||||
"""
|
||||
update_handler.ignore_list = [package_ahriman.base]
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.load")
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.from_aur")
|
||||
|
||||
update_handler.updates_aur([], False)
|
||||
package_load_mock.assert_not_called()
|
||||
@ -105,13 +120,12 @@ def test_updates_local(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
|
||||
|
||||
assert update_handler.updates_local() == [package_ahriman]
|
||||
fetch_mock.assert_called_once_with(package_ahriman.base, remote=None)
|
||||
package_load_mock.assert_called_once_with(
|
||||
package_ahriman.base, PackageSource.Local, update_handler.pacman, update_handler.aur_url)
|
||||
package_load_mock.assert_called_once_with(package_ahriman.base)
|
||||
status_client_mock.assert_called_once_with(package_ahriman.base)
|
||||
|
||||
|
||||
@ -123,7 +137,7 @@ def test_updates_local_unknown(update_handler: UpdateHandler, package_ahriman: P
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
|
||||
|
||||
assert update_handler.updates_local() == [package_ahriman]
|
||||
@ -151,7 +165,7 @@ def test_updates_local_success(update_handler: UpdateHandler, package_ahriman: P
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=False)
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_success")
|
||||
|
||||
assert not update_handler.updates_local()
|
||||
|
@ -51,7 +51,7 @@ def test_leaf_load(package_ahriman: Package, database: SQLite, mocker: MockerFix
|
||||
assert leaf.dependencies == {"ahriman-dependency"}
|
||||
tempdir_mock.assert_called_once_with()
|
||||
load_mock.assert_called_once_with(
|
||||
pytest.helpers.anyvar(int), package_ahriman.git_url, database.patches_get(package_ahriman.base))
|
||||
pytest.helpers.anyvar(int), package_ahriman.remote, database.patches_get(package_ahriman.base))
|
||||
dependencies_mock.assert_called_once_with(pytest.helpers.anyvar(int))
|
||||
rmtree_mock.assert_called_once_with(pytest.helpers.anyvar(int), ignore_errors=True)
|
||||
|
||||
|
@ -1,14 +1,18 @@
|
||||
import datetime
|
||||
import pytest
|
||||
import time
|
||||
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
from ahriman import version
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.counters import Counters
|
||||
from ahriman.models.internal_status import InternalStatus
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_description import PackageDescription
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
from ahriman.models.user_identity import UserIdentity
|
||||
|
||||
|
||||
@ -67,7 +71,7 @@ def package_tpacpi_bat_git() -> Package:
|
||||
return Package(
|
||||
base="tpacpi-bat-git",
|
||||
version="3.1.r12.g4959b52-1",
|
||||
aur_url="https://aur.archlinux.org",
|
||||
remote=RemoteSource.from_remote(PackageSource.AUR, "tpacpi-bat-git", "aur"),
|
||||
packages={"tpacpi-bat-git": PackageDescription()})
|
||||
|
||||
|
||||
@ -88,22 +92,34 @@ def pyalpm_handle(pyalpm_package_ahriman: MagicMock) -> MagicMock:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pyalpm_package_ahriman(package_ahriman: Package) -> MagicMock:
|
||||
def pyalpm_package_ahriman(aur_package_ahriman: AURPackage) -> MagicMock:
|
||||
"""
|
||||
mock object for pyalpm package
|
||||
|
||||
Args:
|
||||
package_ahriman(Package): package fixture
|
||||
aur_package_ahriman(AURPackage): package fixture
|
||||
|
||||
Returns:
|
||||
MagicMock: pyalpm package mock
|
||||
"""
|
||||
mock = MagicMock()
|
||||
type(mock).base = PropertyMock(return_value=package_ahriman.base)
|
||||
type(mock).depends = PropertyMock(return_value=["python-aur"])
|
||||
type(mock).name = PropertyMock(return_value=package_ahriman.base)
|
||||
type(mock).provides = PropertyMock(return_value=["python-ahriman"])
|
||||
type(mock).version = PropertyMock(return_value=package_ahriman.version)
|
||||
db = type(mock).db = MagicMock()
|
||||
|
||||
type(mock).base = PropertyMock(return_value=aur_package_ahriman.package_base)
|
||||
type(mock).builddate = PropertyMock(
|
||||
return_value=aur_package_ahriman.last_modified.replace(tzinfo=datetime.timezone.utc).timestamp())
|
||||
type(mock).conflicts = PropertyMock(return_value=aur_package_ahriman.conflicts)
|
||||
type(db).name = PropertyMock(return_value="aur")
|
||||
type(mock).depends = PropertyMock(return_value=aur_package_ahriman.depends)
|
||||
type(mock).desc = PropertyMock(return_value=aur_package_ahriman.description)
|
||||
type(mock).licenses = PropertyMock(return_value=aur_package_ahriman.license)
|
||||
type(mock).makedepends = PropertyMock(return_value=aur_package_ahriman.make_depends)
|
||||
type(mock).name = PropertyMock(return_value=aur_package_ahriman.name)
|
||||
type(mock).optdepends = PropertyMock(return_value=aur_package_ahriman.opt_depends)
|
||||
type(mock).provides = PropertyMock(return_value=aur_package_ahriman.provides)
|
||||
type(mock).version = PropertyMock(return_value=aur_package_ahriman.version)
|
||||
type(mock).url = PropertyMock(return_value=aur_package_ahriman.url)
|
||||
|
||||
return mock
|
||||
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import datetime
|
||||
import json
|
||||
import pyalpm # typing: ignore
|
||||
|
||||
from dataclasses import asdict, fields
|
||||
from pathlib import Path
|
||||
@ -53,6 +54,22 @@ def test_from_json_2(aur_package_ahriman: AURPackage, mocker: MockerFixture) ->
|
||||
assert AURPackage.from_json(asdict(aur_package_ahriman)) == aur_package_ahriman
|
||||
|
||||
|
||||
def test_from_pacman(pyalpm_package_ahriman: pyalpm.Package, aur_package_ahriman: AURPackage,
|
||||
resource_path_root: Path) -> None:
|
||||
"""
|
||||
must load package from repository database
|
||||
"""
|
||||
model = AURPackage.from_pacman(pyalpm_package_ahriman)
|
||||
# some fields are missing so we are changing them
|
||||
model.id = aur_package_ahriman.id
|
||||
model.package_base_id = aur_package_ahriman.package_base_id
|
||||
model.first_submitted = aur_package_ahriman.first_submitted
|
||||
model.url_path = aur_package_ahriman.url_path
|
||||
model.maintainer = aur_package_ahriman.maintainer
|
||||
|
||||
assert model == aur_package_ahriman
|
||||
|
||||
|
||||
def test_from_repo(aur_package_akonadi: AURPackage, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must load package from repository api json
|
||||
|
@ -4,10 +4,10 @@ from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.exceptions import InvalidPackageInfo
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
@ -21,15 +21,6 @@ def test_depends(package_python_schedule: Package) -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_git_url(package_ahriman: Package) -> None:
|
||||
"""
|
||||
must generate valid git url
|
||||
"""
|
||||
assert package_ahriman.git_url.endswith(".git")
|
||||
assert package_ahriman.git_url.startswith(package_ahriman.aur_url)
|
||||
assert package_ahriman.base in package_ahriman.git_url
|
||||
|
||||
|
||||
def test_groups(package_ahriman: Package) -> None:
|
||||
"""
|
||||
must return list of groups for each package
|
||||
@ -80,30 +71,23 @@ def test_licenses(package_ahriman: Package) -> None:
|
||||
assert sorted(package_ahriman.licenses) == package_ahriman.licenses
|
||||
|
||||
|
||||
def test_web_url(package_ahriman: Package) -> None:
|
||||
"""
|
||||
must generate valid web url
|
||||
"""
|
||||
assert package_ahriman.web_url.startswith(package_ahriman.aur_url)
|
||||
assert package_ahriman.base in package_ahriman.web_url
|
||||
|
||||
|
||||
def test_from_archive(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must construct package from alpm library
|
||||
"""
|
||||
mocker.patch("ahriman.models.package_description.PackageDescription.from_package",
|
||||
return_value=package_ahriman.packages[package_ahriman.base])
|
||||
assert Package.from_archive(Path("path"), pyalpm_handle, package_ahriman.aur_url) == package_ahriman
|
||||
assert Package.from_archive(Path("path"), pyalpm_handle, package_ahriman.remote) == package_ahriman
|
||||
|
||||
|
||||
def test_from_aur(package_ahriman: Package, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
||||
def test_from_aur(package_ahriman: Package, aur_package_ahriman: AURPackage, pacman: Pacman,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must construct package from aur
|
||||
"""
|
||||
mocker.patch("ahriman.core.alpm.remote.aur.AUR.info", return_value=aur_package_ahriman)
|
||||
|
||||
package = Package.from_aur(package_ahriman.base, package_ahriman.aur_url)
|
||||
package = Package.from_aur(package_ahriman.base, pacman)
|
||||
assert package_ahriman.base == package.base
|
||||
assert package_ahriman.version == package.version
|
||||
assert package_ahriman.packages.keys() == package.packages.keys()
|
||||
@ -114,11 +98,12 @@ def test_from_build(package_ahriman: Package, mocker: MockerFixture, resource_pa
|
||||
must construct package from srcinfo
|
||||
"""
|
||||
srcinfo = (resource_path_root / "models" / "package_ahriman_srcinfo").read_text()
|
||||
mocker.patch("pathlib.Path.read_text", return_value=srcinfo)
|
||||
mocker.patch("ahriman.models.package.Package._check_output", return_value=srcinfo)
|
||||
|
||||
package = Package.from_build(Path("path"), package_ahriman.aur_url)
|
||||
package = Package.from_build(Path("path"))
|
||||
assert package_ahriman.packages.keys() == package.packages.keys()
|
||||
package_ahriman.packages = package.packages # we are not going to test PackageDescription here
|
||||
package_ahriman.remote = None
|
||||
assert package_ahriman == package
|
||||
|
||||
|
||||
@ -126,11 +111,11 @@ def test_from_build_failed(package_ahriman: Package, mocker: MockerFixture) -> N
|
||||
"""
|
||||
must raise exception if there are errors during srcinfo load
|
||||
"""
|
||||
mocker.patch("pathlib.Path.read_text", return_value="")
|
||||
mocker.patch("ahriman.models.package.Package._check_output", return_value="")
|
||||
mocker.patch("ahriman.models.package.parse_srcinfo", return_value=({"packages": {}}, ["an error"]))
|
||||
|
||||
with pytest.raises(InvalidPackageInfo):
|
||||
Package.from_build(Path("path"), package_ahriman.aur_url)
|
||||
Package.from_build(Path("path"))
|
||||
|
||||
|
||||
def test_from_json_view_1(package_ahriman: Package) -> None:
|
||||
@ -154,97 +139,24 @@ def test_from_json_view_3(package_tpacpi_bat_git: Package) -> None:
|
||||
assert Package.from_json(package_tpacpi_bat_git.view()) == package_tpacpi_bat_git
|
||||
|
||||
|
||||
def test_from_official(package_ahriman: Package, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
||||
def test_from_official(package_ahriman: Package, aur_package_ahriman: AURPackage, pacman: Pacman,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must construct package from official repository
|
||||
"""
|
||||
mocker.patch("ahriman.core.alpm.remote.official.Official.info", return_value=aur_package_ahriman)
|
||||
|
||||
package = Package.from_official(package_ahriman.base, package_ahriman.aur_url)
|
||||
package = Package.from_official(package_ahriman.base, pacman)
|
||||
assert package_ahriman.base == package.base
|
||||
assert package_ahriman.version == package.version
|
||||
assert package_ahriman.packages.keys() == package.packages.keys()
|
||||
|
||||
|
||||
def test_load_resolve(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must resolve source before package loading
|
||||
"""
|
||||
resolve_mock = mocker.patch("ahriman.models.package_source.PackageSource.resolve",
|
||||
return_value=PackageSource.Archive)
|
||||
mocker.patch("ahriman.models.package.Package.from_archive")
|
||||
|
||||
Package.load("path", PackageSource.Archive, pyalpm_handle, package_ahriman.aur_url)
|
||||
resolve_mock.assert_called_once_with("path")
|
||||
|
||||
|
||||
def test_load_from_archive(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must load package from package archive
|
||||
"""
|
||||
load_mock = mocker.patch("ahriman.models.package.Package.from_archive")
|
||||
Package.load("path", PackageSource.Archive, pyalpm_handle, package_ahriman.aur_url)
|
||||
load_mock.assert_called_once_with(Path("path"), pyalpm_handle, package_ahriman.aur_url)
|
||||
|
||||
|
||||
def test_load_from_aur(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must load package from AUR
|
||||
"""
|
||||
load_mock = mocker.patch("ahriman.models.package.Package.from_aur")
|
||||
Package.load("path", PackageSource.AUR, pyalpm_handle, package_ahriman.aur_url)
|
||||
load_mock.assert_called_once_with("path", package_ahriman.aur_url)
|
||||
|
||||
|
||||
def test_load_from_build(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must load package from build directory
|
||||
"""
|
||||
load_mock = mocker.patch("ahriman.models.package.Package.from_build")
|
||||
Package.load("path", PackageSource.Local, pyalpm_handle, package_ahriman.aur_url)
|
||||
load_mock.assert_called_once_with(Path("path"), package_ahriman.aur_url)
|
||||
|
||||
|
||||
def test_load_from_official(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must load package from AUR
|
||||
"""
|
||||
load_mock = mocker.patch("ahriman.models.package.Package.from_official")
|
||||
Package.load("path", PackageSource.Repository, pyalpm_handle, package_ahriman.aur_url)
|
||||
load_mock.assert_called_once_with("path", package_ahriman.aur_url)
|
||||
|
||||
|
||||
def test_load_failure(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise InvalidPackageInfo on exception
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=InvalidPackageInfo("exception!"))
|
||||
with pytest.raises(InvalidPackageInfo):
|
||||
Package.load("path", PackageSource.AUR, pyalpm_handle, package_ahriman.aur_url)
|
||||
|
||||
|
||||
def test_load_failure_exception(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise InvalidPackageInfo on random eexception
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=Exception())
|
||||
with pytest.raises(InvalidPackageInfo):
|
||||
Package.load("path", PackageSource.AUR, pyalpm_handle, package_ahriman.aur_url)
|
||||
|
||||
|
||||
def test_load_invalid_source(package_ahriman: Package, pyalpm_handle: MagicMock) -> None:
|
||||
"""
|
||||
must raise InvalidPackageInfo on unsupported source
|
||||
"""
|
||||
with pytest.raises(InvalidPackageInfo):
|
||||
Package.load("path", PackageSource.Remote, pyalpm_handle, package_ahriman.aur_url)
|
||||
|
||||
|
||||
def test_dependencies_failed(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise exception if there are errors during srcinfo load
|
||||
"""
|
||||
mocker.patch("pathlib.Path.read_text", return_value="")
|
||||
mocker.patch("ahriman.models.package.Package._check_output", return_value="")
|
||||
mocker.patch("ahriman.models.package.parse_srcinfo", return_value=({"packages": {}}, ["an error"]))
|
||||
|
||||
with pytest.raises(InvalidPackageInfo):
|
||||
@ -256,7 +168,7 @@ def test_dependencies_with_version(mocker: MockerFixture, resource_path_root: Pa
|
||||
must load correct list of dependencies with version
|
||||
"""
|
||||
srcinfo = (resource_path_root / "models" / "package_yay_srcinfo").read_text()
|
||||
mocker.patch("pathlib.Path.read_text", return_value=srcinfo)
|
||||
mocker.patch("ahriman.models.package.Package._check_output", return_value=srcinfo)
|
||||
|
||||
assert Package.dependencies(Path("path")) == {"git", "go", "pacman"}
|
||||
|
||||
@ -266,7 +178,7 @@ def test_dependencies_with_version_and_overlap(mocker: MockerFixture, resource_p
|
||||
must load correct list of dependencies with version
|
||||
"""
|
||||
srcinfo = (resource_path_root / "models" / "package_gcc10_srcinfo").read_text()
|
||||
mocker.patch("pathlib.Path.read_text", return_value=srcinfo)
|
||||
mocker.patch("ahriman.models.package.Package._check_output", return_value=srcinfo)
|
||||
|
||||
assert Package.dependencies(Path("path")) == {"glibc", "doxygen", "binutils", "git", "libmpc", "python", "zstd"}
|
||||
|
||||
@ -328,7 +240,7 @@ def test_full_depends(package_ahriman: Package, package_python_schedule: Package
|
||||
assert package_ahriman.full_depends(pyalpm_handle, [package_python_schedule]) == package_ahriman.depends
|
||||
|
||||
package_python_schedule.packages[package_python_schedule.base].depends = [package_ahriman.base]
|
||||
expected = sorted(set(package_python_schedule.depends + ["python-aur"]))
|
||||
expected = sorted(set(package_python_schedule.depends + package_ahriman.depends))
|
||||
assert package_python_schedule.full_depends(pyalpm_handle, [package_python_schedule]) == expected
|
||||
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
from dataclasses import asdict
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.models.package_description import PackageDescription
|
||||
@ -24,14 +23,14 @@ def test_from_json(package_description_ahriman: PackageDescription) -> None:
|
||||
"""
|
||||
must construct description from json object
|
||||
"""
|
||||
assert PackageDescription.from_json(asdict(package_description_ahriman)) == package_description_ahriman
|
||||
assert PackageDescription.from_json(package_description_ahriman.view()) == package_description_ahriman
|
||||
|
||||
|
||||
def test_from_json_with_unknown_fields(package_description_ahriman: PackageDescription) -> None:
|
||||
"""
|
||||
must construct description from json object containing unknown fields
|
||||
"""
|
||||
dump = asdict(package_description_ahriman)
|
||||
dump = package_description_ahriman.view()
|
||||
dump.update(unknown_field="value")
|
||||
assert PackageDescription.from_json(dump) == package_description_ahriman
|
||||
|
||||
|
82
tests/ahriman/models/test_remote_source.py
Normal file
82
tests/ahriman/models/test_remote_source.py
Normal file
@ -0,0 +1,82 @@
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
|
||||
|
||||
def test_post_init(remote_source: RemoteSource) -> None:
|
||||
"""
|
||||
must convert string source to enum
|
||||
"""
|
||||
remote = RemoteSource(
|
||||
git_url=remote_source.git_url,
|
||||
web_url=remote_source.web_url,
|
||||
path=remote_source.path,
|
||||
branch=remote_source.branch,
|
||||
source=remote_source.source.value,
|
||||
)
|
||||
assert remote == remote_source
|
||||
|
||||
|
||||
def test_pkgbuild_dir(remote_source: RemoteSource) -> None:
|
||||
"""
|
||||
must return path as is in `path` property
|
||||
"""
|
||||
assert isinstance(remote_source.pkgbuild_dir, Path)
|
||||
assert remote_source.pkgbuild_dir.name == ""
|
||||
|
||||
|
||||
def test_from_json(remote_source: RemoteSource) -> None:
|
||||
"""
|
||||
must construct remote source from json
|
||||
"""
|
||||
assert RemoteSource.from_json(remote_source.view()) == remote_source
|
||||
|
||||
|
||||
def test_from_json_empty() -> None:
|
||||
"""
|
||||
must return None in case of empty dictionary, which is required by the database wrapper
|
||||
"""
|
||||
assert RemoteSource.from_json({}) is None
|
||||
|
||||
|
||||
def test_from_remote_aur(package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must construct remote from AUR source
|
||||
"""
|
||||
remote_git_url_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.remote_git_url")
|
||||
remote_web_url_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.remote_web_url")
|
||||
|
||||
remote = RemoteSource.from_remote(PackageSource.AUR, package_ahriman.base, "aur")
|
||||
remote_git_url_mock.assert_called_once_with(package_ahriman.base, "aur")
|
||||
remote_web_url_mock.assert_called_once_with(package_ahriman.base)
|
||||
assert remote.pkgbuild_dir == Path(".")
|
||||
assert remote.branch == "master"
|
||||
assert remote.source == PackageSource.AUR
|
||||
|
||||
|
||||
def test_from_remote_official(package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must construct remote from official repository source
|
||||
"""
|
||||
remote_git_url_mock = mocker.patch("ahriman.core.alpm.remote.official.Official.remote_git_url")
|
||||
remote_web_url_mock = mocker.patch("ahriman.core.alpm.remote.official.Official.remote_web_url")
|
||||
|
||||
remote = RemoteSource.from_remote(PackageSource.Repository, package_ahriman.base, "community")
|
||||
remote_git_url_mock.assert_called_once_with(package_ahriman.base, "community")
|
||||
remote_web_url_mock.assert_called_once_with(package_ahriman.base)
|
||||
assert remote.pkgbuild_dir == Path("trunk")
|
||||
assert remote.branch.endswith(package_ahriman.base)
|
||||
assert remote.source == PackageSource.Repository
|
||||
|
||||
|
||||
def test_from_remote_other() -> None:
|
||||
"""
|
||||
must return None in case if source is not one of AUR or Repository
|
||||
"""
|
||||
assert RemoteSource.from_remote(PackageSource.Archive, "package", "repository") is None
|
||||
assert RemoteSource.from_remote(PackageSource.Directory, "package", "repository") is None
|
||||
assert RemoteSource.from_remote(PackageSource.Local, "package", "repository") is None
|
||||
assert RemoteSource.from_remote(PackageSource.Remote, "package", "repository") is None
|
@ -37,7 +37,7 @@ async def test_get_exception(client: TestClient, mocker: MockerFixture) -> None:
|
||||
response = await client.get("/service-api/v1/search")
|
||||
|
||||
assert response.status == 404
|
||||
search_mock.assert_called_once_with()
|
||||
search_mock.assert_called_once_with(pacman=pytest.helpers.anyvar(int))
|
||||
|
||||
|
||||
async def test_get_join(client: TestClient, mocker: MockerFixture) -> None:
|
||||
@ -48,4 +48,4 @@ async def test_get_join(client: TestClient, mocker: MockerFixture) -> None:
|
||||
response = await client.get("/service-api/v1/search", params=[("for", "ahriman"), ("for", "maybe")])
|
||||
|
||||
assert response.ok
|
||||
search_mock.assert_called_once_with("ahriman", "maybe")
|
||||
search_mock.assert_called_once_with("ahriman", "maybe", pacman=pytest.helpers.anyvar(int))
|
||||
|
@ -4,7 +4,6 @@ logging = logging.ini
|
||||
database = ../../../ahriman-test.db
|
||||
|
||||
[alpm]
|
||||
aur_url = https://aur.archlinux.org
|
||||
database = /var/lib/pacman
|
||||
repositories = core extra community multilib
|
||||
root = /
|
||||
|
Loading…
Reference in New Issue
Block a user