mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 07:17:17 +00:00
add rebuild implementation to interface
This commit is contained in:
parent
20e45845ba
commit
41cc58ed31
@ -20,6 +20,14 @@ ahriman.web.views.service.pgp module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.views.service.rebuild module
|
||||
----------------------------------------
|
||||
|
||||
.. automodule:: ahriman.web.views.service.rebuild
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.views.service.remove module
|
||||
---------------------------------------
|
||||
|
||||
|
@ -40,6 +40,11 @@
|
||||
<i class="bi bi-play"></i> update
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button id="package-rebuild-btn" class="btn dropdown-item" data-bs-toggle="modal" data-bs-target="#package-rebuild-modal" hidden>
|
||||
<i class="bi bi-arrow-clockwise"></i> rebuild
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button id="package-remove-btn" class="btn dropdown-item" onclick="removePackages()" disabled hidden>
|
||||
<i class="bi bi-trash"></i> remove
|
||||
@ -126,6 +131,7 @@
|
||||
{% include "build-status/success-modal.jinja2" %}
|
||||
|
||||
{% include "build-status/package-add-modal.jinja2" %}
|
||||
{% include "build-status/package-rebuild-modal.jinja2" %}
|
||||
{% include "build-status/key-import-modal.jinja2" %}
|
||||
|
||||
{% include "build-status/package-info-modal.jinja2" %}
|
||||
|
@ -0,0 +1,39 @@
|
||||
<div id="package-rebuild-modal" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<form id="package-rebuild-form" onsubmit="return false">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Rebuild depending packages</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label for="dependency-input" class="col-sm-4 col-form-label">dependency</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="dependency-input" type="text" class="form-control" placeholder="packages dependency" name="package" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary" onclick="packagesRebuild()"><i class="bi bi-play"></i> rebuild</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const packageRebuildModal = $("#package-rebuild-modal");
|
||||
const packageRebuildForm = $("#package-rebuild-form");
|
||||
packageRebuildModal.on("hidden.bs.modal", () => { packageRebuildForm.trigger("reset"); });
|
||||
|
||||
const dependencyInput = $("#dependency-input");
|
||||
|
||||
function packagesRebuild() {
|
||||
const packages = dependencyInput.val();
|
||||
if (packages) {
|
||||
packageRebuildModal.modal("hide");
|
||||
doPackageAction("/api/v1/service/rebuild", [packages], "Repository rebuild ran for the following dependencies:", "Repository rebuild failed:");
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,6 +1,7 @@
|
||||
<script>
|
||||
const keyImportButton = $("#key-import-btn");
|
||||
const packageAddButton = $("#package-add-btn");
|
||||
const packageRebuildButton = $("#package-rebuild-btn");
|
||||
const packageRemoveButton = $("#package-remove-btn");
|
||||
const packageUpdateButton = $("#package-update-btn");
|
||||
|
||||
@ -57,6 +58,7 @@
|
||||
function hideControls(hidden) {
|
||||
keyImportButton.attr("hidden", hidden);
|
||||
packageAddButton.attr("hidden", hidden);
|
||||
packageRebuildButton.attr("hidden", hidden);
|
||||
packageRemoveButton.attr("hidden", hidden);
|
||||
packageUpdateButton.attr("hidden", hidden);
|
||||
}
|
||||
|
1
setup.py
1
setup.py
@ -77,6 +77,7 @@ setup(
|
||||
"package/share/ahriman/templates/build-status/login-modal.jinja2",
|
||||
"package/share/ahriman/templates/build-status/package-add-modal.jinja2",
|
||||
"package/share/ahriman/templates/build-status/package-info-modal.jinja2",
|
||||
"package/share/ahriman/templates/build-status/package-rebuild-modal.jinja2",
|
||||
"package/share/ahriman/templates/build-status/success-modal.jinja2",
|
||||
"package/share/ahriman/templates/build-status/table.jinja2",
|
||||
]),
|
||||
|
@ -76,5 +76,5 @@ class Rebuild(Handler):
|
||||
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()]
|
||||
return application.repository.packages()
|
||||
|
@ -102,6 +102,15 @@ class Spawn(Thread, LazyLogging):
|
||||
kwargs["now"] = ""
|
||||
self.spawn_process("package-add", *packages, **kwargs)
|
||||
|
||||
def packages_rebuild(self, depends_on: str) -> None:
|
||||
"""
|
||||
rebuild packages which depend on the specified package
|
||||
|
||||
Args:
|
||||
depends_on(str): packages dependency
|
||||
"""
|
||||
self.spawn_process("repo-rebuild", **{"depends-on": depends_on})
|
||||
|
||||
def packages_remove(self, packages: Iterable[str]) -> None:
|
||||
"""
|
||||
remove packages
|
||||
|
@ -23,6 +23,7 @@ from pathlib import Path
|
||||
from ahriman.web.views.index import IndexView
|
||||
from ahriman.web.views.service.add import AddView
|
||||
from ahriman.web.views.service.pgp import PGPView
|
||||
from ahriman.web.views.service.rebuild import RebuildView
|
||||
from ahriman.web.views.service.remove import RemoveView
|
||||
from ahriman.web.views.service.request import RequestView
|
||||
from ahriman.web.views.service.search import SearchView
|
||||
@ -52,6 +53,8 @@ def setup_routes(application: Application, static_path: Path) -> None:
|
||||
* ``GET /api/v1/service/pgp`` fetch PGP key from the keyserver
|
||||
* ``POST /api/v1/service/pgp`` import PGP key from the keyserver
|
||||
|
||||
* ``POST /api/v1/service/rebuild`` rebuild packages based on their dependency list
|
||||
|
||||
* ``POST /api/v1/service/remove`` remove existing package from repository
|
||||
|
||||
* ``POST /api/v1/service/request`` request to add new packages to repository
|
||||
@ -92,6 +95,8 @@ def setup_routes(application: Application, static_path: Path) -> None:
|
||||
application.router.add_get("/api/v1/service/pgp", PGPView, allow_head=True)
|
||||
application.router.add_post("/api/v1/service/pgp", PGPView)
|
||||
|
||||
application.router.add_post("/api/v1/service/rebuild", RebuildView)
|
||||
|
||||
application.router.add_post("/api/v1/service/remove", RemoveView)
|
||||
|
||||
application.router.add_post("/api/v1/service/request", RequestView)
|
||||
|
75
src/ahriman/web/views/service/rebuild.py
Normal file
75
src/ahriman/web/views/service/rebuild.py
Normal file
@ -0,0 +1,75 @@
|
||||
#
|
||||
# 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 aiohttp.web import HTTPBadRequest, HTTPNoContent
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
class RebuildView(BaseView):
|
||||
"""
|
||||
rebuild packages web view
|
||||
|
||||
Attributes:
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
rebuild packages based on their dependency
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"packages": ["ahriman"] # either list of packages or package name of dependency
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/service/rebuild' -d '{"packages": ["python"]}'
|
||||
> POST /api/v1/service/rebuild HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 24
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Sun, 27 Nov 2022 00:22:26 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
try:
|
||||
data = await self.extract_data(["packages"])
|
||||
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
|
||||
depends_on = next(package for package in packages)
|
||||
except Exception as e:
|
||||
raise HTTPBadRequest(reason=str(e))
|
||||
|
||||
self.spawner.packages_rebuild(depends_on)
|
||||
|
||||
raise HTTPNoContent()
|
@ -75,7 +75,7 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration,
|
||||
args = _default_args(args)
|
||||
args.dry_run = True
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[package_ahriman])
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
|
||||
|
||||
@ -92,6 +92,7 @@ def test_run_filter(args: argparse.Namespace, configuration: Configuration, mock
|
||||
args.depends_on = ["python-aur"]
|
||||
mocker.patch("ahriman.application.application.Application.update")
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
|
||||
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
|
||||
|
||||
Rebuild.run(args, "x86_64", configuration, report=False, unsafe=False)
|
||||
@ -105,6 +106,7 @@ def test_run_without_filter(args: argparse.Namespace, configuration: Configurati
|
||||
args = _default_args(args)
|
||||
mocker.patch("ahriman.application.application.Application.update")
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
|
||||
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
|
||||
|
||||
Rebuild.run(args, "x86_64", configuration, report=False, unsafe=False)
|
||||
@ -120,6 +122,7 @@ def test_run_update_empty_exception(args: argparse.Namespace, configuration: Con
|
||||
args.exit_code = True
|
||||
args.dry_run = True
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages")
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[])
|
||||
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
|
||||
|
||||
@ -135,6 +138,7 @@ def test_run_build_empty_exception(args: argparse.Namespace, configuration: Conf
|
||||
args = _default_args(args)
|
||||
args.exit_code = True
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages")
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.application.application.Application.update", return_value=Result())
|
||||
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
|
||||
@ -147,7 +151,7 @@ def test_extract_packages(application: Application, mocker: MockerFixture) -> No
|
||||
"""
|
||||
must extract packages from database
|
||||
"""
|
||||
packages_mock = mocker.patch("ahriman.core.database.SQLite.packages_get")
|
||||
packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages")
|
||||
Rebuild.extract_packages(application, from_database=False)
|
||||
packages_mock.assert_called_once_with()
|
||||
|
||||
@ -156,6 +160,6 @@ def test_extract_packages_from_database(application: Application, mocker: Mocker
|
||||
"""
|
||||
must extract packages from database
|
||||
"""
|
||||
packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages")
|
||||
packages_mock = mocker.patch("ahriman.core.database.SQLite.packages_get")
|
||||
Rebuild.extract_packages(application, from_database=True)
|
||||
packages_mock.assert_called_once_with()
|
||||
|
@ -72,6 +72,15 @@ def test_packages_add_with_build(spawner: Spawn, mocker: MockerFixture) -> None:
|
||||
spawn_mock.assert_called_once_with("package-add", "ahriman", "linux", source="aur", now="")
|
||||
|
||||
|
||||
def test_packages_rebuild(spawner: Spawn, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call package rebuild
|
||||
"""
|
||||
spawn_mock = mocker.patch("ahriman.core.spawn.Spawn.spawn_process")
|
||||
spawner.packages_rebuild("python")
|
||||
spawn_mock.assert_called_once_with("repo-rebuild", **{"depends-on": "python"})
|
||||
|
||||
|
||||
def test_packages_remove(spawner: Spawn, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call package removal
|
||||
|
@ -329,6 +329,7 @@ def test_walk(resource_path_root: Path) -> None:
|
||||
resource_path_root / "web" / "templates" / "build-status" / "login-modal.jinja2",
|
||||
resource_path_root / "web" / "templates" / "build-status" / "package-add-modal.jinja2",
|
||||
resource_path_root / "web" / "templates" / "build-status" / "package-info-modal.jinja2",
|
||||
resource_path_root / "web" / "templates" / "build-status" / "package-rebuild-modal.jinja2",
|
||||
resource_path_root / "web" / "templates" / "build-status" / "success-modal.jinja2",
|
||||
resource_path_root / "web" / "templates" / "build-status" / "table.jinja2",
|
||||
resource_path_root / "web" / "templates" / "static" / "favicon.ico",
|
||||
|
@ -0,0 +1,38 @@
|
||||
import pytest
|
||||
|
||||
from aiohttp.test_utils import TestClient
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.views.service.rebuild import RebuildView
|
||||
|
||||
|
||||
async def test_get_permission() -> None:
|
||||
"""
|
||||
must return correct permission for the request
|
||||
"""
|
||||
for method in ("POST",):
|
||||
request = pytest.helpers.request("", "", method)
|
||||
assert await RebuildView.get_permission(request) == UserAccess.Full
|
||||
|
||||
|
||||
async def test_post(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call post request correctly
|
||||
"""
|
||||
rebuild_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_rebuild")
|
||||
|
||||
response = await client.post("/api/v1/service/rebuild", json={"packages": ["python", "ahriman"]})
|
||||
assert response.ok
|
||||
rebuild_mock.assert_called_once_with("python")
|
||||
|
||||
|
||||
async def test_post_exception(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise exception on missing packages payload
|
||||
"""
|
||||
rebuild_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_rebuild")
|
||||
|
||||
response = await client.post("/api/v1/service/rebuild")
|
||||
assert response.status == 400
|
||||
rebuild_mock.assert_not_called()
|
Loading…
Reference in New Issue
Block a user