mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-06-28 06:41:43 +00:00
Compare commits
3 Commits
e119f092b4
...
master
Author | SHA1 | Date | |
---|---|---|---|
7f223ecc0a | |||
7769a4a6e0 | |||
066d1b1dde |
@ -1,6 +0,0 @@
|
||||
skips:
|
||||
- B101
|
||||
- B104
|
||||
- B105
|
||||
- B106
|
||||
- B404
|
12
.github/workflows/docker.yml
vendored
12
.github/workflows/docker.yml
vendored
@ -21,18 +21,18 @@ jobs:
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to docker hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to github container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Extract docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
arcan1s/ahriman
|
||||
@ -50,7 +50,7 @@ jobs:
|
||||
type=edge
|
||||
|
||||
- name: Build an image and push
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: docker/Dockerfile
|
||||
push: true
|
||||
|
2
.github/workflows/regress.yml
vendored
2
.github/workflows/regress.yml
vendored
@ -37,8 +37,6 @@ jobs:
|
||||
- repo:/var/lib/ahriman
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- run: pacman -Sy
|
||||
|
||||
- name: Init repository
|
||||
|
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Extract version
|
||||
id: version
|
||||
@ -27,8 +27,7 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
filter: 'Release \d+\.\d+\.\d+'
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ConorMacBride/install-package@v1.1.0
|
||||
- uses: ConorMacBride/install-package@v1.1.0
|
||||
with:
|
||||
apt: tox
|
||||
|
||||
@ -38,7 +37,7 @@ jobs:
|
||||
VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
|
||||
- name: Publish release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
body: |
|
||||
${{ steps.changelog.outputs.compareurl }}
|
||||
|
4
.github/workflows/setup.yml
vendored
4
.github/workflows/setup.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
||||
- ${{ github.workspace }}:/build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup the minimal service in arch linux container
|
||||
run: .github/workflows/setup.sh minimal
|
||||
@ -40,7 +40,7 @@ jobs:
|
||||
options: --privileged -w /build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup the service in arch linux container
|
||||
run: .github/workflows/setup.sh
|
||||
|
10
.github/workflows/tests.sh
vendored
10
.github/workflows/tests.sh
vendored
@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Install dependencies and run test in container
|
||||
|
||||
set -ex
|
||||
|
||||
# install dependencies
|
||||
pacman --noconfirm -Syyu base-devel python-tox
|
||||
|
||||
# run test and check targets
|
||||
tox
|
15
.github/workflows/tests.yml
vendored
15
.github/workflows/tests.yml
vendored
@ -26,7 +26,16 @@ jobs:
|
||||
- ${{ github.workspace }}:/build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: pacman --noconfirm -Syu base-devel git python-tox
|
||||
|
||||
- name: Run check and tests in arch linux container
|
||||
run: .github/workflows/tests.sh
|
||||
- run: git config --global --add safe.directory *
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run check and tests
|
||||
run: tox
|
||||
|
||||
- name: Generate documentation and check if there are untracked changes
|
||||
run: |
|
||||
tox -e docs
|
||||
[ -z "$(git status --porcelain docs/*.rst)" ]
|
||||
|
@ -1,10 +1,10 @@
|
||||
[tool.pylint.main]
|
||||
init-hook = "sys.path.append('pylint_plugins')"
|
||||
init-hook = "sys.path.append('tools')"
|
||||
load-plugins = [
|
||||
"pylint.extensions.docparams",
|
||||
"pylint.extensions.bad_builtin",
|
||||
"definition_order",
|
||||
"import_order",
|
||||
"pylint_plugins.definition_order",
|
||||
"pylint_plugins.import_order",
|
||||
]
|
||||
|
||||
[tool.pylint.classes]
|
||||
|
5
.pytest.ini
Normal file
5
.pytest.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[pytest]
|
||||
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
|
||||
spec_test_format = {result} {docstring_summary}
|
2621
docs/_static/architecture.dot
vendored
2621
docs/_static/architecture.dot
vendored
File diff suppressed because it is too large
Load Diff
@ -413,10 +413,11 @@ Web application
|
||||
Web application requires the following python packages to be installed:
|
||||
|
||||
* Core part requires ``aiohttp`` (application itself), ``aiohttp_jinja2`` and ``Jinja2`` (HTML generation from templates).
|
||||
* Additional web features also require ``aiohttp-apispec`` (autogenerated documentation), ``aiohttp_cors`` (CORS support, required by documentation).
|
||||
* Additional web features also require ``aiohttp-apispec`` (autogenerated documentation, optional), ``aiohttp_cors`` (CORS support, required by documentation).
|
||||
* In addition, authorization feature requires ``aiohttp_security``, ``aiohttp_session`` and ``cryptography``.
|
||||
* In addition to base authorization dependencies, OAuth2 also requires ``aioauth-client`` library.
|
||||
* In addition if you would like to disable authorization for local access (recommended way in order to run the application itself with reporting support), the ``requests-unixsocket2`` library is required.
|
||||
* Application metrics will be automatically enabled after installing ``aiohttp-openmetrics`` package.
|
||||
|
||||
Middlewares
|
||||
^^^^^^^^^^^
|
||||
|
@ -1,36 +1,36 @@
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --group ../pyproject.toml:docs --extra s3 --extra validator --extra web --output-file ../docs/requirements.txt ../pyproject.toml
|
||||
# uv pip compile --group pyproject.toml:docs --extra s3 --extra validator --extra web --output-file docs/requirements.txt pyproject.toml
|
||||
aiohappyeyeballs==2.6.1
|
||||
# via aiohttp
|
||||
aiohttp==3.11.18
|
||||
# via
|
||||
# ahriman (../pyproject.toml)
|
||||
# ahriman (pyproject.toml)
|
||||
# aiohttp-cors
|
||||
# aiohttp-jinja2
|
||||
aiohttp-cors==0.8.1
|
||||
# via ahriman (../pyproject.toml)
|
||||
# via ahriman (pyproject.toml)
|
||||
aiohttp-jinja2==1.6
|
||||
# via ahriman (../pyproject.toml)
|
||||
# via ahriman (pyproject.toml)
|
||||
aiosignal==1.3.2
|
||||
# via aiohttp
|
||||
alabaster==1.0.0
|
||||
# via sphinx
|
||||
argparse-manpage==4.6
|
||||
# via ahriman (../pyproject.toml:docs)
|
||||
# via ahriman (pyproject.toml:docs)
|
||||
attrs==25.3.0
|
||||
# via aiohttp
|
||||
babel==2.17.0
|
||||
# via sphinx
|
||||
bcrypt==4.3.0
|
||||
# via ahriman (../pyproject.toml)
|
||||
# via ahriman (pyproject.toml)
|
||||
boto3==1.38.11
|
||||
# via ahriman (../pyproject.toml)
|
||||
# via ahriman (pyproject.toml)
|
||||
botocore==1.38.11
|
||||
# via
|
||||
# boto3
|
||||
# s3transfer
|
||||
cerberus==1.3.7
|
||||
# via ahriman (../pyproject.toml)
|
||||
# via ahriman (pyproject.toml)
|
||||
certifi==2025.4.26
|
||||
# via requests
|
||||
charset-normalizer==3.4.2
|
||||
@ -51,7 +51,7 @@ idna==3.10
|
||||
imagesize==1.4.1
|
||||
# via sphinx
|
||||
inflection==0.5.1
|
||||
# via ahriman (../pyproject.toml)
|
||||
# via ahriman (pyproject.toml)
|
||||
jinja2==3.1.6
|
||||
# via
|
||||
# aiohttp-jinja2
|
||||
@ -73,37 +73,37 @@ propcache==0.3.1
|
||||
# aiohttp
|
||||
# yarl
|
||||
pydeps==3.0.1
|
||||
# via ahriman (../pyproject.toml:docs)
|
||||
# via ahriman (pyproject.toml:docs)
|
||||
pyelftools==0.32
|
||||
# via ahriman (../pyproject.toml)
|
||||
# via ahriman (pyproject.toml)
|
||||
pygments==2.19.1
|
||||
# via sphinx
|
||||
python-dateutil==2.9.0.post0
|
||||
# via botocore
|
||||
requests==2.32.3
|
||||
# via
|
||||
# ahriman (../pyproject.toml)
|
||||
# ahriman (pyproject.toml)
|
||||
# sphinx
|
||||
roman-numerals-py==3.1.0
|
||||
# via sphinx
|
||||
s3transfer==0.12.0
|
||||
# via boto3
|
||||
shtab==1.7.2
|
||||
# via ahriman (../pyproject.toml:docs)
|
||||
# via ahriman (pyproject.toml:docs)
|
||||
six==1.17.0
|
||||
# via python-dateutil
|
||||
snowballstemmer==3.0.0.1
|
||||
# via sphinx
|
||||
sphinx==8.2.3
|
||||
# via
|
||||
# ahriman (../pyproject.toml:docs)
|
||||
# ahriman (pyproject.toml:docs)
|
||||
# sphinx-argparse
|
||||
# sphinx-rtd-theme
|
||||
# sphinxcontrib-jquery
|
||||
sphinx-argparse==0.5.2
|
||||
# via ahriman (../pyproject.toml:docs)
|
||||
# via ahriman (pyproject.toml:docs)
|
||||
sphinx-rtd-theme==3.0.2
|
||||
# via ahriman (../pyproject.toml:docs)
|
||||
# via ahriman (pyproject.toml:docs)
|
||||
sphinxcontrib-applehelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-devhelp==2.0.0
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
pkgbase='ahriman'
|
||||
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
|
||||
pkgver=2.18.2
|
||||
pkgver=2.18.3
|
||||
pkgrel=1
|
||||
pkgdesc="ArcH linux ReposItory MANager"
|
||||
arch=('any')
|
||||
|
@ -1,6 +1,6 @@
|
||||
.TH AHRIMAN "1" "2025\-06\-16" "ahriman" "Generated Python Manual"
|
||||
.TH AHRIMAN "1" "2025\-06\-23" "ahriman 2.18.3" "ArcH linux ReposItory MANager"
|
||||
.SH NAME
|
||||
ahriman
|
||||
ahriman \- ArcH linux ReposItory MANager
|
||||
.SH SYNOPSIS
|
||||
.B ahriman
|
||||
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {add,aur-search,check,clean,config,config-validate,copy,daemon,help,help-commands-unsafe,help-updates,help-version,init,key-import,package-add,package-changes,package-changes-remove,package-copy,package-remove,package-status,package-status-remove,package-status-update,package-update,patch-add,patch-list,patch-remove,patch-set-add,rebuild,remove,remove-unknown,repo-backup,repo-check,repo-clean,repo-config,repo-config-validate,repo-create-keyring,repo-create-mirrorlist,repo-daemon,repo-init,repo-rebuild,repo-remove-unknown,repo-report,repo-restore,repo-setup,repo-sign,repo-statistics,repo-status-update,repo-sync,repo-tree,repo-triggers,repo-update,report,run,search,service-clean,service-config,service-config-validate,service-key-import,service-repositories,service-run,service-setup,service-shell,service-tree-migrate,setup,shell,sign,status,status-update,sync,update,user-add,user-list,user-remove,version,web} ...
|
||||
|
@ -58,23 +58,23 @@ web = [
|
||||
"aiohttp_cors",
|
||||
"aiohttp_jinja2",
|
||||
]
|
||||
web_api-docs = [
|
||||
"ahriman[web]",
|
||||
"aiohttp-apispec",
|
||||
"setuptools", # required by aiohttp-apispec
|
||||
]
|
||||
web_auth = [
|
||||
web-auth = [
|
||||
"ahriman[web]",
|
||||
"aiohttp_session",
|
||||
"aiohttp_security",
|
||||
"cryptography",
|
||||
]
|
||||
web_metrics = [
|
||||
web-docs = [
|
||||
"ahriman[web]",
|
||||
"aiohttp-apispec",
|
||||
"setuptools", # required by aiohttp-apispec
|
||||
]
|
||||
web-metrics = [
|
||||
"ahriman[web]",
|
||||
"aiohttp-openmetrics",
|
||||
]
|
||||
web_oauth2 = [
|
||||
"ahriman[web_auth]",
|
||||
web-oauth2 = [
|
||||
"ahriman[web-auth]",
|
||||
"aioauth-client",
|
||||
]
|
||||
|
||||
|
@ -17,4 +17,4 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2.18.2"
|
||||
__version__ = "2.18.3"
|
||||
|
@ -210,6 +210,17 @@ class Configuration(configparser.RawConfigParser):
|
||||
raise InitializeError("Configuration path and/or repository id are not set")
|
||||
return self.path, self.repository_id
|
||||
|
||||
def copy_from(self, configuration: Self) -> None:
|
||||
"""
|
||||
copy values from another instance overriding existing
|
||||
|
||||
Args:
|
||||
configuration(Self): configuration instance to merge from
|
||||
"""
|
||||
for section in configuration.sections():
|
||||
for key, value in configuration.items(section):
|
||||
self.set_option(section, key, value)
|
||||
|
||||
def dump(self) -> dict[str, dict[str, str]]:
|
||||
"""
|
||||
dump configuration to dictionary
|
||||
@ -220,6 +231,7 @@ class Configuration(configparser.RawConfigParser):
|
||||
return {
|
||||
section: dict(self.items(section))
|
||||
for section in self.sections()
|
||||
if self[section]
|
||||
}
|
||||
|
||||
# pylint and mypy are too stupid to find these methods
|
||||
|
@ -72,8 +72,8 @@ def setup_routes(application: Application, configuration: Configuration) -> None
|
||||
application(Application): web application instance
|
||||
configuration(Configuration): configuration instance
|
||||
"""
|
||||
application.router.add_static("/static", configuration.getpath("web", "static_path"), name="_static",
|
||||
follow_symlinks=True)
|
||||
application.router.add_static("/static", configuration.getpath("web", "static_path"),
|
||||
name="_static", follow_symlinks=True)
|
||||
|
||||
for route, view in _dynamic_routes(configuration):
|
||||
application.router.add_view(route, view, name=_identifier(route))
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -144,6 +144,7 @@ def test_repositories_extract(args: argparse.Namespace, configuration: Configura
|
||||
args.architecture = "arch"
|
||||
args.configuration = configuration.path
|
||||
args.repository = "repo"
|
||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
||||
|
||||
@ -159,6 +160,7 @@ def test_repositories_extract_repository(args: argparse.Namespace, configuration
|
||||
"""
|
||||
args.architecture = "arch"
|
||||
args.configuration = configuration.path
|
||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
|
||||
return_value={"repo"})
|
||||
@ -175,6 +177,7 @@ def test_repositories_extract_repository_legacy(args: argparse.Namespace, config
|
||||
"""
|
||||
args.architecture = "arch"
|
||||
args.configuration = configuration.path
|
||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
|
||||
return_value=set())
|
||||
@ -191,6 +194,7 @@ def test_repositories_extract_architecture(args: argparse.Namespace, configurati
|
||||
"""
|
||||
args.configuration = configuration.path
|
||||
args.repository = "repo"
|
||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures",
|
||||
return_value={"arch"})
|
||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
||||
@ -207,6 +211,7 @@ def test_repositories_extract_empty(args: argparse.Namespace, configuration: Con
|
||||
"""
|
||||
args.command = "config"
|
||||
args.configuration = configuration.path
|
||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures", return_value=set())
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories", return_value=set())
|
||||
|
||||
@ -221,6 +226,7 @@ def test_repositories_extract_systemd(args: argparse.Namespace, configuration: C
|
||||
"""
|
||||
args.configuration = configuration.path
|
||||
args.repository_id = "i686/some/repo/name"
|
||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
||||
|
||||
@ -236,6 +242,7 @@ def test_repositories_extract_systemd_with_dash(args: argparse.Namespace, config
|
||||
"""
|
||||
args.configuration = configuration.path
|
||||
args.repository_id = "i686-some-repo-name"
|
||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
||||
|
||||
@ -251,6 +258,7 @@ def test_repositories_extract_systemd_legacy(args: argparse.Namespace, configura
|
||||
"""
|
||||
args.configuration = configuration.path
|
||||
args.repository_id = "i686"
|
||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
|
||||
return_value=set())
|
||||
|
@ -6,6 +6,7 @@ from pytest_mock import MockerFixture
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers.copy import Copy
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database import SQLite
|
||||
from ahriman.core.repository import Repository
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
@ -30,11 +31,12 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||
package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args = _default_args(args)
|
||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
|
||||
application_mock = mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
|
||||
@ -51,12 +53,13 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
|
||||
|
||||
|
||||
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||
package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command and remove packages afterward
|
||||
"""
|
||||
args = _default_args(args)
|
||||
args.remove = True
|
||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
|
||||
@ -69,12 +72,14 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo
|
||||
|
||||
|
||||
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||
mocker: MockerFixture) -> None:
|
||||
database: SQLite, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise ExitCode exception on empty result
|
||||
"""
|
||||
args = _default_args(args)
|
||||
args.exit_code = True
|
||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[])
|
||||
mocker.patch("ahriman.application.application.Application.update")
|
||||
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||
|
@ -9,6 +9,7 @@ from urllib.parse import quote_plus as url_encode
|
||||
|
||||
from ahriman.application.handlers.setup import Setup
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database import SQLite
|
||||
from ahriman.core.exceptions import MissingArchitectureError
|
||||
from ahriman.core.repository import Repository
|
||||
from ahriman.models.repository_id import RepositoryId
|
||||
@ -44,11 +45,12 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
database: SQLite, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args = _default_args(args)
|
||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||
ahriman_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
|
||||
devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools")
|
||||
@ -88,12 +90,13 @@ def test_run_no_architecture_or_repository(configuration: Configuration) -> None
|
||||
|
||||
|
||||
def test_run_with_server(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||
mocker: MockerFixture) -> None:
|
||||
database: SQLite, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command with server specified
|
||||
"""
|
||||
args = _default_args(args)
|
||||
args.server = "server"
|
||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
|
||||
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
|
||||
|
@ -51,7 +51,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, database: S
|
||||
update_mock.assert_called_once_with(user)
|
||||
|
||||
|
||||
def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration, database: SQLite,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process users with empty password salt
|
||||
"""
|
||||
@ -59,6 +60,7 @@ def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration,
|
||||
args = _default_args(args)
|
||||
user = User(username=args.username, password=args.password, access=args.role,
|
||||
packager_id=args.packager, key=args.key)
|
||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
||||
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
|
||||
create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
|
||||
update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
|
||||
|
@ -1575,6 +1575,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
args.command = ""
|
||||
args.handler = Handler
|
||||
|
||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
||||
mocker.patch("argparse.ArgumentParser.parse_args", return_value=args)
|
||||
|
||||
assert ahriman.run() == 1
|
||||
|
@ -249,19 +249,25 @@ def auth(configuration: Configuration) -> Auth:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def configuration(repository_id: RepositoryId, resource_path_root: Path) -> Configuration:
|
||||
def configuration(repository_id: RepositoryId, tmp_path: Path, resource_path_root: Path) -> Configuration:
|
||||
"""
|
||||
configuration fixture
|
||||
|
||||
Args:
|
||||
repository_id(RepositoryId): repository identifier fixture
|
||||
tmp_path(Path): temporary path used by the fixture as root
|
||||
resource_path_root(Path): resource path root directory
|
||||
|
||||
Returns:
|
||||
Configuration: configuration test instance
|
||||
"""
|
||||
path = resource_path_root / "core" / "ahriman.ini"
|
||||
return Configuration.from_path(path, repository_id)
|
||||
|
||||
instance = Configuration.from_path(path, repository_id)
|
||||
instance.set_option("repository", "root", str(tmp_path))
|
||||
instance.set_option("settings", "database", str(tmp_path / "ahriman.db"))
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -275,9 +281,7 @@ def database(configuration: Configuration) -> SQLite:
|
||||
Returns:
|
||||
SQLite: database test instance
|
||||
"""
|
||||
database = SQLite.load(configuration)
|
||||
yield database
|
||||
database.path.unlink()
|
||||
return SQLite.load(configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -102,6 +102,15 @@ def test_check_loaded_architecture(configuration: Configuration) -> None:
|
||||
configuration.check_loaded()
|
||||
|
||||
|
||||
def test_copy_from(configuration: Configuration) -> None:
|
||||
"""
|
||||
must copy values from another instance
|
||||
"""
|
||||
instance = Configuration()
|
||||
instance.copy_from(configuration)
|
||||
assert instance.dump() == configuration.dump()
|
||||
|
||||
|
||||
def test_dump(configuration: Configuration) -> None:
|
||||
"""
|
||||
dump must not be empty
|
||||
|
@ -62,8 +62,8 @@ def test_validate_is_ip_address(validator: Validator, mocker: MockerFixture) ->
|
||||
validator._validate_is_ip_address([], "field", "localhost")
|
||||
|
||||
validator._validate_is_ip_address([], "field", "127.0.0.1")
|
||||
validator._validate_is_ip_address([], "field", "::")
|
||||
validator._validate_is_ip_address([], "field", "0.0.0.0")
|
||||
validator._validate_is_ip_address([], "field", "::") # nosec
|
||||
validator._validate_is_ip_address([], "field", "0.0.0.0") # nosec
|
||||
|
||||
validator._validate_is_ip_address([], "field", "random string")
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
[settings]
|
||||
include = .
|
||||
logging = logging.ini
|
||||
database = ../../../ahriman-test.db
|
||||
|
||||
[alpm]
|
||||
database = /var/lib/pacman
|
||||
@ -31,7 +30,6 @@ triggers_known = ahriman.core.distributed.WorkerLoaderTrigger ahriman.core.distr
|
||||
|
||||
[repository]
|
||||
name = aur
|
||||
root = ../../../
|
||||
|
||||
[sign]
|
||||
target =
|
||||
|
19
tools/__init__.py
Normal file
19
tools/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright (c) 2021-2025 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/>.
|
||||
#
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
119
tox.ini
119
tox.ini
@ -1,119 +0,0 @@
|
||||
[tox]
|
||||
envlist = check, tests
|
||||
isolated_build = true
|
||||
labels =
|
||||
release = version, docs, publish
|
||||
dependencies = -e .[journald,pacman,reports,s3,shell,stats,unixsocket,validator,web,web_api-docs,web_auth,web_oauth2,web_metrics]
|
||||
project_name = ahriman
|
||||
|
||||
[mypy]
|
||||
flags = --implicit-reexport --strict --allow-untyped-decorators --allow-subclassing-any
|
||||
|
||||
[pytest]
|
||||
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
|
||||
spec_test_format = {result} {docstring_summary}
|
||||
|
||||
[testenv:archive]
|
||||
description = Create source files tarball
|
||||
deps =
|
||||
build
|
||||
commands =
|
||||
python -m build --sdist
|
||||
|
||||
[testenv:check]
|
||||
description = Run common checks like linter, mypy, etc
|
||||
dependency_groups =
|
||||
check
|
||||
deps =
|
||||
{[tox]dependencies}
|
||||
pip_pre = true
|
||||
setenv =
|
||||
CFLAGS="-Wno-unterminated-string-initialization"
|
||||
MYPYPATH=src
|
||||
commands =
|
||||
autopep8 --exit-code --max-line-length 120 -aa -i -j 0 -r "src/{[tox]project_name}" "tests/{[tox]project_name}"
|
||||
pylint --rcfile=.pylint.toml "src/{[tox]project_name}"
|
||||
bandit -c .bandit.yml -r "src/{[tox]project_name}"
|
||||
bandit -c .bandit-test.yml -r "tests/{[tox]project_name}"
|
||||
mypy {[mypy]flags} -p "{[tox]project_name}" --install-types --non-interactive
|
||||
|
||||
[testenv:docs]
|
||||
description = Generate source files for documentation
|
||||
allowlist_externals =
|
||||
bash
|
||||
find
|
||||
mv
|
||||
changedir = src
|
||||
dependency_groups =
|
||||
docs
|
||||
depends =
|
||||
version
|
||||
deps =
|
||||
{[tox]dependencies}
|
||||
uv
|
||||
pip_pre = true
|
||||
setenv =
|
||||
SPHINX_APIDOC_OPTIONS=members,no-undoc-members,show-inheritance
|
||||
commands =
|
||||
bash -c 'shtab --shell bash --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../package/share/bash-completion/completions/_ahriman'
|
||||
bash -c 'shtab --shell zsh --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../package/share/zsh/site-functions/_ahriman'
|
||||
argparse-manpage --module ahriman.application.ahriman --function _parser --author "ahriman team" --project-name ahriman --author-email "" --url https://github.com/arcan1s/ahriman --output ../package/share/man/man1/ahriman.1
|
||||
pydeps ahriman --no-output --show-dot --dot-output architecture.dot --no-config --cluster
|
||||
mv architecture.dot ../docs/_static/architecture.dot
|
||||
# remove autogenerated modules rst files
|
||||
find ../docs -type f -name "{[tox]project_name}*.rst" -delete
|
||||
sphinx-apidoc -o ../docs .
|
||||
# compile list of dependencies for rtd.io
|
||||
uv pip compile --group ../pyproject.toml:docs --extra s3 --extra validator --extra web --output-file ../docs/requirements.txt --quiet ../pyproject.toml
|
||||
|
||||
[testenv:html]
|
||||
description = Generate html documentation
|
||||
dependency_groups =
|
||||
docs
|
||||
deps =
|
||||
{[tox]dependencies}
|
||||
pip_pre = true
|
||||
recreate = true
|
||||
commands =
|
||||
sphinx-build -b html -a -j auto -W docs {envtmpdir}{/}html
|
||||
|
||||
[testenv:publish]
|
||||
description = Create and publish release to GitHub
|
||||
allowlist_externals =
|
||||
git
|
||||
depends =
|
||||
docs
|
||||
passenv =
|
||||
SSH_AUTH_SOCK
|
||||
commands =
|
||||
git add package/archlinux/PKGBUILD src/ahriman/__init__.py docs/_static/architecture.dot package/share/man/man1/ahriman.1 package/share/bash-completion/completions/_ahriman package/share/zsh/site-functions/_ahriman
|
||||
git commit -m "Release {posargs}"
|
||||
git tag "{posargs}"
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
[testenv:tests]
|
||||
description = Run tests
|
||||
dependency_groups =
|
||||
tests
|
||||
deps =
|
||||
{[tox]dependencies}
|
||||
pip_pre = true
|
||||
setenv =
|
||||
CFLAGS="-Wno-unterminated-string-initialization"
|
||||
commands =
|
||||
pytest {posargs}
|
||||
|
||||
[testenv:version]
|
||||
description = Bump package version
|
||||
allowlist_externals =
|
||||
sed
|
||||
deps =
|
||||
packaging
|
||||
commands =
|
||||
# check if version is set and validate it
|
||||
{envpython} -c 'from packaging.version import Version; Version("{posargs}")'
|
||||
sed -i 's/^__version__ = .*/__version__ = "{posargs}"/' src/ahriman/__init__.py
|
||||
sed -i "s/pkgver=.*/pkgver={posargs}/" package/archlinux/PKGBUILD
|
310
tox.toml
Normal file
310
tox.toml
Normal file
@ -0,0 +1,310 @@
|
||||
env_list = [
|
||||
"check",
|
||||
"tests",
|
||||
]
|
||||
isolated_build = true
|
||||
labels.release = [
|
||||
"version",
|
||||
"docs",
|
||||
"publish",
|
||||
]
|
||||
|
||||
[flags]
|
||||
autopep8 = [
|
||||
"--max-line-length", "120",
|
||||
"-aa",
|
||||
]
|
||||
bandit = [
|
||||
"--configfile", ".bandit.yml",
|
||||
]
|
||||
manpage = [
|
||||
"--author", "{[project]name} team",
|
||||
"--author-email", "",
|
||||
"--description", "ArcH linux ReposItory MANager",
|
||||
"--manual-title", "ArcH linux ReposItory MANager",
|
||||
"--project-name", "{[project]name}",
|
||||
"--version", "{env:VERSION}",
|
||||
"--url", "https://github.com/arcan1s/ahriman",
|
||||
]
|
||||
mypy = [
|
||||
"--implicit-reexport",
|
||||
"--strict",
|
||||
"--allow-untyped-decorators",
|
||||
"--allow-subclassing-any",
|
||||
]
|
||||
pydeps = [
|
||||
"--no-config",
|
||||
"--cluster",
|
||||
]
|
||||
pylint = [
|
||||
"--rcfile", ".pylint.toml",
|
||||
]
|
||||
shtab = [
|
||||
"--prefix", "{[project]name}",
|
||||
"--prog", "{[project]name}",
|
||||
"ahriman.application.ahriman._parser",
|
||||
]
|
||||
|
||||
[project]
|
||||
extras = [
|
||||
"journald",
|
||||
"pacman",
|
||||
"reports",
|
||||
"s3",
|
||||
"shell",
|
||||
"stats",
|
||||
"unixsocket",
|
||||
"validator",
|
||||
"web",
|
||||
"web-auth",
|
||||
"web-docs",
|
||||
"web-oauth2",
|
||||
"web-metrics",
|
||||
]
|
||||
name = "ahriman"
|
||||
|
||||
[env.archive]
|
||||
description = "Create source files tarball"
|
||||
deps = [
|
||||
"build",
|
||||
]
|
||||
commands = [
|
||||
[
|
||||
"{envpython}",
|
||||
"-m", "build",
|
||||
"--sdist",
|
||||
],
|
||||
]
|
||||
|
||||
[env.check]
|
||||
description = "Run common checks like linter, mypy, etc"
|
||||
dependency_groups = [
|
||||
"check",
|
||||
]
|
||||
extras = [
|
||||
{ replace = "ref", of = ["project", "extras"], extend = true },
|
||||
]
|
||||
pip_pre = true
|
||||
set_env.CFLAGS = "-Wno-unterminated-string-initialization"
|
||||
set_env.MYPYPATH = "src"
|
||||
commands = [
|
||||
[
|
||||
"autopep8",
|
||||
{ replace = "ref", of = ["flags", "autopep8"], extend = true },
|
||||
"--exit-code",
|
||||
"--in-place",
|
||||
"--jobs", "0",
|
||||
"--recursive",
|
||||
"src/{[project]name}",
|
||||
"tests/{[project]name}",
|
||||
],
|
||||
[
|
||||
"pylint",
|
||||
{ replace = "ref", of = ["flags", "pylint"], extend = true },
|
||||
"src/{[project]name}",
|
||||
],
|
||||
[
|
||||
"bandit",
|
||||
{ replace = "ref", of = ["flags", "bandit"], extend = true },
|
||||
"--recursive",
|
||||
"src/{[project]name}",
|
||||
],
|
||||
[
|
||||
"bandit",
|
||||
{ replace = "ref", of = ["flags", "bandit"], extend = true },
|
||||
"--skip", "B101,B105,B106",
|
||||
"--recursive",
|
||||
"src/{[project]name}",
|
||||
],
|
||||
[
|
||||
"mypy",
|
||||
{ replace = "ref", of = ["flags", "mypy"], extend = true },
|
||||
"--install-types",
|
||||
"--non-interactive",
|
||||
"--package", "{[project]name}",
|
||||
],
|
||||
]
|
||||
|
||||
[env.docs]
|
||||
description = "Generate source files for documentation"
|
||||
dependency_groups = [
|
||||
"docs",
|
||||
]
|
||||
depends = [
|
||||
"version",
|
||||
]
|
||||
deps = [
|
||||
"uv",
|
||||
]
|
||||
dynamic_version = "{[project]name}.__version__"
|
||||
extras = [
|
||||
{ replace = "ref", of = ["project", "extras"], extend = true },
|
||||
]
|
||||
# TODO: steamline shlex usage after https://github.com/iterative/shtab/pull/192 merge
|
||||
handle_redirect = true
|
||||
pip_pre = true
|
||||
set_env.PYTHONPATH = "src"
|
||||
set_env.SPHINX_APIDOC_OPTIONS = "members,no-undoc-members,show-inheritance"
|
||||
commands = [
|
||||
[
|
||||
"shtab",
|
||||
{ replace = "ref", of = ["flags", "shtab"], extend = true },
|
||||
"--shell",
|
||||
"bash",
|
||||
">",
|
||||
"package/share/bash-completion/completions/_ahriman",
|
||||
],
|
||||
[
|
||||
"shtab",
|
||||
{ replace = "ref", of = ["flags", "shtab"], extend = true },
|
||||
"--shell",
|
||||
"zsh",
|
||||
">",
|
||||
"package/share/zsh/site-functions/_ahriman",
|
||||
],
|
||||
[
|
||||
"argparse-manpage",
|
||||
{ replace = "ref", of = ["flags", "manpage"], extend = true },
|
||||
"--module", "ahriman.application.ahriman",
|
||||
"--function", "_parser",
|
||||
"--output", "package/share/man/man1/ahriman.1",
|
||||
],
|
||||
[
|
||||
"pydeps",
|
||||
{ replace = "ref", of = ["flags", "pydeps"], extend = true },
|
||||
"--dot-output", "{tox_root}/docs/_static/architecture.dot",
|
||||
"--no-output",
|
||||
"--show-dot",
|
||||
"src/ahriman",
|
||||
],
|
||||
[
|
||||
"sphinx-apidoc",
|
||||
"--force",
|
||||
"--no-toc",
|
||||
"--output-dir", "docs",
|
||||
"src",
|
||||
],
|
||||
# compile list of dependencies for rtd.io
|
||||
[
|
||||
"uv",
|
||||
"pip",
|
||||
"compile",
|
||||
"--group", "pyproject.toml:docs",
|
||||
"--extra", "s3",
|
||||
"--extra", "validator",
|
||||
"--extra", "web",
|
||||
"--output-file", "docs/requirements.txt",
|
||||
"--quiet",
|
||||
"pyproject.toml",
|
||||
],
|
||||
]
|
||||
|
||||
[env.html]
|
||||
description = "Generate html documentation"
|
||||
dependency_groups = [
|
||||
"docs",
|
||||
]
|
||||
extras = [
|
||||
{ replace = "ref", of = ["project", "extras"], extend = true },
|
||||
]
|
||||
pip_pre = true
|
||||
recreate = true
|
||||
commands = [
|
||||
[
|
||||
"sphinx-build",
|
||||
"--builder", "html",
|
||||
"--fail-on-warning",
|
||||
"--jobs", "auto",
|
||||
"--write-all",
|
||||
"docs",
|
||||
"{envtmpdir}/html",
|
||||
],
|
||||
]
|
||||
|
||||
[env.publish]
|
||||
description = "Create and publish release to GitHub"
|
||||
allowlist_externals = [
|
||||
"git",
|
||||
]
|
||||
depends = [
|
||||
"docs",
|
||||
]
|
||||
pass_env = [
|
||||
"SSH_AUTH_SOCK",
|
||||
]
|
||||
commands = [
|
||||
[
|
||||
"git",
|
||||
"add",
|
||||
"package/archlinux/PKGBUILD",
|
||||
"src/ahriman/__init__.py",
|
||||
"docs/_static/architecture.dot",
|
||||
"package/share/man/man1/ahriman.1",
|
||||
"package/share/bash-completion/completions/_ahriman",
|
||||
"package/share/zsh/site-functions/_ahriman",
|
||||
],
|
||||
[
|
||||
"git",
|
||||
"commit",
|
||||
"--message", "Release {posargs}",
|
||||
],
|
||||
[
|
||||
"git",
|
||||
"tag",
|
||||
"{posargs}",
|
||||
],
|
||||
[
|
||||
"git",
|
||||
"push",
|
||||
],
|
||||
[
|
||||
"git",
|
||||
"push",
|
||||
"--tags",
|
||||
],
|
||||
]
|
||||
|
||||
[env.tests]
|
||||
description = "Run tests"
|
||||
dependency_groups = [
|
||||
"tests",
|
||||
]
|
||||
extras = [
|
||||
{ replace = "ref", of = ["project", "extras"], extend = true },
|
||||
]
|
||||
pip_pre = true
|
||||
set_env.CFLAGS = "-Wno-unterminated-string-initialization"
|
||||
commands = [
|
||||
[
|
||||
"pytest",
|
||||
{ replace = "posargs", extend = true },
|
||||
],
|
||||
]
|
||||
|
||||
[env.version]
|
||||
description = "Bump package version"
|
||||
allowlist_externals = [
|
||||
"sed",
|
||||
]
|
||||
deps = [
|
||||
"packaging",
|
||||
]
|
||||
commands = [
|
||||
# check if version is set and validate it
|
||||
[
|
||||
"{envpython}",
|
||||
"-c", "from packaging.version import Version; Version('{posargs}')",
|
||||
],
|
||||
[
|
||||
"sed",
|
||||
"--in-place",
|
||||
"s/^__version__ = .*/__version__ = \"{posargs}\"/",
|
||||
"src/ahriman/__init__.py",
|
||||
],
|
||||
[
|
||||
"sed",
|
||||
"--in-place",
|
||||
"s/pkgver=.*/pkgver={posargs}/",
|
||||
"package/archlinux/PKGBUILD",
|
||||
],
|
||||
]
|
128
toxfile.py
Normal file
128
toxfile.py
Normal file
@ -0,0 +1,128 @@
|
||||
#
|
||||
# Copyright (c) 2021-2025 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/>.
|
||||
#
|
||||
import importlib
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
from tox.config.sets import EnvConfigSet
|
||||
from tox.config.types import Command
|
||||
from tox.plugin import impl
|
||||
from tox.session.state import State
|
||||
from tox.tox_env.api import ToxEnv
|
||||
|
||||
|
||||
def _extract_version(env_conf: EnvConfigSet, python_path: str | None = None) -> dict[str, str]:
|
||||
"""
|
||||
extract version dynamically and set VERSION environment variable
|
||||
|
||||
Args:
|
||||
env_conf(EnvConfigSet): the core configuration object
|
||||
python_path(str | None): python path variable if available
|
||||
|
||||
Returns:
|
||||
dict[str, str]: environment variables which must be inserted
|
||||
"""
|
||||
import_path = env_conf["dynamic_version"]
|
||||
if not import_path:
|
||||
return {}
|
||||
|
||||
if python_path is not None:
|
||||
sys.path.append(python_path)
|
||||
|
||||
module_name, variable_name = import_path.rsplit(".", maxsplit=1)
|
||||
module = importlib.import_module(module_name)
|
||||
version = getattr(module, variable_name)
|
||||
|
||||
# reset import paths
|
||||
sys.path.pop()
|
||||
|
||||
return {"VERSION": version}
|
||||
|
||||
|
||||
def _wrap_commands(env_conf: EnvConfigSet, shell: str = "bash") -> None:
|
||||
"""
|
||||
wrap commands into shell if there is redirect
|
||||
|
||||
Args:
|
||||
env_conf(EnvConfigSet): the core configuration object
|
||||
shell(str, optional): shell command to use (Default value = "bash")
|
||||
"""
|
||||
if not env_conf["handle_redirect"]:
|
||||
return
|
||||
|
||||
# append shell just in case
|
||||
env_conf["allowlist_externals"].append(shell)
|
||||
|
||||
for command in env_conf["commands"]:
|
||||
if len(command.args) < 3: # command itself, redirect and output
|
||||
continue
|
||||
|
||||
redirect, output = command.args[-2:]
|
||||
if redirect not in (">", "2>", "&>"):
|
||||
continue
|
||||
|
||||
command.args = [
|
||||
shell,
|
||||
"-c",
|
||||
f"{Command(command.args[:-2]).shell} {redirect} {shlex.quote(output)}",
|
||||
]
|
||||
|
||||
|
||||
@impl
|
||||
def tox_add_env_config(env_conf: EnvConfigSet, state: State) -> None:
|
||||
"""
|
||||
add a command line argument. This is the first hook to be called,
|
||||
right after the logging setup and config source discovery.
|
||||
|
||||
Args:
|
||||
env_conf(EnvConfigSet): the core configuration object
|
||||
state(State): the global tox state object
|
||||
"""
|
||||
del state
|
||||
|
||||
env_conf.add_config(
|
||||
keys=["dynamic_version"],
|
||||
of_type=str,
|
||||
default="",
|
||||
desc="import path for the version variable",
|
||||
)
|
||||
env_conf.add_config(
|
||||
keys=["handle_redirect"],
|
||||
of_type=bool,
|
||||
default=False,
|
||||
desc="wrap commands to handle redirects if any",
|
||||
)
|
||||
|
||||
|
||||
@impl
|
||||
def tox_before_run_commands(tox_env: ToxEnv) -> None:
|
||||
"""
|
||||
called before the commands set is executed
|
||||
|
||||
Args:
|
||||
tox_env(ToxEnv): the tox environment being executed
|
||||
"""
|
||||
env_conf = tox_env.conf
|
||||
set_env = env_conf["set_env"]
|
||||
|
||||
python_path = set_env.load("PYTHONPATH") if "PYTHONPATH" in set_env else None
|
||||
set_env.update(_extract_version(env_conf, python_path))
|
||||
|
||||
_wrap_commands(env_conf)
|
Reference in New Issue
Block a user