From 99bbd9d68fd1029c67e8b7d4b8154c35a41252e9 Mon Sep 17 00:00:00 2001 From: Franck Bret <franck.bret@octobus.net> Date: Fri, 13 Oct 2023 10:49:26 +0200 Subject: [PATCH] Stateful Julia lister Add a state to the lister to store the ``last_seen_commit`` as a Git commit hash. Use Dulwich to retrieve a Git commit walker since ``last_seen_commit`` if any. For each commit detect if it is a new package or a new package version commit and returns its origin with commit date as last_update. --- swh/lister/julia/__init__.py | 14 +- swh/lister/julia/lister.py | 121 +++++++++++++--- .../fake-julia-registry-repository.tar.gz | Bin 7493 -> 0 bytes .../fake-julia-registry-repository_0.tar.gz | Bin 0 -> 7462 bytes .../fake-julia-registry-repository_1.tar.gz | Bin 0 -> 10961 bytes .../data/fake_julia_registry_repository.sh | 55 +++++++- swh/lister/julia/tests/test_lister.py | 129 ++++++++++++++++-- 7 files changed, 284 insertions(+), 35 deletions(-) delete mode 100644 swh/lister/julia/tests/data/fake-julia-registry-repository.tar.gz create mode 100644 swh/lister/julia/tests/data/fake-julia-registry-repository_0.tar.gz create mode 100644 swh/lister/julia/tests/data/fake-julia-registry-repository_1.tar.gz diff --git a/swh/lister/julia/__init__.py b/swh/lister/julia/__init__.py index 8388c0a2..73e5382a 100644 --- a/swh/lister/julia/__init__.py +++ b/swh/lister/julia/__init__.py @@ -28,9 +28,9 @@ Origins retrieval strategy -------------------------- To build a list of origins we clone the `Julia General registry`_ Git repository, then -read the `Registry.toml`_ file to get the path to packages directories. -Each directory have a `Package.toml` file from where we get the Git repository url for -a package. +walk through commits with the help of `Dulwich`_ to detect commit related to a new package +or a new version of a package. For each of those commits we get the path to `Package.toml` +file from where we get the Git repository url for a package. Page listing ------------ @@ -40,7 +40,12 @@ There is only one page listing all origins url. Origins from page ----------------- -The lister is stateless and yields all origins url from one page. +The lister yields all origins url from one page. + +Each time the lister is executed, the HEAD commit id of `Julia General registry`_ +is stored as ``state.last_seen_commit`` and used on next run to retrieve new origins +since the last commit. + Each url corresponds to the Git url of the package repository. Running tests @@ -71,6 +76,7 @@ You can follow lister execution by displaying logs of swh-lister service:: .. _JuliaHub: https://juliahub.com/ .. _Julia Packages: https://julialang.org/packages/ .. _Registry.toml: https://github.com/JuliaRegistries/General/blob/master/Registry.toml +.. _Dulwich: https://www.dulwich.io/ """ # noqa: B950 diff --git a/swh/lister/julia/lister.py b/swh/lister/julia/lister.py index 214cbcef..a31c5cc4 100644 --- a/swh/lister/julia/lister.py +++ b/swh/lister/julia/lister.py @@ -3,27 +3,40 @@ # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information +from dataclasses import asdict, dataclass +import datetime import logging from pathlib import Path import shutil import tempfile -from typing import Any, Iterator, List, Optional, Tuple +from typing import Any, Dict, Iterator, Optional from dulwich import porcelain +from dulwich.repo import Repo +from dulwich.walk import WalkEntry +import iso8601 import toml from swh.scheduler.interface import SchedulerInterface from swh.scheduler.model import ListedOrigin -from ..pattern import CredentialsType, StatelessLister +from ..pattern import CredentialsType, Lister logger = logging.getLogger(__name__) # Aliasing the page results returned by `get_pages` method from the lister. -JuliaListerPage = List[Tuple[str, Any]] +JuliaListerPage = Dict[str, Any] -class JuliaLister(StatelessLister[JuliaListerPage]): +@dataclass +class JuliaListerState: + """Store lister state for incremental mode operations""" + + last_seen_commit: Optional[str] = None + """Hash of the latest Git commit when lister was executed""" + + +class JuliaLister(Lister[JuliaListerState, JuliaListerPage]): """List Julia packages origins""" LISTER_NAME = "julia" @@ -34,7 +47,6 @@ class JuliaLister(StatelessLister[JuliaListerPage]): "https://github.com/JuliaRegistries/General.git" # Julia General Registry ) REPO_PATH = Path(tempfile.mkdtemp(), "General") - REGISTRY_PATH = REPO_PATH / "Registry.toml" def __init__( self, @@ -63,40 +75,111 @@ class JuliaLister(StatelessLister[JuliaListerPage]): except FileExistsError: porcelain.pull(self.REPO_PATH, remote_location=self.url) + def state_from_dict(self, d: Dict[str, Any]) -> JuliaListerState: + return JuliaListerState(**d) + + def state_to_dict(self, state: JuliaListerState) -> Dict[str, Any]: + return asdict(state) + + def get_origin_data(self, entry: WalkEntry) -> Dict[str, Any]: + """ + Given an entry object parse its commit message and other attributes + to detect if the commit is valid to describe a new package or + a new package version. + + Returns a dict with origin url as key and iso8601 commit date as value + """ + assert entry + + if ( + entry.commit + and entry.changes() + and ( + entry.commit.message.startswith(b"New package: ") + or entry.commit.message.startswith(b"New version: ") + ) + ): + package_toml = None + for change in entry.changes(): + if change and hasattr(change, "new"): + if change.new.path.endswith(b"/Package.toml"): + package_toml = self.REPO_PATH / change.new.path.decode() + break + elif change.new.path.endswith(b"/Versions.toml"): + versions_path = self.REPO_PATH / change.new.path.decode() + if versions_path.exists(): + package_path, _ = change.new.path.decode().split( + "Versions.toml" + ) + package_toml = ( + self.REPO_PATH / package_path / "Package.toml" + ) + break + + if package_toml and package_toml.exists(): + origin = toml.load(package_toml)["repo"] + last_update = datetime.datetime.fromtimestamp( + entry.commit.commit_time, + tz=datetime.timezone.utc, + ).isoformat() + return {f"{origin}": last_update} + + return {} + def get_pages(self) -> Iterator[JuliaListerPage]: """Yield an iterator which returns 'page' - To build a list of origins the `Julia General registry` Git - repository is cloned to get a `Registry.toml` file, an index file of - packages directories. + To build a list of origins the ``Julia General registry`` Git + repository is cloned to look at commits history to discover new + package and new package versions. + + Depending on ``last_seen_commit`` state it initiate a commit walker + since the last time the lister has been executed. There is only one page that list all origins urls. """ + # Clone the repository self.get_registry_repository() - assert self.REGISTRY_PATH.exists() - registry = toml.load(self.REGISTRY_PATH) - yield registry["packages"].items() + assert self.REPO_PATH.exists() + + repo = Repo(str(self.REPO_PATH)) + + # Detect commits related to new package and new versions since last_seen_commit + if not self.state.last_seen_commit: + walker = repo.get_walker() + else: + last = repo[self.state.last_seen_commit.encode()] + walker = repo.get_walker(since=last.commit_time, exclude=[last.id]) + + assert walker + packages = {} + for entry in walker: + packages.update(self.get_origin_data(entry=entry)) + + yield packages def get_origins_from_page(self, page: JuliaListerPage) -> Iterator[ListedOrigin]: """Iterate on all pages and yield ListedOrigin instances - Each directory of the Git repository have a `Package.toml` file from - where we get the Git repository url for each package. + Each directory of the Git repository have a ``Package.toml`` file from + where we get the Git repository url as an origin for each package. """ assert self.lister_obj.id is not None - assert self.REPO_PATH.exists() - for uuid, info in page: - package_info_path = self.REPO_PATH / info["path"] / "Package.toml" - package_info = toml.load(package_info_path) + for origin, last_update in page.items(): + last_update = iso8601.parse_date(last_update) yield ListedOrigin( lister_id=self.lister_obj.id, visit_type=self.VISIT_TYPE, - url=package_info["repo"], - last_update=None, + url=origin, + last_update=last_update, ) def finalize(self) -> None: + # Get Git HEAD commit hash + repo = Repo(str(self.REPO_PATH)) + self.state.last_seen_commit = repo.head().decode("ascii") + self.updated = True # Rm tmp directory REPO_PATH if self.REPO_PATH.exists(): shutil.rmtree(self.REPO_PATH) diff --git a/swh/lister/julia/tests/data/fake-julia-registry-repository.tar.gz b/swh/lister/julia/tests/data/fake-julia-registry-repository.tar.gz deleted file mode 100644 index e79d4464de974285e883294c4349226726a91cb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7493 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 I=NK6O2fbD;HUIzs diff --git a/swh/lister/julia/tests/data/fake-julia-registry-repository_0.tar.gz b/swh/lister/julia/tests/data/fake-julia-registry-repository_0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..458e4f1670b5ca6d9abff8b1cb7da305e1222783 GIT binary patch literal 7462 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 literal 0 HcmV?d00001 diff --git a/swh/lister/julia/tests/data/fake-julia-registry-repository_1.tar.gz b/swh/lister/julia/tests/data/fake-julia-registry-repository_1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..59275dd83e8fa9158179e33bfb777eaf56e17c7a GIT binary patch literal 10961 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 bYz0~!c;oo*|KEI69&vljO4tx25E1?lY}!@w literal 0 HcmV?d00001 diff --git a/swh/lister/julia/tests/data/fake_julia_registry_repository.sh b/swh/lister/julia/tests/data/fake_julia_registry_repository.sh index 4dfbc222..5721d129 100755 --- a/swh/lister/julia/tests/data/fake_julia_registry_repository.sh +++ b/swh/lister/julia/tests/data/fake_julia_registry_repository.sh @@ -27,6 +27,9 @@ some amount of consideration when choosing package names. [packages]' > Registry.toml # Init as a git repository +# Force author and commit date to be the same +export GIT_AUTHOR_DATE='2001-01-01T17:18:19+00:00' +export GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git init git add . git commit -m "Init fake Julia registry repository for tests purpose" @@ -50,6 +53,8 @@ git-tree-sha1 = "65301af3ab06b04cf8a52cd43b06222bab5249c2" echo 'a3ea4736-0a3b-4c29-ac8a-20364318a635 = { name = "Fable", path = "F/Fable" }' >> Registry.toml +export GIT_AUTHOR_DATE='2001-01-02T17:18:19+00:00' +export GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git add . git commit -m "New package: Fable v0.0.2" @@ -132,16 +137,60 @@ git-tree-sha1 = "59619a31c56c9e61b5dabdbd339e30c227c5d13d" echo 'f1435218-dba5-11e9-1e4d-f1a5fab5fc13 = { name = "Oscar", path = "O/Oscar" }' >> Registry.toml +export GIT_AUTHOR_DATE='2001-01-03T17:18:19+00:00' +export GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git add . git commit -m "New package: Oscar v0.12.1" # Save some space rm .git/hooks/*.sample -# Archive +# First Archive cd ../ -tar -czf fake-julia-registry-repository.tar.gz General -mv fake-julia-registry-repository.tar.gz ../ +tar -czf fake-julia-registry-repository_0.tar.gz General +mv fake-julia-registry-repository_0.tar.gz ../ + +# Add some more commits and build a second archive for incremental tests purpose +cd General +echo ' + +["0.13.0"] +git-tree-sha1 = "c090495f818a063ed23d2d911fe74cc4358b5351" +' >> O/Oscar/Versions.toml + +# New version, replace previous uuid with a new one +sed -i -e 's/f1435218-dba5-11e9-1e4d-f1a5fab5fc13/a3ea4736-0a3b-4c29-ac8a-20364318a635/g' Registry.toml + +export GIT_AUTHOR_DATE='2001-01-04T17:18:19+00:00' +export GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE +git add . +git commit -m "New version: Oscar v0.13.0" + +mkdir -p V/VulkanSpec + +touch V/VulkanSpec/Package.toml +touch V/VulkanSpec/Versions.toml + +echo 'name = "VulkanSpec" +uuid = "99a7788f-8f0f-454f-8f6c-c6cf389551ae" +repo = "https://github.com/serenity4/VulkanSpec.jl.git" +' > V/VulkanSpec/Package.toml + +echo '["0.1.0"] +git-tree-sha1 = "b5fef67130191c797007a1484f4dc6bfc840caa2" +' > V/VulkanSpec/Versions.toml + +echo '99a7788f-8f0f-454f-8f6c-c6cf389551ae = { name = "VulkanSpec", path = "V/VulkanSpec" }' >> Registry.toml + +export GIT_AUTHOR_DATE='2001-01-05T17:18:19+00:00' +export GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE +git add . +git commit -m "New package: VulkanSpec v0.1.0" + +# Second Archive +cd ../ +tar -czf fake-julia-registry-repository_1.tar.gz General +mv fake-julia-registry-repository_1.tar.gz ../ # Clean up tmp_dir cd ../ diff --git a/swh/lister/julia/tests/test_lister.py b/swh/lister/julia/tests/test_lister.py index 6e5d2ea9..4febe1ae 100644 --- a/swh/lister/julia/tests/test_lister.py +++ b/swh/lister/julia/tests/test_lister.py @@ -5,17 +5,25 @@ from pathlib import Path +from dulwich import porcelain +import iso8601 + from swh.lister.julia.lister import JuliaLister from swh.lister.julia.tests import prepare_repository_from_archive -expected_origins = [ - "https://github.com/leios/Fable.jl.git", - "https://github.com/oscar-system/Oscar.jl.git", -] +expected_origins_0 = { + "https://github.com/leios/Fable.jl.git": "2001-01-02T17:18:19+00:00", + "https://github.com/oscar-system/Oscar.jl.git": "2001-01-03T17:18:19+00:00", +} + +expected_origins_1 = { + "https://github.com/oscar-system/Oscar.jl.git": "2001-01-04T17:18:19+00:00", + "https://github.com/serenity4/VulkanSpec.jl.git": "2001-01-05T17:18:19+00:00", +} def test_julia_get_registry_repository(datadir, tmp_path, swh_scheduler): - archive_path = Path(datadir, "fake-julia-registry-repository.tar.gz") + archive_path = Path(datadir, "fake-julia-registry-repository_0.tar.gz") repo_url = prepare_repository_from_archive(archive_path, "General", tmp_path) lister = JuliaLister(url=repo_url, scheduler=swh_scheduler) @@ -33,17 +41,18 @@ def test_julia_get_registry_repository(datadir, tmp_path, swh_scheduler): def test_julia_lister(datadir, tmp_path, swh_scheduler): - archive_path = Path(datadir, "fake-julia-registry-repository.tar.gz") + archive_path = Path(datadir, "fake-julia-registry-repository_0.tar.gz") repo_url = prepare_repository_from_archive(archive_path, "General", tmp_path) lister = JuliaLister(url=repo_url, scheduler=swh_scheduler) lister.REPO_PATH = Path(tmp_path, "General") lister.REGISTRY_PATH = lister.REPO_PATH / "Registry.toml" res = lister.run() - assert res.origins == 1 + 1 + assert res.origins == len(expected_origins_0) scheduler_origins = swh_scheduler.get_listed_origins(lister.lister_obj.id).results - assert len(scheduler_origins) == len(expected_origins) + assert len(scheduler_origins) == len(expected_origins_0) + assert { ( scheduled.visit_type, @@ -51,4 +60,106 @@ def test_julia_lister(datadir, tmp_path, swh_scheduler): scheduled.last_update, ) for scheduled in scheduler_origins - } == {("git", expected, None) for expected in expected_origins} + } == { + ("git", origin, iso8601.parse_date(last_update)) + for origin, last_update in expected_origins_0.items() + } + + +def test_julia_lister_incremental(datadir, tmp_path, swh_scheduler): + archive_path = Path(datadir, "fake-julia-registry-repository_0.tar.gz") + repo_url = prepare_repository_from_archive(archive_path, "General", tmp_path) + + # Prepare first run + lister = JuliaLister(url=repo_url, scheduler=swh_scheduler) + lister.REPO_PATH = Path(tmp_path, "General") + lister.REGISTRY_PATH = lister.REPO_PATH / "Registry.toml" + # Latest Git commit hash expected + with porcelain.open_repo_closing(lister.REPO_PATH) as r: + expected_last_seen_commit = r.head().decode("ascii") + + assert expected_last_seen_commit is not None + assert lister.state.last_seen_commit is None + + # First run + res = lister.run() + assert res.pages == 1 + assert res.origins == len(expected_origins_0) + assert lister.state.last_seen_commit == expected_last_seen_commit + + scheduler_origins_0 = swh_scheduler.get_listed_origins(lister.lister_obj.id).results + assert len(scheduler_origins_0) == len(expected_origins_0) + assert { + ( + scheduled.visit_type, + scheduled.url, + scheduled.last_update, + ) + for scheduled in scheduler_origins_0 + } == { + ("git", origin, iso8601.parse_date(last_update)) + for origin, last_update in expected_origins_0.items() + } + + # Prepare second run + archive_path = Path(datadir, "fake-julia-registry-repository_1.tar.gz") + repo_url = prepare_repository_from_archive(archive_path, "General", tmp_path) + + lister = JuliaLister(url=repo_url, scheduler=swh_scheduler) + lister.REPO_PATH = Path(tmp_path, "General") + lister.REGISTRY_PATH = lister.REPO_PATH / "Registry.toml" + + assert lister.state.last_seen_commit == expected_last_seen_commit + + with porcelain.open_repo_closing(lister.REPO_PATH) as repo: + new_expected_last_seen_commit = repo.head().decode("ascii") + + assert expected_last_seen_commit != new_expected_last_seen_commit + + # Second run + res = lister.run() + assert lister.state.last_seen_commit == new_expected_last_seen_commit + assert res.pages == 1 + # One new package, one new version + assert res.origins == len(expected_origins_1) + + scheduler_origins = swh_scheduler.get_listed_origins(lister.lister_obj.id).results + expected_origins = {**expected_origins_0, **expected_origins_1} + assert len(scheduler_origins) == len(expected_origins) + + +def test_julia_lister_incremental_no_changes(datadir, tmp_path, swh_scheduler): + archive_path = Path(datadir, "fake-julia-registry-repository_0.tar.gz") + repo_url = prepare_repository_from_archive(archive_path, "General", tmp_path) + lister = JuliaLister(url=repo_url, scheduler=swh_scheduler) + lister.REPO_PATH = Path(tmp_path, "General") + lister.REGISTRY_PATH = lister.REPO_PATH / "Registry.toml" + + # Latest Git commit hash expected + with porcelain.open_repo_closing(lister.REPO_PATH) as r: + expected_last_seen_commit = r.head().decode("ascii") + + assert expected_last_seen_commit is not None + assert lister.state.last_seen_commit is None + + # First run + res = lister.run() + assert res.pages == 1 + assert res.origins == len(expected_origins_0) + assert expected_last_seen_commit is not None + assert lister.state.last_seen_commit == expected_last_seen_commit + + scheduler_origins = swh_scheduler.get_listed_origins(lister.lister_obj.id).results + assert len(scheduler_origins) == len(expected_origins_0) + + # Prepare second run, repository state is the same as the one of the first run + repo_url = prepare_repository_from_archive(archive_path, "General", tmp_path) + lister = JuliaLister(url=repo_url, scheduler=swh_scheduler) + assert lister.state.last_seen_commit == expected_last_seen_commit + + # Second run + res = lister.run() + assert lister.state.last_seen_commit == expected_last_seen_commit + assert res.pages == 1 + # Nothing new + assert res.origins == 0 -- GitLab