Compare commits

..

4 Commits

20 changed files with 2508 additions and 2485 deletions

View File

@ -3,7 +3,7 @@ version: 2
build: build:
os: ubuntu-20.04 os: ubuntu-20.04
tools: tools:
python: "3.11" python: "3.12"
python: python:
install: install:

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -1,7 +1,7 @@
# Maintainer: Evgeniy Alekseev # Maintainer: Evgeniy Alekseev
pkgname='ahriman' pkgname='ahriman'
pkgver=2.13.5 pkgver=2.13.6
pkgrel=1 pkgrel=1
pkgdesc="ArcH linux ReposItory MANager" pkgdesc="ArcH linux ReposItory MANager"
arch=('any') arch=('any')

View File

@ -223,7 +223,6 @@
}); });
}); });
table.bootstrapTable({});
statusBadge.popover(); statusBadge.popover();
selectRepository(); selectRepository();
}); });

View File

@ -1,7 +1,7 @@
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js" integrity="sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs" crossorigin="anonymous" type="application/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js" integrity="sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js" integrity="sha384-8hHkOkbWN1TLWwet/jpbJ0zbx3FJDeYJgQ8dX1mRrv/vfCfHCqFSFZYCgaMML3z9" crossorigin="anonymous" type="application/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js" integrity="sha384-4ZTAzTbfB8H7hkWtXbyNDzDvxirmBT7EmURIvfOJ3Foympc+OD9p+bZNNENaJXgW" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js" integrity="sha384-u4eJN1VWrTf/FnYYQJo2kqJyVxEQf5UmWY4iUcNAoLenOEtEuCkfwc5bKvZOWBi5" crossorigin="anonymous" type="application/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js" integrity="sha384-Gn1XZMJEKL3ycoWq97jYAl+FP3vXQYE2ObBgzgcPMKOZdUZdF6ZuyUxbGC2bAnUT" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/tableexport.jquery.plugin@1.28.0/tableExport.min.js" integrity="sha384-1Rz4Kz/y1rSWw+ZsjTcxB684XgofbO8iizY+UFIzCwFeQ+QUyhBNWBMh/STOyomI" crossorigin="anonymous" type="application/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/tableexport.jquery.plugin@1.28.0/tableExport.min.js" integrity="sha384-1Rz4Kz/y1rSWw+ZsjTcxB684XgofbO8iizY+UFIzCwFeQ+QUyhBNWBMh/STOyomI" crossorigin="anonymous" type="application/javascript"></script>

View File

@ -1,4 +1,4 @@
.TH AHRIMAN "1" "2024\-04\-04" "ahriman" "Generated Python Manual" .TH AHRIMAN "1" "2024\-05\-05" "ahriman" "Generated Python Manual"
.SH NAME .SH NAME
ahriman ahriman
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -81,6 +81,7 @@ web = [
"aiohttp_security", "aiohttp_security",
"cryptography", "cryptography",
"requests-unixsocket", # required by unix socket support "requests-unixsocket", # required by unix socket support
"setuptools", # required by aiohttp-apispec
] ]
[tool.flit.sdist] [tool.flit.sdist]

View File

@ -17,4 +17,4 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
__version__ = "2.13.5" __version__ = "2.13.6"

View File

@ -69,7 +69,8 @@ class OAuth(Mapping):
Returns: Returns:
str: login control as html code to insert str: login control as html code to insert
""" """
return f"""<a class="nav-link" href="/api/v1/login" title="login via OAuth2"><i class="bi bi-{self.icon}"></i> login</a>""" return f"""<a class="nav-link" href="/api/v1/login" title="login via OAuth2"><i class="bi bi-{
self.icon}"></i> login</a>"""
@staticmethod @staticmethod
def get_provider(name: str) -> type[aioauth_client.OAuth2Client]: def get_provider(name: str) -> type[aioauth_client.OAuth2Client]:

View File

@ -330,5 +330,5 @@ class UnsafeRunError(RuntimeError):
root_uid(int): ID of the owner of root directory root_uid(int): ID of the owner of root directory
""" """
RuntimeError.__init__(self, f"Current UID {current_uid} differs from root owner {root_uid}. " RuntimeError.__init__(self, f"Current UID {current_uid} differs from root owner {root_uid}. "
f"Note that for the most actions it is unsafe to run application as different user." f"Note that for the most actions it is unsafe to run application as different user."
f" If you are 100% sure that it must be there try --unsafe option") f" If you are 100% sure that it must be there try --unsafe option")

View File

@ -106,7 +106,7 @@ class RemoteCall(Report):
"aur": self.update_aur, "aur": self.update_aur,
"local": self.update_local, "local": self.update_local,
"manual": self.update_manual, "manual": self.update_manual,
}) })
response_json = response.json() response_json = response.json()
process_id: str = response_json["process_id"] process_id: str = response_json["process_id"]

View File

@ -117,7 +117,8 @@ class Executor(PackageInfo, Cleaner):
# build package list based on user input # build package list based on user input
result = Result() result = Result()
requested = set(packages) packages = set(packages) # remove duplicates
requested = packages | {f"{package}-debug" for package in packages} # append debug packages
for local in self.packages(): for local in self.packages():
if local.base in packages or all(package in requested for package in local.packages): if local.base in packages or all(package in requested for package in local.packages):
packages_to_remove.update({ packages_to_remove.update({
@ -136,7 +137,7 @@ class Executor(PackageInfo, Cleaner):
# check for packages which were requested to remove, but weren't found locally # check for packages which were requested to remove, but weren't found locally
# it might happen for example, if there were no success build before # it might happen for example, if there were no success build before
for unknown in requested: for unknown in packages:
if unknown in packages_to_remove or unknown in bases_to_remove: if unknown in packages_to_remove or unknown in bases_to_remove:
continue continue
bases_to_remove.append(unknown) bases_to_remove.append(unknown)

View File

@ -183,11 +183,12 @@ post_install() {{
Returns: Returns:
str: package() function for PKGBUILD str: package() function for PKGBUILD
""" """
# somehow autopep thinks that construction inside contains valid python code and reformats it
return f"""{{ return f"""{{
install -Dm644 "{Path("$srcdir") / f"{self.name}.gpg"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}.gpg"}" install -Dm644 "{Path("$srcdir") / f"{self.name}.gpg"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}.gpg"}"
install -Dm644 "{Path("$srcdir") / f"{self.name}-revoked"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}-revoked"}" install -Dm644 "{Path("$srcdir") / f"{self.name}-revoked"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}-revoked"}"
install -Dm644 "{Path("$srcdir") / f"{self.name}-trusted"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}-trusted"}" install -Dm644 "{Path("$srcdir") / f"{self.name}-trusted"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}-trusted"}"
}}""" }}""" # nopep8
def sources(self) -> dict[str, Callable[[Path], None]]: def sources(self) -> dict[str, Callable[[Path], None]]:
""" """

View File

@ -162,7 +162,8 @@ class GitHub(Upload, HttpUpload):
Returns: Returns:
dict[str, Any] | None: GitHub API release object if release found and None otherwise dict[str, Any] | None: GitHub API release object if release found and None otherwise
""" """
url = f"https://api.github.com/repos/{self.github_owner}/{self.github_repository}/releases/tags/{self.github_release_tag}" url = f"https://api.github.com/repos/{self.github_owner}/{
self.github_repository}/releases/tags/{self.github_release_tag}"
try: try:
response = self.make_request("GET", url) response = self.make_request("GET", url)
release: dict[str, Any] = response.json() release: dict[str, Any] = response.json()

View File

@ -349,7 +349,7 @@ def pretty_datetime(timestamp: datetime.datetime | float | int | None) -> str:
if timestamp is None: if timestamp is None:
return "" return ""
if isinstance(timestamp, (int, float)): if isinstance(timestamp, (int, float)):
timestamp = datetime.datetime.utcfromtimestamp(timestamp) timestamp = datetime.datetime.fromtimestamp(timestamp, datetime.UTC)
return timestamp.strftime("%Y-%m-%d %H:%M:%S") return timestamp.strftime("%Y-%m-%d %H:%M:%S")
@ -505,7 +505,7 @@ def utcnow() -> datetime.datetime:
Returns: Returns:
datetime.datetime: current time in UTC datetime.datetime: current time in UTC
""" """
return datetime.datetime.utcnow() return datetime.datetime.now(datetime.UTC)
def walk(directory_path: Path) -> Generator[Path, None, None]: def walk(directory_path: Path) -> Generator[Path, None, None]:

View File

@ -137,8 +137,8 @@ class AURPackage:
description=package.desc, description=package.desc,
num_votes=0, num_votes=0,
popularity=0.0, popularity=0.0,
first_submitted=datetime.datetime.utcfromtimestamp(0), first_submitted=datetime.datetime.fromtimestamp(0, datetime.UTC),
last_modified=datetime.datetime.utcfromtimestamp(package.builddate), last_modified=datetime.datetime.fromtimestamp(package.builddate, datetime.UTC),
url_path="", url_path="",
url=package.url, url=package.url,
out_of_date=None, out_of_date=None,
@ -175,13 +175,11 @@ class AURPackage:
description=dump["pkgdesc"], description=dump["pkgdesc"],
num_votes=0, num_votes=0,
popularity=0.0, popularity=0.0,
first_submitted=datetime.datetime.utcfromtimestamp(0), first_submitted=datetime.datetime.fromtimestamp(0, datetime.UTC),
last_modified=datetime.datetime.strptime(dump["last_update"], "%Y-%m-%dT%H:%M:%S.%fZ"), last_modified=datetime.datetime.fromisoformat(dump["last_update"]),
url_path="", url_path="",
url=dump["url"], url=dump["url"],
out_of_date=datetime.datetime.strptime( out_of_date=datetime.datetime.fromisoformat(dump["flag_date"]) if dump.get("flag_date") else None,
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), maintainer=next(iter(dump["maintainers"]), None),
submitter=None, submitter=None,
repository=dump["repo"], repository=dump["repo"],
@ -208,9 +206,9 @@ class AURPackage:
""" """
identity_mapper: Callable[[Any], Any] = lambda value: value identity_mapper: Callable[[Any], Any] = lambda value: value
value_mapper: dict[str, Callable[[Any], Any]] = { value_mapper: dict[str, Callable[[Any], Any]] = {
"out_of_date": lambda value: datetime.datetime.utcfromtimestamp(value) if value is not None else None, "out_of_date": lambda value: datetime.datetime.fromtimestamp(value, datetime.UTC) if value is not None else None,
"first_submitted": datetime.datetime.utcfromtimestamp, "first_submitted": lambda value: datetime.datetime.fromtimestamp(value, datetime.UTC),
"last_modified": datetime.datetime.utcfromtimestamp, "last_modified": lambda value: datetime.datetime.fromtimestamp(value, datetime.UTC),
} }
result: dict[str, Any] = {} result: dict[str, Any] = {}

View File

@ -133,8 +133,8 @@ def aur_package_ahriman() -> AURPackage:
description="ArcH linux ReposItory MANager", description="ArcH linux ReposItory MANager",
num_votes=0, num_votes=0,
popularity=0, popularity=0,
first_submitted=datetime.datetime.utcfromtimestamp(1618008285), first_submitted=datetime.datetime.fromtimestamp(1618008285, datetime.UTC),
last_modified=datetime.datetime.utcfromtimestamp(1673826351), last_modified=datetime.datetime.fromtimestamp(1673826351, datetime.UTC),
url_path="/cgit/aur.git/snapshot/ahriman.tar.gz", url_path="/cgit/aur.git/snapshot/ahriman.tar.gz",
url="https://github.com/arcan1s/ahriman", url="https://github.com/arcan1s/ahriman",
out_of_date=None, out_of_date=None,
@ -200,8 +200,8 @@ def aur_package_akonadi() -> AURPackage:
description="PIM layer, which provides an asynchronous API to access all kind of PIM data", description="PIM layer, which provides an asynchronous API to access all kind of PIM data",
num_votes=0, num_votes=0,
popularity=0.0, popularity=0.0,
first_submitted=datetime.datetime.utcfromtimestamp(0), first_submitted=datetime.datetime.fromtimestamp(0, datetime.UTC),
last_modified=datetime.datetime.utcfromtimestamp(1646555990.610), last_modified=datetime.datetime.fromtimestamp(1646555990.610, datetime.UTC),
url_path="", url_path="",
url="https://kontact.kde.org", url="https://kontact.kde.org",
out_of_date=None, out_of_date=None,

View File

@ -85,7 +85,7 @@ def test_remote_update(remote_call: RemoteCall, mocker: MockerFixture) -> None:
"aur": False, "aur": False,
"local": False, "local": False,
"manual": True, "manual": True,
}) })
def test_remote_wait(remote_call: RemoteCall, mocker: MockerFixture) -> None: def test_remote_wait(remote_call: RemoteCall, mocker: MockerFixture) -> None:

View File

@ -89,6 +89,28 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
commit_sha_mock.assert_called_once_with(package_ahriman.base) commit_sha_mock.assert_called_once_with(package_ahriman.base)
def test_process_remove_with_debug(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run remove debug packages too
"""
package_ahriman.packages = {
package_ahriman.base: package_ahriman.packages[package_ahriman.base],
f"{package_ahriman.base}-debug": package_ahriman.packages[package_ahriman.base],
}
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_clear")
mocker.patch("ahriman.core.database.SQLite.package_clear")
mocker.patch("ahriman.core.status.client.Client.package_remove")
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
executor.process_remove([package_ahriman.base])
# must remove via alpm wrapper
repo_remove_mock.assert_has_calls([
MockCall(package_ahriman.base, package_ahriman.packages[package_ahriman.base].filepath),
MockCall(f"{package_ahriman.base}-debug", package_ahriman.packages[package_ahriman.base].filepath),
])
def test_process_remove_base_multiple(executor: Executor, package_python_schedule: Package, def test_process_remove_base_multiple(executor: Executor, package_python_schedule: Package,
mocker: MockerFixture) -> None: mocker: MockerFixture) -> None:
""" """

View File

@ -6,7 +6,6 @@ from pytest_mock import MockerFixture
from unittest.mock import MagicMock, call as MockCall from unittest.mock import MagicMock, call as MockCall
from ahriman.core.support.pkgbuild.pkgbuild_generator import PkgbuildGenerator from ahriman.core.support.pkgbuild.pkgbuild_generator import PkgbuildGenerator
from ahriman.core.util import utcnow
from ahriman.models.pkgbuild_patch import PkgbuildPatch from ahriman.models.pkgbuild_patch import PkgbuildPatch
@ -38,7 +37,7 @@ def test_pkgver(pkgbuild_generator: PkgbuildGenerator, mocker: MockerFixture) ->
must implement default version as current date must implement default version as current date
""" """
mocker.patch("ahriman.core.support.pkgbuild.pkgbuild_generator.utcnow", return_value=datetime.datetime(2002, 3, 11)) mocker.patch("ahriman.core.support.pkgbuild.pkgbuild_generator.utcnow", return_value=datetime.datetime(2002, 3, 11))
assert pkgbuild_generator.pkgver == utcnow().strftime("20020311") assert pkgbuild_generator.pkgver == "20020311"
def test_url(pkgbuild_generator: PkgbuildGenerator) -> None: def test_url(pkgbuild_generator: PkgbuildGenerator) -> None: