Local packages support improvements (#104)

* handle git author correctly
* make remote source required argument
This commit is contained in:
2023-08-11 18:17:23 +03:00
committed by Evgeniy Alekseev
parent c863ee063c
commit 9259d9c727
43 changed files with 451 additions and 232 deletions

View File

@ -95,7 +95,7 @@ class ApplicationPackages(ApplicationProperties):
if (source_dir := Path(source)).is_dir():
package = Package.from_build(source_dir, self.architecture, username)
cache_dir = self.repository.paths.cache_for(package.base)
shutil.copytree(source_dir, cache_dir) # copy package to store in caches
shutil.copytree(source_dir, cache_dir, dirs_exist_ok=True) # copy package to store in caches
Sources.init(cache_dir) # we need to run init command in directory where we do have permissions
elif (source_dir := self.repository.paths.cache_for(source)).is_dir():
package = Package.from_build(source_dir, self.architecture, username)
@ -145,7 +145,7 @@ class ApplicationPackages(ApplicationProperties):
username(str | None, optional): optional override of username for build process (Default value = None)
"""
for name in names:
resolved_source = source.resolve(name)
resolved_source = source.resolve(name, self.repository.paths)
fn = getattr(self, f"_add_{resolved_source.value}")
fn(name, username)

View File

@ -36,9 +36,11 @@ class Sources(LazyLogging):
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
DEFAULT_COMMIT_AUTHOR(tuple[str, str]): (class attribute) default commit author to be used if none set
"""
DEFAULT_BRANCH = "master" # default fallback branch
DEFAULT_COMMIT_AUTHOR = ("ahriman", "ahriman@localhost")
_check_output = check_output
@ -61,13 +63,13 @@ class Sources(LazyLogging):
return [PkgbuildPatch("arch", list(architectures))]
@staticmethod
def fetch(sources_dir: Path, remote: RemoteSource | None) -> None:
def fetch(sources_dir: Path, remote: RemoteSource) -> None:
"""
either clone repository or update it to origin/``remote.branch``
Args:
sources_dir(Path): local path to fetch
remote(RemoteSource | None): remote target (from where to fetch)
remote(RemoteSource): remote target (from where to fetch)
"""
instance = Sources()
# local directory exists and there is .git directory
@ -77,11 +79,11 @@ class Sources(LazyLogging):
instance.logger.info("skip update at %s because there are no branches configured", sources_dir)
return
branch = remote.branch if remote is not None else instance.DEFAULT_BRANCH
branch = remote.branch or instance.DEFAULT_BRANCH
if is_initialized_git:
instance.logger.info("update HEAD to remote at %s using branch %s", sources_dir, branch)
Sources._check_output("git", "fetch", "origin", branch, cwd=sources_dir, logger=instance.logger)
elif remote is not None:
elif remote.git_url is not None:
instance.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), cwd=sources_dir.parent, logger=instance.logger)
@ -95,7 +97,7 @@ class Sources(LazyLogging):
# 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()
pkgbuild_dir = remote.pkgbuild_dir or sources_dir.resolve()
instance.move((sources_dir / pkgbuild_dir).resolve(), sources_dir)
@staticmethod
@ -122,14 +124,16 @@ class Sources(LazyLogging):
sources_dir(Path): local path to sources
"""
instance = Sources()
Sources._check_output("git", "init", "--initial-branch", instance.DEFAULT_BRANCH,
cwd=sources_dir, logger=instance.logger)
if not (sources_dir / ".git").is_dir():
# skip initializing in case if it was already
Sources._check_output("git", "init", "--initial-branch", instance.DEFAULT_BRANCH,
cwd=sources_dir, logger=instance.logger)
# extract local files...
files = ["PKGBUILD", ".SRCINFO"] + [str(path) for path in Package.local_files(sources_dir)]
instance.add(sources_dir, *files)
# ...and commit them
instance.commit(sources_dir, author="ahriman <ahriman@localhost>")
instance.commit(sources_dir)
@staticmethod
def load(sources_dir: Path, package: Package, patches: list[PkgbuildPatch], paths: RepositoryPaths) -> None:
@ -170,7 +174,8 @@ class Sources(LazyLogging):
return f"{diff}\n" # otherwise, patch will be broken
@staticmethod
def push(sources_dir: Path, remote: RemoteSource, *pattern: str, commit_author: str | None = None) -> None:
def push(sources_dir: Path, remote: RemoteSource, *pattern: str,
commit_author: tuple[str, str] | None = None) -> None:
"""
commit selected changes and push files to the remote repository
@ -178,13 +183,15 @@ class Sources(LazyLogging):
sources_dir(Path): local path to git repository
remote(RemoteSource): remote target, branch and url
*pattern(str): glob patterns
commit_author(str | None, optional): commit author in form of git config (i.e. ``user <user@host>``)
(Default value = None)
commit_author(tuple[str, str] | None, optional): commit author if any (Default value = None)
"""
instance = Sources()
instance.add(sources_dir, *pattern)
instance.commit(sources_dir, author=commit_author)
Sources._check_output("git", "push", remote.git_url, remote.branch, cwd=sources_dir, logger=instance.logger)
if not instance.commit(sources_dir, commit_author=commit_author):
return # no changes to push, just skip action
git_url, branch = remote.git_source()
Sources._check_output("git", "push", git_url, branch, cwd=sources_dir, logger=instance.logger)
def add(self, sources_dir: Path, *pattern: str, intent_to_add: bool = False) -> None:
"""
@ -208,7 +215,8 @@ class Sources(LazyLogging):
Sources._check_output("git", "add", *args, *[str(fn.relative_to(sources_dir)) for fn in found_files],
cwd=sources_dir, logger=self.logger)
def commit(self, sources_dir: Path, message: str | None = None, author: str | None = None) -> None:
def commit(self, sources_dir: Path, message: str | None = None,
commit_author: tuple[str, str] | None = None) -> bool:
"""
commit changes
@ -216,14 +224,28 @@ class Sources(LazyLogging):
sources_dir(Path): local path to git repository
message(str | None, optional): optional commit message if any. If none set, message will be generated
according to the current timestamp (Default value = None)
author(str | None, optional): optional commit author if any (Default value = None)
commit_author(tuple[str, str] | None, optional): optional commit author if any (Default value = None)
Returns:
bool: True in case if changes have been committed and False otherwise
"""
if not self.has_changes(sources_dir):
return False # nothing to commit
if message is None:
message = f"Autogenerated commit at {utcnow()}"
args = ["--allow-empty", "--message", message]
if author is not None:
args.extend(["--author", author])
Sources._check_output("git", "commit", *args, cwd=sources_dir, logger=self.logger)
args = ["--message", message]
environment: dict[str, str] = {}
if commit_author is None:
commit_author = self.DEFAULT_COMMIT_AUTHOR
user, email = commit_author
environment["GIT_AUTHOR_NAME"] = environment["GIT_COMMITTER_NAME"] = user
environment["GIT_AUTHOR_EMAIL"] = environment["GIT_COMMITTER_EMAIL"] = email
Sources._check_output("git", "commit", *args, cwd=sources_dir, logger=self.logger, environment=environment)
return True
def diff(self, sources_dir: Path) -> str:
"""
@ -237,6 +259,20 @@ class Sources(LazyLogging):
"""
return Sources._check_output("git", "diff", cwd=sources_dir, logger=self.logger)
def has_changes(self, sources_dir: Path) -> bool:
"""
check if there are changes in current git tree
Args:
sources_dir(Path): local path to git repository
Returns:
bool: True if there are uncommitted changes and False otherwise
"""
# there is --exit-code argument to diff, however, there might be other process errors
changes = Sources._check_output("git", "diff", "--cached", "--name-only", cwd=sources_dir, logger=self.logger)
return bool(changes)
def move(self, pkgbuild_dir: Path, sources_dir: Path) -> None:
"""
move content from pkgbuild_dir to sources_dir

View File

@ -48,7 +48,7 @@ class Configuration(configparser.RawConfigParser):
>>> from pathlib import Path
>>>
>>> configuration = Configuration.from_path(Path("/etc/ahriman.ini"), "x86_64", quiet=False)
>>> configuration = Configuration.from_path(Path("/etc/ahriman.ini"), "x86_64")
>>> repository_name = configuration.get("repository", "name")
>>> makepkg_flags = configuration.getlist("build", "makepkg_flags")

View File

@ -70,6 +70,7 @@ def migrate_package_remotes(connection: Connection, paths: RepositoryPaths) -> N
connection(Connection): database connection
paths(RepositoryPaths): repository paths instance
"""
from ahriman.core.alpm.remote import AUR
from ahriman.core.database.operations import PackageOperations
def insert_remote(base: str, remote: RemoteSource) -> None:
@ -92,7 +93,11 @@ def migrate_package_remotes(connection: Connection, paths: RepositoryPaths) -> N
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_source(PackageSource.AUR, package_base, "aur")
if remote_source is None:
continue # should never happen
remote_source = RemoteSource(
source=PackageSource.AUR,
git_url=AUR.remote_git_url(package_base, "aur"),
web_url=AUR.remote_web_url(package_base),
path=".",
branch="master",
)
insert_remote(package_base, remote_source)

View File

@ -66,7 +66,7 @@ def migrate_package_depends(connection: Connection, configuration: Configuration
package_list = []
for full_path in filter(package_like, configuration.repository_paths.repository.iterdir()):
base = Package.from_archive(full_path, pacman, remote=None)
base = Package.from_archive(full_path, pacman)
for package, description in base.packages.items():
package_list.append({
"make_depends": description.make_depends,

View File

@ -63,7 +63,7 @@ def migrate_package_check_depends(connection: Connection, configuration: Configu
package_list = []
for full_path in filter(package_like, configuration.repository_paths.repository.iterdir()):
base = Package.from_archive(full_path, pacman, remote=None)
base = Package.from_archive(full_path, pacman)
for package, description in base.packages.items():
package_list.append({
"check_depends": description.check_depends,

View File

@ -69,7 +69,7 @@ def migrate_package_base_packager(connection: Connection, configuration: Configu
package_list = []
for full_path in filter(package_like, configuration.repository_paths.repository.iterdir()):
package = Package.from_archive(full_path, pacman, remote=None)
package = Package.from_archive(full_path, pacman)
package_list.append({
"package_base": package.base,
"packager": package.packager,

View File

@ -0,0 +1,27 @@
#
# Copyright (c) 2021-2023 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/>.
#
__all__ = ["steps"]
steps = [
"""
update package_bases set source = 'local' where source is null
""",
]

View File

@ -86,11 +86,11 @@ class PackageOperations(Operations):
{
"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,
"branch": package.remote.branch,
"git_url": package.remote.git_url,
"path": package.remote.path,
"web_url": package.remote.web_url,
"source": package.remote.source.value,
"packager": package.packager,
}
)
@ -270,5 +270,4 @@ class PackageOperations(Operations):
return {
package_base: package.remote
for package_base, package in packages.items()
if package.remote is not None
}

View File

@ -39,7 +39,7 @@ class RemotePush(LazyLogging):
sync PKGBUILDs to remote repository after actions
Attributes:
commit_author(str | None): optional commit author in form of git config (i.e. ``user <user@host>``)
commit_author(tuple[str, str] | None): optional commit author in form of git config
database(SQLite): database instance
remote_source(RemoteSource): repository remote source (remote pull url and branch)
"""
@ -54,7 +54,11 @@ class RemotePush(LazyLogging):
section(str): settings section name
"""
self.database = database
self.commit_author = configuration.get(section, "commit_author", fallback=None)
commit_email = configuration.get(section, "commit_email", fallback="ahriman@localhost")
commit_user = configuration.get(section, "commit_user", fallback="ahriman")
self.commit_author = (commit_user, commit_email)
self.remote_source = RemoteSource(
git_url=configuration.get(section, "push_url"),
web_url="",

View File

@ -49,7 +49,10 @@ class RemotePushTrigger(Trigger):
"gitremote": {
"type": "dict",
"schema": {
"commit_author": {
"commit_email": {
"type": "string",
},
"commit_user": {
"type": "string",
},
"push_url": {

View File

@ -117,8 +117,9 @@ class Repository(Executor, UpdateHandler):
# we are iterating over bases, not single packages
for full_path in packages:
try:
local = Package.from_archive(full_path, self.pacman, None)
local.remote = sources.get(local.base)
local = Package.from_archive(full_path, self.pacman)
if (source := sources.get(local.base)) is not None:
local.remote = source
current = result.setdefault(local.base, local)
if current.version != local.version:

View File

@ -24,6 +24,7 @@ from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.repository.cleaner import Cleaner
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
class UpdateHandler(Cleaner):
@ -55,12 +56,10 @@ class UpdateHandler(Cleaner):
list[Package]: list of packages which are out-of-dated
"""
def load_remote(package: Package) -> Package:
source = package.remote.source if package.remote is not None else None
# try to load package from base and if none found try to load by separated packages
for probe in [package.base] + sorted(package.packages.keys()):
try:
if source == PackageSource.Repository:
if package.remote.source == PackageSource.Repository:
return Package.from_official(probe, self.pacman, None)
return Package.from_aur(probe, self.pacman, None)
except UnknownPackageError:
@ -71,6 +70,8 @@ class UpdateHandler(Cleaner):
for local in self.packages():
with self.in_package_context(local.base):
if not local.remote.is_remote:
continue # avoid checking local packages
if local.base in self.ignore_list:
continue
if filter_packages and local.base not in filter_packages:
@ -107,7 +108,15 @@ class UpdateHandler(Cleaner):
for cache_dir in self.paths.cache.iterdir():
with self.in_package_context(cache_dir.name):
try:
Sources.fetch(cache_dir, remote=None)
source = RemoteSource(
source=PackageSource.Local,
git_url=cache_dir.absolute().as_uri(),
web_url="",
path=".",
branch="master",
)
Sources.fetch(cache_dir, source)
remote = Package.from_build(cache_dir, self.architecture, None)
local = packages.get(remote.base)

View File

@ -27,7 +27,6 @@ from multiprocessing import Process, Queue
from threading import Lock, Thread
from ahriman.core.log import LazyLogging
from ahriman.models.package_source import PackageSource
class Spawn(Thread, LazyLogging):
@ -133,8 +132,7 @@ class Spawn(Thread, LazyLogging):
username(str | None): optional override of username for build process
now(bool): build packages now
"""
# avoid abusing by building non-aur packages
kwargs = {"source": PackageSource.AUR.value, "username": username}
kwargs = {"username": username}
if now:
kwargs["now"] = ""
self._spawn_process("package-add", *packages, **kwargs)

View File

@ -304,7 +304,7 @@ def parse_version(version: str) -> tuple[str | None, str, str]:
def partition(source: list[T], predicate: Callable[[T], bool]) -> tuple[list[T], list[T]]:
"""
partition list into two based on predicate, based on # https://docs.python.org/dev/library/itertools.html#itertools-recipes
partition list into two based on predicate, based on https://docs.python.org/dev/library/itertools.html#itertools-recipes
Args:
source(list[T]): source list to be partitioned

View File

@ -66,13 +66,12 @@ class AURPackage:
>>> package = AURPackage.from_repo(metadata) # load package from official repository RPC
>>> # properties of the class are built based on ones from AUR RPC, thus additional method is required
>>>
>>>
>>> from ahriman.core.alpm.pacman import Pacman
>>> from ahriman.core.configuration import Configuration
>>>
>>> configuration = Configuration()
>>> pacman = Pacman("x86_64", configuration)
>>> metadata = pacman.get("pacman")
>>> metadata = pacman.package_get("pacman")
>>> package = AURPackage.from_pacman(next(metadata)) # load package from pyalpm wrapper
"""

View File

@ -51,7 +51,7 @@ class Package(LazyLogging):
packager(str | None): package packager if available
packages(dict[str, PackageDescription): map of package names to their properties.
Filled only on load from archive
remote(RemoteSource | None): package remote source if applicable
remote(RemoteSource): package remote source if applicable
version(str): package full version
Examples:
@ -61,7 +61,7 @@ class Package(LazyLogging):
it will contain every data available in the json body. Otherwise, if generate package from local archive::
>>> package = Package.from_archive(local_path, pacman, remote=None)
>>> package = Package.from_archive(local_path, pacman)
it will probably miss file descriptions (in case if there are multiple packages which belong to the base).
@ -76,7 +76,7 @@ class Package(LazyLogging):
base: str
version: str
remote: RemoteSource | None
remote: RemoteSource
packages: dict[str, PackageDescription]
packager: str | None = None
@ -192,22 +192,26 @@ class Package(LazyLogging):
return sorted(packages)
@classmethod
def from_archive(cls, path: Path, pacman: Pacman, remote: RemoteSource | None) -> Self:
def from_archive(cls, path: Path, pacman: Pacman) -> Self:
"""
construct package properties from package archive
Args:
path(Path): path to package archive
pacman(Pacman): alpm wrapper instance
remote(RemoteSource): package remote source if applicable
Returns:
Self: package properties
"""
package = pacman.handle.load_pkg(str(path))
description = PackageDescription.from_package(package, path)
return cls(base=package.base, version=package.version, remote=remote, packages={package.name: description},
packager=package.packager)
return cls(
base=package.base,
version=package.version,
remote=RemoteSource(source=PackageSource.Archive),
packages={package.name: description},
packager=package.packager,
)
@classmethod
def from_aur(cls, name: str, pacman: Pacman, packager: str | None = None) -> Self:
@ -223,7 +227,15 @@ class Package(LazyLogging):
Self: package properties
"""
package = AUR.info(name, pacman=pacman)
remote = RemoteSource.from_source(PackageSource.AUR, package.package_base, package.repository)
remote = RemoteSource(
source=PackageSource.AUR,
git_url=AUR.remote_git_url(package.package_base, package.repository),
web_url=AUR.remote_web_url(package.package_base),
path=".",
branch="master",
)
return cls(
base=package.package_base,
version=package.version,
@ -265,14 +277,20 @@ class Package(LazyLogging):
version = full_version(srcinfo.get("epoch"), srcinfo["pkgver"], srcinfo["pkgrel"])
remote = RemoteSource(
source=PackageSource.Local,
git_url=path.absolute().as_uri(),
web_url="",
web_url=None,
path=".",
branch="master",
source=PackageSource.Local,
)
return cls(base=srcinfo["pkgbase"], version=version, remote=remote, packages=packages, packager=packager)
return cls(
base=srcinfo["pkgbase"],
version=version,
remote=remote,
packages=packages,
packager=packager,
)
@classmethod
def from_json(cls, dump: dict[str, Any]) -> Self:
@ -291,8 +309,13 @@ class Package(LazyLogging):
for key, value in packages_json.items()
}
remote = dump.get("remote") or {}
return cls(base=dump["base"], version=dump["version"], remote=RemoteSource.from_json(remote), packages=packages,
packager=dump.get("packager"))
return cls(
base=dump["base"],
version=dump["version"],
remote=RemoteSource.from_json(remote),
packages=packages,
packager=dump.get("packager"),
)
@classmethod
def from_official(cls, name: str, pacman: Pacman, packager: str | None = None, *, use_syncdb: bool = True) -> Self:
@ -309,7 +332,15 @@ class Package(LazyLogging):
Self: package properties
"""
package = OfficialSyncdb.info(name, pacman=pacman) if use_syncdb else Official.info(name, pacman=pacman)
remote = RemoteSource.from_source(PackageSource.Repository, package.package_base, package.repository)
remote = RemoteSource(
source=PackageSource.Repository,
git_url=Official.remote_git_url(package.package_base, package.repository),
web_url=Official.remote_web_url(package.package_base),
path=".",
branch="main",
)
return cls(
base=package.package_base,
version=package.version,

View File

@ -53,14 +53,13 @@ class PackageDescription:
>>> description = PackageDescription.from_json(dump)
>>>
>>>
>>> from pathlib import Path
>>> from ahriman.core.alpm.pacman import Pacman
>>> from ahriman.core.configuration import Configuration
>>>
>>> configuration = Configuration()
>>> pacman = Pacman("x86_64", configuration)
>>> pyalpm_description = next(package for package in pacman.get("pacman"))
>>> pyalpm_description = next(package for package in pacman.package_get("pacman"))
>>> description = PackageDescription.from_package(
>>> pyalpm_description, Path("/var/cache/pacman/pkg/pacman-6.0.1-4-x86_64.pkg.tar.zst"))
"""

View File

@ -24,6 +24,7 @@ from pathlib import Path
from urllib.parse import urlparse
from ahriman.core.util import package_like
from ahriman.models.repository_paths import RepositoryPaths
class PackageSource(str, Enum):
@ -42,7 +43,7 @@ class PackageSource(str, Enum):
Examples:
In case if source is unknown the ``resolve()`` and the source descriptor is available method must be used::
>>> real_source = PackageSource.Auto.resolve("ahriman")
>>> real_source = PackageSource.Auto.resolve("ahriman", configuration.repository_paths)
the code above will ensure that the presudo-source ``PackageSource.Auto`` will not be processed later.
"""
@ -55,12 +56,13 @@ class PackageSource(str, Enum):
Remote = "remote"
Repository = "repository"
def resolve(self, source: str) -> PackageSource:
def resolve(self, source: str, paths: RepositoryPaths) -> PackageSource:
"""
resolve auto into the correct type
Args:
source(str): source of the package
paths(RepositoryPaths): repository paths instance
Returns:
PackageSource: non-auto type of the package source
@ -74,7 +76,7 @@ class PackageSource(str, Enum):
if maybe_url.scheme and maybe_url.scheme not in ("data", "file") and package_like(maybe_path):
return PackageSource.Remote
try:
if (maybe_path / "PKGBUILD").is_file():
if (maybe_path / "PKGBUILD").is_file() or paths.cache_for(source).is_dir():
return PackageSource.Local
if maybe_path.is_dir():
return PackageSource.Directory

View File

@ -21,6 +21,7 @@ from dataclasses import dataclass, fields
from pathlib import Path
from typing import Any, Self
from ahriman.core.exceptions import InitializeError
from ahriman.core.util import dataclass_view, filter_json
from ahriman.models.package_source import PackageSource
@ -31,18 +32,18 @@ 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
branch(str | None): branch of the git repository
git_url(str | None): url of the git repository
path(str | None): 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
web_url(str | None): url of the package in the web interface
"""
git_url: str
web_url: str
path: str
branch: str
source: PackageSource
git_url: str | None = None
web_url: str | None = None
path: str | None = None
branch: str | None = None
def __post_init__(self) -> None:
"""
@ -51,17 +52,27 @@ class RemoteSource:
object.__setattr__(self, "source", PackageSource(self.source))
@property
def pkgbuild_dir(self) -> Path:
def is_remote(self) -> bool:
"""
check if source is remote
Returns:
bool: True in case if package is well-known remote source (e.g. AUR) and False otherwise
"""
return self.source in (PackageSource.AUR, PackageSource.Repository)
@property
def pkgbuild_dir(self) -> Path | None:
"""
get path to directory with package sources (PKGBUILD etc)
Returns:
Path: path to directory with package sources based on settings
Path | None: path to directory with package sources based on settings if available
"""
return Path(self.path)
return Path(self.path) if self.path is not None else None
@classmethod
def from_json(cls, dump: dict[str, Any]) -> Self | None:
def from_json(cls, dump: dict[str, Any]) -> Self:
"""
construct remote source from the json dump (or database row)
@ -69,47 +80,25 @@ class RemoteSource:
dump(dict[str, Any]): json dump body
Returns:
Self | None: remote source
Self: 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
return cls(**filter_json(dump, known_fields))
@classmethod
def from_source(cls, source: PackageSource, package_base: str, repository: str) -> Self | None:
def git_source(self) -> tuple[str, str]:
"""
generate remote source from the package base
Args:
source(PackageSource): source of the package
package_base(str): package base
repository(str): repository name
get git source if available
Returns:
Self | None: generated remote source if any, None otherwise
tuple[str, str]: git url and branch
Raises:
InitializeError: in case if git url and/or branch are not set
"""
if source == PackageSource.AUR:
from ahriman.core.alpm.remote import AUR
return cls(
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 import Official
return cls(
git_url=Official.remote_git_url(package_base, repository),
web_url=Official.remote_web_url(package_base),
path=".",
branch="main",
source=source,
)
return None
if self.git_url is None or self.branch is None:
raise InitializeError("Remote source is empty")
return self.git_url, self.branch
def view(self) -> dict[str, Any]:
"""

View File

@ -41,7 +41,7 @@ class User:
Simply create user from database data and perform required validation::
>>> password = User.generate_password(24)
>>> user = User(username="ahriman", password=password, access=UserAccess.Full, packager_id=None, key=None)
>>> user = User(username="ahriman", password=password, access=UserAccess.Full)
Since the password supplied may be plain text, the ``hash_password`` method can be used to hash the password::
@ -63,8 +63,8 @@ class User:
username: str
password: str
access: UserAccess
packager_id: str | None
key: str | None
packager_id: str | None = None
key: str | None = None
_HASHER = sha512_crypt

View File

@ -27,22 +27,22 @@ class RemoteSchema(Schema):
request and response package remote schema
"""
branch = fields.String(required=True, metadata={
branch = fields.String(metadata={
"description": "Repository branch",
"example": "master",
})
git_url = fields.String(required=True, metadata={
git_url = fields.String(metadata={
"description": "Package git url",
"example": "https://aur.archlinux.org/ahriman.git",
})
path = fields.String(required=True, metadata={
path = fields.String(metadata={
"description": "Path to package sources in git repository",
"example": ".",
})
source = fields.Enum(PackageSource, by_value=True, required=True, metadata={
"description": "Pacakge source",
})
web_url = fields.String(required=True, metadata={
web_url = fields.String(metadata={
"description": "Package repository page",
"example": "https://aur.archlinux.org/packages/ahriman",
})