diff --git a/src/ahriman/core/repository/executor.py b/src/ahriman/core/repository/executor.py index 41755e79..78916177 100644 --- a/src/ahriman/core/repository/executor.py +++ b/src/ahriman/core/repository/executor.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -import shutil +import shutil # shutil.move is used here to ensure cross fs file movement from collections.abc import Iterable from pathlib import Path @@ -27,7 +27,7 @@ from ahriman.core.build_tools.package_archive import PackageArchive from ahriman.core.build_tools.task import Task from ahriman.core.repository.cleaner import Cleaner from ahriman.core.repository.package_info import PackageInfo -from ahriman.core.utils import safe_filename +from ahriman.core.utils import safe_filename, utcnow from ahriman.models.changes import Changes from ahriman.models.event import EventType from ahriman.models.package import Package @@ -187,9 +187,12 @@ class Executor(PackageInfo, Cleaner): # in theory, it might be NOT packages directory, but we suppose it is full_path = self.paths.packages / name files = self.sign.process_sign_package(full_path, packager_key) + for src in files: - dst = self.paths.repository / safe_filename(src.name) - shutil.move(src, dst) + archive = self.paths.archive_for(package_base) / safe_filename(src.name) + shutil.move(src, archive) + for symlink in (self.paths.repository / archive.name, self.paths.archive_for(utcnow()) / archive.name): + symlink.symlink_to(archive.relative_to(symlink.parent, walk_up=True)) package_path = self.paths.repository / safe_filename(name) self.repo.add(package_path) diff --git a/src/ahriman/models/repository_paths.py b/src/ahriman/models/repository_paths.py index 7e8ae558..a2fdec8d 100644 --- a/src/ahriman/models/repository_paths.py +++ b/src/ahriman/models/repository_paths.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # import contextlib +import datetime import os import shutil @@ -85,6 +86,16 @@ class RepositoryPaths(LazyLogging): return Path(self.repository_id.architecture) # legacy tree suffix return Path(self.repository_id.name) / self.repository_id.architecture + @property + def archive(self) -> Path: + """ + archive directory root + + Returns: + Path: archive directory root + """ + return self.root / "archive" / self._suffix + @property def build_root(self) -> Path: """ @@ -249,6 +260,34 @@ class RepositoryPaths(LazyLogging): set_owner(path) path = path.parent + def archive_for(self, category: datetime.date | str) -> Path: + """ + get path to archive specified search criteria + + Args: + category(datetime.date | str): either time to search or package base name + + Returns: + Path: path to archive directory for specified identifier + + Raises: + NotImplementedError: if category type is not supported + """ + match category: + case datetime.date(): + suffix = Path("repos") / f"{category.year}" / f"{category.month:02d}" / f"{category.day:02d}" + case str() if len(category) > 0: + suffix = Path("packages") / category[0] / category + case _: + raise NotImplementedError + + directory = self.archive / suffix + if not directory.is_dir(): # create if not exists + with self.preserve_owner(self.archive): + directory.mkdir(mode=0o755, parents=True) + + return directory + def cache_for(self, package_base: str) -> Path: """ get path to cached PKGBUILD and package sources for the package base @@ -320,6 +359,7 @@ class RepositoryPaths(LazyLogging): with self.preserve_owner(): for directory in ( + self.archive, self.cache, self.chroot, self.packages,