Compare commits

..

26 Commits

Author SHA1 Message Date
arcanis e2e013a33b straight forward conftest 2026-07-13 16:33:45 +03:00
arcanis da0853b6f1 generic fixtures 2026-07-13 13:01:54 +03:00
arcanis 7db38ff9e4 reorder tests 2026-07-13 13:00:13 +03:00
arcanis 92ac036f78 migrate to hatch 2026-07-11 12:24:53 +03:00
arcanis aef7fa5f93 build: update frontend dependencies 2026-07-06 16:16:31 +03:00
arcanis c412b08135 fix: remove staleTime defaults for query client 2026-07-06 15:33:20 +03:00
arcanis a3b6372c11 fix: override head method for sse endpoint to make it actually working 2026-07-06 14:44:29 +03:00
arcanis 78288befb8 build: use python 3.13 lowerbound 2026-07-06 13:56:02 +03:00
arcanis 49cd3d43c8 fix: handle errors in sse stream 2026-06-09 01:01:08 +03:00
arcanis d9b52806c0 fix: clear subscriber map on shutdown
Even though it is not a case in the application, the interface could be
used externally
2026-06-07 11:58:04 +03:00
arcanis 774db2d780 type: fix typing ignore with the last typing update 2026-05-31 20:04:25 +03:00
arcanis 68afda4c71 feat: allow readonly events with read permission 2026-05-26 20:51:06 +03:00
arcanis fa9fa73078 docs: bump python version to 2.13 2026-05-08 10:25:24 +03:00
arcanis 3e1e24cb50 feat: SSE support (#162)
* event bus implementation

* update tests

* docs update

* review fixes

* update configs

* fix typo

* frontend changes

* install missing pacakge

* queue processing simplification
2026-05-08 10:20:03 +03:00
arcanis 18fe38c30b build: use build_date argument for docker image instead of guessing from pacman root 2026-05-05 13:45:42 +03:00
arcanis 6ee8b26bd5 feat: show aur url in interface 2026-04-22 06:56:28 +03:00
arcanis fce49f22c9 type: fix mypy warnings in updated version 2026-04-03 13:36:41 +03:00
arcanis af8e2c9e9b build: update rtd.io image 2026-03-30 19:20:03 +03:00
arcanis 1c312bb528 feat: add error boundary 2026-03-24 01:17:42 +02:00
arcanis e39194e9f6 feat: etag support 2026-03-23 23:58:56 +02:00
arcanis 21cc029c18 refactor: use usedforsecurity flag for md5 calculations 2026-03-23 23:07:31 +02:00
arcanis 40671b99d5 feat: allow to configure cors 2026-03-23 16:39:07 +02:00
arcanis 3ad2c494af docs: update docstrings 2026-03-22 17:23:16 +02:00
arcanis 34014d1cdd build: add more rules for typescript 2026-03-22 17:23:16 +02:00
arcanis 93ed2b864b docs: describe rollback procedure in docs 2026-03-22 17:23:16 +02:00
arcanis cca931ccd0 fix: handle errors on pkgbuild reading 2026-03-22 13:10:09 +02:00
841 changed files with 3022 additions and 1337 deletions
+6
View File
@@ -26,6 +26,10 @@ jobs:
- uses: docker/setup-buildx-action@v3
- name: Set image date
id: args
run: echo "::set-output name=date::$(date -d yesterday +'%Y-%m-%d')"
- name: Login to docker hub
uses: docker/login-action@v3
with:
@@ -53,6 +57,8 @@ jobs:
- name: Build an image and push
uses: docker/build-push-action@v6
with:
build-args: |
BUILD_DATE=${{ steps.args.outputs.date }}
file: docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
+2 -2
View File
@@ -12,11 +12,11 @@ pacman -Syyu --noconfirm
# main dependencies
pacman -S --noconfirm devtools git npm pyalpm python-bcrypt python-filelock python-inflection python-pyelftools python-requests python-systemd sudo
# make dependencies
pacman -S --noconfirm --asdeps base-devel python-build python-flit python-installer python-tox python-wheel
pacman -S --noconfirm --asdeps base-devel python-build python-hatchling python-installer python-tox python-wheel
# optional dependencies
if [[ -z $MINIMAL_INSTALL ]]; then
# web server
pacman -S --noconfirm python-aioauth-client python-aiohttp python-aiohttp-apispec-git python-aiohttp-cors python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-cryptography python-jinja
pacman -S --noconfirm python-aioauth-client python-aiohttp python-aiohttp-apispec-git python-aiohttp-cors python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-aiohttp-sse-git python-cryptography python-jinja
# additional features
pacman -S --noconfirm gnupg ipython python-boto3 python-cerberus python-matplotlib rsync
fi
+5 -2
View File
@@ -103,5 +103,8 @@ docs/html/
# Frontend
node_modules/
package-lock.json
package/share/ahriman/templates/static/index.js
package/share/ahriman/templates/static/index.css
ahriman-web/package/share/ahriman/templates/static/index.js
ahriman-web/package/share/ahriman/templates/static/index.css
# local configs
/*.ini
+2
View File
@@ -2,4 +2,6 @@
addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec
asyncio_default_fixture_loop_scope = function
asyncio_mode = auto
pythonpath = tests
resource-path.directory-name-test-resources = ../../tests/testresources
spec_test_format = {result} {docstring_summary}
+2 -2
View File
@@ -1,9 +1,9 @@
version: 2
build:
os: ubuntu-20.04
os: ubuntu-lts-latest
tools:
python: "3.12"
python: "3.13"
apt_packages:
- graphviz
+68
View File
@@ -0,0 +1,68 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "ahriman-core"
description = "ArcH linux ReposItory MANager, core package"
readme = "../README.md"
requires-python = ">=3.13"
license = {file = "../COPYING"}
authors = [
{name = "ahriman team"},
]
dependencies = [
"bcrypt",
"filelock",
"inflection",
"pyelftools",
"requests",
]
dynamic = ["version"]
[project.optional-dependencies]
journald = [
"systemd-python",
]
# FIXME technically this dependency is required, but in some cases we do not have access to
# the libalpm which is required in order to install the package. Thus in case if we do not
# really need to run the application we can move it to "optional" dependencies
pacman = [
"pyalpm",
]
reports = [
"Jinja2",
]
s3 = [
"boto3",
]
shell = [
"IPython",
]
stats = [
"matplotlib",
]
unixsocket = [
"requests-unixsocket2",
]
validator = [
"cerberus",
]
[project.scripts]
ahriman = "ahriman.application.ahriman:run"
[project.urls]
Documentation = "https://ahriman.readthedocs.io/"
Repository = "https://github.com/arcan1s/ahriman"
Changelog = "https://github.com/arcan1s/ahriman/releases"
[tool.hatch.version]
path = "src/ahriman/__init__.py"
[tool.hatch.build.targets.wheel]
packages = ["src/ahriman"]
[tool.hatch.build.targets.wheel.shared-data]
"package/lib" = "lib"
"package/share" = "share"
@@ -104,7 +104,7 @@ class PkgbuildParser(shlex.shlex):
# ignore substitution and extend bash symbols
self.wordchars += "${}#:+-@!"
# in case of default behaviour, it will ignore, for example, segment part of url outside of quotes
# in case of default behavior, it will ignore, for example, segment part of url outside of quotes
self.commenters = ""
@staticmethod
@@ -72,7 +72,7 @@ class AUR(Remote):
parse RPC response to package list
Args:
response(dict[str, Any]): RPC response json
response(dict[str, Any]): RPC response JSON
Returns:
list[AURPackage]: list of parsed packages
@@ -74,7 +74,7 @@ class Official(Remote):
parse RPC response to package list
Args:
response(dict[str, Any]): RPC response json
response(dict[str, Any]): RPC response JSON
Returns:
list[AURPackage]: list of parsed packages
@@ -32,7 +32,7 @@ class OfficialSyncdb(Official):
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
separately), the database file might be outdated and must be handled manually (or kind of). This behavior might be
changed in the future.
Still we leave search function based on the official repositories RPC.
@@ -72,17 +72,17 @@ class PackageArchive:
if not PackageArchive.is_elf(binary_file):
return []
elf_file = ELFFile(binary_file) # type: ignore[no-untyped-call]
elf_file = ELFFile(binary_file)
dynamic_section = next(
(section for section in elf_file.iter_sections() # type: ignore[no-untyped-call]
(section for section in elf_file.iter_sections()
if isinstance(section, DynamicSection)),
None)
if dynamic_section is None:
return []
return [
tag.needed
for tag in dynamic_section.iter_tags() # type: ignore[no-untyped-call]
tag.needed # type: ignore[attr-defined]
for tag in dynamic_section.iter_tags()
if tag.entry.d_tag == "DT_NEEDED"
]
@@ -416,7 +416,7 @@ class Sources(LazyLogging):
else:
patch.write(sources_dir / "PKGBUILD")
def read(self, sources_dir: Path, commit_sha: str, path: Path) -> str:
def read(self, sources_dir: Path, commit_sha: str, path: Path) -> str | None:
"""
read file content from the specified commit
@@ -426,6 +426,10 @@ class Sources(LazyLogging):
path(Path): path to file inside the repository
Returns:
str: file content at specified commit
str | None: file content at specified commit if available
"""
try:
return check_output(*self.git(), "show", f"{commit_sha}:{path}", cwd=sources_dir, logger=self.logger)
except CalledProcessError:
self.logger.exception("failed to read file %s at %s", path, commit_sha)
return None
@@ -72,7 +72,7 @@ class Configuration(configparser.RawConfigParser):
def __init__(self, allow_no_value: bool = False, allow_multi_key: bool = True) -> None:
"""
Args:
allow_no_value(bool, optional): copies :class:`configparser.RawConfigParser` behaviour. In case if it is set
allow_no_value(bool, optional): copies :class:`configparser.RawConfigParser` behavior. In case if it is set
to ``True``, the keys without values will be allowed (Default value = False)
allow_multi_key(bool, optional): if set to ``False``, then the default dictionary class will be used to
store keys internally. Otherwise, the special implementation will be used, which supports arrays
@@ -80,7 +80,7 @@ class Configuration(configparser.RawConfigParser):
"""
configparser.RawConfigParser.__init__(
self,
dict_type=ConfigurationMultiDict if allow_multi_key else dict, # type: ignore[arg-type]
dict_type=ConfigurationMultiDict if allow_multi_key else dict,
allow_no_value=allow_no_value,
strict=False,
empty_lines_in_values=not allow_multi_key,
@@ -358,6 +358,38 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"min": 0,
},
},
"cors_allow_headers": {
"type": "list",
"coerce": "list",
"schema": {
"type": "string",
"empty": False,
},
},
"cors_allow_methods": {
"type": "list",
"coerce": "list",
"schema": {
"type": "string",
"empty": False,
},
},
"cors_allow_origins": {
"type": "list",
"coerce": "list",
"schema": {
"type": "string",
"empty": False,
},
},
"cors_expose_headers": {
"type": "list",
"coerce": "list",
"schema": {
"type": "string",
"empty": False,
},
},
"enable_archive_upload": {
"type": "boolean",
"coerce": "boolean",
@@ -150,6 +150,6 @@ class ShellTemplate(Template):
break
kwargs.update(mapping)
substituted = dict(generator(kwargs))
kwargs.update(dict(generator(kwargs)))
return self.safe_substitute(kwargs | substituted)
return self.safe_substitute(kwargs)

Some files were not shown because too many files have changed in this diff Show More