mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-10-18 10:48:03 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f28e47ac4 | ||
|
|
0b371b3e77 | ||
|
|
ef47c6fa72 | ||
|
|
81ea175034 | ||
|
|
b73be7abcc | ||
|
|
4537969d5d | ||
|
|
47c39f93b7 |
No files matched your search
@@ -158,6 +158,8 @@ class ApplicationRepository(ApplicationProperties):
|
|||||||
# process already built packages if any
|
# process already built packages if any
|
||||||
if built_packages := self.repository.packages_built(): # speedup a bit
|
if built_packages := self.repository.packages_built(): # speedup a bit
|
||||||
build_result = self.repository.process_update(built_packages, packagers)
|
build_result = self.repository.process_update(built_packages, packagers)
|
||||||
|
for package in build_result.success:
|
||||||
|
self.database.build_queue_clear(package.base)
|
||||||
self.on_result(build_result)
|
self.on_result(build_result)
|
||||||
result.merge(build_result)
|
result.merge(build_result)
|
||||||
|
|
||||||
@@ -172,6 +174,8 @@ class ApplicationRepository(ApplicationProperties):
|
|||||||
for num, partition in enumerate(partitions):
|
for num, partition in enumerate(partitions):
|
||||||
self.logger.info("processing chunk #%i %s", num, [package.base for package in partition])
|
self.logger.info("processing chunk #%i %s", num, [package.base for package in partition])
|
||||||
build_result = builder.update(partition, packagers, bump_pkgrel=bump_pkgrel)
|
build_result = builder.update(partition, packagers, bump_pkgrel=bump_pkgrel)
|
||||||
|
for package in partition:
|
||||||
|
self.database.build_queue_clear(package.base)
|
||||||
self.on_result(build_result)
|
self.on_result(build_result)
|
||||||
result.merge(build_result)
|
result.merge(build_result)
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ class Sources(LazyLogging):
|
|||||||
|
|
||||||
# extract local files...
|
# extract local files...
|
||||||
files = ["PKGBUILD", ".SRCINFO"] + [str(path) for path in Pkgbuild.local_files(sources_dir)]
|
files = ["PKGBUILD", ".SRCINFO"] + [str(path) for path in Pkgbuild.local_files(sources_dir)]
|
||||||
instance.add(sources_dir, *files)
|
instance.add(sources_dir, *files, force=True)
|
||||||
# ...and commit them
|
# ...and commit them
|
||||||
instance.commit(sources_dir)
|
instance.commit(sources_dir)
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ class Sources(LazyLogging):
|
|||||||
git_url, branch = remote.git_source()
|
git_url, branch = remote.git_source()
|
||||||
check_output(*instance.git(), "push", "--quiet", git_url, branch, cwd=sources_dir, logger=instance.logger)
|
check_output(*instance.git(), "push", "--quiet", git_url, branch, cwd=sources_dir, logger=instance.logger)
|
||||||
|
|
||||||
def add(self, sources_dir: Path, *pattern: str, intent_to_add: bool = False) -> None:
|
def add(self, sources_dir: Path, *pattern: str, intent_to_add: bool = False, force: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
track found files via git
|
track found files via git
|
||||||
|
|
||||||
@@ -241,6 +241,7 @@ class Sources(LazyLogging):
|
|||||||
*pattern(str): glob patterns
|
*pattern(str): glob patterns
|
||||||
intent_to_add(bool, optional): record only the fact that it will be added later, acts as
|
intent_to_add(bool, optional): record only the fact that it will be added later, acts as
|
||||||
--intent-to-add git flag (Default value = False)
|
--intent-to-add git flag (Default value = False)
|
||||||
|
force(bool, optional): allow adding ignored files (Default value = False)
|
||||||
"""
|
"""
|
||||||
# glob directory to find files which match the specified patterns
|
# glob directory to find files which match the specified patterns
|
||||||
found_files: list[Path] = []
|
found_files: list[Path] = []
|
||||||
@@ -251,6 +252,8 @@ class Sources(LazyLogging):
|
|||||||
self.logger.info("found matching files %s", found_files)
|
self.logger.info("found matching files %s", found_files)
|
||||||
# add them to index
|
# add them to index
|
||||||
args = ["--intent-to-add"] if intent_to_add else []
|
args = ["--intent-to-add"] if intent_to_add else []
|
||||||
|
if force:
|
||||||
|
args.append("--force")
|
||||||
check_output(*self.git(), "add", *args, *[str(fn.relative_to(sources_dir)) for fn in found_files],
|
check_output(*self.git(), "add", *args, *[str(fn.relative_to(sources_dir)) for fn in found_files],
|
||||||
cwd=sources_dir, logger=self.logger)
|
cwd=sources_dir, logger=self.logger)
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,12 @@ class ArchiveRotationTrigger(Trigger):
|
|||||||
if self.keep_built_packages == 0:
|
if self.keep_built_packages == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
to_remove = repository.package_archives(package.base)
|
current = next(iter(repository.packages([package.base])), None)
|
||||||
|
to_remove = [
|
||||||
|
single
|
||||||
|
for single in repository.package_archives(package.base)
|
||||||
|
if current is None or single.version != current.version
|
||||||
|
]
|
||||||
|
|
||||||
for single in to_remove[:-self.keep_built_packages]:
|
for single in to_remove[:-self.keep_built_packages]:
|
||||||
self.logger.info("removing version %s of package %s", single.version, single.base)
|
self.logger.info("removing version %s of package %s", single.version, single.base)
|
||||||
|
|||||||
@@ -36,6 +36,17 @@ class UpdateHandler(PackageInfo, Cleaner):
|
|||||||
trait to get package update list
|
trait to get package update list
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def _updates_ignored(self) -> set[str]:
|
||||||
|
"""
|
||||||
|
collect package bases excluded from automatic updates
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
set[str]: held and configured ignored package bases
|
||||||
|
"""
|
||||||
|
return set(self._ignore_list) | {
|
||||||
|
package.base for package, status in self.reporter.package_get(None) if status.is_held
|
||||||
|
}
|
||||||
|
|
||||||
def updates_aur(self, filter_packages: Iterable[str], *, vcs: bool) -> list[Package]:
|
def updates_aur(self, filter_packages: Iterable[str], *, vcs: bool) -> list[Package]:
|
||||||
"""
|
"""
|
||||||
check AUR for updates
|
check AUR for updates
|
||||||
@@ -58,9 +69,7 @@ class UpdateHandler(PackageInfo, Cleaner):
|
|||||||
continue
|
continue
|
||||||
raise UnknownPackageError(package.base)
|
raise UnknownPackageError(package.base)
|
||||||
|
|
||||||
ignore_list = self._ignore_list + [
|
ignore_list = self._updates_ignored()
|
||||||
package.base for package, status in self.reporter.package_get(None) if status.is_held
|
|
||||||
]
|
|
||||||
|
|
||||||
result: list[Package] = []
|
result: list[Package] = []
|
||||||
for local in self.packages(filter_packages):
|
for local in self.packages(filter_packages):
|
||||||
@@ -106,8 +115,13 @@ class UpdateHandler(PackageInfo, Cleaner):
|
|||||||
|
|
||||||
return files
|
return files
|
||||||
|
|
||||||
|
ignore_list = self._updates_ignored()
|
||||||
|
|
||||||
result: list[Package] = []
|
result: list[Package] = []
|
||||||
for local in self.packages(filter_packages):
|
for local in self.packages(filter_packages):
|
||||||
|
if local.base in ignore_list:
|
||||||
|
self.logger.info("package %s is held, skip update check", local.base)
|
||||||
|
continue
|
||||||
dependencies = self.reporter.package_dependencies_get(local.base)
|
dependencies = self.reporter.package_dependencies_get(local.base)
|
||||||
if not dependencies.paths:
|
if not dependencies.paths:
|
||||||
continue # skip check if no package dependencies found
|
continue # skip check if no package dependencies found
|
||||||
@@ -141,9 +155,13 @@ class UpdateHandler(PackageInfo, Cleaner):
|
|||||||
"""
|
"""
|
||||||
packages = {local.base: local for local in self.packages()}
|
packages = {local.base: local for local in self.packages()}
|
||||||
local_versions = {package_base: package.version for package_base, package in packages.items()}
|
local_versions = {package_base: package.version for package_base, package in packages.items()}
|
||||||
|
ignore_list = self._updates_ignored()
|
||||||
|
|
||||||
result: list[Package] = []
|
result: list[Package] = []
|
||||||
for cache_dir in self.paths.cache.iterdir():
|
for cache_dir in self.paths.cache.iterdir():
|
||||||
|
if cache_dir.name in ignore_list:
|
||||||
|
self.logger.info("package %s is held, skip update check", cache_dir.name)
|
||||||
|
continue
|
||||||
with self.in_package_context(cache_dir.name, local_versions.get(cache_dir.name)):
|
with self.in_package_context(cache_dir.name, local_versions.get(cache_dir.name)):
|
||||||
try:
|
try:
|
||||||
source = RemoteSource(
|
source = RemoteSource(
|
||||||
@@ -162,6 +180,9 @@ class UpdateHandler(PackageInfo, Cleaner):
|
|||||||
continue # we don't add packages automatically
|
continue # we don't add packages automatically
|
||||||
if local.remote.is_remote:
|
if local.remote.is_remote:
|
||||||
continue # avoid checking AUR packages
|
continue # avoid checking AUR packages
|
||||||
|
if local.base in ignore_list:
|
||||||
|
self.logger.info("package %s is held, skip update check", local.base)
|
||||||
|
continue
|
||||||
|
|
||||||
if PackageVersion(local).is_outdated(remote, self.configuration, calculate_version=vcs):
|
if PackageVersion(local).is_outdated(remote, self.configuration, calculate_version=vcs):
|
||||||
self.reporter.set_pending(local.base)
|
self.reporter.set_pending(local.base)
|
||||||
@@ -191,7 +212,6 @@ class UpdateHandler(PackageInfo, Cleaner):
|
|||||||
self.reporter.set_pending(local.base)
|
self.reporter.set_pending(local.base)
|
||||||
self.event(local.base, EventType.PackageOutdated, "Manual update is requested")
|
self.event(local.base, EventType.PackageOutdated, "Manual update is requested")
|
||||||
|
|
||||||
self.clear_queue()
|
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.exception("could not load packages from database")
|
self.logger.exception("could not load packages from database")
|
||||||
|
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ def test_update(application_repository: ApplicationRepository, package_ahriman:
|
|||||||
"""
|
"""
|
||||||
must process package updates
|
must process package updates
|
||||||
"""
|
"""
|
||||||
|
application_repository.database.build_queue_insert(package_ahriman)
|
||||||
paths = [package.filepath for package in package_ahriman.packages.values()]
|
paths = [package.filepath for package in package_ahriman.packages.values()]
|
||||||
tree = Tree([Leaf(package_ahriman)])
|
tree = Tree([Leaf(package_ahriman)])
|
||||||
prebuilt_result = Result()
|
prebuilt_result = Result()
|
||||||
@@ -207,12 +208,15 @@ def test_update(application_repository: ApplicationRepository, package_ahriman:
|
|||||||
update_mock.assert_called_once_with(paths, Packagers("username"))
|
update_mock.assert_called_once_with(paths, Packagers("username"))
|
||||||
on_result_mock.assert_has_calls([MockCall(prebuilt_result), MockCall(result)])
|
on_result_mock.assert_has_calls([MockCall(prebuilt_result), MockCall(result)])
|
||||||
|
|
||||||
|
assert application_repository.database.build_queue_get() == []
|
||||||
|
|
||||||
|
|
||||||
def test_update_prebuilt_filter(application_repository: ApplicationRepository, package_ahriman: Package, result: Result,
|
def test_update_prebuilt_filter(application_repository: ApplicationRepository, package_ahriman: Package, result: Result,
|
||||||
mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must filter out packages which were successfully prebuilt
|
must filter out packages which were successfully prebuilt
|
||||||
"""
|
"""
|
||||||
|
application_repository.database.build_queue_insert(package_ahriman)
|
||||||
paths = [package.filepath for package in package_ahriman.packages.values()]
|
paths = [package.filepath for package in package_ahriman.packages.values()]
|
||||||
|
|
||||||
resolve_mock = mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.partition",
|
resolve_mock = mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.partition",
|
||||||
@@ -224,6 +228,8 @@ def test_update_prebuilt_filter(application_repository: ApplicationRepository, p
|
|||||||
application_repository.update([package_ahriman], Packagers("username"), bump_pkgrel=True)
|
application_repository.update([package_ahriman], Packagers("username"), bump_pkgrel=True)
|
||||||
resolve_mock.assert_called_once_with([])
|
resolve_mock.assert_called_once_with([])
|
||||||
|
|
||||||
|
assert application_repository.database.build_queue_get() == []
|
||||||
|
|
||||||
|
|
||||||
def test_update_empty(application_repository: ApplicationRepository, package_ahriman: Package, result: Result,
|
def test_update_empty(application_repository: ApplicationRepository, package_ahriman: Package, result: Result,
|
||||||
mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ def test_init(sources: Sources, mocker: MockerFixture) -> None:
|
|||||||
sources.init(local)
|
sources.init(local)
|
||||||
check_output_mock.assert_called_once_with(*sources.git(), "init", "--quiet", "--initial-branch",
|
check_output_mock.assert_called_once_with(*sources.git(), "init", "--quiet", "--initial-branch",
|
||||||
sources.DEFAULT_BRANCH, cwd=local, logger=pytest.helpers.anyvar(int))
|
sources.DEFAULT_BRANCH, cwd=local, logger=pytest.helpers.anyvar(int))
|
||||||
add_mock.assert_called_once_with(local, "PKGBUILD", ".SRCINFO", "local")
|
add_mock.assert_called_once_with(local, "PKGBUILD", ".SRCINFO", "local", force=True)
|
||||||
commit_mock.assert_called_once_with(local)
|
commit_mock.assert_called_once_with(local)
|
||||||
|
|
||||||
|
|
||||||
@@ -347,6 +347,17 @@ def test_add_skip(sources: Sources, mocker: MockerFixture) -> None:
|
|||||||
check_output_mock.assert_not_called()
|
check_output_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_force(sources: Sources, tmp_path: Path, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must add files to git with --force flag
|
||||||
|
"""
|
||||||
|
mocker.patch("pathlib.Path.glob", return_value=[Path("local/1"), Path("local/2")])
|
||||||
|
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
|
||||||
|
|
||||||
|
sources.add(Path("local"), "PKGBUILD", force=True)
|
||||||
|
assert "--force" in check_output_mock.call_args.args
|
||||||
|
|
||||||
|
|
||||||
def test_commit(sources: Sources, mocker: MockerFixture) -> None:
|
def test_commit(sources: Sources, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must commit changes
|
must commit changes
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ def test_configuration_sections(configuration: Configuration) -> None:
|
|||||||
def test_archives_remove(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
def test_archives_remove(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
||||||
repository: Repository, mocker: MockerFixture) -> None:
|
repository: Repository, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must remove older packages
|
must keep the current package and the configured amount of additional packages
|
||||||
"""
|
"""
|
||||||
packages = []
|
packages = []
|
||||||
for i in range(5):
|
for i in range(1, 7):
|
||||||
generated = replace(package_ahriman, version=str(i))
|
generated = replace(package_ahriman, version=str(i))
|
||||||
generated.packages = {
|
generated.packages = {
|
||||||
key: replace(value, filename=str(i))
|
key: replace(value, filename=str(i))
|
||||||
@@ -33,14 +33,17 @@ def test_archives_remove(archive_rotation_trigger: ArchiveRotationTrigger, packa
|
|||||||
}
|
}
|
||||||
packages.append(generated)
|
packages.append(generated)
|
||||||
|
|
||||||
|
archive_rotation_trigger.keep_built_packages = 2
|
||||||
|
mocker.patch.object(repository, "packages", return_value=[replace(package_ahriman, version="3")])
|
||||||
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=packages)
|
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=packages)
|
||||||
mocker.patch("pathlib.Path.glob", return_value=[Path(str(i)) for i in range(5)])
|
mocker.patch("pathlib.Path.glob", side_effect=lambda pattern: [Path(pattern.removesuffix("*"))])
|
||||||
unlink_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
|
unlink_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
|
||||||
|
|
||||||
archive_rotation_trigger.archives_remove(package_ahriman, repository)
|
archive_rotation_trigger.archives_remove(package_ahriman, repository)
|
||||||
unlink_mock.assert_has_calls([
|
unlink_mock.assert_has_calls([
|
||||||
MockCall(Path("0")),
|
|
||||||
MockCall(Path("1")),
|
MockCall(Path("1")),
|
||||||
|
MockCall(Path("2")),
|
||||||
|
MockCall(Path("4")),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,20 @@ from ahriman.models.package_source import PackageSource
|
|||||||
from ahriman.models.remote_source import RemoteSource
|
from ahriman.models.remote_source import RemoteSource
|
||||||
|
|
||||||
|
|
||||||
|
def test_updates_ignored(update_handler: UpdateHandler, package_ahriman: Package,
|
||||||
|
package_python_schedule: Package, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must combine configured ignored packages and held packages only
|
||||||
|
"""
|
||||||
|
update_handler._ignore_list = ["ignored-package"]
|
||||||
|
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=[
|
||||||
|
(package_ahriman, BuildStatus(is_held=True)),
|
||||||
|
(package_python_schedule, BuildStatus(is_held=False)),
|
||||||
|
])
|
||||||
|
|
||||||
|
assert update_handler._updates_ignored() == {"ignored-package", package_ahriman.base}
|
||||||
|
|
||||||
|
|
||||||
def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
|
def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
|
||||||
mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -194,6 +208,18 @@ def test_updates_dependencies(update_handler: UpdateHandler, package_ahriman: Pa
|
|||||||
pytest.helpers.anyvar(str, True))
|
pytest.helpers.anyvar(str, True))
|
||||||
|
|
||||||
|
|
||||||
|
def test_updates_dependencies_ignore(update_handler: UpdateHandler, package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must skip held packages when checking broken dependencies
|
||||||
|
"""
|
||||||
|
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get",
|
||||||
|
return_value=[(package_ahriman, BuildStatus(is_held=True))])
|
||||||
|
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||||
|
|
||||||
|
assert update_handler.updates_dependencies([]) == []
|
||||||
|
|
||||||
|
|
||||||
def test_updates_dependencies_skip_unknown(update_handler: UpdateHandler, package_ahriman: Package,
|
def test_updates_dependencies_skip_unknown(update_handler: UpdateHandler, package_ahriman: Package,
|
||||||
mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -249,6 +275,19 @@ def test_updates_local(update_handler: UpdateHandler, package_ahriman: Package,
|
|||||||
calculate_version=True)
|
calculate_version=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_updates_local_ignore(update_handler: UpdateHandler, package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must skip held packages before fetching local sources
|
||||||
|
"""
|
||||||
|
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get",
|
||||||
|
return_value=[(package_ahriman, BuildStatus(is_held=True))])
|
||||||
|
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||||
|
mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
|
||||||
|
|
||||||
|
assert update_handler.updates_local(vcs=True) == []
|
||||||
|
|
||||||
|
|
||||||
def test_updates_local_ignore_vcs(update_handler: UpdateHandler, package_ahriman: Package,
|
def test_updates_local_ignore_vcs(update_handler: UpdateHandler, package_ahriman: Package,
|
||||||
mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -306,17 +345,32 @@ def test_updates_local_with_failures(update_handler: UpdateHandler, package_ahri
|
|||||||
assert not update_handler.updates_local(vcs=True)
|
assert not update_handler.updates_local(vcs=True)
|
||||||
|
|
||||||
|
|
||||||
def test_updates_manual_clear(update_handler: UpdateHandler, mocker: MockerFixture) -> None:
|
def test_updates_local_held_metadata(update_handler: UpdateHandler, package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
requesting manual updates must clear packages directory
|
must respect the held base even when the source directory has a different name
|
||||||
"""
|
"""
|
||||||
mocker.patch("pathlib.Path.iterdir", return_value=[])
|
package_ahriman.remote = RemoteSource(source=PackageSource.Local)
|
||||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages")
|
mocker.patch.object(update_handler.reporter, "package_get",
|
||||||
|
return_value=[(package_ahriman, BuildStatus(is_held=True))])
|
||||||
|
mocker.patch.object(update_handler, "packages", return_value=[package_ahriman])
|
||||||
|
mocker.patch("pathlib.Path.iterdir", return_value=[Path("different-base")])
|
||||||
|
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||||
|
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||||
|
|
||||||
update_handler.updates_manual()
|
assert update_handler.updates_local(vcs=True) == []
|
||||||
|
|
||||||
from ahriman.core.repository.cleaner import Cleaner
|
|
||||||
Cleaner.clear_queue.assert_called_once_with()
|
def test_updates_manual_preserves_queue(update_handler: UpdateHandler, package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
discovery must leave manual requests queued for execution
|
||||||
|
"""
|
||||||
|
update_handler.database.build_queue_insert(package_ahriman)
|
||||||
|
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||||
|
|
||||||
|
assert update_handler.updates_manual() == [package_ahriman]
|
||||||
|
assert update_handler.database.build_queue_get() == [package_ahriman]
|
||||||
|
|
||||||
|
|
||||||
def test_updates_manual_status_known(update_handler: UpdateHandler, package_ahriman: Package,
|
def test_updates_manual_status_known(update_handler: UpdateHandler, package_ahriman: Package,
|
||||||
|
|||||||
@@ -257,6 +257,10 @@ There are few ways for packages to be marked as out-of-date and hence requiring
|
|||||||
#. In addition to the above, if package is named as VCS (e.g. has suffix ``-git``) and the last update was more than specified threshold ago, the service will also try to fetch sources and check if the revision is newer than the built one.
|
#. In addition to the above, if package is named as VCS (e.g. has suffix ``-git``) and the last update was more than specified threshold ago, the service will also try to fetch sources and check if the revision is newer than the built one.
|
||||||
#. In addition, there is ability to check if the dependencies of the package have been updated (e.g. if linked library has been renamed or the modules directory - e.g. in case of python and ruby packages - has been changed). And if so, the package will be marked as out-of-date as well.
|
#. In addition, there is ability to check if the dependencies of the package have been updated (e.g. if linked library has been renamed or the modules directory - e.g. in case of python and ruby packages - has been changed). And if so, the package will be marked as out-of-date as well.
|
||||||
|
|
||||||
|
Held packages and bases listed in ``build.ignore_packages`` are excluded from automatic AUR, official-repository, local-source, and broken-dependency update checks. These checks leave their package status unchanged. Manual updates and explicit rebuilds remain available.
|
||||||
|
|
||||||
|
Reading manual updates, including during ``repo-update --dry-run``, leaves the build queue intact. Execution removes requests by package base after a build chunk returns or a remote worker accepts it; successful prebuilt imports also remove requests by package base. Interrupted chunks and work that was not selected remain queued. Concurrent requests for the same package base are not distinguished; request revisions and crash recovery are separate work.
|
||||||
|
|
||||||
Update packages
|
Update packages
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ Web server settings. This feature requires ``aiohttp`` libraries to be installed
|
|||||||
|
|
||||||
Describes settings for packages archives management extensions.
|
Describes settings for packages archives management extensions.
|
||||||
|
|
||||||
* ``keep_built_packages`` - keep this amount of built packages with different versions, integer, required. ``0`` will effectively disable archives removal.
|
* ``keep_built_packages`` - keep this amount of additional built package versions alongside the version currently published in the repository, integer, required. ``0`` will effectively disable archives removal.
|
||||||
|
|
||||||
``keyring`` group
|
``keyring`` group
|
||||||
-----------------
|
-----------------
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ This trigger will be called right after build process (``on_result``). It will p
|
|||||||
``ahriman.core.housekeeping.ArchiveRotationTrigger``
|
``ahriman.core.housekeeping.ArchiveRotationTrigger``
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
This trigger removes old package versions from the archive directory. It implements ``on_result`` and, after each build, compares available versions for updated packages and removes the older ones, keeping only the last N versions as configured by ``keep_built_packages`` option.
|
This trigger removes old package versions from the archive directory. It implements ``on_result`` and, after each build, compares available versions for updated packages and removes older ones, keeping the version currently published in the repository plus the last N other versions as configured by ``keep_built_packages`` option.
|
||||||
|
|
||||||
``ahriman.core.housekeeping.LogsRotationTrigger``
|
``ahriman.core.housekeeping.LogsRotationTrigger``
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
@@ -143,9 +143,9 @@ export default function PackageTable(): React.JSX.Element {
|
|||||||
.then(() => table.setSelectionModel([])),
|
.then(() => table.setSelectionModel([])),
|
||||||
}}
|
}}
|
||||||
isAuthorized={table.isAuthorized}
|
isAuthorized={table.isAuthorized}
|
||||||
hasSelection={table.selectionModel.length > 0}
|
|
||||||
onSearchChange={table.setSearchText}
|
onSearchChange={table.setSearchText}
|
||||||
searchText={table.searchText}
|
searchText={table.searchText}
|
||||||
|
selectedPackageCount={table.selectionModel.length}
|
||||||
status={table.status}
|
status={table.status}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -48,19 +48,19 @@ export interface ToolbarActions {
|
|||||||
|
|
||||||
interface PackageTableToolbarProps {
|
interface PackageTableToolbarProps {
|
||||||
actions: ToolbarActions;
|
actions: ToolbarActions;
|
||||||
hasSelection: boolean;
|
|
||||||
isAuthorized: boolean;
|
isAuthorized: boolean;
|
||||||
onSearchChange: (text: string) => void;
|
onSearchChange: (text: string) => void;
|
||||||
searchText: string;
|
searchText: string;
|
||||||
|
selectedPackageCount: number;
|
||||||
status?: BuildStatus;
|
status?: BuildStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PackageTableToolbar({
|
export default function PackageTableToolbar({
|
||||||
actions,
|
actions,
|
||||||
hasSelection,
|
|
||||||
isAuthorized,
|
isAuthorized,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
searchText,
|
searchText,
|
||||||
|
selectedPackageCount,
|
||||||
status,
|
status,
|
||||||
}: PackageTableToolbarProps): React.JSX.Element {
|
}: PackageTableToolbarProps): React.JSX.Element {
|
||||||
const [packagesAnchorEl, setPackagesAnchorEl] = useState<HTMLElement | null>(null);
|
const [packagesAnchorEl, setPackagesAnchorEl] = useState<HTMLElement | null>(null);
|
||||||
@@ -103,7 +103,8 @@ export default function PackageTableToolbar({
|
|||||||
<MenuItem onClick={() => {
|
<MenuItem onClick={() => {
|
||||||
setPackagesAnchorEl(null); actions.onUpdateClick();
|
setPackagesAnchorEl(null); actions.onUpdateClick();
|
||||||
}}>
|
}}>
|
||||||
<PlayArrowIcon fontSize="small" sx={{ mr: 1 }} /> update
|
<PlayArrowIcon fontSize="small" sx={{ mr: 1 }} />
|
||||||
|
{selectedPackageCount === 0 ? "update all packages" : `update selected packages (${selectedPackageCount})`}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={() => {
|
<MenuItem onClick={() => {
|
||||||
setPackagesAnchorEl(null); actions.onRefreshDatabaseClick();
|
setPackagesAnchorEl(null); actions.onRefreshDatabaseClick();
|
||||||
@@ -116,7 +117,7 @@ export default function PackageTableToolbar({
|
|||||||
<ReplayIcon fontSize="small" sx={{ mr: 1 }} /> rebuild
|
<ReplayIcon fontSize="small" sx={{ mr: 1 }} /> rebuild
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Divider />
|
<Divider />
|
||||||
<MenuItem disabled={!hasSelection} onClick={() => {
|
<MenuItem disabled={selectedPackageCount === 0} onClick={() => {
|
||||||
setPackagesAnchorEl(null); actions.onRemoveClick();
|
setPackagesAnchorEl(null); actions.onRemoveClick();
|
||||||
}}>
|
}}>
|
||||||
<DeleteIcon fontSize="small" sx={{ mr: 1 }} /> remove
|
<DeleteIcon fontSize="small" sx={{ mr: 1 }} /> remove
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import PauseCircleIcon from "@mui/icons-material/PauseCircle";
|
import PauseCircleIcon from "@mui/icons-material/PauseCircle";
|
||||||
import { Chip } from "@mui/material";
|
import { Chip, Tooltip } from "@mui/material";
|
||||||
import type { BuildStatus } from "models/BuildStatus";
|
import type { BuildStatus } from "models/BuildStatus";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { StatusColors } from "theme/StatusColors";
|
import { StatusColors } from "theme/StatusColors";
|
||||||
@@ -29,7 +29,9 @@ interface StatusCellProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function StatusCell({ isHeld, status }: StatusCellProps): React.JSX.Element {
|
export default function StatusCell({ isHeld, status }: StatusCellProps): React.JSX.Element {
|
||||||
return <Chip
|
return <Tooltip title={isHeld ? "Held: automatic updates are skipped" : ""}>
|
||||||
|
<Chip
|
||||||
|
aria-label={isHeld ? `${status}; held: automatic updates are skipped` : status}
|
||||||
icon={isHeld ? <PauseCircleIcon /> : undefined}
|
icon={isHeld ? <PauseCircleIcon /> : undefined}
|
||||||
label={status}
|
label={status}
|
||||||
size="small"
|
size="small"
|
||||||
@@ -38,5 +40,6 @@ export default function StatusCell({ isHeld, status }: StatusCellProps): React.J
|
|||||||
color: "common.white",
|
color: "common.white",
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
}}
|
}}
|
||||||
/>;
|
/>
|
||||||
|
</Tooltip>;
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import { createTheme, type Theme } from "@mui/material/styles";
|
import { createTheme, type Theme } from "@mui/material/styles";
|
||||||
|
import type {} from "@mui/x-data-grid/themeAugmentation";
|
||||||
|
|
||||||
export function createAppTheme(mode: "light" | "dark"): Theme {
|
export function createAppTheme(mode: "light" | "dark"): Theme {
|
||||||
return createTheme({
|
return createTheme({
|
||||||
@@ -30,6 +31,14 @@ export function createAppTheme(mode: "light" | "dark"): Theme {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MuiDataGrid: {
|
||||||
|
styleOverrides: {
|
||||||
|
cell: {
|
||||||
|
alignItems: "center",
|
||||||
|
display: "flex",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
MuiDialog: {
|
MuiDialog: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
fullWidth: true,
|
fullWidth: true,
|
||||||
|
|||||||
Reference in new issue
Block a user