mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 15:27:17 +00:00
add shell and version parser
This commit is contained in:
parent
9f134e37b6
commit
f4131b8cd7
10
.github/ISSUE_TEMPLATE/bug-report.md
vendored
10
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@ -11,9 +11,9 @@ assignees: ''
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
### Steps to Reproduce
|
||||
### Steps to reproduce
|
||||
|
||||
Steps to reproduce the behavior (commands, environment etc)
|
||||
Steps to reproduce the behavior (commands, environment etc).
|
||||
|
||||
### Expected behavior
|
||||
|
||||
@ -21,4 +21,8 @@ A clear and concise description of what you expected to happen.
|
||||
|
||||
### Logs
|
||||
|
||||
Add logs to help explain your problem. Logs to stderr can be generated by using `--no-log` command line option.
|
||||
Add logs to help explain your problem. By default, the application writes logs into `/dev/log` which is usually default systemd journal and can be accessed by `journalctl` command.
|
||||
|
||||
You can also attach any additional information which can be helpful, e.g. configuration used by the application (be aware of passwords and other secrets if any); it can be generated by using `ahriman config` command.
|
||||
|
||||
It is also sometimes useful to have information about installed packages which can be accessed by `ahriman version` command.
|
||||
|
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@ -13,7 +13,7 @@ Brief description of the feature required
|
||||
|
||||
### Cause of the feature request
|
||||
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
A clear and concise description of what the problem is. E.g. I'm always frustrated when [...]
|
||||
|
||||
### Proposed changes and/or features
|
||||
|
||||
|
@ -3,9 +3,9 @@
|
||||
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,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,user-add,user-list,user-remove,web} ...
|
||||
[-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,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
|
||||
ArcH linux ReposItory MANager
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
@ -37,7 +37,7 @@ force disable any logging
|
||||
allow to run ahriman as non\-ahriman user. Some actions might be unavailable
|
||||
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
show program's version number and exit
|
||||
|
||||
.SH
|
||||
@ -121,6 +121,9 @@ run triggers
|
||||
\fBahriman\fR \fI\,repo-update\/\fR
|
||||
update packages
|
||||
.TP
|
||||
\fBahriman\fR \fI\,shell\/\fR
|
||||
envoke python shell
|
||||
.TP
|
||||
\fBahriman\fR \fI\,user-add\/\fR
|
||||
create or update user
|
||||
.TP
|
||||
@ -130,6 +133,9 @@ user known users and their access
|
||||
\fBahriman\fR \fI\,user-remove\/\fR
|
||||
remove user
|
||||
.TP
|
||||
\fBahriman\fR \fI\,version\/\fR
|
||||
application version
|
||||
.TP
|
||||
\fBahriman\fR \fI\,web\/\fR
|
||||
web server
|
||||
.SH COMMAND \fI\,'ahriman aur-search'\/\fR
|
||||
@ -544,6 +550,11 @@ do not include manual updates
|
||||
\fB\-\-no\-vcs\fR
|
||||
do not check VCS packages
|
||||
|
||||
.SH COMMAND \fI\,'ahriman shell'\/\fR
|
||||
usage: ahriman shell [-h]
|
||||
|
||||
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 {UserAccess.Unauthorized,UserAccess.Read,UserAccess.Reporter,UserAccess.Full}] [-s]
|
||||
@ -606,6 +617,11 @@ username for web service
|
||||
\fB\-s\fR, \fB\-\-secure\fR
|
||||
set file permissions to user\-only
|
||||
|
||||
.SH COMMAND \fI\,'ahriman version'\/\fR
|
||||
usage: ahriman version [-h]
|
||||
|
||||
print application and its dependencies versions
|
||||
|
||||
.SH COMMAND \fI\,'ahriman web'\/\fR
|
||||
usage: ahriman web [-h]
|
||||
|
||||
|
@ -116,6 +116,14 @@ ahriman.application.handlers.setup module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.shell module
|
||||
-----------------------------------------
|
||||
|
||||
.. automodule:: ahriman.application.handlers.shell
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.sign module
|
||||
----------------------------------------
|
||||
|
||||
@ -172,6 +180,14 @@ ahriman.application.handlers.users module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.versions module
|
||||
--------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.application.handlers.versions
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.web module
|
||||
---------------------------------------
|
||||
|
||||
|
@ -76,6 +76,14 @@ ahriman.core.formatters.user\_printer module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.formatters.version\_printer module
|
||||
-----------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.formatters.version_printer
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
|
@ -104,5 +104,4 @@ autodoc_member_order = "groupwise"
|
||||
|
||||
autodoc_default_options = {
|
||||
"no-undoc-members": True,
|
||||
"special-members": "__init__",
|
||||
}
|
||||
|
18
package/share/ahriman/templates/shell
Normal file
18
package/share/ahriman/templates/shell
Normal file
@ -0,0 +1,18 @@
|
||||
[00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[49;38;5;60m▄[39m [38;5;60m▄▄▄▄▄▄[48;5;60m█[49m▀[39m [00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67;38;5;60m▄[48;5;60;38;5;103m▄[48;5;103;38;5;110m▄[48;5;60;38;5;103m▄[38;5;67m▄▄▄[48;5;67m██[38;5;60m▄▄▄[38;5;67m█[48;5;60m▄[49;38;5;60m▄[39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m█[48;5;60;38;5;103m▄[48;5;103;38;5;110m▄[48;5;110m██[48;5;103;38;5;103m█[48;5;60;38;5;67m▄▄▄▄[48;5;67;38;5;60m▄[38;5;67m██[48;5;60m▄[48;5;67;38;5;60m▄[38;5;67m█[48;5;60m▄[49;38;5;60m▄[39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67m▄[48;5;103;38;5;103m█[48;5;110;38;5;110m█[48;5;103;38;5;103m█[48;5;110;38;5;110m██[48;5;103;38;5;103m█[48;5;60;38;5;110m▄▄[38;5;103m▄[48;5;110;38;5;110m█[48;5;60;38;5;60m█[48;5;67;38;5;67m██[48;5;60m▄[48;5;67;38;5;60m▄[38;5;67m█[48;5;60m▄[49;38;5;60m▄[39m [38;5;60m▄[39m[00m
|
||||
[48;5;60;38;5;60m█[38;5;67m▄[48;5;103;38;5;60m▄[48;5;110;38;5;103m▄[38;5;110m█████████[48;5;60m▄▄[48;5;67;38;5;60m▄▄[49m▀[48;5;67m▄[38;5;67m█[48;5;60m▄[38;5;60m█[49m▀[39m[00m
|
||||
[48;5;60;38;5;60m█[49m▄▀[48;5;67m▄[48;5;103;38;5;103m█[48;5;110;38;5;110m███[38;5;232m▄[38;5;110m█[38;5;232m▄▄▄▄[38;5;110m███[48;5;103m▄[49;38;5;103m▄[39m [38;5;60m▀▀[39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67m▄[48;5;60;38;5;67m▄▄[48;5;103;38;5;103m█[48;5;110;38;5;110m███[38;5;232m▄[48;5;232m█[48;5;188;38;5;188m█[48;5;231;38;5;231m██[48;5;232;38;5;232m██[48;5;110;38;5;110m███[48;5;103;38;5;103m█[49;39m [00m
|
||||
[38;5;60m▄▄[48;5;60;38;5;67m▄▄▄▄[49;38;5;60m▄▄▄[39m [38;5;60m▀[48;5;60m█[38;5;67m▄[48;5;103;38;5;103m█[48;5;110;38;5;110m████[48;5;232m▄[48;5;145;38;5;231m▄[48;5;232;38;5;145m▄[38;5;231m▄[38;5;232m█[38;5;231m▄[48;5;110;38;5;110m██[48;5;103;38;5;103m█[49m▄[39m [00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m███[38;5;60m▄▄▄▄▄▄[48;5;60;38;5;67m▄▄[49;38;5;60m▄[39m [38;5;60m▀[39m [38;5;103m▀[48;5;110m▄[38;5;110m█████[48;5;188m▄▄[48;5;110m█[38;5;103m▄[38;5;110m██[48;5;103;38;5;103m█[49;39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m██[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m████[38;5;60m▄▄[48;5;60m█[48;5;67m▄[38;5;67m█[48;5;60;38;5;60m█[49;38;5;103m▄▄▄[39m [48;5;103;38;5;103m█[48;5;110;38;5;110m██[48;5;103m▄[48;5;110;38;5;103m▄▄▄▄▄[48;5;103m█[49m▀▀[39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m█[48;5;60;38;5;60m█[48;5;67;38;5;67m████[38;5;60m▄[49m▀[39m [48;5;60;38;5;103m▄[48;5;103;38;5;110m▄[48;5;110;38;5;179m▄[38;5;110m██[48;5;103m▄▄▄[48;5;110m████[48;5;103;38;5;103m█[49;39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m█[48;5;60m▄[48;5;67;38;5;60m▄[38;5;67m███[48;5;60;38;5;60m█[49;39m [48;5;103;38;5;103m█[48;5;185;38;5;110m▄[48;5;110m█[38;5;179m▄[48;5;179;38;5;110m▄[48;5;110m████████[48;5;103;38;5;103m█[49;39m [00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m█[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m██[38;5;60m▄[49m▀[39m [38;5;103m▀[48;5;110m▄[38;5;110m████[38;5;103m▄[38;5;110m██[38;5;103m▄[38;5;110m██[38;5;103m▄[48;5;103;38;5;67m▄[49;39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m██[48;5;60;38;5;60m█[48;5;67;38;5;67m███[48;5;60;38;5;60m█[49;39m [38;5;103m▄[48;5;103m█[48;5;110;38;5;110m██[38;5;103m▄[48;5;103;38;5;110m▄[38;5;67m▄[38;5;103m█[38;5;110m▄[48;5;110m█[48;5;103;38;5;103m█[48;5;110;38;5;110m█[48;5;67;38;5;67m█[49;39m [00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67;38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m███[38;5;60m▄[49m▀[39m [38;5;103m▄[48;5;103;38;5;110m▄[48;5;110m██[48;5;103;38;5;103m█[48;5;110;38;5;110m█[48;5;67;38;5;67m█[49;39m [48;5;103;38;5;103m█[48;5;110;38;5;110m██[48;5;103m▄[48;5;110;38;5;103m▄[48;5;67;38;5;110m▄[49;38;5;67m▄[39m [00m
|
||||
[38;5;60m▄▄▄[48;5;60m█[48;5;67;38;5;67m█[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m█[38;5;60m▄▄▄[49m▀[39m [48;5;103;38;5;103m█[48;5;110;38;5;110m███[48;5;103;38;5;103m█[48;5;110;38;5;67m▄[48;5;67m█[49;39m [48;5;103;38;5;103m█[48;5;110;38;5;110m███[48;5;103;38;5;103m█[48;5;110;38;5;67m▄[48;5;67m█[49;39m [00m
|
||||
[38;5;60m▀▀▀▀▀▀▀[39m [38;5;103m▀▀▀▀[39m [38;5;103m▀▀▀▀[39m [00m
|
1
setup.py
1
setup.py
@ -66,6 +66,7 @@ setup(
|
||||
"package/share/ahriman/templates/build-status.jinja2",
|
||||
"package/share/ahriman/templates/email-index.jinja2",
|
||||
"package/share/ahriman/templates/repo-index.jinja2",
|
||||
"package/share/ahriman/templates/shell",
|
||||
"package/share/ahriman/templates/telegram-index.jinja2",
|
||||
]),
|
||||
("share/ahriman/templates/build-status", [
|
||||
|
@ -26,6 +26,7 @@ from typing import List, TypeVar
|
||||
|
||||
from ahriman import version
|
||||
from ahriman.application import handlers
|
||||
from ahriman.core.util import enum_values
|
||||
from ahriman.models.action import Action
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
from ahriman.models.package_source import PackageSource
|
||||
@ -76,7 +77,7 @@ def _parser() -> argparse.ArgumentParser:
|
||||
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")
|
||||
parser.add_argument("-v", "--version", action="version", version=version.__version__)
|
||||
parser.add_argument("-V", "--version", action="version", version=version.__version__)
|
||||
|
||||
subparsers = parser.add_subparsers(title="command", help="command to run", dest="command", required=True)
|
||||
|
||||
@ -106,9 +107,11 @@ def _parser() -> argparse.ArgumentParser:
|
||||
_set_repo_sync_parser(subparsers)
|
||||
_set_repo_triggers_parser(subparsers)
|
||||
_set_repo_update_parser(subparsers)
|
||||
_set_shell_parser(subparsers)
|
||||
_set_user_add_parser(subparsers)
|
||||
_set_user_list_parser(subparsers)
|
||||
_set_user_remove_parser(subparsers)
|
||||
_set_version_parser(subparsers)
|
||||
_set_web_parser(subparsers)
|
||||
|
||||
return parser
|
||||
@ -225,7 +228,7 @@ def _set_package_add_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("-n", "--now", help="run update function after", action="store_true")
|
||||
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
|
||||
type=PackageSource, choices=PackageSource, default=PackageSource.Auto)
|
||||
type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
|
||||
parser.add_argument("--without-dependencies", help="do not add dependencies", action="store_true")
|
||||
parser.set_defaults(handler=handlers.Add)
|
||||
return parser
|
||||
@ -267,7 +270,7 @@ def _set_package_status_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("-i", "--info", help="show additional package information", action="store_true")
|
||||
parser.add_argument("-s", "--status", help="filter packages by status",
|
||||
type=BuildStatusEnum, choices=BuildStatusEnum)
|
||||
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
|
||||
parser.set_defaults(handler=handlers.Status, lock=None, no_report=True, quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
@ -309,7 +312,7 @@ def _set_package_status_update_parser(root: SubParserAction) -> argparse.Argumen
|
||||
"If no packages supplied, service status will be updated",
|
||||
nargs="*")
|
||||
parser.add_argument("-s", "--status", help="new status",
|
||||
type=BuildStatusEnum, choices=BuildStatusEnum, default=BuildStatusEnum.Success)
|
||||
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,
|
||||
unsafe=True)
|
||||
return parser
|
||||
@ -556,7 +559,7 @@ def _set_repo_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
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=SignSettings)
|
||||
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)
|
||||
return parser
|
||||
@ -594,7 +597,7 @@ def _set_repo_status_update_parser(root: SubParserAction) -> argparse.ArgumentPa
|
||||
parser = root.add_parser("repo-status-update", help="update repository status",
|
||||
description="update repository status on the status page", formatter_class=_formatter)
|
||||
parser.add_argument("-s", "--status", help="new status",
|
||||
type=BuildStatusEnum, choices=BuildStatusEnum, default=BuildStatusEnum.Success)
|
||||
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
|
||||
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, no_report=True, package=[],
|
||||
quiet=True, unsafe=True)
|
||||
return parser
|
||||
@ -661,6 +664,24 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
return parser
|
||||
|
||||
|
||||
def _set_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for shell subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("shell", help="envoke python shell",
|
||||
description="drop into python shell while having created application",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("-v", "--verbose", help=argparse.SUPPRESS, action="store_true")
|
||||
parser.set_defaults(handler=handlers.Shell, lock=None, no_report=True)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for create user subcommand
|
||||
@ -680,7 +701,7 @@ def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser.add_argument("-p", "--password", help="user password. Blank password will be treated as empty password, "
|
||||
"which is in particular must be used for OAuth2 authorization type.")
|
||||
parser.add_argument("-r", "--role", help="user access level",
|
||||
type=UserAccess, choices=UserAccess, default=UserAccess.Read)
|
||||
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,
|
||||
quiet=True, unsafe=True)
|
||||
@ -702,7 +723,7 @@ def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
formatter_class=_formatter)
|
||||
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=UserAccess)
|
||||
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
|
||||
password="", quiet=True, unsafe=True)
|
||||
return parser
|
||||
@ -728,6 +749,23 @@ def _set_user_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
return parser
|
||||
|
||||
|
||||
def _set_version_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for version subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
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,
|
||||
unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for web subcommand
|
||||
|
@ -32,6 +32,7 @@ from ahriman.application.handlers.remove_unknown import RemoveUnknown
|
||||
from ahriman.application.handlers.restore import Restore
|
||||
from ahriman.application.handlers.search import Search
|
||||
from ahriman.application.handlers.setup import Setup
|
||||
from ahriman.application.handlers.shell import Shell
|
||||
from ahriman.application.handlers.sign import Sign
|
||||
from ahriman.application.handlers.status import Status
|
||||
from ahriman.application.handlers.status_update import StatusUpdate
|
||||
@ -39,4 +40,5 @@ from ahriman.application.handlers.triggers import Triggers
|
||||
from ahriman.application.handlers.unsafe_commands import UnsafeCommands
|
||||
from ahriman.application.handlers.update import Update
|
||||
from ahriman.application.handlers.users import Users
|
||||
from ahriman.application.handlers.versions import Versions
|
||||
from ahriman.application.handlers.web import Web
|
||||
|
59
src/ahriman/application/handlers/shell.py
Normal file
59
src/ahriman/application/handlers/shell.py
Normal file
@ -0,0 +1,59 @@
|
||||
#
|
||||
# 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 argparse
|
||||
import code
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Type
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.formatters import StringPrinter
|
||||
|
||||
|
||||
class Shell(Handler):
|
||||
"""
|
||||
python 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:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
Args:
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force 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)
|
||||
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())
|
87
src/ahriman/application/handlers/versions.py
Normal file
87
src/ahriman/application/handlers/versions.py
Normal file
@ -0,0 +1,87 @@
|
||||
#
|
||||
# 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 argparse
|
||||
import pkg_resources
|
||||
import sys
|
||||
|
||||
from typing import Dict, List, Tuple, Type
|
||||
|
||||
from ahriman import version
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.formatters import VersionPrinter
|
||||
|
||||
|
||||
class Versions(Handler):
|
||||
"""
|
||||
version 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:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
Args:
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
VersionPrinter(f"Module version {version.__version__}",
|
||||
{"Python": sys.version}).print(verbose=False, separator=" ")
|
||||
packages = Versions.package_dependencies("ahriman", ("pacman", "s3", "web"))
|
||||
VersionPrinter("Installed packages", packages).print(verbose=False, separator=" ")
|
||||
|
||||
@staticmethod
|
||||
def package_dependencies(root: str, root_extras: Tuple[str, ...] = ()) -> Dict[str, str]:
|
||||
"""
|
||||
extract list of ahriman package dependencies installed into system with their versions
|
||||
|
||||
Args:
|
||||
root(str): root package name
|
||||
root_extras(Tuple[str, ...]): extras for the root package (Default value = ())
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: map of installed dependency to its version
|
||||
"""
|
||||
resources: Dict[str, pkg_resources.Distribution] = pkg_resources.working_set.by_key # type: ignore
|
||||
|
||||
def dependencies_by_key(key: str, extras: Tuple[str, ...] = ()) -> List[str]:
|
||||
return [entry.key for entry in resources[key].requires(extras)]
|
||||
|
||||
keys: List[str] = []
|
||||
portion = {key for key in dependencies_by_key(root, root_extras) if key in resources}
|
||||
while portion:
|
||||
keys.extend(portion)
|
||||
portion = {
|
||||
key
|
||||
for key in sum([dependencies_by_key(key) for key in portion], start=[])
|
||||
if key not in keys and key in resources
|
||||
}
|
||||
|
||||
return {
|
||||
resource.project_name: resource.version
|
||||
for resource in map(lambda key: resources[key], keys)
|
||||
}
|
@ -27,3 +27,4 @@ from ahriman.core.formatters.package_printer import PackagePrinter
|
||||
from ahriman.core.formatters.status_printer import StatusPrinter
|
||||
from ahriman.core.formatters.update_printer import UpdatePrinter
|
||||
from ahriman.core.formatters.user_printer import UserPrinter
|
||||
from ahriman.core.formatters.version_printer import VersionPrinter
|
||||
|
@ -25,6 +25,9 @@ from ahriman.core.formatters import Printer
|
||||
class StringPrinter(Printer):
|
||||
"""
|
||||
print content of the random string
|
||||
|
||||
Attributes:
|
||||
content(str): any content string
|
||||
"""
|
||||
|
||||
def __init__(self, content: str) -> None:
|
||||
|
55
src/ahriman/core/formatters/version_printer.py
Normal file
55
src/ahriman/core/formatters/version_printer.py
Normal file
@ -0,0 +1,55 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
from typing import Dict, List
|
||||
|
||||
from ahriman.core.formatters import StringPrinter
|
||||
from ahriman.models.property import Property
|
||||
|
||||
|
||||
class VersionPrinter(StringPrinter):
|
||||
"""
|
||||
print content of the python package versions
|
||||
|
||||
Attributes:
|
||||
packages(Dict[str, str]): map of package name to its version
|
||||
"""
|
||||
|
||||
def __init__(self, title: str, packages: Dict[str, str]) -> None:
|
||||
"""
|
||||
default constructor
|
||||
|
||||
Args:
|
||||
title(str): title of the message
|
||||
packages(Dict[str, str]): map of package name to its version
|
||||
"""
|
||||
StringPrinter.__init__(self, title)
|
||||
self.packages = packages
|
||||
|
||||
def properties(self) -> List[Property]:
|
||||
"""
|
||||
convert content into printable data
|
||||
|
||||
Returns:
|
||||
List[Property]: list of content properties
|
||||
"""
|
||||
return [
|
||||
Property(package, version, is_required=True)
|
||||
for package, version in sorted(self.packages.items())
|
||||
]
|
@ -18,7 +18,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import datetime
|
||||
import io
|
||||
import os
|
||||
from enum import Enum
|
||||
|
||||
import requests
|
||||
import shutil
|
||||
import subprocess
|
||||
@ -27,14 +30,14 @@ import tempfile
|
||||
from contextlib import contextmanager
|
||||
from logging import Logger
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Generator, Iterable, List, Optional, Union
|
||||
from typing import Any, Dict, Generator, IO, Iterable, List, Optional, Type, Union
|
||||
|
||||
from ahriman.core.exceptions import InvalidOption, UnsafeRun
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
__all__ = ["check_output", "check_user", "exception_response_text", "filter_json", "full_version", "package_like",
|
||||
"pretty_datetime", "pretty_size", "tmpdir", "walk"]
|
||||
__all__ = ["check_output", "check_user", "exception_response_text", "filter_json", "full_version", "enum_values",
|
||||
"package_like", "pretty_datetime", "pretty_size", "tmpdir", "walk"]
|
||||
|
||||
|
||||
def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path] = None,
|
||||
@ -73,6 +76,11 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
|
||||
|
||||
>>> check_output("false", exception=RuntimeError("An exception occurred"))
|
||||
"""
|
||||
# hack for Optional[IO[str]] handle
|
||||
def get_io(proc: subprocess.Popen[str], channel_name: str) -> IO[str]:
|
||||
channel: Optional[IO[str]] = getattr(proc, channel_name, None)
|
||||
return channel if channel is not None else io.StringIO()
|
||||
|
||||
def log(single: str) -> None:
|
||||
if logger is not None:
|
||||
logger.debug(single)
|
||||
@ -80,14 +88,15 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
|
||||
# FIXME additional workaround for linter and type check which do not know that user arg is supported
|
||||
# pylint: disable=unexpected-keyword-arg
|
||||
with subprocess.Popen(args, cwd=cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
user=user, text=True, encoding="utf8", bufsize=1) as process: # type: ignore
|
||||
user=user, text=True, encoding="utf8", bufsize=1) as process:
|
||||
if input_data is not None:
|
||||
process.stdin.write(input_data)
|
||||
process.stdin.close()
|
||||
input_channel = get_io(process, "stdin")
|
||||
input_channel.write(input_data)
|
||||
input_channel.close()
|
||||
|
||||
# read stdout and append to output result
|
||||
result: List[str] = []
|
||||
for line in iter(process.stdout.readline, ""):
|
||||
for line in iter(get_io(process, "stdout").readline, ""):
|
||||
line = line.strip()
|
||||
if not line: # skip empty lines
|
||||
continue
|
||||
@ -95,7 +104,7 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
|
||||
log(line)
|
||||
|
||||
# read stderr and write info to logs
|
||||
for line in iter(process.stderr.readline, ""):
|
||||
for line in iter(get_io(process, "stderr").readline, ""):
|
||||
log(line.strip())
|
||||
|
||||
process.terminate() # make sure that process is terminated
|
||||
@ -134,6 +143,19 @@ def check_user(paths: RepositoryPaths, unsafe: bool) -> None:
|
||||
raise UnsafeRun(current_uid, root_uid)
|
||||
|
||||
|
||||
def enum_values(enum: Type[Enum]) -> List[str]:
|
||||
"""
|
||||
generate list of enumeration values from the source
|
||||
|
||||
Args:
|
||||
enum(Type[Enum]): source enumeration class
|
||||
|
||||
Returns:
|
||||
List[str]: available enumeration values as string
|
||||
"""
|
||||
return [key.value for key in enum]
|
||||
|
||||
|
||||
def exception_response_text(exception: requests.exceptions.HTTPError) -> str:
|
||||
"""
|
||||
safe response exception text generation
|
||||
|
48
tests/ahriman/application/handlers/test_handler_shell.py
Normal file
48
tests/ahriman/application/handlers/test_handler_shell.py
Normal file
@ -0,0 +1,48 @@
|
||||
import argparse
|
||||
import pytest
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Shell
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
||||
"""
|
||||
default arguments for these test cases
|
||||
|
||||
Args:
|
||||
args(argparse.Namespace): command line arguments fixture
|
||||
|
||||
Returns:
|
||||
argparse.Namespace: generated arguments for these test cases
|
||||
"""
|
||||
args.verbose = False
|
||||
return args
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args = _default_args(args)
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
application_mock = mocker.patch("code.interact")
|
||||
|
||||
Shell.run(args, "x86_64", configuration, True, False)
|
||||
application_mock.assert_called_once_with(local=pytest.helpers.anyvar(int))
|
||||
|
||||
|
||||
def test_run_verbose(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command with verbose option
|
||||
"""
|
||||
args = _default_args(args)
|
||||
args.verbose = True
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
|
||||
application_mock = mocker.patch("code.interact")
|
||||
|
||||
Shell.run(args, "x86_64", configuration, True, False)
|
||||
application_mock.assert_called_once_with(local=pytest.helpers.anyvar(int))
|
||||
print_mock.assert_called_once_with(verbose=False)
|
38
tests/ahriman/application/handlers/test_handler_versions.py
Normal file
38
tests/ahriman/application/handlers/test_handler_versions.py
Normal file
@ -0,0 +1,38 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.application.handlers import Versions
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
application_mock = mocker.patch("ahriman.application.handlers.Versions.package_dependencies")
|
||||
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
|
||||
|
||||
Versions.run(args, "x86_64", configuration, True, False)
|
||||
application_mock.assert_called_once_with("ahriman", ("pacman", "s3", "web"))
|
||||
print_mock.assert_has_calls([mock.call(verbose=False, separator=" "), mock.call(verbose=False, separator=" ")])
|
||||
|
||||
|
||||
def test_package_dependencies() -> None:
|
||||
"""
|
||||
must extract package dependencies
|
||||
"""
|
||||
packages = Versions.package_dependencies("srcinfo")
|
||||
assert packages
|
||||
assert packages.get("parse") is not None
|
||||
|
||||
|
||||
def test_package_dependencies_missing() -> None:
|
||||
"""
|
||||
must extract package dependencies even if some of them are missing
|
||||
"""
|
||||
packages = Versions.package_dependencies("ahriman", ("docs", "pacman", "s3", "web"))
|
||||
assert packages
|
||||
assert packages.get("pyalpm") is not None
|
||||
assert packages.get("Sphinx") is None
|
@ -492,6 +492,15 @@ def test_subparsers_repo_update_architecture(parser: argparse.ArgumentParser) ->
|
||||
assert args.architecture == ["x86_64"]
|
||||
|
||||
|
||||
def test_subparsers_shell(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
shell command must imply lock and no-report
|
||||
"""
|
||||
args = parser.parse_args(["shell"])
|
||||
assert args.lock is None
|
||||
assert args.no_report
|
||||
|
||||
|
||||
def test_subparsers_user_add(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
user-add command must imply action, architecture, lock, no-report, quiet and unsafe
|
||||
@ -575,6 +584,26 @@ def test_subparsers_user_remove_architecture(parser: argparse.ArgumentParser) ->
|
||||
assert args.architecture == [""]
|
||||
|
||||
|
||||
def test_subparsers_version(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
version command must imply architecture, lock, no-report, quiet and unsafe
|
||||
"""
|
||||
args = parser.parse_args(["version"])
|
||||
assert args.architecture == [""]
|
||||
assert args.lock is None
|
||||
assert args.no_report
|
||||
assert args.quiet
|
||||
assert args.unsafe
|
||||
|
||||
|
||||
def test_subparsers_version_architecture(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
version command must correctly parse architecture list
|
||||
"""
|
||||
args = parser.parse_args(["-a", "x86_64", "version"])
|
||||
assert args.architecture == [""]
|
||||
|
||||
|
||||
def test_subparsers_web(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
web command must imply lock, no_report and parser
|
||||
|
@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from ahriman.core.formatters import AurPrinter, ConfigurationPrinter, PackagePrinter, StatusPrinter, StringPrinter, UpdatePrinter, UserPrinter
|
||||
from ahriman.core.formatters import AurPrinter, ConfigurationPrinter, PackagePrinter, StatusPrinter, StringPrinter, \
|
||||
UpdatePrinter, UserPrinter, VersionPrinter
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
from ahriman.models.build_status import BuildStatus
|
||||
from ahriman.models.package import Package
|
||||
@ -94,3 +95,17 @@ def user_printer(user: User) -> UserPrinter:
|
||||
UserPrinter: user printer test instance
|
||||
"""
|
||||
return UserPrinter(user)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def version_printer(package_ahriman: Package) -> VersionPrinter:
|
||||
"""
|
||||
fixture for version printer
|
||||
|
||||
Args:
|
||||
package_ahriman(Package): package fixture
|
||||
|
||||
Returns:
|
||||
VersionPrinter: version printer test instance
|
||||
"""
|
||||
return VersionPrinter("package", {package_ahriman.base: package_ahriman.version})
|
||||
|
15
tests/ahriman/core/formatters/test_version_printer.py
Normal file
15
tests/ahriman/core/formatters/test_version_printer.py
Normal file
@ -0,0 +1,15 @@
|
||||
from ahriman.core.formatters import VersionPrinter
|
||||
|
||||
|
||||
def test_properties(version_printer: VersionPrinter) -> None:
|
||||
"""
|
||||
must return empty properties list
|
||||
"""
|
||||
assert version_printer.properties()
|
||||
|
||||
|
||||
def test_title(version_printer: VersionPrinter) -> None:
|
||||
"""
|
||||
must return non empty title
|
||||
"""
|
||||
assert version_printer.title() is not None
|
@ -10,8 +10,9 @@ from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.exceptions import BuildFailed, InvalidOption, UnsafeRun
|
||||
from ahriman.core.util import check_output, check_user, exception_response_text, filter_json, full_version, \
|
||||
package_like, pretty_datetime, pretty_size, tmpdir, walk
|
||||
enum_values, package_like, pretty_datetime, pretty_size, tmpdir, walk
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
@ -177,6 +178,15 @@ def test_filter_json_empty_value(package_ahriman: Package) -> None:
|
||||
assert "base" not in filter_json(probe, probe.keys())
|
||||
|
||||
|
||||
def test_enum_values() -> None:
|
||||
"""
|
||||
must correctly generate choices from enumeration classes
|
||||
"""
|
||||
values = enum_values(PackageSource)
|
||||
for value in values:
|
||||
assert PackageSource(value).value == value
|
||||
|
||||
|
||||
def test_full_version() -> None:
|
||||
"""
|
||||
must construct full version
|
||||
@ -331,6 +341,7 @@ def test_walk(resource_path_root: Path) -> None:
|
||||
resource_path_root / "web" / "templates" / "build-status.jinja2",
|
||||
resource_path_root / "web" / "templates" / "email-index.jinja2",
|
||||
resource_path_root / "web" / "templates" / "repo-index.jinja2",
|
||||
resource_path_root / "web" / "templates" / "shell",
|
||||
resource_path_root / "web" / "templates" / "telegram-index.jinja2",
|
||||
])
|
||||
local_files = list(sorted(walk(resource_path_root)))
|
||||
|
Loading…
Reference in New Issue
Block a user