Compare commits

...

20 Commits

Author SHA1 Message Date
b2ed383de0 Release 2.3.0rc3 2022-11-11 21:20:55 +02:00
551ee670bf rollback cwd parameter for clone 2022-11-11 21:19:27 +02:00
5d4bd9e459 Release 2.3.0rc2 2022-11-11 17:25:11 +02:00
4f21eb6fe6 Fix issue when there is no cached source directory yet (closes #75) 2022-11-11 17:23:15 +02:00
9a008ddafa Release 2.3.0rc1 2022-11-11 16:36:13 +02:00
0cd07afa0f use intersection of from_database and depends_on filters for the rebuild subcommand
Old logic used OR condition, i.e. if set from-database, it would ignore
the --depends-on flag. In new logic it calculates dependencies based on
the package list, which can be retrieved from database
2022-11-11 16:02:19 +02:00
f590136197 limit max module size and improve some help messages 2022-11-11 16:01:54 +02:00
1855e513a8 speedup docker build command by adding dockerignore listing 2022-11-11 12:47:31 +02:00
233dda8420 man update 2022-11-10 20:10:07 +02:00
9556779ee2 add exec to shell subcommand 2022-11-10 18:52:55 +02:00
8b32bd43db make exception optional argument for check_output method 2022-11-10 18:05:32 +02:00
0eadef597a replace several store_true keys to booleanoptionalaction alternative (#74) 2022-11-10 18:34:01 +03:00
e58ccdc8ad write .makepkg.conf to home dir instead of repository root (#72) 2022-11-08 17:02:13 +02:00
2a07356d24 add ability to use ahriman pacman database instead of system one (#71)
By default this feature is enabled. On the first run it will copy (if
exists) databases from filesystem to local cache (one per each
architecture). Later it will use this cache for all alpm operations. In
order to update this cache, some commands (mainly package building)
provide `-y`/`--refresh` option which has same semantics as pacman -Sy
does.

Note however that due to extending `Pacman` class some methods were
renamed in order to be more descriptive:
* `Pacman.all_packages` -> `Pacman.packages`
* `Pacman.get` -> `Pacman.package_get`

This commit also adds multilib repository to the default docker image
which was missed.
2022-11-08 17:26:51 +03:00
43c553a3db improve repo-setup command
* Move devtools executable to ahriman home, because we don't really
  need to use executable inside root
* Use named sudoers file instead of single file. It will allow easily to
  remove file as well as use setup command for several
  repositories/architectures
2022-11-06 14:25:44 +02:00
0d1a890bd0 fix case in which if no source PKGBUILDs were updated, an exception will occur 2022-11-03 03:39:54 +02:00
3aeaa8ce2c faq update 2022-11-02 17:15:17 +02:00
7cee859f19 docs update 2022-11-02 04:17:26 +02:00
fb6b22cdd7 change gitremote api to be same as report and upload
These changes are keeping fallback to old settings, but will allow to
run multiple git targets with different settings
2022-11-02 04:09:42 +02:00
4f35ec6542 fix update handler tests
If daemon tests are run before the update handler it causes test
failure, because there are other calls
2022-10-31 11:51:12 +02:00
157 changed files with 5650 additions and 4417 deletions

14
.dockerignore Normal file
View File

@ -0,0 +1,14 @@
.eggs/
.git/
.github/
.idea/
.mypy_cache/
.pytest_cache/
.tox/
.venv/
*.egg-info/
__pycache__/
*.pyc
*.pyd
*.pyo

View File

@ -149,7 +149,7 @@ indent-string=' '
max-line-length=100
# Maximum number of lines in a module.
max-module-lines=1000
max-module-lines=400
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.

View File

@ -1,6 +1,6 @@
# Contributing to ahriman
Welcome to ahriman! The goal of the project is to provide the best user experience to manage Archlinux repositories. In order to follow this we set some limitations for the issue creations and heavily restricted code contribution.
Welcome to ahriman! The goal of the project is to provide the best user experience to manage Arch linux repositories. In order to follow this we set some limitations for the issue creations and heavily restricted code contribution.
## Create an issue
@ -26,12 +26,13 @@ In order to resolve all difficult cases the `autopep8` is used. You can perform
Again, the most checks can be performed by `make check` command, though some additional guidelines must be applied:
* Every class, every function (including private and protected), every attribute must be documented. The project follows [Google style documentation](https://google.github.io/styleguide/pyguide.html). The only exception is local functions.
* Type annotations are the must, even for local functions.
* For any path interactions `pathlib.Path` must be used.
* Configuration interactions must go through `ahriman.core.configuration.Configuration` class instance.
* In case if class load requires some actions, it is recommended to create class method which can be used for class instantiating.
* The code must follow the exception safety, unless it is explicitly asked by end user. It means that most exceptions must be handled and printed to log, no other actions must be done (e.g. raising another exception).
* For the external command `ahriman.core.util.check_output` function must be used.
* Every temporary file/directory must be removed at the end of processing, no matter what. The ``tempfile`` module provides good ways to do it.
* Every temporary file/directory must be removed at the end of processing, no matter what. The `tempfile` module provides good ways to do it.
* Import order must be the following:
```python
@ -55,10 +56,10 @@ Again, the most checks can be performed by `make check` command, though some add
from ahriman.core.configuration import Configuration
```
* One file should define only one class, exception is class satellites in case if file length remain less than 200 lines.
* One file should define only one class, exception is class satellites in case if file length remains less than 400 lines.
* It is possible to create file which contains some functions (e.g. `ahriman.core.util`), but in this case you would need to define `__all__` attribute.
* The file size mentioned above must be applicable in general. In case of big classes consider splitting them into traits.
* No global variable allowed outside of `ahriman.version` module.
* The file size mentioned above must be applicable in general. In case of big classes consider splitting them into traits. Note, however, that `pylint` includes comments and docstrings into counter, thus you need to check file size by other tools.
* No global variable is allowed outside of `ahriman.version` module.
* Single quotes are not allowed. The reason behind this restriction is the fact that docstrings must be written by using double quotes only, and we would like to make style consistent.
* If your class writes anything to log, the `ahriman.core.lazy_logging.LazyLogging` trait must be used.

View File

@ -13,11 +13,14 @@ ENV AHRIMAN_REPOSITORY_ROOT="/var/lib/ahriman/ahriman"
ENV AHRIMAN_USER="ahriman"
# install environment
## update pacman.conf with multilib
RUN echo "[multilib]" >> "/etc/pacman.conf" && \
echo "Include = /etc/pacman.d/mirrorlist" >> "/etc/pacman.conf"
## install minimal required packages
RUN pacman --noconfirm -Syu binutils fakeroot git make sudo
## create build user
RUN useradd -m -d /home/build -s /usr/bin/nologin build && \
echo "build ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/build
RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build && \
echo "build ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/build"
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 537 KiB

After

Width:  |  Height:  |  Size: 613 KiB

View File

@ -1,9 +1,9 @@
.TH AHRIMAN "1" Manual
.TH AHRIMAN "1" "2022\-11\-11" "ahriman" "Generated Python Manual"
.SH NAME
ahriman
.SH SYNOPSIS
.B ahriman
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--no-report] [-q] [--unsafe] [-V] {aur-search,search,help,help-commands-unsafe,key-import,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-clean,clean,repo-config,config,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-setup,init,repo-init,setup,repo-sign,sign,repo-status-update,repo-sync,sync,repo-triggers,repo-update,update,shell,user-add,user-list,user-remove,version,web} ...
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--report | --no-report] [-q] [--unsafe] [-V] {aur-search,search,daemon,help,help-commands-unsafe,key-import,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-clean,clean,repo-config,config,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-setup,init,repo-init,setup,repo-sign,sign,repo-status-update,repo-sync,sync,repo-triggers,repo-update,update,shell,user-add,user-list,user-remove,version,web} ...
.SH DESCRIPTION
ArcH linux ReposItory MANager
@ -25,8 +25,8 @@ force run, remove file lock
lock file
.TP
\fB\-\-no\-report\fR
force disable reporting to web service
\fB\-\-report\fR, \fB\-\-no\-report\fR
force enable or disable reporting to web service (default: True)
.TP
\fB\-q\fR, \fB\-\-quiet\fR
@ -43,97 +43,100 @@ show program's version number and exit
.SH
COMMAND
.TP
\fBahriman\fR \fI\,aur-search\/\fR
\fBahriman\fR \fI\,aur\-search\/\fR
search for package
.TP
\fBahriman\fR \fI\,daemon\/\fR
run application as daemon
.TP
\fBahriman\fR \fI\,help\/\fR
show help message
.TP
\fBahriman\fR \fI\,help-commands-unsafe\/\fR
\fBahriman\fR \fI\,help\-commands\-unsafe\/\fR
list unsafe commands
.TP
\fBahriman\fR \fI\,key-import\/\fR
\fBahriman\fR \fI\,key\-import\/\fR
import PGP key
.TP
\fBahriman\fR \fI\,package-add\/\fR
\fBahriman\fR \fI\,package\-add\/\fR
add package
.TP
\fBahriman\fR \fI\,package-remove\/\fR
\fBahriman\fR \fI\,package\-remove\/\fR
remove package
.TP
\fBahriman\fR \fI\,package-status\/\fR
\fBahriman\fR \fI\,package\-status\/\fR
get package status
.TP
\fBahriman\fR \fI\,package-status-remove\/\fR
\fBahriman\fR \fI\,package\-status\-remove\/\fR
remove package status
.TP
\fBahriman\fR \fI\,package-status-update\/\fR
\fBahriman\fR \fI\,package\-status\-update\/\fR
update package status
.TP
\fBahriman\fR \fI\,patch-add\/\fR
\fBahriman\fR \fI\,patch\-add\/\fR
add patch for PKGBUILD function
.TP
\fBahriman\fR \fI\,patch-list\/\fR
\fBahriman\fR \fI\,patch\-list\/\fR
list patch sets
.TP
\fBahriman\fR \fI\,patch-remove\/\fR
\fBahriman\fR \fI\,patch\-remove\/\fR
remove patch set
.TP
\fBahriman\fR \fI\,patch-set-add\/\fR
\fBahriman\fR \fI\,patch\-set\-add\/\fR
add patch set
.TP
\fBahriman\fR \fI\,repo-backup\/\fR
\fBahriman\fR \fI\,repo\-backup\/\fR
backup repository data
.TP
\fBahriman\fR \fI\,repo-check\/\fR
\fBahriman\fR \fI\,repo\-check\/\fR
check for updates
.TP
\fBahriman\fR \fI\,repo-clean\/\fR
\fBahriman\fR \fI\,repo\-clean\/\fR
clean local caches
.TP
\fBahriman\fR \fI\,repo-config\/\fR
\fBahriman\fR \fI\,repo\-config\/\fR
dump configuration
.TP
\fBahriman\fR \fI\,repo-rebuild\/\fR
\fBahriman\fR \fI\,repo\-rebuild\/\fR
rebuild repository
.TP
\fBahriman\fR \fI\,repo-remove-unknown\/\fR
\fBahriman\fR \fI\,repo\-remove\-unknown\/\fR
remove unknown packages
.TP
\fBahriman\fR \fI\,repo-report\/\fR
\fBahriman\fR \fI\,repo\-report\/\fR
generate report
.TP
\fBahriman\fR \fI\,repo-restore\/\fR
\fBahriman\fR \fI\,repo\-restore\/\fR
restore repository data
.TP
\fBahriman\fR \fI\,repo-setup\/\fR
\fBahriman\fR \fI\,repo\-setup\/\fR
initial service configuration
.TP
\fBahriman\fR \fI\,repo-sign\/\fR
\fBahriman\fR \fI\,repo\-sign\/\fR
sign packages
.TP
\fBahriman\fR \fI\,repo-status-update\/\fR
\fBahriman\fR \fI\,repo\-status\-update\/\fR
update repository status
.TP
\fBahriman\fR \fI\,repo-sync\/\fR
\fBahriman\fR \fI\,repo\-sync\/\fR
sync repository
.TP
\fBahriman\fR \fI\,repo-triggers\/\fR
\fBahriman\fR \fI\,repo\-triggers\/\fR
run triggers
.TP
\fBahriman\fR \fI\,repo-update\/\fR
\fBahriman\fR \fI\,repo\-update\/\fR
update packages
.TP
\fBahriman\fR \fI\,shell\/\fR
envoke python shell
.TP
\fBahriman\fR \fI\,user-add\/\fR
\fBahriman\fR \fI\,user\-add\/\fR
create or update user
.TP
\fBahriman\fR \fI\,user-list\/\fR
\fBahriman\fR \fI\,user\-list\/\fR
user known users and their access
.TP
\fBahriman\fR \fI\,user-remove\/\fR
\fBahriman\fR \fI\,user\-remove\/\fR
remove user
.TP
\fBahriman\fR \fI\,version\/\fR
@ -141,33 +144,65 @@ application version
.TP
\fBahriman\fR \fI\,web\/\fR
web server
.SH COMMAND \fI\,'ahriman aur-search'\/\fR
usage: ahriman aur-search [-h] [-e] [-i]
[--sort-by {description,first_submitted,id,last_modified,maintainer,name,num_votes,out_of_date,package_base,package_base_id,popularity,repository,url,url_path,version}]
.SH COMMAND \fI\,'ahriman aur\-search'\/\fR
usage: ahriman aur\-search [\-h] [\-e] [\-\-info | \-\-no\-info]
[\-\-sort\-by {description,first_submitted,id,last_modified,maintainer,name,num_votes,out_of_date,package_base,package_base_id,popularity,repository,url,url_path,version}]
search [search ...]
search for package in AUR using API
.TP
\fBsearch\fR
search terms, can be specified multiple times, result will match all terms
search terms, can be specified multiple times, the result will match all terms
.SH OPTIONS \fI\,'ahriman aur-search'\/\fR
.SH OPTIONS \fI\,'ahriman aur\-search'\/\fR
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
.TP
\fB\-i\fR, \fB\-\-info\fR
show additional package information
\fB\-\-info\fR, \fB\-\-no\-info\fR
show additional package information (default: False)
.TP
\fB\-\-sort\-by\fR \fI\,{description,first_submitted,id,last_modified,maintainer,name,num_votes,out_of_date,package_base,package_base_id,popularity,repository,url,url_path,version}\/\fR
sort field by this field. In case if two packages have the same value of the specified field, they will be always sorted
by name
.SH COMMAND \fI\,'ahriman daemon'\/\fR
usage: ahriman daemon [\-h] [\-i INTERVAL] [\-\-aur | \-\-no\-aur] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual]
[\-\-vcs | \-\-no\-vcs] [\-y]
start process which periodically will run update process
.SH OPTIONS \fI\,'ahriman daemon'\/\fR
.TP
\fB\-i\fR \fI\,INTERVAL\/\fR, \fB\-\-interval\fR \fI\,INTERVAL\/\fR
interval between runs in seconds
.TP
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
enable or disable checking for AUR updates. Implies \-\-no\-vcs (default: True)
.TP
\fB\-\-local\fR, \fB\-\-no\-local\fR
enable or disable checking of local packages for updates (default: True)
.TP
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
include or exclude manual updates (default: True)
.TP
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
enable or disable checking of VCS packages (default: True)
.TP
\fB\-y\fR, \fB\-\-refresh\fR
download fresh package databases from the mirror before actions, \-yy to force refresh even if up to date
.SH COMMAND \fI\,'ahriman help'\/\fR
usage: ahriman help [-h] [command]
usage: ahriman help [\-h] [command]
show help message for application or command and exit
@ -175,19 +210,19 @@ show help message for application or command and exit
\fBcommand\fR
show help message for specific command
.SH COMMAND \fI\,'ahriman help-commands-unsafe'\/\fR
usage: ahriman help-commands-unsafe [-h] [--command COMMAND]
.SH COMMAND \fI\,'ahriman help\-commands\-unsafe'\/\fR
usage: ahriman help\-commands\-unsafe [\-h] [\-\-command COMMAND]
list unsafe commands as defined in default args
.SH OPTIONS \fI\,'ahriman help-commands-unsafe'\/\fR
.SH OPTIONS \fI\,'ahriman help\-commands\-unsafe'\/\fR
.TP
\fB\-\-command\fR \fI\,COMMAND\/\fR
instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1
otherwise
.SH COMMAND \fI\,'ahriman key-import'\/\fR
usage: ahriman key-import [-h] [--key-server KEY_SERVER] key
.SH COMMAND \fI\,'ahriman key\-import'\/\fR
usage: ahriman key\-import [\-h] [\-\-key\-server KEY_SERVER] key
import PGP key from public sources to the repository user
@ -195,14 +230,14 @@ import PGP key from public sources to the repository user
\fBkey\fR
PGP key to import from public server
.SH OPTIONS \fI\,'ahriman key-import'\/\fR
.SH OPTIONS \fI\,'ahriman key\-import'\/\fR
.TP
\fB\-\-key\-server\fR \fI\,KEY_SERVER\/\fR
key server for key import
.SH COMMAND \fI\,'ahriman package-add'\/\fR
usage: ahriman package-add [-h] [-e] [-n] [-s {auto,archive,aur,directory,local,remote,repository}]
[--without-dependencies]
.SH COMMAND \fI\,'ahriman package\-add'\/\fR
usage: ahriman package\-add [\-h] [\-e] [\-n] [\-y] [\-s {auto,archive,aur,directory,local,remote,repository}]
[\-\-without\-dependencies]
package [package ...]
add existing or new package to the build queue
@ -211,7 +246,7 @@ add existing or new package to the build queue
\fBpackage\fR
package source (base name, path to local files, remote URL)
.SH OPTIONS \fI\,'ahriman package-add'\/\fR
.SH OPTIONS \fI\,'ahriman package\-add'\/\fR
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
@ -220,6 +255,10 @@ return non\-zero exit status if result is empty
\fB\-n\fR, \fB\-\-now\fR
run update function after
.TP
\fB\-y\fR, \fB\-\-refresh\fR
download fresh package databases from the mirror before actions, \-yy to force refresh even if up to date
.TP
\fB\-s\fR \fI\,{auto,archive,aur,directory,local,remote,repository}\/\fR, \fB\-\-source\fR \fI\,{auto,archive,aur,directory,local,remote,repository}\/\fR
explicitly specify the package source for this command
@ -228,8 +267,8 @@ explicitly specify the package source for this command
\fB\-\-without\-dependencies\fR
do not add dependencies
.SH COMMAND \fI\,'ahriman package-remove'\/\fR
usage: ahriman package-remove [-h] package [package ...]
.SH COMMAND \fI\,'ahriman package\-remove'\/\fR
usage: ahriman package\-remove [\-h] package [package ...]
remove package from the repository
@ -237,8 +276,8 @@ remove package from the repository
\fBpackage\fR
package name or base
.SH COMMAND \fI\,'ahriman package-status'\/\fR
usage: ahriman package-status [-h] [--ahriman] [-e] [-i] [-s {unknown,pending,building,failed,success}] [package ...]
.SH COMMAND \fI\,'ahriman package\-status'\/\fR
usage: ahriman package\-status [\-h] [\-\-ahriman] [\-e] [\-i] [\-s {unknown,pending,building,failed,success}] [package ...]
request status of the package
@ -246,7 +285,7 @@ request status of the package
\fBpackage\fR
filter status by package base
.SH OPTIONS \fI\,'ahriman package-status'\/\fR
.SH OPTIONS \fI\,'ahriman package\-status'\/\fR
.TP
\fB\-\-ahriman\fR
get service status itself
@ -263,17 +302,17 @@ show additional package information
\fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
filter packages by status
.SH COMMAND \fI\,'ahriman package-status-remove'\/\fR
usage: ahriman package-status-remove [-h] package [package ...]
.SH COMMAND \fI\,'ahriman package\-status\-remove'\/\fR
usage: ahriman package\-status\-remove [\-h] package [package ...]
remove the package from the status page
.TP
\fBpackage\fR
remove specified packages
remove specified packages from status page
.SH COMMAND \fI\,'ahriman package-status-update'\/\fR
usage: ahriman package-status-update [-h] [-s {unknown,pending,building,failed,success}] [package ...]
.SH COMMAND \fI\,'ahriman package\-status\-update'\/\fR
usage: ahriman package\-status\-update [\-h] [\-s {unknown,pending,building,failed,success}] [package ...]
update package status on the status page
@ -281,13 +320,13 @@ update package status on the status page
\fBpackage\fR
set status for specified packages. If no packages supplied, service status will be updated
.SH OPTIONS \fI\,'ahriman package-status-update'\/\fR
.SH OPTIONS \fI\,'ahriman package\-status\-update'\/\fR
.TP
\fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
new status
new package build status
.SH COMMAND \fI\,'ahriman patch-add'\/\fR
usage: ahriman patch-add [-h] [-p PATCH] package variable
.SH COMMAND \fI\,'ahriman patch\-add'\/\fR
usage: ahriman patch\-add [\-h] package variable [patch]
create or update patched PKGBUILD function or variable
@ -299,13 +338,12 @@ package base
\fBvariable\fR
PKGBUILD variable or function name. If variable is a function, it must end with ()
.SH OPTIONS \fI\,'ahriman patch-add'\/\fR
.TP
\fB\-p\fR \fI\,PATCH\/\fR, \fB\-\-patch\fR \fI\,PATCH\/\fR
\fBpatch\fR
path to file which contains function or variable value. If not set, the value will be read from stdin
.SH COMMAND \fI\,'ahriman patch-list'\/\fR
usage: ahriman patch-list [-h] [-e] [-v VARIABLE] [package]
.SH COMMAND \fI\,'ahriman patch\-list'\/\fR
usage: ahriman patch\-list [\-h] [\-e] [\-v VARIABLE] [package]
list available patches for the package
@ -313,7 +351,7 @@ list available patches for the package
\fBpackage\fR
package base
.SH OPTIONS \fI\,'ahriman patch-list'\/\fR
.SH OPTIONS \fI\,'ahriman patch\-list'\/\fR
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
@ -322,8 +360,8 @@ return non\-zero exit status if result is empty
\fB\-v\fR \fI\,VARIABLE\/\fR, \fB\-\-variable\fR \fI\,VARIABLE\/\fR
if set, show only patches for specified PKGBUILD variables
.SH COMMAND \fI\,'ahriman patch-remove'\/\fR
usage: ahriman patch-remove [-h] [-v VARIABLE] package
.SH COMMAND \fI\,'ahriman patch\-remove'\/\fR
usage: ahriman patch\-remove [\-h] [\-v VARIABLE] package
remove patches for the package
@ -331,14 +369,14 @@ remove patches for the package
\fBpackage\fR
package base
.SH OPTIONS \fI\,'ahriman patch-remove'\/\fR
.SH OPTIONS \fI\,'ahriman patch\-remove'\/\fR
.TP
\fB\-v\fR \fI\,VARIABLE\/\fR, \fB\-\-variable\fR \fI\,VARIABLE\/\fR
should be used for single\-function patches in case if you wold like to remove only specified PKGBUILD variables. In case
if not set, it will remove all patches related to the package
.SH COMMAND \fI\,'ahriman patch-set-add'\/\fR
usage: ahriman patch-set-add [-h] [-t TRACK] package
.SH COMMAND \fI\,'ahriman patch\-set\-add'\/\fR
usage: ahriman patch\-set\-add [\-h] [\-t TRACK] package
create or update source patches
@ -346,74 +384,83 @@ create or update source patches
\fBpackage\fR
path to directory with changed files for patch addition/update
.SH OPTIONS \fI\,'ahriman patch-set-add'\/\fR
.SH OPTIONS \fI\,'ahriman patch\-set\-add'\/\fR
.TP
\fB\-t\fR \fI\,TRACK\/\fR, \fB\-\-track\fR \fI\,TRACK\/\fR
files which has to be tracked
.SH COMMAND \fI\,'ahriman repo-backup'\/\fR
usage: ahriman repo-backup [-h] path
.SH COMMAND \fI\,'ahriman repo\-backup'\/\fR
usage: ahriman repo\-backup [\-h] path
backup settings and database
backup repository settings and database
.TP
\fBpath\fR
path of the output archive
.SH COMMAND \fI\,'ahriman repo-check'\/\fR
usage: ahriman repo-check [-h] [-e] [--no-vcs] [package ...]
.SH COMMAND \fI\,'ahriman repo\-check'\/\fR
usage: ahriman repo\-check [\-h] [\-e] [\-\-vcs | \-\-no\-vcs] [\-y] [package ...]
check for packages updates. Same as update \-\-dry\-run \-\-no\-manual
check for packages updates. Same as repo\-update \-\-dry\-run \-\-no\-manual
.TP
\fBpackage\fR
filter check by package base
.SH OPTIONS \fI\,'ahriman repo-check'\/\fR
.SH OPTIONS \fI\,'ahriman repo\-check'\/\fR
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
.TP
\fB\-\-no\-vcs\fR
do not check VCS packages
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
enable or disable checking of VCS packages (default: True)
.SH COMMAND \fI\,'ahriman repo-clean'\/\fR
usage: ahriman repo-clean [-h] [--cache] [--chroot] [--manual] [--packages]
.TP
\fB\-y\fR, \fB\-\-refresh\fR
download fresh package databases from the mirror before actions, \-yy to force refresh even if up to date
.SH COMMAND \fI\,'ahriman repo\-clean'\/\fR
usage: ahriman repo\-clean [\-h] [\-\-cache | \-\-no\-cache] [\-\-chroot | \-\-no\-chroot] [\-\-manual | \-\-no\-manual]
[\-\-packages | \-\-no\-packages] [\-\-pacman | \-\-no\-pacman]
remove local caches
.SH OPTIONS \fI\,'ahriman repo-clean'\/\fR
.SH OPTIONS \fI\,'ahriman repo\-clean'\/\fR
.TP
\fB\-\-cache\fR
clear directory with package caches
\fB\-\-cache\fR, \fB\-\-no\-cache\fR
clear directory with package caches (default: False)
.TP
\fB\-\-chroot\fR
clear build chroot
\fB\-\-chroot\fR, \fB\-\-no\-chroot\fR
clear build chroot (default: False)
.TP
\fB\-\-manual\fR
clear manually added packages queue
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
clear manually added packages queue (default: False)
.TP
\fB\-\-packages\fR
clear directory with built packages
\fB\-\-packages\fR, \fB\-\-no\-packages\fR
clear directory with built packages (default: False)
.SH COMMAND \fI\,'ahriman repo-config'\/\fR
usage: ahriman repo-config [-h]
.TP
\fB\-\-pacman\fR, \fB\-\-no\-pacman\fR
clear directory with pacman local database cache (default: False)
.SH COMMAND \fI\,'ahriman repo\-config'\/\fR
usage: ahriman repo\-config [\-h]
dump configuration for the specified architecture
.SH COMMAND \fI\,'ahriman repo-rebuild'\/\fR
usage: ahriman repo-rebuild [-h] [--depends-on DEPENDS_ON] [--dry-run] [--from-database] [-e]
.SH COMMAND \fI\,'ahriman repo\-rebuild'\/\fR
usage: ahriman repo\-rebuild [\-h] [\-\-depends\-on DEPENDS_ON] [\-\-dry\-run] [\-\-from\-database] [\-e]
force rebuild whole repository
.SH OPTIONS \fI\,'ahriman repo-rebuild'\/\fR
.SH OPTIONS \fI\,'ahriman repo\-rebuild'\/\fR
.TP
\fB\-\-depends\-on\fR \fI\,DEPENDS_ON\/\fR
only rebuild packages that depend on specified package
only rebuild packages that depend on specified packages
.TP
\fB\-\-dry\-run\fR
@ -429,27 +476,23 @@ original ahriman instance run with web service and have run repo\-update at leas
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
.SH COMMAND \fI\,'ahriman repo-remove-unknown'\/\fR
usage: ahriman repo-remove-unknown [-h] [--dry-run] [-i]
.SH COMMAND \fI\,'ahriman repo\-remove\-unknown'\/\fR
usage: ahriman repo\-remove\-unknown [\-h] [\-\-dry\-run]
remove packages which are missing in AUR and do not have local PKGBUILDs
.SH OPTIONS \fI\,'ahriman repo-remove-unknown'\/\fR
.SH OPTIONS \fI\,'ahriman repo\-remove\-unknown'\/\fR
.TP
\fB\-\-dry\-run\fR
just perform check for packages without removal
.TP
\fB\-i\fR, \fB\-\-info\fR
show additional package information
.SH COMMAND \fI\,'ahriman repo-report'\/\fR
usage: ahriman repo-report [-h]
.SH COMMAND \fI\,'ahriman repo\-report'\/\fR
usage: ahriman repo\-report [\-h]
generate repository report according to current settings
.SH COMMAND \fI\,'ahriman repo-restore'\/\fR
usage: ahriman repo-restore [-h] [-o OUTPUT] path
.SH COMMAND \fI\,'ahriman repo\-restore'\/\fR
usage: ahriman repo\-restore [\-h] [\-o OUTPUT] path
restore settings and database
@ -457,20 +500,20 @@ restore settings and database
\fBpath\fR
path of the input archive
.SH OPTIONS \fI\,'ahriman repo-restore'\/\fR
.SH OPTIONS \fI\,'ahriman repo\-restore'\/\fR
.TP
\fB\-o\fR \fI\,OUTPUT\/\fR, \fB\-\-output\fR \fI\,OUTPUT\/\fR
root path of the extracted files
.SH COMMAND \fI\,'ahriman repo-setup'\/\fR
usage: ahriman repo-setup [-h] [--build-as-user BUILD_AS_USER] [--build-command BUILD_COMMAND]
[--from-configuration FROM_CONFIGURATION] [--no-multilib] --packager PACKAGER --repository
REPOSITORY [--sign-key SIGN_KEY] [--sign-target {disabled,pacakges,repository}]
[--web-port WEB_PORT]
.SH COMMAND \fI\,'ahriman repo\-setup'\/\fR
usage: ahriman repo\-setup [\-h] [\-\-build\-as\-user BUILD_AS_USER] [\-\-build\-command BUILD_COMMAND]
[\-\-from\-configuration FROM_CONFIGURATION] [\-\-multilib | \-\-no\-multilib] \-\-packager PACKAGER
\-\-repository REPOSITORY [\-\-sign\-key SIGN_KEY] [\-\-sign\-target {disabled,pacakges,repository}]
[\-\-web\-port WEB_PORT]
create initial service configuration, requires root
.SH OPTIONS \fI\,'ahriman repo-setup'\/\fR
.SH OPTIONS \fI\,'ahriman repo\-setup'\/\fR
.TP
\fB\-\-build\-as\-user\fR \fI\,BUILD_AS_USER\/\fR
force makepkg user to the specific one
@ -484,8 +527,8 @@ build command prefix
path to default devtools pacman configuration
.TP
\fB\-\-no\-multilib\fR
do not add multilib repository
\fB\-\-multilib\fR, \fB\-\-no\-multilib\fR
add or do not multilib repository (default: True)
.TP
\fB\-\-packager\fR \fI\,PACKAGER\/\fR
@ -507,8 +550,8 @@ sign options
\fB\-\-web\-port\fR \fI\,WEB_PORT\/\fR
port of the web service
.SH COMMAND \fI\,'ahriman repo-sign'\/\fR
usage: ahriman repo-sign [-h] [package ...]
.SH COMMAND \fI\,'ahriman repo\-sign'\/\fR
usage: ahriman repo\-sign [\-h] [package ...]
(re\-)sign packages and repository database according to current settings
@ -516,23 +559,23 @@ usage: ahriman repo-sign [-h] [package ...]
\fBpackage\fR
sign only specified packages
.SH COMMAND \fI\,'ahriman repo-status-update'\/\fR
usage: ahriman repo-status-update [-h] [-s {unknown,pending,building,failed,success}]
.SH COMMAND \fI\,'ahriman repo\-status\-update'\/\fR
usage: ahriman repo\-status\-update [\-h] [\-s {unknown,pending,building,failed,success}]
update repository status on the status page
.SH OPTIONS \fI\,'ahriman repo-status-update'\/\fR
.SH OPTIONS \fI\,'ahriman repo\-status\-update'\/\fR
.TP
\fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
new status
.SH COMMAND \fI\,'ahriman repo-sync'\/\fR
usage: ahriman repo-sync [-h]
.SH COMMAND \fI\,'ahriman repo\-sync'\/\fR
usage: ahriman repo\-sync [\-h]
sync repository files to remote server according to current settings
.SH COMMAND \fI\,'ahriman repo-triggers'\/\fR
usage: ahriman repo-triggers [-h] [trigger ...]
.SH COMMAND \fI\,'ahriman repo\-triggers'\/\fR
usage: ahriman repo\-triggers [\-h] [trigger ...]
run triggers on empty build result as configured by settings
@ -540,8 +583,10 @@ run triggers on empty build result as configured by settings
\fBtrigger\fR
instead of running all triggers as set by configuration, just process specified ones oin order of metion
.SH COMMAND \fI\,'ahriman repo-update'\/\fR
usage: ahriman repo-update [-h] [--dry-run] [-e] [--no-aur] [--no-local] [--no-manual] [--no-vcs] [package ...]
.SH COMMAND \fI\,'ahriman repo\-update'\/\fR
usage: ahriman repo\-update [\-h] [\-\-dry\-run] [\-e] [\-\-aur | \-\-no\-aur] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual]
[\-\-vcs | \-\-no\-vcs] [\-y]
[package ...]
check for packages updates and run build process if requested
@ -549,7 +594,7 @@ check for packages updates and run build process if requested
\fBpackage\fR
filter check by package base
.SH OPTIONS \fI\,'ahriman repo-update'\/\fR
.SH OPTIONS \fI\,'ahriman repo\-update'\/\fR
.TP
\fB\-\-dry\-run\fR
just perform check for updates, same as check command
@ -559,28 +604,36 @@ just perform check for updates, same as check command
return non\-zero exit status if result is empty
.TP
\fB\-\-no\-aur\fR
do not check for AUR updates. Implies \-\-no\-vcs
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
enable or disable checking for AUR updates. Implies \-\-no\-vcs (default: True)
.TP
\fB\-\-no\-local\fR
do not check local packages for updates
\fB\-\-local\fR, \fB\-\-no\-local\fR
enable or disable checking of local packages for updates (default: True)
.TP
\fB\-\-no\-manual\fR
do not include manual updates
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
include or exclude manual updates (default: True)
.TP
\fB\-\-no\-vcs\fR
do not check VCS packages
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
enable or disable checking of VCS packages (default: True)
.TP
\fB\-y\fR, \fB\-\-refresh\fR
download fresh package databases from the mirror before actions, \-yy to force refresh even if up to date
.SH COMMAND \fI\,'ahriman shell'\/\fR
usage: ahriman shell [-h]
usage: ahriman shell [\-h] [code]
drop into python shell while having created application
.SH COMMAND \fI\,'ahriman user-add'\/\fR
usage: ahriman user-add [-h] [--as-service] [-p PASSWORD] [-r {unauthorized,read,reporter,full}] [-s] username
.TP
\fBcode\fR
instead of dropping into shell, just execute the specified code
.SH COMMAND \fI\,'ahriman user\-add'\/\fR
usage: ahriman user\-add [\-h] [\-\-as\-service] [\-p PASSWORD] [\-r {unauthorized,read,reporter,full}] [\-s] username
update user for web services with the given password and role. In case if password was not entered it will be asked interactively
@ -588,7 +641,7 @@ update user for web services with the given password and role. In case if passwo
\fBusername\fR
username for web service
.SH OPTIONS \fI\,'ahriman user-add'\/\fR
.SH OPTIONS \fI\,'ahriman user\-add'\/\fR
.TP
\fB\-\-as\-service\fR
add user as service user
@ -606,8 +659,8 @@ user access level
\fB\-s\fR, \fB\-\-secure\fR
set file permissions to user\-only
.SH COMMAND \fI\,'ahriman user-list'\/\fR
usage: ahriman user-list [-h] [-e] [-r {unauthorized,read,reporter,full}] [username]
.SH COMMAND \fI\,'ahriman user\-list'\/\fR
usage: ahriman user\-list [\-h] [\-e] [\-r {unauthorized,read,reporter,full}] [username]
list users from the user mapping and their roles
@ -615,7 +668,7 @@ list users from the user mapping and their roles
\fBusername\fR
filter users by username
.SH OPTIONS \fI\,'ahriman user-list'\/\fR
.SH OPTIONS \fI\,'ahriman user\-list'\/\fR
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
@ -624,8 +677,8 @@ return non\-zero exit status if result is empty
\fB\-r\fR \fI\,{unauthorized,read,reporter,full}\/\fR, \fB\-\-role\fR \fI\,{unauthorized,read,reporter,full}\/\fR
filter users by role
.SH COMMAND \fI\,'ahriman user-remove'\/\fR
usage: ahriman user-remove [-h] [-s] username
.SH COMMAND \fI\,'ahriman user\-remove'\/\fR
usage: ahriman user\-remove [\-h] [\-s] username
remove user from the user mapping and update the configuration
@ -633,18 +686,18 @@ remove user from the user mapping and update the configuration
\fBusername\fR
username for web service
.SH OPTIONS \fI\,'ahriman user-remove'\/\fR
.SH OPTIONS \fI\,'ahriman user\-remove'\/\fR
.TP
\fB\-s\fR, \fB\-\-secure\fR
set file permissions to user\-only
.SH COMMAND \fI\,'ahriman version'\/\fR
usage: ahriman version [-h]
usage: ahriman version [\-h]
print application and its dependencies versions
.SH COMMAND \fI\,'ahriman web'\/\fR
usage: ahriman web [-h]
usage: ahriman web [\-h]
start web server
@ -654,7 +707,8 @@ Argument list can also be read from file by using @ prefix.
.SH AUTHORS
.nf
ahriman team
.fi.nf
.fi
.nf
.fi

View File

@ -28,6 +28,14 @@ ahriman.application.handlers.clean module
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.daemon module
------------------------------------------
.. automodule:: ahriman.application.handlers.daemon
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.dump module
----------------------------------------

View File

@ -4,6 +4,14 @@ ahriman.core.gitremote package
Submodules
----------
ahriman.core.gitremote.remote\_pull module
------------------------------------------
.. automodule:: ahriman.core.gitremote.remote_pull
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.gitremote.remote\_pull\_trigger module
---------------------------------------------------
@ -12,6 +20,14 @@ ahriman.core.gitremote.remote\_pull\_trigger module
:no-undoc-members:
:show-inheritance:
ahriman.core.gitremote.remote\_push module
------------------------------------------
.. automodule:: ahriman.core.gitremote.remote_push
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.gitremote.remote\_push\_trigger module
---------------------------------------------------

View File

@ -104,7 +104,7 @@ Type conversions
By default, it parses rows into python dictionary. In addition, the following pseudo-types are supported:
* ``Dict[str, Any]``, ``List[Any]`` - for storing JSON data structures in database (technically there is no restriction on types for dictionary keys and values, but it is recommended to use only string keys). The type is stored as ``json`` datatype and ``json.loads`` and ``json.dumps`` methods are used in order to read and write from/to database respectively.
* ``Dict[str, Any]``, ``List[Any]`` - for storing JSON data structures in database (technically there is no restriction on types for dictionary keys and values, but it is recommended to use only string keys). The type is stored as ``json`` data type and ``json.loads`` and ``json.dumps`` methods are used in order to read and write from/to database respectively.
Basic flows
-----------

View File

@ -26,9 +26,11 @@ Base configuration settings.
libalpm and AUR related configuration.
* ``database`` - path to pacman local database cache, string, required.
* ``database`` - path to pacman system database cache, string, required.
* ``mirror`` - package database mirror used by pacman for syncronization, string, required. This option supports standard pacman substitutions with ``$arch`` and ``$repo``. Note that the mentioned mirror should contain all repositories which are set by ``alpm.repositories`` option.
* ``repositories`` - list of pacman repositories, space separated list of strings, required.
* ``root`` - root for alpm library, string, required.
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands).
``auth`` group
--------------
@ -75,15 +77,33 @@ Settings for signing packages or repository. Group name can refer to architectur
* ``key`` - default PGP key, string, required. This key will also be used for database signing if enabled.
* ``key_*`` settings - PGP key which will be used for specific packages, string, optional. For example, if there is ``key_yay`` option the specified key will be used for yay package and default key for others.
``gitremote`` group
-------------------
``remote-pull`` group
---------------------
Remote git source synchronization settings. Unlike ``Upload`` triggers those triggers are used for PKGBUILD synchronization - e.g. fetch from remote repository PKGBUILDs before updating process or pulling updated PKGBUILDs to the remote repository.
Remote git source synchronization settings. Unlike ``Upload`` triggers those triggers are used for PKGBUILD synchronization - fetch from remote repository PKGBUILDs before updating process.
Both urls support authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details.
It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details.
* ``target`` - list of remote pull triggers to be used, space separated list of strings, optional, defaults to ``gitremote``. It must point to valid section (or to section with architecture), e.g. ``gitremote`` must point to either ``gitremote`` or ``gitremote:x86_64`` (the one with architecture has higher priority).
Remote pull trigger
^^^^^^^^^^^^^^^^^^^
* ``pull_url`` - url of the remote repository from which PKGBUILDs can be pulled before build process, string, required.
* ``pull_branch`` - branch of the remote repository from which PKGBUILDs can be pulled before build process, string, optional, default is ``master``.
``remote-push`` group
---------------------
Remote git source synchronization settings. Same as remote pull triggers those triggers are used for PKGBUILD synchronization - push updated PKGBUILDs to the remote repository after build process.
It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details.
* ``target`` - list of remote push triggers to be used, space separated list of strings, optional, defaults to ``gitremote``. It must point to valid section (or to section with architecture), e.g. ``gitremote`` must point to either ``gitremote`` or ``gitremote:x86_64`` (the one with architecture has higher priority).
Remote push trigger
^^^^^^^^^^^^^^^^^^^
* ``push_url`` - url of the remote repository to which PKGBUILDs should be pushed after build process, string, required.
* ``push_branch`` - branch of the remote repository to which PKGBUILDs should be pushed after build process, string, optional, default is ``master``.

View File

@ -1,16 +1,17 @@
FAQ
===
General topics
--------------
What is the purpose of the project?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
What is the purpose of the project
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This project has been created in order to maintain self-hosted Arch Linux user repository without manual intervention - checking for updates and building packages.
How do I install it?
^^^^^^^^^^^^^^^^^^^^
How to install ahriman
^^^^^^^^^^^^^^^^^^^^^^
TL;DR
@ -25,8 +26,8 @@ Long answer
The idea is to install the package as usual, create working directory tree, create configuration for ``sudo`` and ``devtools``. Detailed description of the setup instruction can be found :doc:`here <setup>`.
What does "architecture specific" mean? / How to configure for different architectures?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
What does "architecture specific" mean / How to configure for different architectures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Some sections can be configured per architecture. The service will merge architecture specific values into common settings. In order to specify settings for specific architecture you must point it in section name.
@ -46,8 +47,8 @@ states that default build command is ``extra-x86_64-build``. But if there is sec
the ``extra-i686-build`` command will be used for ``i686`` architecture.
How to use reporter/upload settings?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to generate build reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Normally you probably like to generate only one report for the specific type, e.g. only one email report. In order to do it you will need to have the following configuration:
@ -87,8 +88,8 @@ But for some cases you would like to have multiple different reports with the sa
type = email
...
Okay, I've installed ahriman, how do I add new package?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How do I add new package
^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: shell
@ -101,8 +102,8 @@ Okay, I've installed ahriman, how do I add new package?
sudo -u ahriman ahriman package-add ahriman
sudo -u ahriman ahriman repo-update
AUR is fine, but I would like to create package from local PKGBUILD
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to build package from local PKGBUILD
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TL;DR
@ -113,8 +114,8 @@ TL;DR
Before using this command you will need to create local directory, put ``PKGBUILD`` there and generate ``.SRCINFO`` by using ``makepkg --printsrcinfo > .SRCINFO`` command. These packages will be stored locally and *will be ignored* during automatic update; in order to update the package you will need to run ``package-add`` command again.
Err, I have remote repository with PKGBUILDs and would like to get versions from there automatically
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to fetch PKGBUILDs from remote repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For that purpose you could use ``RemotePullTrigger`` trigger. To do so you will need:
@ -131,13 +132,16 @@ For that purpose you could use ``RemotePullTrigger`` trigger. To do so you will
.. code-block:: ini
[remote-pull]
target = gitremote
[gitremote]
pull_url = https://github.com/username/repository
During the next application run it will fetch repository from the specified url and will try to find packages there which can be used as local sources.
I would like to push PKGBUILDs to the remote repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to push updated PKGBUILDs to remote repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For that purpose you'd need to use another trigger called ``RemotePushTrigger``. Configure it as following:
@ -154,13 +158,16 @@ For that purpose you'd need to use another trigger called ``RemotePushTrigger``.
.. code-block:: ini
[remote-push]
target = gitremote
[gitremote]
push_url = https://github.com/username/repository
Unlike ``RemotePullTrigger`` trigger, the ``RemotePushTrigger`` more likely will require authorization. It is highly recommended to use application tokens for that instead of using your password (e.g. for Github you can generate tokens `here <https://github.com/settings/tokens>`_ with scope ``public_repo``). Authorization can be supplied by using authorization part of the url, e.g. ``https://key:token@github.com/username/repository``.
But I just wanted to change PKGBUILD from AUR a bit!
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to change PKGBUILDs before build
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Well it is supported also. The recommended way is to patch specific function, e.g. by running ``sudo -u ahriman ahriman patch-add ahriman version``. This command will prompt for new value of the PKGBUILD variable ``version``. You can also write it to file and read from it ``sudo -u ahriman ahriman patch-add ahriman version version.patch``.
@ -172,8 +179,8 @@ Alternatively you can create full-diff patches, which are calculated by using ``
The last command will calculate diff from current tree to the ``HEAD`` and will store it locally. Patches will be applied on any package actions (e.g. it can be used for dependency management).
Hey, I would like to rebuild the official repository package
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to build package from official repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
So it is the same as adding any other package, but due to restrictions you must specify source explicitly, e.g.:
@ -192,17 +199,17 @@ TL;DR
sudo -u ahriman ahriman key-import ...
How do I check if there are new commits for VCS packages?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to update VCS packages
^^^^^^^^^^^^^^^^^^^^^^^^^^
Normally the service handles VCS packages correctly, but it requires additional dependencies:
Normally the service handles VCS packages correctly, however it requires additional dependencies:
.. code-block:: shell
pacman -S breezy darcs mercurial subversion
I would like to remove package because it is no longer needed/moved to official repositories
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to remove package
^^^^^^^^^^^^^^^^^^^^^
.. code-block:: shell
@ -212,8 +219,8 @@ Also, there is command ``repo-remove-unknown`` which checks packages in AUR and
Remove commands also remove any package files (patches, caches etc).
There is new major release of %library-name%, how do I rebuild packages?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to rebuild packages after library update
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TL;DR
@ -225,8 +232,8 @@ You can even rebuild the whole repository (which is particular useful in case if
However, note that you do not need to rebuild repository in case if you just changed signing option, just use ``repo-sign`` command instead.
Hmm, I have packages built, but how can I use it?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to install built packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to your ``pacman.conf``:
@ -237,8 +244,8 @@ Add the following lines to your ``pacman.conf``:
(You might need to add ``SigLevel`` option according to the pacman documentation.)
I would like to serve the repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to serve repository
^^^^^^^^^^^^^^^^^^^^^^^
Easy. For example, nginx configuration (without SSL) will look like:
@ -275,7 +282,7 @@ Example of the status page configuration is the following (status service is usi
Docker image
------------
We provide official images which can be found under ``arcan1s/ahriman`` repository. Docker image is being updated on each master commit as well as on each version. If you would like to use last (probably unstable) build you can use ``edge`` tag or ``latest`` for any tagged versions; otherwise you can use any version tag available.
We provide official images which can be found under ``arcan1s/ahriman`` repository. Docker image is being updated on each commit to master as well as on each version. If you would like to use last (probably unstable) build you can use ``edge`` tag or ``latest`` for any tagged versions; otherwise you can use any version tag available.
The default action (in case if no arguments provided) is ``repo-update``. Basically the idea is to run container, e.g.:
@ -336,11 +343,22 @@ For every next container run use arguments ``-e AHRIMAN_PORT=8080 --net=host``,
docker run --privileged -e AHRIMAN_PORT=8080 --net=host -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
Daemon service
^^^^^^^^^^^^^^
There is special subcommand which emulates systemd timer and will perform repository update periodically:
.. code-block:: shell
docker run arcan1s/ahriman:latest daemon
This command uses same rules as ``repo-update``, thus, e.g. requires ``--privileged`` flag.
Remote synchronization
----------------------
Wait I would like to use the repository from another server
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to sync repository to another server
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There are several choices:
@ -369,8 +387,8 @@ There are several choices:
After that just add ``/srv/repo`` to the ``pacman.conf`` as usual. You can also upload to S3 (e.g. ``Server = https://s3.eu-central-1.amazonaws.com/repository/x86_64``) or to Github (e.g. ``Server = https://github.com/ahriman/repository/releases/download/x86_64``).
How do I configure S3?
^^^^^^^^^^^^^^^^^^^^^^
How to sync to S3
^^^^^^^^^^^^^^^^^
#.
Install dependencies:
@ -428,8 +446,8 @@ How do I configure S3?
region = eu-central-1
secret_key = ...
How do I configure Github?
^^^^^^^^^^^^^^^^^^^^^^^^^^
How to sync to Github releases
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Create a repository.
@ -452,8 +470,8 @@ How do I configure Github?
Reporting
---------
I would like to get report to email
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to report by email
^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
@ -479,8 +497,8 @@ I would like to get report to email
sender = me@example.com
user = me@example.com
I'm using synchronization to S3 and would like to generate index page
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to generate index page for S3
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
@ -503,8 +521,8 @@ I'm using synchronization to S3 and would like to generate index page
After these steps ``index.html`` file will be automatically synced to S3
I would like to get messages to my telegram account/channel
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to post build report to telegram
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
It still requires additional dependencies:
@ -553,8 +571,8 @@ If you did everything fine you should receive the message with the next update.
Web service
-----------
Readme mentions web interface, how do I use it?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to setup web service
^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
@ -574,8 +592,8 @@ Readme mentions web interface, how do I use it?
#.
Start the web service ``systemctl enable --now ahriman-web@x86_64``.
I would like to limit user access to the status page
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to enable basic authorization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies 😊:
@ -606,8 +624,8 @@ I would like to limit user access to the status page
#. Restart web service ``systemctl restart ahriman-web@x86_64``.
I would like to use OAuth
^^^^^^^^^^^^^^^^^^^^^^^^^
How to enable OAuth authorization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Create OAuth web application, download its ``client_id`` and ``client_secret``.
@ -687,51 +705,55 @@ Other topics
How does it differ from %another-manager%?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Short answer - I do not know.
Short answer - I do not know. Also for some references credits to `Alad <https://github.com/AladW>`_, he `did <https://wiki.archlinux.org/title/User:Alad/Local_repo_tools>`_ really good investigation of existing alternatives.
`arch-repo-manager <https://github.com/Martchus/arch-repo-manager>`_
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Looks actually pretty good, in case if I would find it, I would probably didn't start this project, most of features (like web interface or additional helpers) are already implemented or planned to be. However, this project seems to be at early alpha stage (as for Nov 2022), written in C++ (not pro or con) and misses code documentation.
`archrepo2 <https://github.com/lilydjwg/archrepo2>`_
""""""""""""""""""""""""""""""""""""""""""""""""""""
Don't know, haven't tried it. But it lacks of documentation at least.
* Web interface.
* No synchronization and reporting.
* ``ahriman`` has web interface.
* ``archrepo2`` doesn't have synchronization and reporting.
* ``archrepo2`` actively uses direct shell calls and ``yaourt`` components.
* It has constantly running process instead of timer process (it is not pro or con).
* ``archrepo2`` has constantly running process instead of timer process (it is not pro or con).
`repoctl <https://github.com/cassava/repoctl>`_
"""""""""""""""""""""""""""""""""""""""""""""""
* Web interface.
* No reporting.
* Local packages and patches support.
* Some actions are not fully automated (e.g. package update still requires manual intervention for the build itself).
* ``ahriman`` has web interface.
* ``repoctl`` does not have reporting feature.
* ``repoctl`` does not support local packages and patches.
* Some actions are not fully automated in ``repoctl`` (e.g. package update still requires manual intervention for the build itself).
* ``repoctl`` has better AUR interaction features. With colors!
* ``repoctl`` has much easier configuration and even completion.
* ``repoctl`` is able to store old packages.
* Ability to host repository from same command vs external services (e.g. nginx) in ``ahriman``.
* Ability to host repository from same command in ``repoctl`` vs external services (e.g. nginx) in ``ahriman``.
`repo-scripts <https://github.com/arcan1s/repo-scripts>`_
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Though originally I've created ahriman by trying to improve the project, it still lacks a lot of features:
* Web interface.
* Better reporting with template support.
* Synchronization features (there was only ``rsync`` based).
* Local packages and patches support.
* No dependency management.
* And so on.
* ``ahriman`` has web interface.
* ``ahriman`` has better reporting with template support.
* ``ahriman`` has more synchronization features (there was only ``rsync`` based).
* ``ahriman`` supports local packages and patches.
* ``repo-scripts`` doesn't have dependency management.
``repo-scripts`` also have bad architecture and bad quality code and uses out-of-dated ``yaourt`` and ``package-query``.
...and so on. ``repo-scripts`` also has bad architecture and bad quality code and uses out-of-dated ``yaourt`` and ``package-query``.
`toolbox <https://github.com/chaotic-aur/toolbox>`_
"""""""""""""""""""""""""""""""""""""""""""""""""""
It is automation tools for ``repoctl`` mentioned above. Except for using shell it looks pretty cool and also offers some additional features like patches, remote synchronization (isn't it?) and reporting.
I would like to check service logs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to check service logs
^^^^^^^^^^^^^^^^^^^^^^^^^
By default, the service writes logs to ``/dev/log`` which can be accessed by using ``journalctl`` command (logs are written to the journal of the user under which command is run).

View File

@ -6,7 +6,7 @@ Initial setup
#.
Change settings if required, see :doc:`configuration reference <configuration>` for more details.
#.
TL;DR:
Perform initial setup:
.. code-block:: shell
@ -67,6 +67,8 @@ Initial setup
echo 'ahriman ALL=(ALL) NOPASSWD: CARCHBUILD_CMD' | tee -a /etc/sudoers.d/ahriman
chmod 400 /etc/sudoers.d/ahriman
This command supports several arguments, kindly refer to its help message.
#.
Start and enable ``ahriman@.timer`` via ``systemctl``:

View File

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

View File

@ -2,6 +2,6 @@
Description=ArcH linux ReposItory MANager (%I architecture)
[Service]
ExecStart=/usr/bin/ahriman --architecture %i update
ExecStart=/usr/bin/ahriman --architecture %i repo-update --refresh
User=ahriman
Group=ahriman

View File

@ -5,8 +5,10 @@ database = /var/lib/ahriman/ahriman.db
[alpm]
database = /var/lib/pacman
mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
repositories = core extra community multilib
root = /
use_ahriman_cache = yes
[auth]
target = disabled

View File

@ -17,6 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# pylint: disable=too-many-lines
import argparse
import sys
import tempfile
@ -73,7 +74,8 @@ def _parser() -> argparse.ArgumentParser:
parser.add_argument("--force", help="force run, remove file lock", action="store_true")
parser.add_argument("-l", "--lock", help="lock file", type=Path,
default=Path(tempfile.gettempdir()) / "ahriman.lock")
parser.add_argument("--no-report", help="force disable reporting to web service", action="store_true")
parser.add_argument("--report", help="force enable or disable reporting to web service",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-q", "--quiet", help="force disable any logging", action="store_true")
parser.add_argument("--unsafe", help="allow to run ahriman as non-ahriman user. Some actions might be unavailable",
action="store_true")
@ -131,14 +133,15 @@ def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
parser = root.add_parser("aur-search", aliases=["search"], help="search for package",
description="search for package in AUR using API", formatter_class=_formatter)
parser.add_argument("search", help="search terms, can be specified multiple times, result will match all terms",
parser.add_argument("search", help="search terms, can be specified multiple times, the result will match all terms",
nargs="+")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
parser.add_argument("--info", help="show additional package information",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--sort-by", help="sort field by this field. In case if two packages have the same value of "
"the specified field, they will be always sorted by name",
default="name", choices=sorted(handlers.Search.SORT_FIELDS))
parser.set_defaults(handler=handlers.Search, architecture=[""], lock=None, no_report=True, quiet=True, unsafe=True)
parser.set_defaults(handler=handlers.Search, architecture=[""], lock=None, report=False, quiet=True, unsafe=True)
return parser
@ -156,10 +159,17 @@ def _set_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="start process which periodically will run update process",
formatter_class=_formatter)
parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12)
parser.add_argument("--no-aur", help="do not check for AUR updates. Implies --no-vcs", action="store_true")
parser.add_argument("--no-local", help="do not check local packages for updates", action="store_true")
parser.add_argument("--no-manual", help="do not include manual updates", action="store_true")
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
parser.add_argument("--aur", help="enable or disable checking for AUR updates. Implies --no-vcs",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--local", help="enable or disable checking of local packages for updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--manual", help="include or exclude manual updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--vcs", help="enable or disable checking of VCS packages",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
"-yy to force refresh even if up to date",
action="count", default=0)
parser.set_defaults(handler=handlers.Daemon, dry_run=False, exit_code=False, package=[])
return parser
@ -178,7 +188,7 @@ def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="show help message for application or command and exit",
formatter_class=_formatter)
parser.add_argument("command", help="show help message for specific command", nargs="?")
parser.set_defaults(handler=handlers.Help, architecture=[""], lock=None, no_report=True, quiet=True,
parser.set_defaults(handler=handlers.Help, architecture=[""], lock=None, report=False, quiet=True,
unsafe=True, parser=_parser)
return parser
@ -197,7 +207,7 @@ def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.Argument
description="list unsafe commands as defined in default args", formatter_class=_formatter)
parser.add_argument("--command", help="instead of showing commands, just test command line for unsafe subcommand "
"and return 0 in case if command is safe and 1 otherwise")
parser.set_defaults(handler=handlers.UnsafeCommands, architecture=[""], lock=None, no_report=True, quiet=True,
parser.set_defaults(handler=handlers.UnsafeCommands, architecture=[""], lock=None, report=False, quiet=True,
unsafe=True, parser=_parser)
return parser
@ -221,7 +231,7 @@ def _set_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=_formatter)
parser.add_argument("--key-server", help="key server for key import", default="pgp.mit.edu")
parser.add_argument("key", help="PGP key to import from public server")
parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, no_report=True)
parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, report=False)
return parser
@ -251,6 +261,9 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-n", "--now", help="run update function after", action="store_true")
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
"-yy to force refresh even if up to date",
action="count", default=0)
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
parser.add_argument("--without-dependencies", help="do not add dependencies", action="store_true")
@ -295,7 +308,7 @@ def _set_package_status_parser(root: SubParserAction) -> argparse.ArgumentParser
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
parser.add_argument("-s", "--status", help="filter packages by status",
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
parser.set_defaults(handler=handlers.Status, lock=None, no_report=True, quiet=True, unsafe=True)
parser.set_defaults(handler=handlers.Status, lock=None, report=False, quiet=True, unsafe=True)
return parser
@ -314,8 +327,8 @@ def _set_package_status_remove_parser(root: SubParserAction) -> argparse.Argumen
epilog="Please note that this subcommand does not remove the package itself, it just "
"clears the status page.",
formatter_class=_formatter)
parser.add_argument("package", help="remove specified packages", nargs="+")
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Remove, lock=None, no_report=True, quiet=True,
parser.add_argument("package", help="remove specified packages from status page", nargs="+")
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Remove, lock=None, report=False, quiet=True,
unsafe=True)
return parser
@ -335,9 +348,9 @@ def _set_package_status_update_parser(root: SubParserAction) -> argparse.Argumen
parser.add_argument("package", help="set status for specified packages. "
"If no packages supplied, service status will be updated",
nargs="*")
parser.add_argument("-s", "--status", help="new status",
parser.add_argument("-s", "--status", help="new package build status",
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, no_report=True, quiet=True,
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, report=False, quiet=True,
unsafe=True)
return parser
@ -354,9 +367,9 @@ def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
parser = root.add_parser("patch-add", help="add patch for PKGBUILD function",
description="create or update patched PKGBUILD function or variable",
epilog="Unlike ``patch-set-add``, this function allows to patch only one PKGBUILD f"
"unction, e.g. typing ``ahriman patch-add ahriman version`` it will change the "
"``version`` inside PKGBUILD, typing ``ahriman patch-add ahriman build()`` "
epilog="Unlike ``patch-set-add``, this function allows to patch only one PKGBUILD "
"function, e.g. typing ``ahriman patch-add ahriman pkgver`` it will change the "
"``pkgver`` inside PKGBUILD, typing ``ahriman patch-add ahriman build()`` "
"it will change ``build()`` function inside PKGBUILD",
formatter_class=_formatter)
parser.add_argument("package", help="package base")
@ -364,7 +377,7 @@ def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
"it must end with ()")
parser.add_argument("patch", help="path to file which contains function or variable value. If not set, "
"the value will be read from stdin", type=Path, nargs="?")
parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, no_report=True)
parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, report=False)
return parser
@ -384,7 +397,7 @@ def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables",
action="append")
parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture=[""], lock=None, no_report=True)
parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture=[""], lock=None, report=False)
return parser
@ -405,7 +418,7 @@ def _set_patch_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
"to remove only specified PKGBUILD variables. In case if not set, "
"it will remove all patches related to the package",
action="append")
parser.set_defaults(handler=handlers.Patch, action=Action.Remove, architecture=[""], lock=None, no_report=True)
parser.set_defaults(handler=handlers.Patch, action=Action.Remove, architecture=[""], lock=None, report=False)
return parser
@ -429,7 +442,7 @@ def _set_patch_set_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("package", help="path to directory with changed files for patch addition/update", type=Path)
parser.add_argument("-t", "--track", help="files which has to be tracked", action="append",
default=["*.diff", "*.patch"])
parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, no_report=True,
parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, report=False,
variable=None)
return parser
@ -445,9 +458,9 @@ def _set_repo_backup_parser(root: SubParserAction) -> argparse.ArgumentParser:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-backup", help="backup repository data",
description="backup settings and database", formatter_class=_formatter)
description="backup repository settings and database", formatter_class=_formatter)
parser.add_argument("path", help="path of the output archive", type=Path)
parser.set_defaults(handler=handlers.Backup, architecture=[""], lock=None, no_report=True, unsafe=True)
parser.set_defaults(handler=handlers.Backup, architecture=[""], lock=None, report=False, unsafe=True)
return parser
@ -462,12 +475,16 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-check", aliases=["check"], help="check for updates",
description="check for packages updates. Same as update --dry-run --no-manual",
description="check for packages updates. Same as repo-update --dry-run --no-manual",
formatter_class=_formatter)
parser.add_argument("package", help="filter check by package base", nargs="*")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
parser.set_defaults(handler=handlers.Update, dry_run=True, no_aur=False, no_local=False, no_manual=True)
parser.add_argument("--vcs", help="enable or disable checking of VCS packages",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
"-yy to force refresh even if up to date",
action="count", default=0)
parser.set_defaults(handler=handlers.Update, dry_run=True, aur=True, local=True, manual=False)
return parser
@ -487,10 +504,15 @@ def _set_repo_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
"you should not run this command manually. Also in case if you are going to clear "
"the chroot directories you will need root privileges.",
formatter_class=_formatter)
parser.add_argument("--cache", help="clear directory with package caches", action="store_true")
parser.add_argument("--chroot", help="clear build chroot", action="store_true")
parser.add_argument("--manual", help="clear manually added packages queue", action="store_true")
parser.add_argument("--packages", help="clear directory with built packages", action="store_true")
parser.add_argument("--cache", help="clear directory with package caches",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--chroot", help="clear build chroot", action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--manual", help="clear manually added packages queue",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--packages", help="clear directory with built packages",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--pacman", help="clear directory with pacman local database cache",
action=argparse.BooleanOptionalAction, default=False)
parser.set_defaults(handler=handlers.Clean, quiet=True, unsafe=True)
return parser
@ -508,7 +530,7 @@ def _set_repo_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser = root.add_parser("repo-config", aliases=["config"], help="dump configuration",
description="dump configuration for the specified architecture",
formatter_class=_formatter)
parser.set_defaults(handler=handlers.Dump, lock=None, no_report=True, quiet=True, unsafe=True)
parser.set_defaults(handler=handlers.Dump, lock=None, report=False, quiet=True, unsafe=True)
return parser
@ -524,7 +546,7 @@ def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
parser = root.add_parser("repo-rebuild", aliases=["rebuild"], help="rebuild repository",
description="force rebuild whole repository", formatter_class=_formatter)
parser.add_argument("--depends-on", help="only rebuild packages that depend on specified package", action="append")
parser.add_argument("--depends-on", help="only rebuild packages that depend on specified packages", action="append")
parser.add_argument("--dry-run", help="just perform check for packages without rebuild process itself",
action="store_true")
parser.add_argument("--from-database",
@ -552,7 +574,6 @@ def _set_repo_remove_unknown_parser(root: SubParserAction) -> argparse.ArgumentP
description="remove packages which are missing in AUR and do not have local PKGBUILDs",
formatter_class=_formatter)
parser.add_argument("--dry-run", help="just perform check for packages without removal", action="store_true")
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
parser.set_defaults(handler=handlers.RemoveUnknown)
return parser
@ -589,7 +610,7 @@ def _set_repo_restore_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="restore settings and database", formatter_class=_formatter)
parser.add_argument("path", help="path of the input archive", type=Path)
parser.add_argument("-o", "--output", help="root path of the extracted files", type=Path, default=Path("/"))
parser.set_defaults(handler=handlers.Restore, architecture=[""], lock=None, no_report=True, unsafe=True)
parser.set_defaults(handler=handlers.Restore, architecture=[""], lock=None, report=False, unsafe=True)
return parser
@ -611,14 +632,15 @@ def _set_repo_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("--build-command", help="build command prefix", default="ahriman")
parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
type=Path, default=Path("/usr/share/devtools/pacman-extra.conf"))
parser.add_argument("--no-multilib", help="do not add multilib repository", action="store_true")
parser.add_argument("--multilib", help="add or do not multilib repository",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--packager", help="packager name and email", required=True)
parser.add_argument("--repository", help="repository name", required=True)
parser.add_argument("--sign-key", help="sign key id")
parser.add_argument("--sign-target", help="sign options", action="append",
type=SignSettings.from_option, choices=enum_values(SignSettings))
parser.add_argument("--web-port", help="port of the web service", type=int)
parser.set_defaults(handler=handlers.Setup, lock=None, no_report=True, quiet=True, unsafe=True)
parser.set_defaults(handler=handlers.Setup, lock=None, report=False, quiet=True, unsafe=True)
return parser
@ -655,7 +677,7 @@ def _set_repo_status_update_parser(root: SubParserAction) -> argparse.ArgumentPa
description="update repository status on the status page", formatter_class=_formatter)
parser.add_argument("-s", "--status", help="new status",
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, no_report=True, package=[],
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, report=False, package=[],
quiet=True, unsafe=True)
return parser
@ -713,10 +735,17 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("package", help="filter check by package base", nargs="*")
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("--no-aur", help="do not check for AUR updates. Implies --no-vcs", action="store_true")
parser.add_argument("--no-local", help="do not check local packages for updates", action="store_true")
parser.add_argument("--no-manual", help="do not include manual updates", action="store_true")
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
parser.add_argument("--aur", help="enable or disable checking for AUR updates. Implies --no-vcs",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--local", help="enable or disable checking of local packages for updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--manual", help="include or exclude manual updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--vcs", help="enable or disable checking of VCS packages",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
"-yy to force refresh even if up to date",
action="count", default=0)
parser.set_defaults(handler=handlers.Update)
return parser
@ -734,8 +763,9 @@ def _set_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser = root.add_parser("shell", help="envoke python shell",
description="drop into python shell while having created application",
formatter_class=_formatter)
parser.add_argument("code", help="instead of dropping into shell, just execute the specified code", nargs="?")
parser.add_argument("-v", "--verbose", help=argparse.SUPPRESS, action="store_true")
parser.set_defaults(handler=handlers.Shell, lock=None, no_report=True)
parser.set_defaults(handler=handlers.Shell, lock=None, report=False)
return parser
@ -760,7 +790,7 @@ def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("-r", "--role", help="user access level",
type=UserAccess, choices=enum_values(UserAccess), default=UserAccess.Read)
parser.add_argument("-s", "--secure", help="set file permissions to user-only", action="store_true")
parser.set_defaults(handler=handlers.Users, action=Action.Update, architecture=[""], lock=None, no_report=True,
parser.set_defaults(handler=handlers.Users, action=Action.Update, architecture=[""], lock=None, report=False,
quiet=True, unsafe=True)
return parser
@ -781,7 +811,7 @@ def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("username", help="filter users by username", nargs="?")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-r", "--role", help="filter users by role", type=UserAccess, choices=enum_values(UserAccess))
parser.set_defaults(handler=handlers.Users, action=Action.List, architecture=[""], lock=None, no_report=True, # nosec
parser.set_defaults(handler=handlers.Users, action=Action.List, architecture=[""], lock=None, report=False, # nosec
password="", quiet=True, unsafe=True)
return parser
@ -801,7 +831,7 @@ def _set_user_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=_formatter)
parser.add_argument("username", help="username for web service")
parser.add_argument("-s", "--secure", help="set file permissions to user-only", action="store_true")
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, no_report=True, # nosec
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, report=False, # nosec
password="", quiet=True, unsafe=True)
return parser
@ -818,7 +848,7 @@ def _set_version_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
parser = root.add_parser("version", help="application version",
description="print application and its dependencies versions", formatter_class=_formatter)
parser.set_defaults(handler=handlers.Versions, architecture=[""], lock=None, no_report=True, quiet=True,
parser.set_defaults(handler=handlers.Versions, architecture=[""], lock=None, report=False, quiet=True,
unsafe=True)
return parser
@ -834,7 +864,7 @@ def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("web", help="web server", description="start web server", formatter_class=_formatter)
parser.set_defaults(handler=handlers.Web, lock=None, no_report=True, parser=_parser)
parser.set_defaults(handler=handlers.Web, lock=None, report=False, parser=_parser)
return parser

View File

@ -36,12 +36,12 @@ class Application(ApplicationPackages, ApplicationRepository):
>>> from ahriman.models.package_source import PackageSource
>>>
>>> configuration = Configuration()
>>> application = Application("x86_64", configuration, no_report=False, unsafe=False)
>>> application = Application("x86_64", configuration, report=True, unsafe=False)
>>> # add packages to build queue
>>> application.add(["ahriman"], PackageSource.AUR, without_dependencies=False)
>>>
>>> # check for updates
>>> updates = application.updates([], no_aur=False, no_local=False, no_manual=False, no_vcs=False, log_fn=print)
>>> updates = application.updates([], aur=True, local=True, manual=True, vcs=True, log_fn=print)
>>> # updates for specified packages
>>> application.update(updates)
@ -62,7 +62,7 @@ class Application(ApplicationPackages, ApplicationRepository):
for package, properties in base.packages.items():
known_packages.add(package)
known_packages.update(properties.provides)
known_packages.update(self.repository.pacman.all_packages())
known_packages.update(self.repository.pacman.packages())
return known_packages
def on_result(self, result: Result) -> None:

View File

@ -34,17 +34,20 @@ class ApplicationProperties(LazyLogging):
repository(Repository): repository instance
"""
def __init__(self, architecture: str, configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def __init__(self, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> None:
"""
default constructor
Args:
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
refresh_pacman_database(int): pacman database syncronization level, ``0`` is disabled
"""
self.configuration = configuration
self.architecture = architecture
self.database = SQLite.load(configuration)
self.repository = Repository(architecture, configuration, self.database, no_report, unsafe)
self.repository = Repository(architecture, configuration, self.database,
report=report, unsafe=unsafe, refresh_pacman_database=refresh_pacman_database)

View File

@ -47,7 +47,7 @@ class ApplicationRepository(ApplicationProperties):
"""
raise NotImplementedError
def clean(self, cache: bool, chroot: bool, manual: bool, packages: bool) -> None:
def clean(self, *, cache: bool, chroot: bool, manual: bool, packages: bool, pacman: bool) -> None:
"""
run all clean methods. Warning: some functions might not be available under non-root
@ -56,6 +56,7 @@ class ApplicationRepository(ApplicationProperties):
chroot(bool): clear build chroot
manual(bool): clear directory with manually added packages
packages(bool): clear directory with built packages
pacman(bool): clear directory with pacman databases
"""
if cache:
self.repository.clear_cache()
@ -65,6 +66,8 @@ class ApplicationRepository(ApplicationProperties):
self.repository.clear_queue()
if packages:
self.repository.clear_packages()
if pacman:
self.repository.clear_pacman()
def sign(self, packages: Iterable[str]) -> None:
"""
@ -156,17 +159,17 @@ class ApplicationRepository(ApplicationProperties):
return build_result
def updates(self, filter_packages: Iterable[str], no_aur: bool, no_local: bool, no_manual: bool, no_vcs: bool,
log_fn: Callable[[str], None]) -> List[Package]:
def updates(self, filter_packages: Iterable[str], *,
aur: bool, local: bool, manual: bool, vcs: bool, log_fn: Callable[[str], None]) -> List[Package]:
"""
get list of packages to run update process
Args:
filter_packages(Iterable[str]): do not check every package just specified in the list
no_aur(bool): do not check for aur updates
no_local(bool): do not check local packages for updates
no_manual(bool): do not check for manual updates
no_vcs(bool): do not check VCS packages
aur(bool): enable or disable checking for AUR updates
local(bool): enable or disable checking of local packages for updates
manual(bool): include or exclude manual updates
vcs(bool): enable or disable checking of VCS packages
log_fn(Callable[[str], None]): logger function to log updates
Returns:
@ -174,11 +177,11 @@ class ApplicationRepository(ApplicationProperties):
"""
updates = {}
if not no_aur:
updates.update({package.base: package for package in self.repository.updates_aur(filter_packages, no_vcs)})
if not no_local:
if aur:
updates.update({package.base: package for package in self.repository.updates_aur(filter_packages, vcs=vcs)})
if local:
updates.update({package.base: package for package in self.repository.updates_local()})
if not no_manual:
if manual:
updates.update({package.base: package for package in self.repository.updates_manual()})
local_versions = {package.base: package.version for package in self.repository.packages()}

View File

@ -32,8 +32,8 @@ class Add(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -41,15 +41,17 @@ class Add(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration,
report=report, unsafe=unsafe, refresh_pacman_database=args.refresh)
application.on_start()
application.add(args.package, args.source, args.without_dependencies)
if not args.now:
return
packages = application.updates(args.package, True, True, False, True, application.logger.info)
packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False,
log_fn=application.logger.info)
result = application.update(packages)
Add.check_if_empty(args.exit_code, result.is_empty)

View File

@ -37,8 +37,8 @@ class Backup(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -46,7 +46,7 @@ class Backup(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
backup_paths = Backup.get_paths(configuration)

View File

@ -32,8 +32,8 @@ class Clean(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -41,9 +41,10 @@ class Clean(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application.on_start()
application.clean(args.cache, args.chroot, args.manual, args.packages)
application.clean(cache=args.cache, chroot=args.chroot, manual=args.manual, packages=args.packages,
pacman=args.pacman)

View File

@ -32,8 +32,8 @@ class Daemon(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -41,11 +41,12 @@ class Daemon(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
from ahriman.application.handlers import Update
Update.run(args, architecture, configuration, no_report, unsafe)
timer = threading.Timer(args.interval, Daemon.run, (args, architecture, configuration, no_report, unsafe))
Update.run(args, architecture, configuration, report=report, unsafe=unsafe)
timer = threading.Timer(args.interval, Daemon.run, args=[args, architecture, configuration],
kwargs={"report": report, "unsafe": unsafe})
timer.start()
timer.join()

View File

@ -34,8 +34,8 @@ class Dump(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -43,7 +43,7 @@ class Dump(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
dump = configuration.dump()

View File

@ -27,7 +27,7 @@ from typing import List, Type
from ahriman.application.lock import Lock
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ExitCode, MissingArchitecture, MultipleArchitectures
from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError
from ahriman.models.repository_paths import RepositoryPaths
@ -67,7 +67,7 @@ class Handler:
if not cls.ALLOW_AUTO_ARCHITECTURE_RUN and args.architecture is None:
# for some parsers (e.g. config) we need to run with specific architecture
# for those cases architecture must be set explicitly
raise MissingArchitecture(args.command)
raise MissingArchitectureError(args.command)
if args.architecture: # architecture is specified explicitly
return sorted(set(args.architecture))
@ -78,7 +78,7 @@ class Handler:
architectures = RepositoryPaths.known_architectures(root)
if not architectures: # well we did not find anything
raise MissingArchitecture(args.command)
raise MissingArchitectureError(args.command)
return sorted(architectures)
@classmethod
@ -96,7 +96,7 @@ class Handler:
try:
configuration = Configuration.from_path(args.configuration, architecture, args.quiet)
with Lock(args, architecture, configuration):
cls.run(args, architecture, configuration, args.no_report, args.unsafe)
cls.run(args, architecture, configuration, report=args.report, unsafe=args.unsafe)
return True
except ExitCode:
return False
@ -124,7 +124,7 @@ class Handler:
# actually we do not have to spawn another process if it is single-process application, do we?
if len(architectures) > 1:
if not cls.ALLOW_MULTI_ARCHITECTURE_RUN:
raise MultipleArchitectures(args.command)
raise MultipleArchitecturesError(args.command)
with Pool(len(architectures)) as pool:
result = pool.starmap(
@ -135,8 +135,8 @@ class Handler:
return 0 if all(result) else 1
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -144,7 +144,7 @@ class Handler:
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
Raises:

View File

@ -33,8 +33,8 @@ class Help(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -42,7 +42,7 @@ class Help(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
parser: argparse.ArgumentParser = args.parser()

View File

@ -34,8 +34,8 @@ class KeyImport(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -43,8 +43,8 @@ class KeyImport(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application.repository.sign.key_import(args.key_server, args.key)

View File

@ -39,8 +39,8 @@ class Patch(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -48,10 +48,10 @@ class Patch(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application.on_start()
if args.action == Action.Update and args.variable is not None:

View File

@ -34,8 +34,8 @@ class Rebuild(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -43,18 +43,16 @@ class Rebuild(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
depends_on = set(args.depends_on) if args.depends_on else None
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application.on_start()
if args.from_database:
updates = Rebuild.extract_packages(application)
else:
updates = application.repository.packages_depend_on(depends_on)
packages = Rebuild.extract_packages(application, from_database=args.from_database)
updates = application.repository.packages_depend_on(packages, depends_on)
Rebuild.check_if_empty(args.exit_code, not updates)
if args.dry_run:
@ -66,14 +64,17 @@ class Rebuild(Handler):
Rebuild.check_if_empty(args.exit_code, result.is_empty)
@staticmethod
def extract_packages(application: Application) -> List[Package]:
def extract_packages(application: Application, *, from_database: bool) -> List[Package]:
"""
extract packages from database file
Args:
application(Application): application instance
from_database(bool): extract packages from database instead of repository filesystem
Returns:
List[Package]: list of packages which were stored in database
"""
if from_database:
return application.repository.packages()
return [package for (package, _) in application.database.packages_get()]

View File

@ -32,8 +32,8 @@ class Remove(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -41,9 +41,9 @@ class Remove(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application.on_start()
application.remove(args.package)

View File

@ -33,8 +33,8 @@ class RemoveUnknown(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -42,16 +42,16 @@ class RemoveUnknown(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application.on_start()
unknown_packages = application.unknown()
if args.dry_run:
for package in sorted(unknown_packages):
StringPrinter(package).print(args.info)
StringPrinter(package).print(False)
return
application.remove(unknown_packages)

View File

@ -34,8 +34,8 @@ class Restore(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -43,7 +43,7 @@ class Restore(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
with TarFile(args.path) as archive:

View File

@ -26,7 +26,7 @@ from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.alpm.remote import AUR, Official
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import InvalidOption
from ahriman.core.exceptions import OptionError
from ahriman.core.formatters import AurPrinter
from ahriman.models.aur_package import AURPackage
@ -43,8 +43,8 @@ class Search(Handler):
SORT_FIELDS = {field.name for field in fields(AURPackage) if field.default_factory is not list}
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -52,10 +52,10 @@ class Search(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
official_packages_list = Official.multisearch(*args.search, pacman=application.repository.pacman)
aur_packages_list = AUR.multisearch(*args.search, pacman=application.repository.pacman)
@ -82,7 +82,7 @@ class Search(Handler):
InvalidOption: if search fields is not in list of allowed ones
"""
if sort_by not in Search.SORT_FIELDS:
raise InvalidOption(sort_by)
raise OptionError(sort_by)
# always sort by package name at the last
# well technically it is not a string, but we can deal with it
comparator: Callable[[AURPackage], Tuple[str, str]] =\

View File

@ -20,6 +20,7 @@
import argparse
from pathlib import Path
from pwd import getpwuid
from typing import Type
from ahriman.application.application import Application
@ -34,21 +35,19 @@ class Setup(Handler):
Attributes:
ARCHBUILD_COMMAND_PATH(Path): (class attribute) default devtools command
BIN_DIR_PATH(Path): (class attribute) directory for custom binaries
MIRRORLIST_PATH(Path): (class attribute) path to pacman default mirrorlist (used by multilib repository)
SUDOERS_PATH(Path): (class attribute) path to sudoers.d include configuration
SUDOERS_DIR_PATH(Path): (class attribute) path to sudoers.d includes directory
"""
ALLOW_AUTO_ARCHITECTURE_RUN = False
ARCHBUILD_COMMAND_PATH = Path("/usr/bin/archbuild")
BIN_DIR_PATH = Path("/usr/local/bin")
MIRRORLIST_PATH = Path("/etc/pacman.d/mirrorlist")
SUDOERS_PATH = Path("/etc/sudoers.d/ahriman")
SUDOERS_DIR_PATH = Path("/etc/sudoers.d")
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -56,39 +55,41 @@ class Setup(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
Setup.configuration_create_ahriman(args, architecture, args.repository, configuration.include)
Setup.configuration_create_ahriman(args, architecture, args.repository, configuration.include,
configuration.repository_paths)
configuration.reload()
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
Setup.configuration_create_makepkg(args.packager, application.repository.paths)
Setup.executable_create(args.build_command, architecture)
Setup.executable_create(application.repository.paths, args.build_command, architecture)
Setup.configuration_create_devtools(args.build_command, architecture, args.from_configuration,
args.no_multilib, args.repository, application.repository.paths)
Setup.configuration_create_sudo(args.build_command, architecture)
args.multilib, args.repository, application.repository.paths)
Setup.configuration_create_sudo(application.repository.paths, args.build_command, architecture)
application.repository.repo.init()
@staticmethod
def build_command(prefix: str, architecture: str) -> Path:
def build_command(root: Path, prefix: str, architecture: str) -> Path:
"""
generate build command name
Args:
root(Path): root directory for the build command (must be root of the reporitory)
prefix(str): command prefix in {prefix}-{architecture}-build
architecture(str): repository architecture
Returns:
Path: valid devtools command name
"""
return Setup.BIN_DIR_PATH / f"{prefix}-{architecture}-build"
return root / f"{prefix}-{architecture}-build"
@staticmethod
def configuration_create_ahriman(args: argparse.Namespace, architecture: str, repository: str,
include_path: Path) -> None:
include_path: Path, paths: RepositoryPaths) -> None:
"""
create service specific configuration
@ -97,11 +98,13 @@ class Setup(Handler):
architecture(str): repository architecture
repository(str): repository name
include_path(Path): path to directory with configuration includes
paths(RepositoryPaths): repository paths instance
"""
configuration = Configuration()
section = Configuration.section_name("build", architecture)
configuration.set_option(section, "build_command", str(Setup.build_command(args.build_command, architecture)))
build_command = Setup.build_command(paths.root, args.build_command, architecture)
configuration.set_option(section, "build_command", str(build_command))
configuration.set_option("repository", "name", repository)
if args.build_as_user is not None:
configuration.set_option(section, "makechrootpkg_flags", f"-U {args.build_as_user}")
@ -121,15 +124,18 @@ class Setup(Handler):
@staticmethod
def configuration_create_devtools(prefix: str, architecture: str, source: Path,
no_multilib: bool, repository: str, paths: RepositoryPaths) -> None:
multilib: bool, repository: str, paths: RepositoryPaths) -> None:
"""
create configuration for devtools based on ``source`` configuration
Note:
devtools does not allow to specify the pacman configuration, thus we still have to use configuration in /usr
Args:
prefix(str): command prefix in {prefix}-{architecture}-build
architecture(str): repository architecture
source(Path): path to source configuration file
no_multilib(bool): do not add multilib repository
multilib(bool): add or do not multilib repository
repository(str): repository name
paths(RepositoryPaths): repository paths instance
"""
@ -148,7 +154,7 @@ class Setup(Handler):
configuration.set_option("options", "Architecture", architecture)
# add multilib
if not no_multilib:
if multilib:
configuration.set_option("multilib", "Include", str(Setup.MIRRORLIST_PATH))
# add repository itself
@ -168,30 +174,36 @@ class Setup(Handler):
packager(str): packager identifier (e.g. name, email)
paths(RepositoryPaths): repository paths instance
"""
(paths.root / ".makepkg.conf").write_text(f"PACKAGER='{packager}'\n", encoding="utf8")
uid, _ = paths.root_owner
home_dir = Path(getpwuid(uid).pw_dir)
(home_dir / ".makepkg.conf").write_text(f"PACKAGER='{packager}'\n", encoding="utf8")
@staticmethod
def configuration_create_sudo(prefix: str, architecture: str) -> None:
def configuration_create_sudo(paths: RepositoryPaths, prefix: str, architecture: str) -> None:
"""
create configuration to run build command with sudo without password
Args:
paths(RepositoryPaths): repository paths instance
prefix(str): command prefix in {prefix}-{architecture}-build
architecture(str): repository architecture
"""
command = Setup.build_command(prefix, architecture)
Setup.SUDOERS_PATH.write_text(f"ahriman ALL=(ALL) NOPASSWD: {command} *\n", encoding="utf8")
Setup.SUDOERS_PATH.chmod(0o400) # security!
command = Setup.build_command(paths.root, prefix, architecture)
sudoers_file = Setup.build_command(Setup.SUDOERS_DIR_PATH, prefix, architecture)
sudoers_file.write_text(f"ahriman ALL=(ALL) NOPASSWD: {command} *\n", encoding="utf8")
sudoers_file.chmod(0o400) # security!
@staticmethod
def executable_create(prefix: str, architecture: str) -> None:
def executable_create(paths: RepositoryPaths, prefix: str, architecture: str) -> None:
"""
create executable for the service
Args:
paths(RepositoryPaths): repository paths instance
prefix(str): command prefix in {prefix}-{architecture}-build
architecture(str): repository architecture
"""
command = Setup.build_command(prefix, architecture)
command = Setup.build_command(paths.root, prefix, architecture)
command.unlink(missing_ok=True)
command.symlink_to(Setup.ARCHBUILD_COMMAND_PATH)
paths.chown(command) # we would like to keep owner inside ahriman's home

View File

@ -38,8 +38,8 @@ class Shell(Handler):
ALLOW_MULTI_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -47,13 +47,16 @@ class Shell(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
# pylint: disable=possibly-unused-variable
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
if args.verbose:
# licensed by https://creativecommons.org/licenses/by-sa/3.0
path = Path(sys.prefix) / "share" / "ahriman" / "templates" / "shell"
StringPrinter(path.read_text(encoding="utf8")).print(verbose=False)
code.interact(local=locals())
if args.code is None:
code.interact(local=locals())
else:
code.InteractiveConsole(locals=locals()).runcode(args.code)

View File

@ -32,8 +32,8 @@ class Sign(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -41,7 +41,7 @@ class Sign(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
Application(architecture, configuration, no_report, unsafe).sign(args.package)
Application(architecture, configuration, report=report, unsafe=unsafe).sign(args.package)

View File

@ -37,8 +37,8 @@ class Status(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -46,11 +46,11 @@ class Status(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
# we are using reporter here
client = Application(architecture, configuration, no_report=False, unsafe=unsafe).repository.reporter
client = Application(architecture, configuration, report=True, unsafe=unsafe).repository.reporter
if args.ahriman:
service_status = client.get_internal()
StatusPrinter(service_status.status).print(args.info)

View File

@ -35,8 +35,8 @@ class StatusUpdate(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -44,11 +44,11 @@ class StatusUpdate(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
# we are using reporter here
client = Application(architecture, configuration, no_report=False, unsafe=unsafe).repository.reporter
client = Application(architecture, configuration, report=True, unsafe=unsafe).repository.reporter
if args.action == Action.Update and args.package:
# update packages statuses

View File

@ -33,8 +33,8 @@ class Triggers(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -42,10 +42,10 @@ class Triggers(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe)
if args.trigger:
loader = application.repository.triggers
loader.triggers = [loader.load_trigger(trigger) for trigger in args.trigger]

View File

@ -35,8 +35,8 @@ class UnsafeCommands(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -44,7 +44,7 @@ class UnsafeCommands(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
parser = args.parser()
@ -79,6 +79,7 @@ class UnsafeCommands(Handler):
Returns:
List[str]: list of commands with default unsafe flag
"""
# should never fail
# pylint: disable=protected-access
subparser = next(action for action in parser._actions if isinstance(action, argparse._SubParsersAction))
return [action_name for action_name, action in subparser.choices.items() if action.get_default("unsafe")]

View File

@ -32,8 +32,8 @@ class Update(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -41,13 +41,14 @@ class Update(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, no_report, unsafe)
application = Application(architecture, configuration, report=report, unsafe=unsafe,
refresh_pacman_database=args.refresh)
application.on_start()
packages = application.updates(args.package, args.no_aur, args.no_local, args.no_manual, args.no_vcs,
Update.log_fn(application, args.dry_run))
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs,
log_fn=Update.log_fn(application, args.dry_run))
Update.check_if_empty(args.exit_code, not packages)
if args.dry_run:
return

View File

@ -39,8 +39,8 @@ class Users(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -48,7 +48,7 @@ class Users(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
database = SQLite.load(configuration)

View File

@ -37,8 +37,8 @@ class Versions(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -46,7 +46,7 @@ class Versions(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
VersionPrinter(f"Module version {version.__version__}",

View File

@ -35,8 +35,8 @@ class Web(Handler):
ALLOW_MULTI_ARCHITECTURE_RUN = False # required to be able to spawn external processes
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
@ -44,7 +44,7 @@ class Web(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
# we are using local import for optional dependencies

View File

@ -27,7 +27,7 @@ from typing import Literal, Optional, Type
from ahriman import version
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import DuplicateRun
from ahriman.core.exceptions import DuplicateRunError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.core.status.client import Client
from ahriman.core.util import check_user
@ -73,7 +73,7 @@ class Lock(LazyLogging):
self.unsafe = args.unsafe
self.paths = configuration.repository_paths
self.reporter = Client() if args.no_report else Client.load(configuration)
self.reporter = Client.load(configuration) if args.report else Client()
def __enter__(self) -> Lock:
"""
@ -122,7 +122,7 @@ class Lock(LazyLogging):
"""
check if current user is actually owner of ahriman root
"""
check_user(self.paths, self.unsafe)
check_user(self.paths, unsafe=self.unsafe)
def clear(self) -> None:
"""
@ -144,4 +144,4 @@ class Lock(LazyLogging):
try:
self.path.touch(exist_ok=self.force)
except FileExistsError:
raise DuplicateRun()
raise DuplicateRunError()

View File

@ -17,13 +17,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from pyalpm import Handle, Package, SIG_PACKAGE # type: ignore
import shutil
from pathlib import Path
from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore
from typing import Generator, Set
from ahriman.core.configuration import Configuration
from ahriman.core.lazy_logging import LazyLogging
from ahriman.models.repository_paths import RepositoryPaths
class Pacman:
class Pacman(LazyLogging):
"""
alpm wrapper
@ -31,35 +36,96 @@ class Pacman:
handle(Handle): pyalpm root ``Handle``
"""
def __init__(self, configuration: Configuration) -> None:
def __init__(self, architecture: str, configuration: Configuration, *, refresh_database: int) -> None:
"""
default constructor
Args:
architecture(str): repository architecture
configuration(Configuration): configuration instance
refresh_database(int): synchronize local cache to remote. If set to ``0``, no syncronization will be
enabled, if set to ``1`` - normal syncronization, if set to ``2`` - force syncronization
"""
root = configuration.get("alpm", "root")
root = configuration.getpath("alpm", "root")
pacman_root = configuration.getpath("alpm", "database")
self.handle = Handle(root, str(pacman_root))
for repository in configuration.getlist("alpm", "repositories"):
self.handle.register_syncdb(repository, SIG_PACKAGE)
use_ahriman_cache = configuration.getboolean("alpm", "use_ahriman_cache")
mirror = configuration.get("alpm", "mirror")
paths = configuration.repository_paths
database_path = paths.pacman if use_ahriman_cache else pacman_root
def all_packages(self) -> Set[str]:
self.handle = Handle(str(root), str(database_path))
for repository in configuration.getlist("alpm", "repositories"):
database = self.database_init(repository, mirror, architecture)
self.database_copy(database, pacman_root, paths, use_ahriman_cache=use_ahriman_cache)
if use_ahriman_cache and refresh_database:
self.database_sync(refresh_database > 1)
def database_copy(self, database: DB, pacman_root: Path, paths: RepositoryPaths, *,
use_ahriman_cache: bool) -> None:
"""
get list of packages known for alpm
copy database from the operating system root to the ahriman local home
Args:
database(DB): pacman database instance to be copied
pacman_root(Path): operating system pacman's root
paths(RepositoryPaths): repository paths instance
use_ahriman_cache(bool): use local ahriman cache instead of system one
"""
def repository_database(root: Path) -> Path:
return root / "sync" / f"{database.name}.db"
if not use_ahriman_cache:
return
# copy root database if no local copy found
pacman_db_path = Path(self.handle.dbpath)
if not pacman_db_path.is_dir():
return # root directory does not exist yet
dst = repository_database(pacman_db_path)
if dst.is_file():
return # file already exists, do not copy
src = repository_database(pacman_root)
if not src.is_file():
self.logger.warning("repository %s is set to be used, however, no working copy was found", database.name)
return # database for some reasons deos not exist
self.logger.info("copy pacman database from operating system root to ahriman's home")
shutil.copy(src, dst)
paths.chown(dst)
def database_init(self, repository: str, mirror: str, architecture: str) -> DB:
"""
create database instance from pacman handler and set its properties
Args:
repository(str): pacman repository name (e.g. core)
mirror(str): arch linux mirror url
architecture(str): repository architecture
Returns:
Set[str]: list of package names
DB: loaded pacman database instance
"""
result: Set[str] = set()
database: DB = self.handle.register_syncdb(repository, SIG_PACKAGE)
# replace variables in mirror address
database.servers = [mirror.replace("$repo", repository).replace("$arch", architecture)]
return database
def database_sync(self, force: bool) -> None:
"""
sync local database
Args:
force(bool): force database syncronization (same as ``pacman -Syy``)
"""
self.logger.info("refresh ahriman's home pacman database (force refresh %s)", force)
transaction = self.handle.init_transaction()
for database in self.handle.get_syncdbs():
for package in database.pkgcache:
result.add(package.name) # package itself
result.update(package.provides) # provides list for meta-packages
try:
database.update(force)
except PyalpmError:
self.logger.exception("exception during update %s", database.name)
transaction.release()
return result
def get(self, package_name: str) -> Generator[Package, None, None]:
def package_get(self, package_name: str) -> Generator[Package, None, None]:
"""
retrieve list of the packages from the repository by name
@ -74,3 +140,18 @@ class Pacman:
if package is None:
continue
yield package
def packages(self) -> Set[str]:
"""
get list of packages known for alpm
Returns:
Set[str]: list of package names
"""
result: Set[str] = set()
for database in self.handle.get_syncdbs():
for package in database.pkgcache:
result.add(package.name) # package itself
result.update(package.provides) # provides list for meta-packages
return result

View File

@ -23,7 +23,7 @@ from typing import Any, Dict, List, Type
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Remote
from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
from ahriman.core.util import exception_response_text
from ahriman.models.aur_package import AURPackage
@ -61,7 +61,7 @@ class AUR(Remote):
response_type = response["type"]
if response_type == "error":
error_details = response.get("error", "Unknown API error")
raise InvalidPackageInfo(error_details)
raise PackageInfoError(error_details)
return [AURPackage.from_json(package) for package in response["results"]]
@classmethod
@ -140,7 +140,10 @@ class AUR(Remote):
AURPackage: package which match the package name
"""
packages = self.make_request("info", package_name)
return next(package for package in packages if package.name == package_name)
try:
return next(package for package in packages if package.name == package_name)
except StopIteration:
raise UnknownPackageError(package_name)
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
"""

View File

@ -23,7 +23,7 @@ from typing import Any, Dict, List, Type
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Remote
from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
from ahriman.core.util import exception_response_text
from ahriman.models.aur_package import AURPackage
@ -59,7 +59,7 @@ class Official(Remote):
InvalidPackageInfo: for error API response
"""
if not response["valid"]:
raise InvalidPackageInfo("API validation error")
raise PackageInfoError("API validation error")
return [AURPackage.from_repo(package) for package in response["results"]]
@classmethod
@ -128,7 +128,10 @@ class Official(Remote):
AURPackage: package which match the package name
"""
packages = self.make_request(package_name, by="name")
return next(package for package in packages if package.name == package_name)
try:
return next(package for package in packages if package.name == package_name)
except StopIteration:
raise UnknownPackageError(package_name)
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
"""

View File

@ -19,6 +19,7 @@
#
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Official
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.aur_package import AURPackage
@ -48,4 +49,7 @@ class OfficialSyncdb(Official):
Returns:
AURPackage: package which match the package name
"""
return next(AURPackage.from_pacman(package) for package in pacman.get(package_name))
try:
return next(AURPackage.from_pacman(package) for package in pacman.package_get(package_name))
except StopIteration:
raise UnknownPackageError(package_name)

View File

@ -20,7 +20,7 @@
from pathlib import Path
from typing import List
from ahriman.core.exceptions import BuildFailed
from ahriman.core.exceptions import BuildError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.core.util import check_output
from ahriman.models.repository_paths import RepositoryPaths
@ -72,7 +72,7 @@ class Repo(LazyLogging):
"""
Repo._check_output(
"repo-add", *self.sign_args, "-R", str(self.repo_path), str(path),
exception=BuildFailed(path.name),
exception=BuildError(path.name),
cwd=self.paths.repository,
logger=self.logger,
user=self.uid)
@ -81,12 +81,8 @@ class Repo(LazyLogging):
"""
create empty repository database
"""
Repo._check_output(
"repo-add", *self.sign_args, str(self.repo_path),
exception=None,
cwd=self.paths.repository,
logger=self.logger,
user=self.uid)
Repo._check_output("repo-add", *self.sign_args, str(self.repo_path),
cwd=self.paths.repository, logger=self.logger, user=self.uid)
def remove(self, package: str, filename: Path) -> None:
"""
@ -103,7 +99,7 @@ class Repo(LazyLogging):
# remove package from registry
Repo._check_output(
"repo-remove", *self.sign_args, str(self.repo_path), package,
exception=BuildFailed(package),
exception=BuildError(package),
cwd=self.paths.repository,
logger=self.logger,
user=self.uid)

View File

@ -24,7 +24,7 @@ from typing import Optional, Type
from ahriman.core.auth import Mapping
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import InvalidOption
from ahriman.core.exceptions import OptionError
from ahriman.models.auth_settings import AuthSettings
@ -91,7 +91,7 @@ class OAuth(Mapping):
except TypeError: # what if it is random string?
is_oauth2_client = False
if not is_oauth2_client:
raise InvalidOption(name)
raise OptionError(name)
return provider
def get_client(self) -> aioauth_client.OAuth2Client:

View File

@ -82,22 +82,18 @@ class Sources(LazyLogging):
branch = remote.branch if remote is not None else 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,
exception=None, cwd=sources_dir, logger=instance.logger)
Sources._check_output("git", "fetch", "origin", branch, cwd=sources_dir, logger=instance.logger)
elif remote 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),
exception=None, cwd=sources_dir, logger=instance.logger)
remote.git_url, str(sources_dir), cwd=sources_dir.parent, logger=instance.logger)
else:
# it will cause an exception later
instance.logger.error("%s is not initialized, but no remote provided", sources_dir)
# and now force reset to our branch
Sources._check_output("git", "checkout", "--force", branch,
exception=None, cwd=sources_dir, logger=instance.logger)
Sources._check_output("git", "reset", "--hard", f"origin/{branch}",
exception=None, cwd=sources_dir, logger=instance.logger)
Sources._check_output("git", "checkout", "--force", branch, cwd=sources_dir, logger=instance.logger)
Sources._check_output("git", "reset", "--hard", f"origin/{branch}", cwd=sources_dir, logger=instance.logger)
# move content if required
# we are using full path to source directory in order to make append possible
@ -116,7 +112,7 @@ class Sources(LazyLogging):
bool: True in case if there is any remote and false otherwise
"""
instance = Sources()
remotes = Sources._check_output("git", "remote", exception=None, cwd=sources_dir, logger=instance.logger)
remotes = Sources._check_output("git", "remote", cwd=sources_dir, logger=instance.logger)
return bool(remotes)
@staticmethod
@ -129,7 +125,7 @@ class Sources(LazyLogging):
"""
instance = Sources()
Sources._check_output("git", "init", "--initial-branch", instance.DEFAULT_BRANCH,
exception=None, cwd=sources_dir, logger=instance.logger)
cwd=sources_dir, logger=instance.logger)
@staticmethod
def load(sources_dir: Path, package: Package, patches: List[PkgbuildPatch], paths: RepositoryPaths) -> None:
@ -182,8 +178,7 @@ class Sources(LazyLogging):
instance = Sources()
instance.add(sources_dir, *pattern)
instance.commit(sources_dir)
Sources._check_output("git", "push", remote.git_url, remote.branch,
exception=None, cwd=sources_dir, logger=instance.logger)
Sources._check_output("git", "push", remote.git_url, remote.branch, cwd=sources_dir, logger=instance.logger)
def add(self, sources_dir: Path, *pattern: str) -> None:
"""
@ -203,7 +198,7 @@ class Sources(LazyLogging):
# add them to index
Sources._check_output("git", "add", "--intent-to-add",
*[str(fn.relative_to(sources_dir)) for fn in found_files],
exception=None, cwd=sources_dir, logger=self.logger)
cwd=sources_dir, logger=self.logger)
def commit(self, sources_dir: Path, commit_message: Optional[str] = None) -> None:
"""
@ -216,8 +211,8 @@ class Sources(LazyLogging):
"""
if commit_message is None:
commit_message = f"Autogenerated commit at {datetime.datetime.utcnow()}"
Sources._check_output("git", "commit", "--all", "--message", commit_message,
exception=None, cwd=sources_dir, logger=self.logger)
Sources._check_output("git", "commit", "--allow-empty", "--message", commit_message,
cwd=sources_dir, logger=self.logger)
def diff(self, sources_dir: Path) -> str:
"""
@ -229,7 +224,7 @@ class Sources(LazyLogging):
Returns:
str: patch as plain string
"""
return Sources._check_output("git", "diff", exception=None, cwd=sources_dir, logger=self.logger)
return Sources._check_output("git", "diff", cwd=sources_dir, logger=self.logger)
def move(self, pkgbuild_dir: Path, sources_dir: Path) -> None:
"""
@ -258,6 +253,6 @@ class Sources(LazyLogging):
self.logger.info("apply patch %s from database at %s", patch.key, sources_dir)
if patch.is_plain_diff:
Sources._check_output("git", "apply", "--ignore-space-change", "--ignore-whitespace",
exception=None, cwd=sources_dir, input_data=patch.serialize(), logger=self.logger)
cwd=sources_dir, input_data=patch.serialize(), logger=self.logger)
else:
patch.write(sources_dir / "PKGBUILD")

View File

@ -23,7 +23,7 @@ from typing import List
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import BuildFailed
from ahriman.core.exceptions import BuildError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.core.util import check_output
from ahriman.models.package import Package
@ -78,14 +78,14 @@ class Task(LazyLogging):
Task._check_output(
*command,
exception=BuildFailed(self.package.base),
exception=BuildError(self.package.base),
cwd=sources_dir,
logger=self.logger,
user=self.uid)
# well it is not actually correct, but we can deal with it
packages = Task._check_output("makepkg", "--packagelist",
exception=BuildFailed(self.package.base),
exception=BuildError(self.package.base),
cwd=sources_dir,
logger=self.logger).splitlines()
return [Path(package) for package in packages]

View File

@ -27,7 +27,7 @@ from logging.config import fileConfig
from pathlib import Path
from typing import Any, Dict, Generator, List, Optional, Tuple, Type
from ahriman.core.exceptions import InitializeException
from ahriman.core.exceptions import InitializeError
from ahriman.models.repository_paths import RepositoryPaths
@ -208,7 +208,7 @@ class Configuration(configparser.RawConfigParser):
InitializeException: in case if architecture and/or path are not set
"""
if self.path is None or self.architecture is None:
raise InitializeException("Configuration path and/or architecture are not set")
raise InitializeError("Configuration path and/or architecture are not set")
return self.path, self.architecture
def dump(self) -> Dict[str, Dict[str, str]]:
@ -225,14 +225,14 @@ class Configuration(configparser.RawConfigParser):
# pylint and mypy are too stupid to find these methods
# pylint: disable=missing-function-docstring,multiple-statements,unused-argument
def getlist(self, *args: Any, **kwargs: Any) -> List[str]: ...
def getlist(self, *args: Any, **kwargs: Any) -> List[str]: ... # type: ignore
def getpath(self, *args: Any, **kwargs: Any) -> Path: ...
def getpath(self, *args: Any, **kwargs: Any) -> Path: ... # type: ignore
def gettype(self, section: str, architecture: str) -> Tuple[str, str]:
"""
get type variable with fallback to old logic
Despite the fact that it has same semantics as other get* methods, but it has different argument list
get type variable with fallback to old logic. Despite the fact that it has same semantics as other get* methods,
but it has different argument list
Args:
section(str): section name

View File

@ -61,7 +61,7 @@ class Operations(LazyLogging):
result[column[0]] = row[index]
return result
def with_connection(self, query: Callable[[sqlite3.Connection], T], commit: bool = False) -> T:
def with_connection(self, query: Callable[[sqlite3.Connection], T], *, commit: bool = False) -> T:
"""
perform operation in connection

View File

@ -21,7 +21,7 @@ from pathlib import Path
from typing import Any
class BuildFailed(RuntimeError):
class BuildError(RuntimeError):
"""
base exception for failed builds
"""
@ -36,7 +36,7 @@ class BuildFailed(RuntimeError):
RuntimeError.__init__(self, f"Package {package_base} build failed, check logs for details")
class DuplicateRun(RuntimeError):
class DuplicateRunError(RuntimeError):
"""
exception which will be raised if there is another application instance
"""
@ -55,7 +55,25 @@ class ExitCode(RuntimeError):
"""
class InitializeException(RuntimeError):
class ExtensionError(RuntimeError):
"""
exception being raised by trigger load in case of errors
"""
class GitRemoteError(RuntimeError):
"""
git remote exception
"""
def __init__(self) -> None:
"""
default constructor
"""
RuntimeError.__init__(self, "Git remote failed")
class InitializeError(RuntimeError):
"""
base service initialization exception
"""
@ -70,58 +88,6 @@ class InitializeException(RuntimeError):
RuntimeError.__init__(self, f"Could not load service: {details}")
class InvalidExtension(RuntimeError):
"""
exception being raised by trigger load in case of errors
"""
class InvalidOption(ValueError):
"""
exception which will be raised on configuration errors
"""
def __init__(self, value: Any) -> None:
"""
default constructor
Args:
value(Any): option value
"""
ValueError.__init__(self, f"Invalid or unknown option value `{value}`")
class InvalidPath(ValueError):
"""
exception which will be raised on path which is not belong to root directory
"""
def __init__(self, path: Path, root: Path) -> None:
"""
default constructor
Args:
path(Path): path which raised an exception
root(Path): repository root (i.e. ahriman home)
"""
ValueError.__init__(self, f"Path `{path}` does not belong to repository root `{root}`")
class InvalidPackageInfo(RuntimeError):
"""
exception which will be raised on package load errors
"""
def __init__(self, details: Any) -> None:
"""
default constructor
Args:
details(Any): error details
"""
RuntimeError.__init__(self, f"There are errors during reading package information: `{details}`")
class MigrationError(RuntimeError):
"""
exception which will be raised on migration error
@ -137,7 +103,7 @@ class MigrationError(RuntimeError):
RuntimeError.__init__(self, details)
class MissingArchitecture(ValueError):
class MissingArchitectureError(ValueError):
"""
exception which will be raised if architecture is required, but missing
"""
@ -152,7 +118,7 @@ class MissingArchitecture(ValueError):
ValueError.__init__(self, f"Architecture required for subcommand {command}, but missing")
class MultipleArchitectures(ValueError):
class MultipleArchitecturesError(ValueError):
"""
exception which will be raised if multiple architectures are not supported by the handler
"""
@ -167,7 +133,53 @@ class MultipleArchitectures(ValueError):
ValueError.__init__(self, f"Multiple architectures are not supported by subcommand {command}")
class ReportFailed(RuntimeError):
class OptionError(ValueError):
"""
exception which will be raised on configuration errors
"""
def __init__(self, value: Any) -> None:
"""
default constructor
Args:
value(Any): option value
"""
ValueError.__init__(self, f"Invalid or unknown option value `{value}`")
class PackageInfoError(RuntimeError):
"""
exception which will be raised on package load errors
"""
def __init__(self, details: Any) -> None:
"""
default constructor
Args:
details(Any): error details
"""
RuntimeError.__init__(self, f"There are errors during reading package information: `{details}`")
class PathError(ValueError):
"""
exception which will be raised on path which is not belong to root directory
"""
def __init__(self, path: Path, root: Path) -> None:
"""
default constructor
Args:
path(Path): path which raised an exception
root(Path): repository root (i.e. ahriman home)
"""
ValueError.__init__(self, f"Path `{path}` does not belong to repository root `{root}`")
class ReportError(RuntimeError):
"""
report generation exception
"""
@ -179,22 +191,7 @@ class ReportFailed(RuntimeError):
RuntimeError.__init__(self, "Report failed")
class SuccessFailed(ValueError):
"""
exception for merging invalid statues
"""
def __init__(self, package_base: str) -> None:
"""
default constructor
Args:
package_base(str): package base name
"""
ValueError.__init__(self, f"Package base {package_base} had status failed, but new status is success")
class SyncFailed(RuntimeError):
class SynchronizationError(RuntimeError):
"""
remote synchronization exception
"""
@ -206,7 +203,7 @@ class SyncFailed(RuntimeError):
RuntimeError.__init__(self, "Sync failed")
class UnknownPackage(ValueError):
class UnknownPackageError(ValueError):
"""
exception for status watcher which will be thrown on unknown package
"""
@ -221,7 +218,22 @@ class UnknownPackage(ValueError):
ValueError.__init__(self, f"Package base {package_base} is unknown")
class UnsafeRun(RuntimeError):
class UnprocessedPackageStatusError(ValueError):
"""
exception for merging invalid statues
"""
def __init__(self, package_base: str) -> None:
"""
default constructor
Args:
package_base(str): package base name
"""
ValueError.__init__(self, f"Package base {package_base} had status failed, but new status is success")
class UnsafeRunError(RuntimeError):
"""
exception which will be raised in case if user is not owner of repository
"""

View File

@ -1,21 +1,21 @@
#
# copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2022 ahriman team.
#
# this file is part of ahriman
# 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
# 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.
# 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/>.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.core.formatters.printer import Printer
from ahriman.core.formatters.string_printer import StringPrinter

View File

@ -1,21 +1,21 @@
#
# copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2022 ahriman team.
#
# this file is part of ahriman
# 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
# 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.
# 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/>.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.core.gitremote.remote_pull_trigger import RemotePullTrigger
from ahriman.core.gitremote.remote_push_trigger import RemotePushTrigger

View File

@ -0,0 +1,90 @@
#
# Copyright (c) 2021-2022 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import shutil
from pathlib import Path
from tempfile import TemporaryDirectory
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import GitRemoteError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.core.util import walk
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
class RemotePull(LazyLogging):
"""
fetch PKGBUILDs from remote repository and use them for following actions
Attributes:
remote_source(RemoteSource): repository remote source (remote pull url and branch)
repository_paths(RepositoryPaths): repository paths instance
"""
def __init__(self, configuration: Configuration, section: str) -> None:
"""
default constructor
Args:
configuration(Configuration): configuration instance
section(str): settings section name
"""
self.remote_source = RemoteSource(
git_url=configuration.get(section, "pull_url"),
web_url="",
path=".",
branch=configuration.get(section, "pull_branch", fallback="master"),
source=PackageSource.Local,
)
self.repository_paths = configuration.repository_paths
def repo_clone(self) -> None:
"""
clone repository from remote source
"""
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
Sources.fetch(clone_dir, self.remote_source)
self.repo_copy(clone_dir)
def repo_copy(self, clone_dir: Path) -> None:
"""
copy directories from cloned remote source to local cache
Args:
clone_dir(Path): path to temporary cloned directory
"""
for pkgbuild_path in filter(lambda path: path.name == "PKGBUILD", walk(clone_dir)):
cloned_pkgbuild_dir = pkgbuild_path.parent
package_base = cloned_pkgbuild_dir.name
local_pkgbuild_dir = self.repository_paths.cache_for(package_base)
shutil.copytree(cloned_pkgbuild_dir, local_pkgbuild_dir, dirs_exist_ok=True)
Sources.init(local_pkgbuild_dir) # initialized git repository is required for local sources
def run(self) -> None:
"""
run git pull action
"""
try:
self.repo_clone()
except Exception:
self.logger.exception("git pull failed")
raise GitRemoteError()

View File

@ -1,42 +1,30 @@
#
# copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2022 ahriman team.
#
# this file is part of ahriman
# 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
# 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.
# 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/>.
# 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 shutil
from pathlib import Path
from tempfile import TemporaryDirectory
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
from ahriman.core.gitremote.remote_pull import RemotePull
from ahriman.core.triggers import Trigger
from ahriman.core.util import walk
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
class RemotePullTrigger(Trigger):
"""
trigger for fetching PKGBUILDs from remote repository
Attributes:
remote_source(RemoteSource): repository remote source (remote pull url and branch)
repository_paths(RepositoryPaths): repository paths instance
trigger based on pulling PKGBUILDs before the actions
"""
def __init__(self, architecture: str, configuration: Configuration) -> None:
@ -48,39 +36,13 @@ class RemotePullTrigger(Trigger):
configuration(Configuration): configuration instance
"""
Trigger.__init__(self, architecture, configuration)
self.remote_source = RemoteSource(
git_url=configuration.get("gitremote", "pull_url"),
web_url="",
path=".",
branch=configuration.get("gitremote", "pull_branch", fallback="master"),
source=PackageSource.Local,
)
self.repository_paths = configuration.repository_paths
self.targets = configuration.getlist("remote-pull", "target", fallback=["gitremote"])
def on_start(self) -> None:
"""
trigger action which will be called at the start of the application
"""
self.repo_clone()
def repo_clone(self) -> None:
"""
clone repository from remote source
"""
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
Sources.fetch(clone_dir, self.remote_source)
self.repo_copy(clone_dir)
def repo_copy(self, clone_dir: Path) -> None:
"""
copy directories from cloned remote source to local cache
Args:
clone_dir(Path): path to temporary cloned directory
"""
for pkgbuild_path in filter(lambda path: path.name == "PKGBUILD", walk(clone_dir)):
cloned_pkgbuild_dir = pkgbuild_path.parent
package_base = cloned_pkgbuild_dir.name
local_pkgbuild_dir = self.repository_paths.cache_for(package_base)
shutil.copytree(cloned_pkgbuild_dir, local_pkgbuild_dir, dirs_exist_ok=True)
Sources.init(local_pkgbuild_dir) # initialized git repository is required for local sources
for target in self.targets:
section, _ = self.configuration.gettype(target, self.architecture)
runner = RemotePull(self.configuration, section)
runner.run()

View File

@ -0,0 +1,113 @@
#
# Copyright (c) 2021-2022 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import shutil
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Generator
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import GitRemoteError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
from ahriman.models.result import Result
class RemotePush(LazyLogging):
"""
sync PKGBUILDs to remote repository after actions
Attributes:
remote_source(RemoteSource): repository remote source (remote pull url and branch)
"""
def __init__(self, configuration: Configuration, section: str) -> None:
"""
default constructor
Args:
configuration(Configuration): configuration instance
remote_push_trigger.py
"""
self.remote_source = RemoteSource(
git_url=configuration.get(section, "push_url"),
web_url="",
path=".",
branch=configuration.get(section, "push_branch", fallback="master"),
source=PackageSource.Local,
)
@staticmethod
def package_update(package: Package, target_dir: Path) -> str:
"""
clone specified package and update its content in cloned PKGBUILD repository
Args:
package(Package): built package to update pkgbuild repository
target_dir(Path): path to the cloned PKGBUILD repository
Returns:
str: relative path to be added as new file
"""
# instead of iterating by directory we can simplify the process
# firstly, we need to remove old data to make sure that removed files are not tracked anymore...
package_target_dir = target_dir / package.base
shutil.rmtree(package_target_dir, ignore_errors=True)
# ...secondly, we copy whole tree...
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
Sources.fetch(clone_dir, package.remote)
shutil.copytree(clone_dir, package_target_dir)
# ...and last, but not least, we remove the dot-git directory...
shutil.rmtree(package_target_dir / ".git", ignore_errors=True)
# ...and finally return path to the copied directory
return package.base
@staticmethod
def packages_update(result: Result, target_dir: Path) -> Generator[str, None, None]:
"""
update all packages from the build result
Args:
result(Result): build result
target_dir(Path): path to the cloned PKGBUILD repository
Yields:
str: path to updated files
"""
for package in result.success:
yield RemotePush.package_update(package, target_dir)
def run(self, result: Result) -> None:
"""
run git pull action
Args:
result(Result): build result
"""
try:
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
Sources.fetch(clone_dir, self.remote_source)
Sources.push(clone_dir, self.remote_source, *RemotePush.packages_update(result, clone_dir))
except Exception:
self.logger.exception("git push failed")
raise GitRemoteError()

View File

@ -1,43 +1,34 @@
#
# copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2022 ahriman team.
#
# this file is part of ahriman
# 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
# 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.
# 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/>.
# 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 shutil
from typing import Iterable
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Generator, Iterable
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
from ahriman.core.gitremote.remote_push import RemotePush
from ahriman.core.triggers import Trigger
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
from ahriman.models.result import Result
class RemotePushTrigger(Trigger):
"""
trigger for syncing PKGBUILDs to remote repository
Attributes:
remote_source(RemoteSource): repository remote source (remote pull url and branch)
"""
def __init__(self, architecture: str, configuration: Configuration) -> None:
@ -49,53 +40,7 @@ class RemotePushTrigger(Trigger):
configuration(Configuration): configuration instance
"""
Trigger.__init__(self, architecture, configuration)
self.remote_source = RemoteSource(
git_url=configuration.get("gitremote", "push_url"),
web_url="",
path=".",
branch=configuration.get("gitremote", "push_branch", fallback="master"),
source=PackageSource.Local,
)
@staticmethod
def package_update(package: Package, target_dir: Path) -> str:
"""
clone specified package and update its content in cloned PKGBUILD repository
Args:
package(Package): built package to update pkgbuild repository
target_dir(Path): path to the cloned PKGBUILD repository
Returns:
str: relative path to be added as new file
"""
# instead of iterating by directory we can simplify the process
# firstly, we need to remove old data to make sure that removed files are not tracked anymore...
package_target_dir = target_dir / package.base
shutil.rmtree(package_target_dir, ignore_errors=True)
# ...secondly, we copy whole tree...
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
Sources.fetch(clone_dir, package.remote)
shutil.copytree(clone_dir, package_target_dir)
# ...and last, but not least, we remove the dot-git directory...
shutil.rmtree(package_target_dir / ".git", ignore_errors=True)
# ...and finally return path to the copied directory
return package.base
@staticmethod
def packages_update(result: Result, target_dir: Path) -> Generator[str, None, None]:
"""
update all packages from the build result
Args:
result(Result): build result
target_dir(Path): path to the cloned PKGBUILD repository
Yields:
str: path to updated files
"""
for package in result.success:
yield RemotePushTrigger.package_update(package, target_dir)
self.targets = configuration.getlist("remote-push", "target", fallback=["gitremote"])
def on_result(self, result: Result, packages: Iterable[Package]) -> None:
"""
@ -105,6 +50,7 @@ class RemotePushTrigger(Trigger):
result(Result): build result
packages(Iterable[Package]): list of all available packages
"""
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
Sources.fetch(clone_dir, self.remote_source)
Sources.push(clone_dir, self.remote_source, *RemotePushTrigger.packages_update(result, clone_dir))
for target in self.targets:
section, _ = self.configuration.gettype(target, self.architecture)
runner = RemotePush(self.configuration, section)
runner.run(result)

View File

@ -61,4 +61,4 @@ class LazyLogging:
"""
clazz = self.__class__
prefix = "" if clazz.__module__ is None else f"{clazz.__module__}."
return f"{prefix}{self.__class__.__qualname__}"
return f"{prefix}{clazz.__qualname__}"

View File

@ -22,7 +22,7 @@ from __future__ import annotations
from typing import Iterable, Type
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ReportFailed
from ahriman.core.exceptions import ReportError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.models.package import Package
from ahriman.models.report_settings import ReportSettings
@ -121,4 +121,4 @@ class Report(LazyLogging):
self.generate(packages, result)
except Exception:
self.logger.exception("report generation failed")
raise ReportFailed()
raise ReportError()

View File

@ -17,8 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# technically we could use python-telegram-bot, but it is just a single request, cmon
import requests
import requests # technically we could use python-telegram-bot, but it is just a single request, cmon
from typing import Iterable

View File

@ -66,6 +66,14 @@ class Cleaner(RepositoryProperties):
for package in self.packages_built():
package.unlink()
def clear_pacman(self) -> None:
"""
clear directory with pacman databases
"""
self.logger.info("clear pacman database directory")
for pacman in self.paths.pacman.iterdir():
shutil.rmtree(pacman)
def clear_queue(self) -> None:
"""
clear packages which were queued for the update

View File

@ -21,7 +21,7 @@ import shutil
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Iterable, List, Optional, Set
from typing import Iterable, List, Optional
from ahriman.core.build_tools.task import Task
from ahriman.core.repository.cleaner import Cleaner
@ -187,8 +187,12 @@ class Executor(Cleaner):
self.reporter.set_success(local)
result.add_success(local)
current_package_archives: Set[str] = next(
(set(current.packages) for current in current_packages if current.base == local.base), set())
current_package_archives = {
package
for current in current_packages
if current.base == local.base
for package in current.packages
}
removed_packages.extend(current_package_archives.difference(local.packages))
except Exception:
self.reporter.set_failed(local.base)

View File

@ -39,7 +39,7 @@ class Repository(Executor, UpdateHandler):
>>>
>>> configuration = Configuration()
>>> database = SQLite.load(configuration)
>>> repository = Repository("x86_64", configuration, database, no_report=False, unsafe=False)
>>> repository = Repository("x86_64", configuration, database, report=True, unsafe=False)
>>> known_packages = repository.packages()
>>>
>>> build_result = repository.process_build(known_packages)
@ -99,17 +99,17 @@ class Repository(Executor, UpdateHandler):
"""
return list(filter(package_like, self.paths.packages.iterdir()))
def packages_depend_on(self, depends_on: Optional[Iterable[str]]) -> List[Package]:
def packages_depend_on(self, packages: List[Package], depends_on: Optional[Iterable[str]]) -> List[Package]:
"""
extract list of packages which depends on specified package
Args:
packages(List[Package]): list of packages to be filtered
depends_on(Optional[Iterable[str]]): dependencies of the packages
Returns:
List[Package]: list of repository packages which depend on specified packages
"""
packages = self.packages()
if depends_on is None:
return packages # no list provided extract everything by default
depends_on = set(depends_on)

View File

@ -21,7 +21,7 @@ from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.repo import Repo
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import UnsafeRun
from ahriman.core.exceptions import UnsafeRunError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.core.sign.gpg import GPG
from ahriman.core.status.client import Client
@ -47,8 +47,8 @@ class RepositoryProperties(LazyLogging):
triggers(TriggerLoader): triggers holder
"""
def __init__(self, architecture: str, configuration: Configuration, database: SQLite,
no_report: bool, unsafe: bool) -> None:
def __init__(self, architecture: str, configuration: Configuration, database: SQLite, *,
report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> None:
"""
default constructor
@ -56,8 +56,9 @@ class RepositoryProperties(LazyLogging):
architecture(str): repository architecture
configuration(Configuration): configuration instance
database(SQLite): database instance
no_report(bool): force disable reporting
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
refresh_pacman_database(int): pacman database syncronization level, ``0`` is disabled
"""
self.architecture = architecture
self.configuration = configuration
@ -67,14 +68,14 @@ class RepositoryProperties(LazyLogging):
self.paths = configuration.repository_paths
try:
check_user(self.paths, unsafe)
check_user(self.paths, unsafe=unsafe)
self.paths.tree_create()
except UnsafeRun:
except UnsafeRunError:
self.logger.warning("root owner differs from the current user, skipping tree creation")
self.ignore_list = configuration.getlist("build", "ignore_packages", fallback=[])
self.pacman = Pacman(configuration)
self.pacman = Pacman(architecture, configuration, refresh_database=refresh_pacman_database)
self.sign = GPG(architecture, configuration)
self.repo = Repo(self.name, self.paths, self.sign.repository_sign_args)
self.reporter = Client() if no_report else Client.load(configuration)
self.reporter = Client.load(configuration) if report else Client()
self.triggers = TriggerLoader(architecture, configuration)

View File

@ -42,13 +42,13 @@ class UpdateHandler(Cleaner):
"""
raise NotImplementedError
def updates_aur(self, filter_packages: Iterable[str], no_vcs: bool) -> List[Package]:
def updates_aur(self, filter_packages: Iterable[str], *, vcs: bool) -> List[Package]:
"""
check AUR for updates
Args:
filter_packages(Iterable[str]): do not check every package just specified in the list
no_vcs(bool): do not check VCS packages
vcs(bool): enable or disable checking of VCS packages
Returns:
List[Package]: list of packages which are out-of-dated
@ -58,7 +58,7 @@ class UpdateHandler(Cleaner):
for local in self.packages():
if local.base in self.ignore_list:
continue
if local.is_vcs and no_vcs:
if local.is_vcs and not vcs:
continue
if filter_packages and local.base not in filter_packages:
continue

View File

@ -23,7 +23,7 @@ from pathlib import Path
from typing import List, Optional, Set, Tuple
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import BuildFailed
from ahriman.core.exceptions import BuildError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.core.util import check_output, exception_response_text
from ahriman.models.sign_settings import SignSettings
@ -138,7 +138,7 @@ class GPG(LazyLogging):
key(str): key ID to import
"""
key_body = self.key_download(server, key)
GPG._check_output("gpg", "--import", input_data=key_body, exception=None, logger=self.logger)
GPG._check_output("gpg", "--import", input_data=key_body, logger=self.logger)
def process(self, path: Path, key: str) -> List[Path]:
"""
@ -153,7 +153,7 @@ class GPG(LazyLogging):
"""
GPG._check_output(
*GPG.sign_command(path, key),
exception=BuildFailed(path.name),
exception=BuildError(path.name),
logger=self.logger)
return [path, path.parent / f"{path.name}.sig"]

View File

@ -78,7 +78,7 @@ class Spawn(Thread, LazyLogging):
result = callback(args, architecture)
queue.put((process_id, result))
def packages_add(self, packages: Iterable[str], now: bool) -> None:
def packages_add(self, packages: Iterable[str], *, now: bool) -> None:
"""
add packages

View File

@ -21,7 +21,7 @@ from typing import Dict, List, Optional, Tuple
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import UnknownPackage
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.core.repository import Repository
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
@ -52,7 +52,7 @@ class Watcher(LazyLogging):
"""
self.architecture = architecture
self.database = database
self.repository = Repository(architecture, configuration, database, no_report=True, unsafe=False)
self.repository = Repository(architecture, configuration, database, report=False, unsafe=False)
self.known: Dict[str, Tuple[Package, BuildStatus]] = {}
self.status = BuildStatus()
@ -83,7 +83,7 @@ class Watcher(LazyLogging):
try:
return self.known[base]
except KeyError:
raise UnknownPackage(base)
raise UnknownPackageError(base)
def load(self) -> None:
"""
@ -127,7 +127,7 @@ class Watcher(LazyLogging):
try:
package, _ = self.known[package_base]
except KeyError:
raise UnknownPackage(package_base)
raise UnknownPackageError(package_base)
full_status = BuildStatus(status)
self.known[package_base] = (package, full_status)
self.database.package_update(package, full_status)

View File

@ -110,7 +110,7 @@ class Tree:
>>>
>>> configuration = Configuration()
>>> database = SQLite.load(configuration)
>>> repository = Repository("x86_64", configuration, database, no_report=False, unsafe=False)
>>> repository = Repository("x86_64", configuration, database, report=True, unsafe=False)
>>> packages = repository.packages()
>>>
>>> tree = Tree.load(packages, configuration.repository_paths, database)

View File

@ -26,7 +26,7 @@ from types import ModuleType
from typing import Generator, Iterable
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import InvalidExtension
from ahriman.core.exceptions import ExtensionError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.core.triggers import Trigger
from ahriman.models.package import Package
@ -136,7 +136,7 @@ class TriggerLoader(LazyLogging):
try:
return importlib.import_module(package)
except ModuleNotFoundError:
raise InvalidExtension(f"Module {package} not found")
raise ExtensionError(f"Module {package} not found")
def load_trigger(self, module_path: str) -> Trigger:
"""
@ -163,15 +163,15 @@ class TriggerLoader(LazyLogging):
trigger_type = getattr(module, class_name, None)
if not isinstance(trigger_type, type):
raise InvalidExtension(f"{class_name} of {package_or_path} is not a type")
raise ExtensionError(f"{class_name} of {package_or_path} is not a type")
self.logger.info("loaded type %s of package %s", class_name, package_or_path)
try:
trigger = trigger_type(self.architecture, self.configuration)
except Exception:
raise InvalidExtension(f"Could not load instance of trigger from {class_name} of {package_or_path}")
raise ExtensionError(f"Could not load instance of trigger from {class_name} of {package_or_path}")
if not isinstance(trigger, Trigger):
raise InvalidExtension(f"Class {class_name} of {package_or_path} is not a Trigger")
raise ExtensionError(f"Class {class_name} of {package_or_path} is not a Trigger")
return trigger

View File

@ -58,4 +58,4 @@ class Rsync(Upload):
path(Path): local path to sync
built_packages(Iterable[Package]): list of packages which has just been built
"""
Rsync._check_output(*self.command, str(path), self.remote, exception=None, logger=self.logger)
Rsync._check_output(*self.command, str(path), self.remote, logger=self.logger)

View File

@ -23,7 +23,7 @@ from pathlib import Path
from typing import Iterable, Type
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import SyncFailed
from ahriman.core.exceptions import SynchronizationError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.models.package import Package
from ahriman.models.upload_settings import UploadSettings
@ -108,7 +108,7 @@ class Upload(LazyLogging):
self.sync(path, built_packages)
except Exception:
self.logger.exception("remote sync failed")
raise SyncFailed()
raise SynchronizationError()
def sync(self, path: Path, built_packages: Iterable[Package]) -> None:
"""

View File

@ -29,7 +29,7 @@ from logging import Logger
from pathlib import Path
from typing import Any, Dict, Generator, IO, Iterable, List, Optional, Type, Union
from ahriman.core.exceptions import InvalidOption, UnsafeRun
from ahriman.core.exceptions import OptionError, UnsafeRunError
from ahriman.models.repository_paths import RepositoryPaths
@ -37,7 +37,7 @@ __all__ = ["check_output", "check_user", "exception_response_text", "filter_json
"package_like", "pretty_datetime", "pretty_size", "safe_filename", "walk"]
def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path] = None,
def check_output(*args: str, exception: Optional[Exception] = None, cwd: Optional[Path] = None,
input_data: Optional[str] = None, logger: Optional[Logger] = None, user: Optional[int] = None) -> str:
"""
subprocess wrapper
@ -59,7 +59,7 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
Examples:
Simply call the function::
>>> check_output("echo", "hello world", exception=None)
>>> check_output("echo", "hello world")
The more complicated calls which include result logging and input data are also possible::
@ -67,7 +67,7 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
>>>
>>> logger = logging.getLogger()
>>> check_output("python", "-c", "greeting = input('say hello: '); print(); print(greeting)",
>>> exception=None, input_data="hello world", logger=logger)
>>> input_data="hello world", logger=logger)
An additional argument ``exception`` can be supplied in order to override the default exception::
@ -114,7 +114,7 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
return "\n".join(result)
def check_user(paths: RepositoryPaths, unsafe: bool) -> None:
def check_user(paths: RepositoryPaths, *, unsafe: bool) -> None:
"""
check if current user is the owner of the root
@ -137,7 +137,7 @@ def check_user(paths: RepositoryPaths, unsafe: bool) -> None:
current_uid = os.getuid()
root_uid, _ = paths.root_owner
if current_uid != root_uid:
raise UnsafeRun(current_uid, root_uid)
raise UnsafeRunError(current_uid, root_uid)
def enum_values(enum: Type[Enum]) -> List[str]:
@ -261,7 +261,7 @@ def pretty_size(size: Optional[float], level: int = 0) -> str:
return "MiB"
if level == 3:
return "GiB"
raise InvalidOption(level) # must never happen actually
raise OptionError(level) # must never happen actually
if size is None:
return ""

View File

@ -67,8 +67,10 @@ class AURPackage:
>>>
>>>
>>> from ahriman.core.alpm.pacman import Pacman
>>> from ahriman.core.configuration import Configuration
>>>
>>> pacman = Pacman(configuration)
>>> configuration = Configuration()
>>> pacman = Pacman("x86_64", configuration)
>>> metadata = pacman.get("pacman")
>>> package = AURPackage.from_pacman(next(metadata)) # load package from pyalpm wrapper
"""

View File

@ -29,7 +29,7 @@ from typing import Any, Dict, Iterable, List, Optional, Set, Type
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import AUR, Official, OfficialSyncdb
from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.core.exceptions import PackageInfoError
from ahriman.core.lazy_logging import LazyLogging
from ahriman.core.util import check_output, full_version
from ahriman.models.package_description import PackageDescription
@ -183,10 +183,10 @@ class Package(LazyLogging):
Raises:
InvalidPackageInfo: if there are parsing errors
"""
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo", exception=None, cwd=path)
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo", cwd=path)
srcinfo, errors = parse_srcinfo(srcinfo_source)
if errors:
raise InvalidPackageInfo(errors)
raise PackageInfoError(errors)
packages = {key: PackageDescription() for key in srcinfo["packages"]}
version = full_version(srcinfo.get("epoch"), srcinfo["pkgver"], srcinfo["pkgrel"])
@ -211,7 +211,7 @@ class Package(LazyLogging):
return cls(base=dump["base"], version=dump["version"], remote=RemoteSource.from_json(remote), packages=packages)
@classmethod
def from_official(cls: Type[Package], name: str, pacman: Pacman, use_syncdb: bool = True) -> Package:
def from_official(cls: Type[Package], name: str, pacman: Pacman, *, use_syncdb: bool = True) -> Package:
"""
construct package properties from official repository page
@ -254,10 +254,10 @@ class Package(LazyLogging):
package_name = package_name.split(symbol)[0]
return package_name
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo", exception=None, cwd=path)
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo", cwd=path)
srcinfo, errors = parse_srcinfo(srcinfo_source)
if errors:
raise InvalidPackageInfo(errors)
raise PackageInfoError(errors)
makedepends = extract_packages(srcinfo.get("makedepends", []))
# sum over each package
depends = extract_packages(srcinfo.get("depends", []))
@ -281,10 +281,10 @@ class Package(LazyLogging):
Raises:
InvalidPackageInfo: if there are parsing errors
"""
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo", exception=None, cwd=path)
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo", cwd=path)
srcinfo, errors = parse_srcinfo(srcinfo_source)
if errors:
raise InvalidPackageInfo(errors)
raise PackageInfoError(errors)
return set(srcinfo.get("arch", []))
def actual_version(self, paths: RepositoryPaths) -> str:
@ -310,13 +310,13 @@ class Package(LazyLogging):
try:
# update pkgver first
Package._check_output("makepkg", "--nodeps", "--nobuild",
exception=None, cwd=paths.cache_for(self.base), logger=self.logger)
cwd=paths.cache_for(self.base), logger=self.logger)
# generate new .SRCINFO and put it to parser
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo",
exception=None, cwd=paths.cache_for(self.base), logger=self.logger)
cwd=paths.cache_for(self.base), logger=self.logger)
srcinfo, errors = parse_srcinfo(srcinfo_source)
if errors:
raise InvalidPackageInfo(errors)
raise PackageInfoError(errors)
return full_version(srcinfo.get("epoch"), srcinfo["pkgver"], srcinfo["pkgrel"])
except Exception:
@ -358,7 +358,7 @@ class Package(LazyLogging):
return sorted(result)
def is_outdated(self, remote: Package, paths: RepositoryPaths, calculate_version: bool = True) -> bool:
def is_outdated(self, remote: Package, paths: RepositoryPaths, *, calculate_version: bool = True) -> bool:
"""
check if package is out-of-dated

View File

@ -57,7 +57,7 @@ class PackageDescription:
>>> from ahriman.core.configuration import Configuration
>>>
>>> configuration = Configuration()
>>> pacman = Pacman(configuration)
>>> pacman = Pacman("x86_64", configuration)
>>> pyalpm_description = next(package for package in pacman.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

@ -26,7 +26,7 @@ from dataclasses import dataclass
from pathlib import Path
from typing import Set, Tuple, Type
from ahriman.core.exceptions import InvalidPath
from ahriman.core.exceptions import PathError
@dataclass(frozen=True)
@ -87,6 +87,16 @@ class RepositoryPaths:
"""
return self.root / "packages" / self.architecture
@property
def pacman(self) -> Path:
"""
get directory for pacman local package cache
Returns:
Path: full path to pacman local database cache
"""
return self.root / "pacman" / self.architecture
@property
def repository(self) -> Path:
"""
@ -168,7 +178,7 @@ class RepositoryPaths:
os.chown(current, root_uid, root_gid, follow_symlinks=False)
if self.root not in path.parents:
raise InvalidPath(path, self.root)
raise PathError(path, self.root)
root_uid, root_gid = self.root_owner
while path != self.root:
set_owner(path)
@ -194,6 +204,7 @@ class RepositoryPaths:
self.cache,
self.chroot,
self.packages,
self.pacman / "sync", # we need sync directory in order to be able to copy databases
self.repository,
):
directory.mkdir(mode=0o755, parents=True, exist_ok=True)

View File

@ -21,7 +21,7 @@ from __future__ import annotations
from typing import Any, List, Optional, Iterable
from ahriman.core.exceptions import SuccessFailed
from ahriman.core.exceptions import UnprocessedPackageStatusError
from ahriman.models.package import Package
@ -111,7 +111,7 @@ class Result:
self.add_failed(package)
for base, package in other._success.items():
if base in self._failed:
raise SuccessFailed(base)
raise UnprocessedPackageStatusError(base)
self.add_success(package)
return self

View File

@ -21,8 +21,8 @@ from __future__ import annotations
from dataclasses import dataclass, replace
from typing import Optional, Type
from passlib.pwd import genword as generate_password # type: ignore
from passlib.handlers.sha2_crypt import sha512_crypt # type: ignore
from passlib.pwd import genword as generate_password
from passlib.handlers.sha2_crypt import sha512_crypt
from ahriman.models.user_access import UserAccess

View File

@ -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.2.2"
__version__ = "2.3.0rc3"

View File

@ -19,7 +19,7 @@
#
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
from ahriman.core.exceptions import UnknownPackage
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
@ -54,7 +54,7 @@ class PackageView(BaseView):
try:
package, status = self.service.get(base)
except UnknownPackage:
except UnknownPackageError:
raise HTTPNotFound()
response = [
@ -104,7 +104,7 @@ class PackageView(BaseView):
try:
self.service.update(base, status, package)
except UnknownPackage:
except UnknownPackageError:
raise HTTPBadRequest(reason=f"Package {base} is unknown, but no package body set")
raise HTTPNoContent()

View File

@ -26,7 +26,7 @@ from aiohttp import web
from ahriman.core.auth import Auth
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import InitializeException
from ahriman.core.exceptions import InitializeError
from ahriman.core.spawn import Spawn
from ahriman.core.status.watcher import Watcher
from ahriman.web.middlewares.exception_handler import exception_handler
@ -62,7 +62,7 @@ async def on_startup(application: web.Application) -> None:
except Exception:
message = "could not load packages"
application.logger.exception(message)
raise InitializeException(message)
raise InitializeError(message)
def run_server(application: web.Application) -> None:

View File

@ -24,7 +24,7 @@ def application_packages(configuration: Configuration, database: SQLite, mocker:
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
return ApplicationPackages("x86_64", configuration, no_report=True, unsafe=False)
return ApplicationPackages("x86_64", configuration, report=False, unsafe=False)
@pytest.fixture
@ -43,7 +43,7 @@ def application_properties(configuration: Configuration, database: SQLite,
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
return ApplicationProperties("x86_64", configuration, no_report=True, unsafe=False)
return ApplicationProperties("x86_64", configuration, report=False, unsafe=False)
@pytest.fixture
@ -62,4 +62,4 @@ def application_repository(configuration: Configuration, database: SQLite,
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
return ApplicationRepository("x86_64", configuration, no_report=True, unsafe=False)
return ApplicationRepository("x86_64", configuration, report=False, unsafe=False)

View File

@ -1,7 +1,7 @@
import pytest
from pytest_mock import MockerFixture
from unittest import mock
from unittest.mock import call as MockCall
from ahriman.application.application.application_repository import ApplicationRepository
from ahriman.core.tree import Leaf, Tree
@ -22,7 +22,7 @@ def test_clean_cache(application_repository: ApplicationRepository, mocker: Mock
must clean cache directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache")
application_repository.clean(True, False, False, False)
application_repository.clean(cache=True, chroot=False, manual=False, packages=False, pacman=False)
clear_mock.assert_called_once_with()
@ -31,7 +31,7 @@ def test_clean_chroot(application_repository: ApplicationRepository, mocker: Moc
must clean chroot directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
application_repository.clean(False, True, False, False)
application_repository.clean(cache=False, chroot=True, manual=False, packages=False, pacman=False)
clear_mock.assert_called_once_with()
@ -40,7 +40,7 @@ def test_clean_manual(application_repository: ApplicationRepository, mocker: Moc
must clean manual directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_queue")
application_repository.clean(False, False, True, False)
application_repository.clean(cache=False, chroot=False, manual=True, packages=False, pacman=False)
clear_mock.assert_called_once_with()
@ -49,7 +49,16 @@ def test_clean_packages(application_repository: ApplicationRepository, mocker: M
must clean packages directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
application_repository.clean(False, False, False, True)
application_repository.clean(cache=False, chroot=False, manual=False, packages=True, pacman=False)
clear_mock.assert_called_once_with()
def test_clean_pacman(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must clean packages directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_pacman")
application_repository.clean(cache=False, chroot=False, manual=False, packages=False, pacman=True)
clear_mock.assert_called_once_with()
@ -68,8 +77,8 @@ def test_sign(application_repository: ApplicationRepository, package_ahriman: Pa
application_repository.sign([])
copy_mock.assert_has_calls([
mock.call(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int)),
mock.call(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int))
MockCall(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int)),
MockCall(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int))
])
update_mock.assert_called_once_with([])
sign_repository_mock.assert_called_once_with(application_repository.repository.repo.repo_path)
@ -168,8 +177,8 @@ def test_update(application_repository: ApplicationRepository, package_ahriman:
application_repository.update([package_ahriman])
build_mock.assert_called_once_with([package_ahriman])
update_mock.assert_has_calls([mock.call(paths), mock.call(paths)])
on_result_mock.assert_has_calls([mock.call(result), mock.call(result)])
update_mock.assert_has_calls([MockCall(paths), MockCall(paths)])
on_result_mock.assert_has_calls([MockCall(result), MockCall(result)])
def test_update_empty(application_repository: ApplicationRepository, package_ahriman: Package,
@ -199,8 +208,8 @@ def test_updates_all(application_repository: ApplicationRepository, package_ahri
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=False, no_local=False, no_manual=False, no_vcs=False, log_fn=print)
updates_aur_mock.assert_called_once_with([], False)
application_repository.updates([], aur=True, local=True, manual=True, vcs=True, log_fn=print)
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_called_once_with()
updates_manual_mock.assert_called_once_with()
@ -214,7 +223,7 @@ def test_updates_disabled(application_repository: ApplicationRepository, mocker:
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=True, no_local=True, no_manual=True, no_vcs=False, log_fn=print)
application_repository.updates([], aur=False, local=False, manual=False, vcs=True, log_fn=print)
updates_aur_mock.assert_not_called()
updates_local_mock.assert_not_called()
updates_manual_mock.assert_not_called()
@ -229,7 +238,7 @@ def test_updates_no_aur(application_repository: ApplicationRepository, mocker: M
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=True, no_local=False, no_manual=False, no_vcs=False, log_fn=print)
application_repository.updates([], aur=False, local=True, manual=True, vcs=True, log_fn=print)
updates_aur_mock.assert_not_called()
updates_local_mock.assert_called_once_with()
updates_manual_mock.assert_called_once_with()
@ -244,8 +253,8 @@ def test_updates_no_local(application_repository: ApplicationRepository, mocker:
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=False, no_local=True, no_manual=False, no_vcs=False, log_fn=print)
updates_aur_mock.assert_called_once_with([], False)
application_repository.updates([], aur=True, local=False, manual=True, vcs=True, log_fn=print)
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_not_called()
updates_manual_mock.assert_called_once_with()
@ -259,8 +268,8 @@ def test_updates_no_manual(application_repository: ApplicationRepository, mocker
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=False, no_local=False, no_manual=True, no_vcs=False, log_fn=print)
updates_aur_mock.assert_called_once_with([], False)
application_repository.updates([], aur=True, local=True, manual=False, vcs=True, log_fn=print)
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_called_once_with()
updates_manual_mock.assert_not_called()
@ -274,8 +283,8 @@ def test_updates_no_vcs(application_repository: ApplicationRepository, mocker: M
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=False, no_local=False, no_manual=False, no_vcs=True, log_fn=print)
updates_aur_mock.assert_called_once_with([], True)
application_repository.updates([], aur=True, local=True, manual=True, vcs=False, log_fn=print)
updates_aur_mock.assert_called_once_with([], vcs=False)
updates_local_mock.assert_called_once_with()
updates_manual_mock.assert_called_once_with()
@ -289,8 +298,7 @@ def test_updates_with_filter(application_repository: ApplicationRepository, mock
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates(["filter"], no_aur=False, no_local=False, no_manual=False, no_vcs=False,
log_fn=print)
updates_aur_mock.assert_called_once_with(["filter"], False)
application_repository.updates(["filter"], aur=True, local=True, manual=True, vcs=True, log_fn=print)
updates_aur_mock.assert_called_once_with(["filter"], vcs=True)
updates_local_mock.assert_called_once_with()
updates_manual_mock.assert_called_once_with()

View File

@ -25,7 +25,7 @@ def application(configuration: Configuration, database: SQLite, mocker: MockerFi
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
return Application("x86_64", configuration, no_report=True, unsafe=False)
return Application("x86_64", configuration, report=False, unsafe=False)
@pytest.fixture
@ -36,7 +36,7 @@ def args() -> argparse.Namespace:
Returns:
argparse.Namespace: command line arguments test instance
"""
return argparse.Namespace(architecture=None, lock=None, force=False, unsafe=False, no_report=True)
return argparse.Namespace(architecture=None, lock=None, force=False, unsafe=False, report=False)
@pytest.fixture

View File

@ -0,0 +1,17 @@
import pytest
from collections import namedtuple
_passwd = namedtuple("passwd", ["pw_dir"])
@pytest.fixture
def passwd() -> _passwd:
"""
get passwd structure for the user
Returns:
_passwd: passwd structure test instance
"""
return _passwd("home")

View File

@ -6,7 +6,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ExitCode, MissingArchitecture, MultipleArchitectures
from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError
def test_architectures_extract(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
@ -29,7 +29,7 @@ def test_architectures_extract_empty(args: argparse.Namespace, configuration: Co
args.configuration = configuration.path
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures", return_value=set())
with pytest.raises(MissingArchitecture):
with pytest.raises(MissingArchitectureError):
Handler.architectures_extract(args)
@ -39,7 +39,7 @@ def test_architectures_extract_exception(args: argparse.Namespace, mocker: Mocke
"""
args.command = "config"
mocker.patch.object(Handler, "ALLOW_AUTO_ARCHITECTURE_RUN", False)
with pytest.raises(MissingArchitecture):
with pytest.raises(MissingArchitectureError):
Handler.architectures_extract(args)
@ -112,7 +112,7 @@ def test_execute_multiple_not_supported(args: argparse.Namespace, mocker: Mocker
args.command = "web"
mocker.patch.object(Handler, "ALLOW_MULTI_ARCHITECTURE_RUN", False)
with pytest.raises(MultipleArchitectures):
with pytest.raises(MultipleArchitecturesError):
Handler.execute(args)
@ -135,7 +135,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration) -> None:
must raise NotImplemented for missing method
"""
with pytest.raises(NotImplementedError):
Handler.run(args, "x86_64", configuration, True, True)
Handler.run(args, "x86_64", configuration, report=True, unsafe=True)
def test_check_if_empty() -> None:

View File

@ -23,6 +23,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
args.package = []
args.exit_code = False
args.now = False
args.refresh = 0
args.source = PackageSource.Auto
args.without_dependencies = False
return args
@ -37,7 +38,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
application_mock = mocker.patch("ahriman.application.application.Application.add")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
Add.run(args, "x86_64", configuration, True, False)
Add.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with(args.package, args.source, args.without_dependencies)
on_start_mock.assert_called_once_with()
@ -57,8 +58,9 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
Add.run(args, "x86_64", configuration, True, False)
updates_mock.assert_called_once_with(args.package, True, True, False, True, pytest.helpers.anyvar(int))
Add.run(args, "x86_64", configuration, report=False, unsafe=False)
updates_mock.assert_called_once_with(args.package, aur=False, local=False, manual=True, vcs=False,
log_fn=pytest.helpers.anyvar(int))
application_mock.assert_called_once_with([package_ahriman])
check_mock.assert_called_once_with(False, False)
@ -76,5 +78,5 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
mocker.patch("ahriman.application.application.Application.updates")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
Add.run(args, "x86_64", configuration, True, False)
Add.run(args, "x86_64", configuration, report=False, unsafe=False)
check_mock.assert_called_once_with(True, True)

View File

@ -33,7 +33,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
add_mock = tarfile.__enter__.return_value = MagicMock()
mocker.patch("tarfile.TarFile.__new__", return_value=tarfile)
Backup.run(args, "x86_64", configuration, True, False)
Backup.run(args, "x86_64", configuration, report=False, unsafe=False)
add_mock.add.assert_called_once_with(Path("path"))

View File

@ -20,6 +20,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
args.chroot = False
args.manual = False
args.packages = False
args.pacman = False
return args
@ -32,6 +33,6 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
application_mock = mocker.patch("ahriman.application.application.Application.clean")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
Clean.run(args, "x86_64", configuration, True, False)
application_mock.assert_called_once_with(False, False, False, False)
Clean.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with(cache=False, chroot=False, manual=False, packages=False, pacman=False)
on_start_mock.assert_called_once_with()

View File

@ -17,10 +17,10 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
argparse.Namespace: generated arguments for these test cases
"""
args.interval = 60 * 60 * 12
args.no_aur = False
args.no_local = False
args.no_manual = False
args.no_vcs = False
args.aur = True
args.local = True
args.manual = True
args.vcs = True
return args
@ -33,8 +33,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
start_mock = mocker.patch("threading.Timer.start")
join_mock = mocker.patch("threading.Timer.join")
Daemon.run(args, "x86_64", configuration, True, False)
Daemon._SHOULD_RUN = False
run_mock.assert_called_once_with(args, "x86_64", configuration, True, False)
Daemon.run(args, "x86_64", configuration, report=True, unsafe=False)
run_mock.assert_called_once_with(args, "x86_64", configuration, report=True, unsafe=False)
start_mock.assert_called_once_with()
join_mock.assert_called_once_with()

View File

@ -15,7 +15,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
application_mock = mocker.patch("ahriman.core.configuration.Configuration.dump",
return_value=configuration.dump())
Dump.run(args, "x86_64", configuration, True, False)
Dump.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with()
print_mock.assert_called()

View File

@ -29,7 +29,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
args = _default_args(args)
parse_mock = mocker.patch("argparse.ArgumentParser.parse_args")
Help.run(args, "x86_64", configuration, True, False)
Help.run(args, "x86_64", configuration, report=False, unsafe=False)
parse_mock.assert_called_once_with(["--help"])
@ -41,7 +41,7 @@ def test_run_command(args: argparse.Namespace, configuration: Configuration, moc
args.command = "aur-search"
parse_mock = mocker.patch("argparse.ArgumentParser.parse_args")
Help.run(args, "x86_64", configuration, True, False)
Help.run(args, "x86_64", configuration, report=False, unsafe=False)
parse_mock.assert_called_once_with(["aur-search", "--help"])

View File

@ -29,7 +29,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
application_mock = mocker.patch("ahriman.core.sign.gpg.GPG.key_import")
KeyImport.run(args, "x86_64", configuration, True, False)
KeyImport.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with(args.key_server, args.key)

View File

@ -42,7 +42,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
return_value=(args.package, PkgbuildPatch(None, "patch")))
application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_create")
Patch.run(args, "x86_64", configuration, True, False)
Patch.run(args, "x86_64", configuration, report=False, unsafe=False)
patch_mock.assert_called_once_with(args.package, args.track)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, PkgbuildPatch(None, "patch"))
@ -60,7 +60,7 @@ def test_run_function(args: argparse.Namespace, configuration: Configuration, mo
patch_mock = mocker.patch("ahriman.application.handlers.Patch.patch_create_from_function", return_value=patch)
application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_create")
Patch.run(args, "x86_64", configuration, True, False)
Patch.run(args, "x86_64", configuration, report=False, unsafe=False)
patch_mock.assert_called_once_with(args.variable, args.patch)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, patch)
@ -75,7 +75,7 @@ def test_run_list(args: argparse.Namespace, configuration: Configuration, mocker
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_list")
Patch.run(args, "x86_64", configuration, True, False)
Patch.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, ["version"], False)
@ -89,7 +89,7 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, mock
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_remove")
Patch.run(args, "x86_64", configuration, True, False)
Patch.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, ["version"])

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