From aedfc80fa5a8499007772f5b009719cc3b6de5f1 Mon Sep 17 00:00:00 2001 From: Franck Bret <franck.bret@octobus.net> Date: Tue, 2 Aug 2022 09:21:24 +0200 Subject: [PATCH] crates: Loader implements incremental mode Add incremental support based on sha256 EXTID Manage release date for each versions of a package Adapt test dataset and add incremental test cases Related T4104 --- docs/package-loader-specifications.rst | 10 +- swh/loader/package/crates/loader.py | 300 ++++-------- .../package/crates/tests/data/expected.json | 133 +++++ .../package/crates/tests/data/fake_crates.sh | 15 +- .../https_crates.io/api_v1_crates_hg-core | 2 - .../https_crates.io/api_v1_crates_micro-timer | 2 - .../crates_hg-core_hg-core-0.0.1.crate | Bin 426 -> 427 bytes ...crates_micro-timer_micro-timer-0.1.0.crate | Bin 456 -> 484 bytes ...crates_micro-timer_micro-timer-0.1.1.crate | Bin 458 -> 456 bytes ...crates_micro-timer_micro-timer-0.1.2.crate | Bin 485 -> 484 bytes ...crates_micro-timer_micro-timer-0.2.0.crate | Bin 419 -> 419 bytes ...crates_micro-timer_micro-timer-0.2.1.crate | Bin 420 -> 420 bytes ...crates_micro-timer_micro-timer-0.3.0.crate | Bin 413 -> 419 bytes ...crates_micro-timer_micro-timer-0.3.1.crate | Bin 421 -> 416 bytes ...crates_micro-timer_micro-timer-0.4.0.crate | Bin 417 -> 419 bytes .../package/crates/tests/test_crates.py | 456 +++++++++++++----- swh/loader/package/crates/tests/test_tasks.py | 18 +- 17 files changed, 585 insertions(+), 351 deletions(-) create mode 100644 swh/loader/package/crates/tests/data/expected.json delete mode 100644 swh/loader/package/crates/tests/data/https_crates.io/api_v1_crates_hg-core delete mode 100644 swh/loader/package/crates/tests/data/https_crates.io/api_v1_crates_micro-timer diff --git a/docs/package-loader-specifications.rst b/docs/package-loader-specifications.rst index 01abe7df..25c2bf11 100644 --- a/docs/package-loader-specifications.rst +++ b/docs/package-loader-specifications.rst @@ -67,13 +67,13 @@ Here is an overview of the fields (+ internal version name + branch name) used b - metadata is intrinsic * - crates - ``p_info.​version`` - - ``release_name(​version, filename) + "\n\n" + i_metadata.description + "\n"`` + - ``release_name(​version)`` - =version - - Synthetic release for Crate source package {p_info.name} version {p_info.version} {description} + - Synthetic release for Crate source package {p_info.name} version {p_info.version} - true - - from int metadata - - from ext metadata - - ``i_metadata`` for intrinsic metadata, ``e_metadata`` for extrinsic metadata + - from intrinsic metadata + - from extrinsic metadata + - "" * - debian - =``version`` - ``release_name(​version)`` diff --git a/swh/loader/package/crates/loader.py b/swh/loader/package/crates/loader.py index 2943ae9d..51a513b2 100644 --- a/swh/loader/package/crates/loader.py +++ b/swh/loader/package/crates/loader.py @@ -3,139 +3,35 @@ # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information -from distutils.version import StrictVersion +from datetime import datetime import json from pathlib import Path +import string from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple from urllib.parse import urlparse import attr +from packaging.version import parse as parse_version import toml -from typing_extensions import TypedDict -from swh.loader.package.loader import BasePackageInfo, PackageLoader -from swh.loader.package.utils import cached_method, get_url_body, release_name -from swh.model.model import ObjectType, Person, Release, Sha1Git, TimestampWithTimezone +from swh.loader.package.loader import ( + BasePackageInfo, + PackageLoader, + RawExtrinsicMetadataCore, +) +from swh.loader.package.utils import EMPTY_AUTHOR, release_name +from swh.model.model import ( + MetadataAuthority, + MetadataAuthorityType, + ObjectType, + Person, + Release, + Sha1Git, + TimestampWithTimezone, +) from swh.storage.interface import StorageInterface -class ExtrinsicPackageMetadata(TypedDict): - """Data structure for package extrinsic metadata pulled from http api endpoint. - - We set only the keys we need according to what is available when querying - https://crates.io/api/v1/crates/<name>, where `name` is the name of the crate - package (see JSON response example at https://crates.io/api/v1/crates/hg-core). - - Usage example: - - .. code-block:: python - - e_metadata = ExtrinsicPackageMetadata(**self.info()) - - """ # noqa - - categories: List[Dict[Any, Any]] - """Related categories""" - - crate: Dict[Any, Any] - """Crate project information""" - - keywords: List[Any] - """Keywords""" - - versions: List[Dict[Any, Any]] - """A list of released versions for a crate""" - - -class ExtrinsicVersionPackageMetadata(TypedDict): - """Data structure for specific package version extrinsic metadata, pulled - from http api endpoint. - - Similar to `ExtrinsicPackageMetadata` in its usage, but we flatten the data - related to a specific version. - """ - - crate: str - """The package name""" - - crate_size: int - """The package size""" - - created_at: str - """First released at""" - - downloads: str - """Number of downloads""" - - license: str - """Package license""" - - num: str - """Package version""" - - published_by: Dict[Any, Any] - """Publishers information""" - - updated_at: str - """Last update""" - - yanked: bool - """Is that version yanked? (yanked means release-level deprecation)""" - - -class IntrinsicPackageMetadata(TypedDict): - """Data structure for specific package version intrinsic metadata. - - Data is extracted from the crate package's .toml file. Then the data of the - 'package' entry is flattened. - - Cargo.toml file content example: - - .. code-block:: toml - - [package] - name = "hg-core" - version = "0.0.1" - authors = ["Georges Racinet <georges.racinet@octobus.net>"] - description = "Mercurial pure Rust core library, with no assumption on - Python bindings (FFI)" - homepage = "https://mercurial-scm.org" - license = "GPL-2.0-or-later" - repository = "https://www.mercurial-scm.org/repo/hg" - - [lib] - name = "hg" - [dev-dependencies.rand] - version = "~0.6" - - [dev-dependencies.rand_pcg] - version = "~0.1" - - :param toml: toml object - """ - - name: str - """The package name""" - - version: str - """Package version""" - - authors: List[str] - """Authors""" - - description: str - """Package and release description""" - - homepage: str - """Homepage of the project""" - - license: str - """Package license""" - - repository: str - """Source code repository""" - - @attr.s class CratesPackageInfo(BasePackageInfo): @@ -145,16 +41,20 @@ class CratesPackageInfo(BasePackageInfo): version = attr.ib(type=str) """Current version""" - e_metadata: Dict[str, Any] = attr.ib(factory=ExtrinsicPackageMetadata) - """Extrinsic package metadata, common to all versions""" + sha256 = attr.ib(type=str) + """Extid as sha256""" - e_metadata_version: Dict[str, Any] = attr.ib( - factory=ExtrinsicVersionPackageMetadata - ) - """Extrinsic package metadata specific to a version""" + last_update = attr.ib(type=datetime) + """Last update as release date""" - i_metadata: Dict[str, Any] = attr.ib(factory=IntrinsicPackageMetadata) - """Intrinsic metadata of the current package version""" + yanked = attr.ib(type=bool) + """Whether the package is yanked or not""" + + MANIFEST_FORMAT = string.Template( + "name $name\nshasum $sha256\nurl $url\nversion $version\nlast_update $last_update" + ) + EXTID_TYPE = "crates-manifest-sha256" + EXTID_VERSION = 0 def extract_intrinsic_metadata(dir_path: Path) -> Dict[str, Any]: @@ -171,35 +71,6 @@ def extract_intrinsic_metadata(dir_path: Path) -> Dict[str, Any]: return toml.load(dir_path / "Cargo.toml") -def extract_author(p_info: CratesPackageInfo) -> Person: - """Extract package author from intrinsic metadata and return it as a - `Person` model. - - Args: - p_info: CratesPackageInfo that should contains i_metadata entries - - Returns: - Only one author (Person) of the package. Currently limited by internal detail - of the swh stack (see T3887). - """ - authors = p_info.i_metadata["authors"] - fullname = authors[0] # TODO: here we have a list of author, see T3887 - return Person.from_fullname(fullname.encode()) - - -def extract_description(p_info: CratesPackageInfo) -> str: - """Extract package description from intrinsic metadata and return it as a - string. - - Args: - p_info: CratesPackageInfo that should contains i_metadata and entries - - Returns: - Package description from metadata. - """ - return p_info.i_metadata["description"] - - class CratesLoader(PackageLoader[CratesPackageInfo]): """Load Crates package origins into swh archive.""" @@ -210,6 +81,7 @@ class CratesLoader(PackageLoader[CratesPackageInfo]): storage: StorageInterface, url: str, artifacts: List[Dict[str, Any]], + crates_metadata: List[Dict[str, Any]], **kwargs, ): """Constructor @@ -223,8 +95,8 @@ class CratesLoader(PackageLoader[CratesPackageInfo]): A list of dict listing all existing released versions for a package (Usually set with crates lister `extra_loader_arguments`). Each line is a dict that should have an `url` - (where to download package specific version) and a `version` entry. - + (where to download package specific version), a `version`, a + `filename` and a `checksums['sha256']` entry. Example:: @@ -232,29 +104,34 @@ class CratesLoader(PackageLoader[CratesPackageInfo]): { "version": <version>, "url": "https://static.crates.io/crates/<package_name>/<package_name>-<version>.crate", + "filename": "<package_name>-<version>.crate", + "checksums": { + "sha256": "<sha256>", + }, } ] + + crates_metadata: + Same as previously but for Crates metadata. + For now it only has one boolean key `yanked`. + + Example:: + + [ + { + "version": "<version>", + "yanked": <yanked>, + }, + ] """ # noqa super().__init__(storage=storage, url=url, **kwargs) self.url = url self.artifacts: Dict[str, Dict] = { artifact["version"]: artifact for artifact in artifacts } - - @cached_method - def _raw_info(self) -> bytes: - """Get crate metadata (fetched from http api endpoint set as self.url) - - Returns: - Content response as bytes. Content response is a json document. - """ - return get_url_body(self.url) - - @cached_method - def info(self) -> Dict: - """Parse http api json response and return the crate metadata information - as a Dict.""" - return json.loads(self._raw_info()) + self.crates_metadata: Dict[str, Dict] = { + data["version"]: data for data in crates_metadata + } def get_versions(self) -> Sequence[str]: """Get all released versions of a crate @@ -267,7 +144,7 @@ class CratesLoader(PackageLoader[CratesPackageInfo]): ["0.1.1", "0.10.2"] """ versions = list(self.artifacts.keys()) - versions.sort(key=StrictVersion) + versions.sort(key=parse_version) return versions def get_default_version(self) -> str: @@ -282,6 +159,12 @@ class CratesLoader(PackageLoader[CratesPackageInfo]): """ return self.get_versions()[-1] + def get_metadata_authority(self): + return MetadataAuthority( + type=MetadataAuthorityType.FORGE, + url="https://crates.io/", + ) + def get_package_info(self, version: str) -> Iterator[Tuple[str, CratesPackageInfo]]: """Get release name and package information from version @@ -291,62 +174,63 @@ class CratesLoader(PackageLoader[CratesPackageInfo]): Returns: Iterator of tuple (release_name, p_info) """ - artifact = self.artifacts[version] + artifact = self.artifacts[version].copy() filename = artifact["filename"] + assert artifact["checksums"]["sha256"] + sha256 = artifact["checksums"]["sha256"] package_name = urlparse(self.url).path.split("/")[-1] url = artifact["url"] - # Get extrinsic metadata from http api - e_metadata = ExtrinsicPackageMetadata(**self.info()) # type: ignore[misc] + crate_metadata = self.crates_metadata[version].copy() + yanked = crate_metadata["yanked"] + last_update = datetime.fromisoformat(crate_metadata["last_update"]) - # Extract crate info for current version (One .crate file for a given version) - (crate_version,) = [ - crate for crate in e_metadata["versions"] if crate["num"] == version - ] - e_metadata_version = ExtrinsicVersionPackageMetadata( # type: ignore[misc] - **crate_version - ) + # Remove "version" from artifact to follow "original-artifacts-json" extrinsic + # metadata format specifications + # See https://docs.softwareheritage.org/devel/swh-storage/extrinsic-metadata-specification.html#extrinsic-metadata-formats # noqa: B950 + del artifact["version"] p_info = CratesPackageInfo( name=package_name, filename=filename, url=url, version=version, - e_metadata=e_metadata, - e_metadata_version=e_metadata_version, + sha256=sha256, + checksums={"sha256": sha256}, + yanked=yanked, + last_update=last_update, + directory_extrinsic_metadata=[ + RawExtrinsicMetadataCore( + format="crates-package-json", + metadata=json.dumps([crate_metadata]).encode(), + ), + ], ) yield release_name(version, filename), p_info def build_release( self, p_info: CratesPackageInfo, uncompressed_path: str, directory: Sha1Git ) -> Optional[Release]: + # Extract intrinsic metadata from dir_path/Cargo.toml - name = p_info.name - version = p_info.version - dir_path = Path(uncompressed_path, f"{name}-{version}") - i_metadata_raw = extract_intrinsic_metadata(dir_path) - # Get only corresponding key of IntrinsicPackageMetadata - i_metadata_keys = [k for k in IntrinsicPackageMetadata.__annotations__.keys()] - # We use data only from "package" entry - i_metadata = { - k: v for k, v in i_metadata_raw["package"].items() if k in i_metadata_keys - } - p_info.i_metadata = IntrinsicPackageMetadata(**i_metadata) # type: ignore[misc] + dir_path = Path(uncompressed_path, f"{p_info.name}-{p_info.version}") + i_metadata = extract_intrinsic_metadata(dir_path) + + author = EMPTY_AUTHOR + authors = i_metadata.get("package", {}).get("authors") + if authors and isinstance(authors, list): + # TODO: here we have a list of author, see T3887 + author = Person.from_fullname(authors[0].encode()) - author = extract_author(p_info) - description = extract_description(p_info) message = ( f"Synthetic release for Crate source package {p_info.name} " - f"version {p_info.version}\n\n" - f"{description}\n" + f"version {p_info.version}\n" ) - # The only way to get a value for updated_at is through extrinsic metadata - updated_at = p_info.e_metadata_version.get("updated_at") return Release( - name=version.encode(), + name=p_info.version.encode(), + date=TimestampWithTimezone.from_datetime(p_info.last_update), author=author, - date=TimestampWithTimezone.from_iso8601(updated_at), message=message.encode(), target_type=ObjectType.DIRECTORY, target=directory, diff --git a/swh/loader/package/crates/tests/data/expected.json b/swh/loader/package/crates/tests/data/expected.json new file mode 100644 index 00000000..7cb069f5 --- /dev/null +++ b/swh/loader/package/crates/tests/data/expected.json @@ -0,0 +1,133 @@ +[ + { + "url": "https://crates.io/api/v1/crates/hg-core", + "artifacts": [ + { + "version": "0.0.1", + "checksums": { + "sha256": "233409cca39eccab8075d3414884a2e9096aa3f7ceb4139685134c9f2561e734" + }, + "filename": "hg-core-0.0.1.crate", + "url": "https://static.crates.io/crates/hg-core/hg-core-0.0.1.crate" + } + ], + "crates_metadata": [ + { + "version": "0.0.1", + "yanked": false, + "last_update": "2019-04-16T18:48:11.404457+00:00" + } + ] + }, + { + "url": "https://crates.io/api/v1/crates/micro-timer", + "artifacts": [ + { + "version": "0.1.0", + "checksums": { + "sha256": "837c3955f0dfbd1a95f4388e06acb2f1a8030e28d251867e603b6be7b93da783" + }, + "filename": "micro-timer-0.1.0.crate", + "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.1.0.crate" + }, + { + "version": "0.1.1", + "checksums": { + "sha256": "bfb0e972e961d6bfd8d344ccf05c29ae39b7d817ced30568204495aee4bab519" + }, + "filename": "micro-timer-0.1.1.crate", + "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.1.1.crate" + }, + { + "version": "0.1.2", + "checksums": { + "sha256": "53251c2ab69c97447b4d6bfb327f7608ba0f7108e4aa3d4f559934d6cd088d11" + }, + "filename": "micro-timer-0.1.2.crate", + "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.1.2.crate" + }, + { + "version": "0.2.0", + "checksums": { + "sha256": "d5a39e92178f6f0c8acb1111258327eab2447e2ab574e5868e2770ed28940d70" + }, + "filename": "micro-timer-0.2.0.crate", + "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.2.0.crate" + }, + { + "version": "0.2.1", + "checksums": { + "sha256": "7889cfebb345bd706ecb93e7b67e7e6af1d68ce16e3f0c109bf8b7bcb79e55af" + }, + "filename": "micro-timer-0.2.1.crate", + "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.2.1.crate" + }, + { + "version": "0.3.0", + "checksums": { + "sha256": "d92b84c26f807891fd492ceecc11ca2bdaf73583665275080ead1c127ed861a4" + }, + "filename": "micro-timer-0.3.0.crate", + "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.3.0.crate" + }, + { + "version": "0.3.1", + "checksums": { + "sha256": "740acae0cfa83cf78207aab16ba4a41dce7101bca774c63eac8c41fd5ce382a5" + }, + "filename": "micro-timer-0.3.1.crate", + "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.3.1.crate" + }, + { + "version": "0.4.0", + "checksums": { + "sha256": "475222c2def55b7166390398a0c033ceb9a1cdfe008ff6ebd22773fb2029a60f" + }, + "filename": "micro-timer-0.4.0.crate", + "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.4.0.crate" + } + ], + "crates_metadata": [ + { + "version": "0.1.0", + "yanked": false, + "last_update": "2020-02-27T14:31:49.131258+00:00" + }, + { + "version": "0.1.1", + "yanked": false, + "last_update": "2020-02-27T15:17:53.486346+00:00" + }, + { + "version": "0.1.2", + "yanked": false, + "last_update": "2020-02-27T23:35:41.872176+00:00" + }, + { + "version": "0.2.0", + "yanked": false, + "last_update": "2020-03-23T10:57:04.418462+00:00" + }, + { + "version": "0.2.1", + "yanked": false, + "last_update": "2020-03-23T11:22:26.288804+00:00" + }, + { + "version": "0.3.0", + "yanked": false, + "last_update": "2020-06-02T11:38:33.047581+00:00" + }, + { + "version": "0.3.1", + "yanked": false, + "last_update": "2020-06-22T16:40:06.754009+00:00" + }, + { + "version": "0.4.0", + "yanked": false, + "last_update": "2020-09-28T13:40:49.593030+00:00" + } + ] + } +] diff --git a/swh/loader/package/crates/tests/data/fake_crates.sh b/swh/loader/package/crates/tests/data/fake_crates.sh index 9bfff253..c0eba7b4 100644 --- a/swh/loader/package/crates/tests/data/fake_crates.sh +++ b/swh/loader/package/crates/tests/data/fake_crates.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Script to generate fake crates files and fake http api response. +# Script to generate fake crates files. set -euo pipefail @@ -45,7 +45,7 @@ echo -e '''[package] edition = "2018" name = "micro-timer" version = "0.1.0" -authors = ["Raphaël Gomès <rgomes@octobus.net>"] +# authors = ["Raphaël Gomès <rgomes@octobus.net>"] # commented for testing empty authors description = "Dumb tiny logging timer" homepage = "https://heptapod.octobus.net/Alphare/micro-timer" readme = "README.md" @@ -243,17 +243,6 @@ cp micro-timer-0.3.0.crate ../../https_static.crates.io/crates_micro-timer_micro cp micro-timer-0.3.1.crate ../../https_static.crates.io/crates_micro-timer_micro-timer-0.3.1.crate cp micro-timer-0.4.0.crate ../../https_static.crates.io/crates_micro-timer_micro-timer-0.4.0.crate -# Creates some http file response for test purposes. -mkdir ../../https_crates.io - -# hg-core, https://crates.io/api/v1/crates/hg-core -echo -e '''{"categories":[],"crate":{"badges":[],"categories":[],"created_at":"2019-04-16T18:48:11.404457+00:00","description":"Mercurial pure Rust core library, with no assumption on Python bindings (FFI)","documentation":null,"downloads":442,"exact_match":false,"homepage":"https://mercurial-scm.org","id":"hg-core","keywords":[],"links":{"owner_team":"/api/v1/crates/hg-core/owner_team","owner_user":"/api/v1/crates/hg-core/owner_user","owners":"/api/v1/crates/hg-core/owners","reverse_dependencies":"/api/v1/crates/hg-core/reverse_dependencies","version_downloads":"/api/v1/crates/hg-core/downloads","versions":null},"max_stable_version":"0.0.1","max_version":"0.0.1","name":"hg-core","newest_version":"0.0.1","recent_downloads":40,"repository":"https://www.mercurial-scm.org/repo/hg","updated_at":"2019-04-16T18:48:11.404457+00:00","versions":[145309]},"keywords":[],"versions":[{"audit_actions":[],"crate":"hg-core","crate_size":21344,"created_at":"2019-04-16T18:48:11.404457+00:00","dl_path":"/api/v1/crates/hg-core/0.0.1/download","downloads":442,"features":{},"id":145309,"license":"GPL-2.0-or-later","links":{"authors":"/api/v1/crates/hg-core/0.0.1/authors","dependencies":"/api/v1/crates/hg-core/0.0.1/dependencies","version_downloads":"/api/v1/crates/hg-core/0.0.1/downloads"},"num":"0.0.1","published_by":{"avatar":"https://avatars0.githubusercontent.com/u/474220?v=4","id":45544,"login":"gracinet","name":"Georges Racinet","url":"https://github.com/gracinet"},"readme_path":"/api/v1/crates/hg-core/0.0.1/readme","updated_at":"2019-04-16T18:48:11.404457+00:00","yanked":false}]} -''' > ../../https_crates.io/api_v1_crates_hg-core - -# micro-timer, https://crates.io/api/v1/crates/micro-timer -echo -e '''{"categories":[],"crate":{"badges":[],"categories":[],"created_at":"2020-02-27T14:31:49.131258+00:00","description":"Dumb tiny logging timer","documentation":null,"downloads":44245,"exact_match":false,"homepage":"https://foss.heptapod.net/octobus/rust/micro-timer","id":"micro-timer","keywords":[],"links":{"owner_team":"/api/v1/crates/micro-timer/owner_team","owner_user":"/api/v1/crates/micro-timer/owner_user","owners":"/api/v1/crates/micro-timer/owners","reverse_dependencies":"/api/v1/crates/micro-timer/reverse_dependencies","version_downloads":"/api/v1/crates/micro-timer/downloads","versions":null},"max_stable_version":"0.4.0","max_version":"0.4.0","name":"micro-timer","newest_version":"0.4.0","recent_downloads":3910,"repository":"https://foss.heptapod.net/octobus/rust/micro-timer","updated_at":"2020-09-28T13:40:49.593030+00:00","versions":[288167,254896,248120,223660,223652,216405,216156,216139]},"keywords":[],"versions":[{"audit_actions":[{"action":"publish","time":"2020-09-28T13:40:49.593030+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":3513,"created_at":"2020-09-28T13:40:49.593030+00:00","dl_path":"/api/v1/crates/micro-timer/0.4.0/download","downloads":337,"features":{},"id":288167,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.4.0/authors","dependencies":"/api/v1/crates/micro-timer/0.4.0/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.4.0/downloads"},"num":"0.4.0","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.4.0/readme","updated_at":"2020-09-28T13:40:49.593030+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-06-22T16:40:06.754009+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":3357,"created_at":"2020-06-22T16:40:06.754009+00:00","dl_path":"/api/v1/crates/micro-timer/0.3.1/download","downloads":37853,"features":{},"id":254896,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.3.1/authors","dependencies":"/api/v1/crates/micro-timer/0.3.1/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.3.1/downloads"},"num":"0.3.1","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.3.1/readme","updated_at":"2020-06-22T16:40:06.754009+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-06-02T11:38:33.047581+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":3306,"created_at":"2020-06-02T11:38:33.047581+00:00","dl_path":"/api/v1/crates/micro-timer/0.3.0/download","downloads":4163,"features":{},"id":248120,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.3.0/authors","dependencies":"/api/v1/crates/micro-timer/0.3.0/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.3.0/downloads"},"num":"0.3.0","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.3.0/readme","updated_at":"2020-06-02T11:38:33.047581+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-03-23T11:22:26.288804+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":2937,"created_at":"2020-03-23T11:22:26.288804+00:00","dl_path":"/api/v1/crates/micro-timer/0.2.1/download","downloads":1301,"features":{},"id":223660,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.2.1/authors","dependencies":"/api/v1/crates/micro-timer/0.2.1/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.2.1/downloads"},"num":"0.2.1","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.2.1/readme","updated_at":"2020-03-23T11:22:26.288804+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-03-23T10:57:04.418462+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":2941,"created_at":"2020-03-23T10:57:04.418462+00:00","dl_path":"/api/v1/crates/micro-timer/0.2.0/download","downloads":104,"features":{},"id":223652,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.2.0/authors","dependencies":"/api/v1/crates/micro-timer/0.2.0/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.2.0/downloads"},"num":"0.2.0","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.2.0/readme","updated_at":"2020-03-23T10:57:04.418462+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-02-27T23:35:41.872176+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":4927,"created_at":"2020-02-27T23:35:41.872176+00:00","dl_path":"/api/v1/crates/micro-timer/0.1.2/download","downloads":258,"features":{},"id":216405,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.1.2/authors","dependencies":"/api/v1/crates/micro-timer/0.1.2/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.1.2/downloads"},"num":"0.1.2","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.1.2/readme","updated_at":"2020-02-27T23:35:41.872176+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-02-27T15:17:53.486346+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":2916,"created_at":"2020-02-27T15:17:53.486346+00:00","dl_path":"/api/v1/crates/micro-timer/0.1.1/download","downloads":111,"features":{},"id":216156,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.1.1/authors","dependencies":"/api/v1/crates/micro-timer/0.1.1/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.1.1/downloads"},"num":"0.1.1","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.1.1/readme","updated_at":"2020-02-27T15:17:53.486346+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-02-27T14:31:49.131258+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":2902,"created_at":"2020-02-27T14:31:49.131258+00:00","dl_path":"/api/v1/crates/micro-timer/0.1.0/download","downloads":118,"features":{},"id":216139,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.1.0/authors","dependencies":"/api/v1/crates/micro-timer/0.1.0/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.1.0/downloads"},"num":"0.1.0","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.1.0/readme","updated_at":"2020-02-27T14:31:49.131258+00:00","yanked":false}]} -''' > ../../https_crates.io/api_v1_crates_micro-timer - # Clean up removing tmp_dir cd ../../ rm -r tmp_dir/ diff --git a/swh/loader/package/crates/tests/data/https_crates.io/api_v1_crates_hg-core b/swh/loader/package/crates/tests/data/https_crates.io/api_v1_crates_hg-core deleted file mode 100644 index c76874d8..00000000 --- a/swh/loader/package/crates/tests/data/https_crates.io/api_v1_crates_hg-core +++ /dev/null @@ -1,2 +0,0 @@ -{"categories":[],"crate":{"badges":[],"categories":[],"created_at":"2019-04-16T18:48:11.404457+00:00","description":"Mercurial pure Rust core library, with no assumption on Python bindings (FFI)","documentation":null,"downloads":442,"exact_match":false,"homepage":"https://mercurial-scm.org","id":"hg-core","keywords":[],"links":{"owner_team":"/api/v1/crates/hg-core/owner_team","owner_user":"/api/v1/crates/hg-core/owner_user","owners":"/api/v1/crates/hg-core/owners","reverse_dependencies":"/api/v1/crates/hg-core/reverse_dependencies","version_downloads":"/api/v1/crates/hg-core/downloads","versions":null},"max_stable_version":"0.0.1","max_version":"0.0.1","name":"hg-core","newest_version":"0.0.1","recent_downloads":40,"repository":"https://www.mercurial-scm.org/repo/hg","updated_at":"2019-04-16T18:48:11.404457+00:00","versions":[145309]},"keywords":[],"versions":[{"audit_actions":[],"crate":"hg-core","crate_size":21344,"created_at":"2019-04-16T18:48:11.404457+00:00","dl_path":"/api/v1/crates/hg-core/0.0.1/download","downloads":442,"features":{},"id":145309,"license":"GPL-2.0-or-later","links":{"authors":"/api/v1/crates/hg-core/0.0.1/authors","dependencies":"/api/v1/crates/hg-core/0.0.1/dependencies","version_downloads":"/api/v1/crates/hg-core/0.0.1/downloads"},"num":"0.0.1","published_by":{"avatar":"https://avatars0.githubusercontent.com/u/474220?v=4","id":45544,"login":"gracinet","name":"Georges Racinet","url":"https://github.com/gracinet"},"readme_path":"/api/v1/crates/hg-core/0.0.1/readme","updated_at":"2019-04-16T18:48:11.404457+00:00","yanked":false}]} - diff --git a/swh/loader/package/crates/tests/data/https_crates.io/api_v1_crates_micro-timer b/swh/loader/package/crates/tests/data/https_crates.io/api_v1_crates_micro-timer deleted file mode 100644 index e6878b68..00000000 --- a/swh/loader/package/crates/tests/data/https_crates.io/api_v1_crates_micro-timer +++ /dev/null @@ -1,2 +0,0 @@ -{"categories":[],"crate":{"badges":[],"categories":[],"created_at":"2020-02-27T14:31:49.131258+00:00","description":"Dumb tiny logging timer","documentation":null,"downloads":44245,"exact_match":false,"homepage":"https://foss.heptapod.net/octobus/rust/micro-timer","id":"micro-timer","keywords":[],"links":{"owner_team":"/api/v1/crates/micro-timer/owner_team","owner_user":"/api/v1/crates/micro-timer/owner_user","owners":"/api/v1/crates/micro-timer/owners","reverse_dependencies":"/api/v1/crates/micro-timer/reverse_dependencies","version_downloads":"/api/v1/crates/micro-timer/downloads","versions":null},"max_stable_version":"0.4.0","max_version":"0.4.0","name":"micro-timer","newest_version":"0.4.0","recent_downloads":3910,"repository":"https://foss.heptapod.net/octobus/rust/micro-timer","updated_at":"2020-09-28T13:40:49.593030+00:00","versions":[288167,254896,248120,223660,223652,216405,216156,216139]},"keywords":[],"versions":[{"audit_actions":[{"action":"publish","time":"2020-09-28T13:40:49.593030+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":3513,"created_at":"2020-09-28T13:40:49.593030+00:00","dl_path":"/api/v1/crates/micro-timer/0.4.0/download","downloads":337,"features":{},"id":288167,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.4.0/authors","dependencies":"/api/v1/crates/micro-timer/0.4.0/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.4.0/downloads"},"num":"0.4.0","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.4.0/readme","updated_at":"2020-09-28T13:40:49.593030+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-06-22T16:40:06.754009+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":3357,"created_at":"2020-06-22T16:40:06.754009+00:00","dl_path":"/api/v1/crates/micro-timer/0.3.1/download","downloads":37853,"features":{},"id":254896,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.3.1/authors","dependencies":"/api/v1/crates/micro-timer/0.3.1/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.3.1/downloads"},"num":"0.3.1","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.3.1/readme","updated_at":"2020-06-22T16:40:06.754009+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-06-02T11:38:33.047581+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":3306,"created_at":"2020-06-02T11:38:33.047581+00:00","dl_path":"/api/v1/crates/micro-timer/0.3.0/download","downloads":4163,"features":{},"id":248120,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.3.0/authors","dependencies":"/api/v1/crates/micro-timer/0.3.0/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.3.0/downloads"},"num":"0.3.0","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.3.0/readme","updated_at":"2020-06-02T11:38:33.047581+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-03-23T11:22:26.288804+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":2937,"created_at":"2020-03-23T11:22:26.288804+00:00","dl_path":"/api/v1/crates/micro-timer/0.2.1/download","downloads":1301,"features":{},"id":223660,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.2.1/authors","dependencies":"/api/v1/crates/micro-timer/0.2.1/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.2.1/downloads"},"num":"0.2.1","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.2.1/readme","updated_at":"2020-03-23T11:22:26.288804+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-03-23T10:57:04.418462+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":2941,"created_at":"2020-03-23T10:57:04.418462+00:00","dl_path":"/api/v1/crates/micro-timer/0.2.0/download","downloads":104,"features":{},"id":223652,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.2.0/authors","dependencies":"/api/v1/crates/micro-timer/0.2.0/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.2.0/downloads"},"num":"0.2.0","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.2.0/readme","updated_at":"2020-03-23T10:57:04.418462+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-02-27T23:35:41.872176+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":4927,"created_at":"2020-02-27T23:35:41.872176+00:00","dl_path":"/api/v1/crates/micro-timer/0.1.2/download","downloads":258,"features":{},"id":216405,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.1.2/authors","dependencies":"/api/v1/crates/micro-timer/0.1.2/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.1.2/downloads"},"num":"0.1.2","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.1.2/readme","updated_at":"2020-02-27T23:35:41.872176+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-02-27T15:17:53.486346+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":2916,"created_at":"2020-02-27T15:17:53.486346+00:00","dl_path":"/api/v1/crates/micro-timer/0.1.1/download","downloads":111,"features":{},"id":216156,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.1.1/authors","dependencies":"/api/v1/crates/micro-timer/0.1.1/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.1.1/downloads"},"num":"0.1.1","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.1.1/readme","updated_at":"2020-02-27T15:17:53.486346+00:00","yanked":false},{"audit_actions":[{"action":"publish","time":"2020-02-27T14:31:49.131258+00:00","user":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"}}],"crate":"micro-timer","crate_size":2902,"created_at":"2020-02-27T14:31:49.131258+00:00","dl_path":"/api/v1/crates/micro-timer/0.1.0/download","downloads":118,"features":{},"id":216139,"license":"non-standard","links":{"authors":"/api/v1/crates/micro-timer/0.1.0/authors","dependencies":"/api/v1/crates/micro-timer/0.1.0/dependencies","version_downloads":"/api/v1/crates/micro-timer/0.1.0/downloads"},"num":"0.1.0","published_by":{"avatar":"https://avatars.githubusercontent.com/u/9445758?v=4","id":79957,"login":"Alphare","name":"Raphaël Gomès","url":"https://github.com/Alphare"},"readme_path":"/api/v1/crates/micro-timer/0.1.0/readme","updated_at":"2020-02-27T14:31:49.131258+00:00","yanked":false}]} - diff --git a/swh/loader/package/crates/tests/data/https_static.crates.io/crates_hg-core_hg-core-0.0.1.crate b/swh/loader/package/crates/tests/data/https_static.crates.io/crates_hg-core_hg-core-0.0.1.crate index e51e4372bfc9817a3cac1ee5f9be8d549ae8d479..d55446c3398f4a4b66695760dbba79eb7d5cef0c 100644 GIT binary patch literal 427 zcmV;c0aX4UiwFP!000001MSjLYuhjo0AN4sR|q|AFw1sqCor}#7_<yFD18lKDDuq~ zwIy?s8}hQ>J~?S2Z5Q^^vW?w`!A3rvPC9jMsvSa_%Ul*&@*?tRRoC;oSk?LI`(9F1 zWl@y*sw!h#u_za-gx4=q&O?(A3OP@{qq6$ueBAH;|D(DG{r{w}buzf2f6g`L1~04X zZ2y;a`KbTZVp%3Uf6gM#`Oo{m9+kdPEo_)o1Mv4;+}#MlZUMb<b{;$24#CtEI)^@v zti>fbY{BzurHzHa-?hg9iN}GDP6xN0yo}!;#D+ECHJUNpW&8@LCp4<(<AlJkV;lIq zEbq-074F~goe3Scjw|n{;W#foSNm9k<y&JLV_VN(U0i&AEm-FU7~?h$8wg?aZ?kN; zUn2E-kg+(ydZWSmL)7KfmvkZX)M47I04Ny3=)4II_orEQyPbTJDVt-nE@ouwSn%oL z3AS$FHf>-8+kn;Pa3r?boSx^8TrTIizl{7g>h^JTJbd=2uL>cA5JCtcgb+dqA%qY@ V2qA<JLJ0W_{RA<$TCD&m004?a(Bc39 literal 426 zcmV;b0agAViwFP!000001MSjZYuhjo0B}F+QwTkEF#nGo7~3cmS_&JKzJ@Rq`DTmS zk~zr@dD&;5oV1X(3wvqV#_or~Mn0WRI(2QVEJB<mS(2yGo5+G?Rn6~wS!KuXM^U~g z%6wT=WnM>FUX*1K@#<~Ld1`WABInU}lt$g1j{DvJe^mFR{}&Qln|M3)uerwD;Cit* z+5fsKpY?xH)OEzO*DT_c|GfXJQK~!H!kQU50RP0r!;KK^9?)rP=CS?l5KK<Kv*^Oe zN?d`(7978oN*nO}V|y4#@Gx+0mA9M8CE@$CShEJ4LOuG2j9&rOgj)7|oDleJXak>@ z<-OjZ#NB(o)xP7#a_QVO9Oi}RW*15@e4|aHP3!pC<>mD|!8$v@7`CzBfcK;Okfy`q z60uW55{eV7*9wf=M_t`~iHjtQEylg{fPx{6)@g5XcbsLr-6k(GrE_fBg^X+!3O+tO z!B!32#|?~N8Zb)lkHj?V<MaHHCG|Y_myzE_)jp37htK}>RUw2BLI@#*5JCtcgb+dq UA%qY@2qAx=pRPhKuK*|j01*t<@&Et; diff --git a/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.1.0.crate b/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.1.0.crate index dffc2fc86ec38139ee956d8009280c830d215dbf..c7cfa0f645b6aa1d655e08ccdd6f1755a68d76f2 100644 GIT binary patch delta 471 zcmV;|0Vw{+1LOmc7k_)L|6(U8r7c~up%mI4_8NRp?6ED@`l%!@%^MZ^82e(C_Xm=O zw6Fx0()qv`TO(<t87XS3V=pAwI7{**OQW;Pg5`WZz4PTf+kZca^0LVDB3qV4h|7yv zF^lN@{HYupo#&R)sI^?xH>czNHvW&2NA+KE+v&t>Ih<>cseizWvOHD)JS&&K)qkEZ z%7|v?O5jia^ZIWLuWxt<6@f;0p%r~c*(}RnGorYJ>1(e=7`X-O9;5Etg^>$B`d(WX zer?z_H$A`mIna+<-u-a&ZRjNl?t`wq-i|I&;NP?Af(qq9d7=2QqqUYYd_Y56ZK((6 zsTr-&4oCy8wtvDr<#jd6Ee*4FbkJQVR7W4P>cb``oMH;<y*KVnn)YBkH@Zpo_D(N{ zke`M0;7k^H^KjJb_2t!<bs`%^22q1@5VvBuANu@hwf?$VPthSY;kDg8Z#S|T#I_>F z>N=J@41{I89f52bFrXSxwE&lV8?_Hr*}Ly>+sSO2e^3zTcIqf9Uog@F_oIb}vb5tc zuve79cW-$d9^oCUrj>5vgVn;lR);k%lh6v}rP~xCgb+dqA%qY@2qA<JLI@#*5JCu@ N@C(1WjX3})006rd>)-$Y delta 443 zcmV;s0Yv`f1IPoA7k}saH*s7_Te@UJDYQN8HRPb!6I-nQD#=UpMuk4czF6h`fn>wB z&<0BBd|-sFkw%`860I|75DHvcWMx+5$=MZAQ`gg2Hg$10K1#}YH7lE{o|TKFC}-8I zPH27pqnsLj;EvK{=eTaKpO5?1{69*a^nb}+Z?a&NJXemX!GDYS{Mr6D&EiS_>#}JQ zTAb?uKl7jWe{Fet&3o7o=tK}k(>Ih=Mfs8u%@s^<2UmoV8*u(1>h8KQ!pATe=i`?( zyW)1>x8EgwZ`AEKPhZEYq`<#3Z7|!>XBxs=wjmw()(QKV?s8OH8id|cY5HF1o<1ZS z;$j;QHw6tLSbzU2&j+x9ThnC+d*&B1=5>%C9n}Hv?*HIwb#eJ=m8p&qDO%7T(w&fZ zQy)JpSD%-wDcTw@f^qxb%SP5xY&XO@)252YiMUO0Ban3m7IX)?72vZkqY1It2W5;! z%&KYqQJmlFlc;jR$PRcI9o(0DH%iG~PzGOv<7s?E@HDZ!ai^Q~Xt!9`&2j7IS?n<R l+qVxPgb+dqA%qY@2qA<JLI@#*5JCw3sUNF^?k)f*0083E+<pK6 diff --git a/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.1.1.crate b/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.1.1.crate index 6cc3eb6a24c53be899c9133e4c80d564b46fdd04..a3e861c483f6f7b4e90a107fa039abd29496f98b 100644 GIT binary patch literal 456 zcmV;(0XP01iwFP!000001MSkyirX*{0N|YU6hi0vN3k83(v~jSPzr4idkr}#_QV!z z{Zx{d=8Xz{jD4}n`vb{_ZJ`a6()qv$TO*A;BPAPWk{}hhBu{zDv)S3@QC(Hj$m=RU z93RbiS#VzDby>tXUd)SPMyvB5<<#hdaFot=PUz<P`M6)r|D)7N|ChpbCJjan=gKiP zcu|(m_P?$dPx@cQ`5Dd6b%3Aw&-=f&qPZ3wYzVY6NTcZ+%H}zL$%qyTrniGD!pIFc z{}6R|T^JE#=#BI7%bHyY+l$-p1ATAQ?Ke+f$E&2kzcWoR+tH^Q!dtc>E%?Ss`<U)> zR9hOP-qXQ!ozxwDNY=;2HXd#Y>O-*pRhIQ&gRrJe5BAJ1hM3nuc63w+qP_ovtJTHj zr&X$2Mh4k{_K@u4a5wew!*ca`xtgM_@iG{<|GjKvJ;?2bSZA6<i8v9r32p?kZoz_X zK{pb7`eigB7W<%#Jk8VjwEig0@AXL(Uof%*5k?31<=&0Mz+O-WUxO1#d}Q#kym6<S y<Y>28*UfS3$~1PE{O#L^5JCtcgb+dqA%qY@2qA<JLI@#*{?rdx&Ev@cC;$MhW9P8| literal 458 zcmV;*0X6;~iwFP!000001MSkyirX*{0AQc>6vF3PwwyRFr7c~up%mI4_8M|f?1?Sb z`l%!@%^MZ^82e(C_Xm;<+d>;CrSpL?w#FKHW|VB4$%0hivVw8O^W^M`sIIE%&g-f; zygy3#yqxj6tY&<X6ns|B774A+f0R?B55iHJ?3~ce_49GRTK`9>lm0J->kJD<4d==+ zHFz<fKimJhUOef4RmPE^#kmgfGyi%2*H$#wqJs^ARt9M_eM8f-;4f36g@WnnU_?@K z1I|B0-Hl61#29+xe0;M`uY~Qz?e~GcH|qAAr?2BEDe&)16U=t>Ohb5^Zb%Ehane4f zyByV)2C4USFkL5gM<0^)@nahgHwE<}SpO=|d$2)R)3Sp-^NS(ob&ww&)q!a5|KMtM zartS*RGX4PHlRIZJ2~7fef+RoeO|7nXluL-#_fME8(9x>yCK$@CQ~9V#BG8bfvj7w zpj*(51kb*VCd6VNl##Q7mDBe}ael8)qWB^uI}l-Xa9{4-I1K3vn!?xML>4a@d@OI= z=_WhcE!K5&+`2i79VUN!`Vc}0A%qY@2qA<JLI@#*5JCtcgwUV*0lGg`*Z?R10LcL0 AEC2ui diff --git a/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.1.2.crate b/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.1.2.crate index 3a0637033f921e8cfaefcc860b181fbdbf6dbcc9..28a7e65bf610f8bbe02c124b140da600cbec6263 100644 GIT binary patch literal 484 zcmV<A0UQ1wiwFP!000001MSk?YTGar2k>0$DTJ?Q+44uyjcu%?VGOoi^frW{*eABA zZE2ERmNy#gG5TVYo0i2jZD9n)w)2Bv937pb<gZp1MukqcV8WPjmPE6Yi@imj4^O_x z<KySO8P8JA(|D1kzK*9!n$2i_`lC$s)(J!DY-dDQTs>a*-1=Xto#ek1rc%tQwmH>| zA>etIJ(fS`er=q8&gVY=_*4gY%732!T8rXJRInjXN+(rE-_Rh5`B^|Z(ZcX{bVUMk z4aVM8-Cmb~h~Cx8SpUsBxD>h;H$NKsUbQ#hEq(2;QVaH-DxBK(mUZCX1{+d>EsWIn z<1YGkOP%cYv{6+hyNccotNmu}4>uIm&T0E9ifYhKXjQVKp3!;Z=NgDcM>QbI!!Nj8 zonL%fv9=6IBMay(gge>XE`9v4Tzy`yhH9;>bjs|1ZyH%Qa=Rhgs3L5IU+`_5>4B_E z(9o686%s7_wVrH_|4hze_HyVls{Yz52a~#FQfK$wq>9f2vIF6I1BY;SebWRlXaL`w z5uyJ`XMGsH({(uN=2O^=gU;C5VdjHMZNRzx7hx@!Ax-;W-<)y3?@!-xgb+dqA%qY@ a2qA<JLI@#*5JCu{f7ws)+V+G1C;$L9|MvR; literal 485 zcmV<B0UG`viwFP!000001MSk$YTGar24G+NDTJ<P*>Ym18{1e(!x(J4=xqo?u}^GK z+tMVtEN?W}WAw!)H!X{6+QJBoZRdkv937pb<X0;Tqar6;Fj2xdOXJ|=5^s@Z!;>$v z<oJ0n;PZ6G7il)*d64i~I$H!JJN;3ndh3KCB-k0z6<3efJ-7asYA5+Gg{c&Cs%=g+ zV+c5(&mYU5bH6suKjXR2KRML_p7NjPzt*C-5*2JhD5aCCBX0;z6Mja+PP8z*9bFL` zUW2iBRkzoLhN5@1GS+{yrk6t3;^s#~-mCWJyCtvvRcgV$Q-xF8-m(tdTe=BLu!WKO ze%wXhZi$oKo;0eeWLJ^9VYT0^{o#h9+Bt1s#c>VV39U+Y)H6PB{9FU^=%@xndH4mF ztMiLbE7q1YY-9nQg=i<6+og{mmaEUp)ljXKl}?%c?@bHWjofZRZB!Ap!Y}wX&h!wj zOVH4j&=nFa`?a2Kj{i)~683WFGOGUCD+iOhbW&&c-K2`=G~9u3y@5kGyS{1Y3qs+W zGa~XI>8uaKce;*7-Fyn0anN&ic9{8~QX6n?|3z2}W=PXM*q1Zz_x<TRju1i!A%qY@ b2qA<JLI@#*5JCtc^e_7f^cjQC04M+eDYou; diff --git a/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.2.0.crate b/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.2.0.crate index c304d0445e115d4406b473631066e953070e4e6f..185a165db8b301f87fa1107c7ccab83821cf7651 100644 GIT binary patch literal 419 zcmV;U0bKqciwFP!000001MSn@YTGar2XJ5eDTMDMTd|##(T#>Al)<`--iBN#_K7WG zeIUt!zR_Th(HE=SHISt11|zJDo!^Z=^tX?!BTG?PJ@P_=9c6S(voyN6ESQ$%aAeam zJ3byntjHP5vuTls9LvXf9+C3$Q+cg-o?Akqt>voPy`B5r_&-X%>i?YEMpLh4cc~gf zgC|AtX8)(-;;jEAn@l2-U1|cq@}KvAWq7sY4Xk6Rg%?_pk0cpq>?(;BmoQu%ZILA2 zgLN;d=k1cjy!WlPF5Foq_uRDn@q0&ZwS4^M$aUCC65Nfhyx#PVD)66@bzFn1tT2ai zi(YPs7wUm@x@m-J$V;&{JZ!?`hEVIhaUasO1>?EVH9dM~I_pAR3+c&JE%5rs7u+vr zi@PP2brN@?0_9+|72WgDm(TO%*ZFct8|{SG_TkT}t!gl!YEYE`M~{Ci$-}u@|77XT z9&(j7(DdBaCt*g}5DwY>=p-Lj>+G!s4SkE>dru*R5JCtcgb+dqA%qY@2qA<JLI|P1 N=Lr>Cqa*+*003xM)HVPB literal 419 zcmV;U0bKqciwFP!000001MSnzYU3~v0AQc}6vFq&mSe}I>@Hi<uoT)JdJQ?K_S6=& zevsrs->A^X=!;cu8%WYWD51-;^Ic?HW6fA2OHo@r@j`-~WHhH)8tq*cRAo7itSYn3 zaV=s+K4n#2PT4HV*fgKcB2w<Zl-CC5xg{jJvRu{I@5g;M|BsTd`akEk)zoX*?<>dH z;8{_;+y5#rcKTnkibW*bR{<aS&-=eLyuRiwtYT<{7g~{@B*`;&ki?2h81FWYND|+G zbuUp*$0dpR;5%(yc(P0`xas)AeNX;q`EcjRk8qSExZk?=`f_kof&Z1P;s#u8g;}RN z9^@tQLfw*Hx2;euc}doVmrW>c4C=f$?l4U|FrFLT(2X_Iqdw%dkZzS~fj7@zaIrW# zK3h=PBylflP!1+nqJNq?J((}g=Zi7gXeYe3w_nS)Y`}nOK-B^q-TbX257%z>#@6k; z<Z5l89k^|_f*ECFaEQH`Y{iG&+F7-rp>OfuK2r!Ggb+dqA%qY@2qA<JLI@#*5JKqt NJOWBG0NVg4007KD&P4zK diff --git a/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.2.1.crate b/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.2.1.crate index 3d844f0cf4edf2f90e5d689b19dacb7a51c63fd0..8e193d425fcd121c5012f3e050499ac0cdf5dcd3 100644 GIT binary patch literal 420 zcmV;V0bBkbiwFP!000001MSn@YTGar2XJ5eDTMDMTd^IF(v5{Ql)<)(-iBPL_Ngsm zeIUt&zR_Th(HEPXwU7_8LJ2Kp=Xc`|{p};`$Wqi+PrQ&|CmEemmPTin1yxy&M^=^D z?(tc~ikz`LtBO42SU$~*h?M7_%CW(DZV8F*ELZjBbnd0`4@w^Of5C05sn@bUSB<g3 zv!XcJ|7uzs^uJ`YDk9mrCh#x+dH+|2*Bjo#I)+Agp%wW|l4-_1C9&cX#;ctzlEn95 z-BW72U6Pm&zSGu)JFDb|n~p#J?#Z>5kG~wb3|mQp`>JcNZwE&e_%F#iZot)6nCEg= zgS;hPs0Y&PwiT)+PsO_MunChJL!I}=U8HFT#&e?^y7SI--iNvt(!Hr#;LYDJxLMAx zek`eMlDHQ&C<l`}(Qk*oe_JenE|z23XeYe353j4XYQTVMK-B^q-Tkd359e-uxO7<( zA3Wr0ZJ-^vZT7;9vN0U8_mjPRSgnJ%7BuuNe(ybn5JCtcgb+dqA%qY@2qA<JLI@#* O-p(Hb{8qXEC;$K`643|% literal 420 zcmV;V0bBkbiwFP!000001MSn@YTGar0AOGHDTMDMTaF!%(v5{Al)<)(-iBPL_Ngsm z{UFJKzR_Th(HEPXwUDHAg%VoI&UYi(I?|DKWGQN^CtgUflZ@t+rP0}CK~<Jx%c?Rv zY#&9e$fvBz%PE^h8Jp(QDkA0ixAN5BJhy~Ix0b7V`+VF>;~$iKQvU_Ft)^bf{#-f6 z0?&%#+4@&`aZ>*>bR&|TYXkrCpVxnFc)jH<Y+`7H7g~|eB*`=ODTx)AFg_hzktE)M zbq`Vd>ypHL@SV0UyjdsL+;sf@cTcXgy#M9MWw=Tb+*e(DeKR<!z<)_LaRaWl!aSy1 z4)TV0q3%ep+g7NSJS6MFWD_<w26f&Wcaf$Y7|)Gv=)s!lybpOTq(@t|z?;8caJ`x@ zf2^o%lDHQ&C<l{U(eH=8e_O16E>>f-(N1`6?_QT}-GBkrfT{&JdiYyO9{O%`dURP5 zpG<PKHqZ{-Hb=pXvN1Ts?j}d^;j~UxEoi7){N8s8A%qY@2qA<JLI@#*5JCtcgb+dq Oy`4X(IPzrxC;$MZe$A8s diff --git a/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.3.0.crate b/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.3.0.crate index ff912271d82473be662447f912096679956cad09..d60abaaaf1fcb233e4d848e393166c4a7a92351d 100644 GIT binary patch literal 419 zcmV;U0bKqciwFP!000001MSnxYTPgs24JuK6vF$Ft+720r3nd>Ar#UqvK?lj*eAA# zbwQE?d80xeV_vLsl7YE2ErcPZ^n4p%^xH?)k)^1uo_Ha_PBNO)ERBvX3#zglkE|-Q z?c+woikz`LtBO42SU$~*h?K{l%Avt|ZV8F*Em!r!`?)`j|D)tV|L5GcntCn!W7QZN zJS&QK`(I6qz5bVMRz)N`)&zd%Kkxt2@cMzbu!^A(UT8&5NixmYNfIkAVZ7SfB1!xR z*1e@(wo4N8!FSraaA%p^a?|nkb5E|cT>o(7YuHK>+_|p3z8f4>;Lnm(+<>dCFq?9h zgS;bNs3+3vwiT)+Z^gRsunChJL!I}=eM!>}jORu-bnBhzMIY)~NOz`cfj6&TaJ#s; z{Jx;FN#b7Apd3u@MgKB%{cXOunJ>n)(N1`6pFUP?*?<AnfT{&Jy8T;89?sqB7fZMI zkgK(UcHp+z2{X#ZaL7JRcJg7h_TF01(6{(+?<s^3LI@#*5JCtcgb+dqA%qY@2qE-& NegZOygi!z}007<J(|!N| literal 413 zcmV;O0b>3iiwFP!000001MSnxO2jY}0MM@c6`}h{-&RCL83z&6h1+2kvAwpz=0TDQ z{*mBk_+zF%U}k)Q42pQqCTVk%o93p1*G5eo7hom{&1n(`8<%)hS&nU5mC17ZDoBeg zPphoV)2$#$^DM6dQf_`LZw=NlLr8FCn5?fqj{9o-j}mX{KV_y>)G5(#D#uvht)lp_ z{#912)xY%JfFzsRz-Rt>{pXt1*Q|v_2n}~!NwP<xEJ?ScP%;7I)6x}*!W%I5CF<$A zL?Ihor;PP)=FtVy9ecR%$*~d-cb4q>t0cf4sM@K^!BPosKU#zh*xGRYn(k;2m&9>- zOM2C|T(;yTS?4Efzqv7}b57fxIPO3@rd30i){GB(pVvUVvQ-1DdH#Zn+2PUIjEW`- zdtQUIFuCIW)6nV3bap<SjnP_J?v%OxUbcAy8e{{q=3wdaZzY)T+r>Law>HVvN<%v^ z)2sxiG#!I|?9F5)-k;Xmss;6R3xE4gA%qY@2qA<JLI@#*5JCtcgb+dqp`Y^znN1*$ H04M+eKQhW8 diff --git a/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.3.1.crate b/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.3.1.crate index fcbe1422ea5bc2945a2459bb17297e42c53af1d8..25339c58200171fa3db9cdb90ca06fb4f5f0c964 100644 GIT binary patch literal 416 zcmV;R0bl+fiwFP!000001MSn@O2aS|0N`EsDMIg;Hnw#lD#{!ps2ARjUBvdR4K_a{ zso)z4K87!5Iwx%N4`e9f`EJrSr#YuNsT8HvLoX!QVM;T~lEK!c!Kf(uk&TLUb$m5o zdB#|lj`A$Tv1}&{q}YBbZ*|UdOUPhuxhiizj{9o<kCJctKjya9)N9#pD@Whp-8}!W z|D$ZP{{`C({ZF@5z-Rt>|EGqRx4ec~1eNeYD{@HUos{jxk>V2iyOkpnM|WV|GHP*L z;)r*?(bk10)A*X3hCe;F<V?$_2S*OVQIg<}b?Nm@=codI6wjgxTxo@QO?TSK8{&n! zC#|k)p=z>B)`XW$D6S7`yf<z?N#@!)+JN!g=&D~ZSuUQ~(8^@3SPQ&*`G)Js$?4^U z$|{apQG#+XoQrlbb#XqPT#YAvw9!s@ZSTLEZ(4x?Re>r6IJ)|4Ngl4<?49k~c*&L8 zK;3a$tpzj6Xu3#nH(ZMkJGQZEPDAga-#%9eA%qY@2qA<JLI@#*5JCtcgb+gLXFLO9 Kq5+TqC;$Mf<I3Ow delta 414 zcmV;P0b%~21Em9hABzY80000000ZsQ&1&N?5CCAG{S?CY$X0CErEE)=G%SVPJ@guK zQ0<8=YW*O|g}zZ?AEPg}ayEq|{eu#=l+JgNZH+Z!jVwiN^~4Jac9PMYvNSrlEU3zI z99dOn+vB5%71Nwm(=umAQO5FVkwv6Dd?`;2&T~shw6a`()i*E4y*2+y$tV4vaocL@ zwd@a-V{GtIQM}myD&Oya$*R!*>`(=~=8N}#VR(JRTUf@>2rslErzDwX>^O-PmoVOK z9g!ry1?x6Z>*JEdeDIyNE<9NzSKM^`;ZIL~X!&sO$hUBmB)D^3dwo4Ps=%Km%eVno zTVWp4T@3Pnns}k^NUz&gsFrM!b>U?biW`GE?~OZ2)0K9Pc3?a=x)~QtH;bn>v@+c( z)&g(-eZ$rK`^B$0l}!@&q6Xz)vJ(Az>gVNb{(Ckbqm6dLYkT+Je2WGQs0LImz|rkr zOY(5-md|Y8-b=352HJt!W+#|YMzeK-+sRIR*s;9{RSOz=7k~7*LX&_26bt{yUoYAv IJOC&F03ztkcK`qY diff --git a/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.4.0.crate b/swh/loader/package/crates/tests/data/https_static.crates.io/crates_micro-timer_micro-timer-0.4.0.crate index 1b50fa5b14b54d89b9be70bab3bb8eacb29050c0..6f30efba4df066d516a3182b5671d731e5935bac 100644 GIT binary patch literal 419 zcmV;U0bKqciwFP!000001MSn@YTGav2H+g~DumBtTeaPcv5k#1jKQ{po`xK#_7_{! z`azNly`#Y{qc=9WOCf326-w9`d!G~8)@w=DCo9=FGY?Xcn-^J?75VJsifB>SQ_B~1 zvEM$J@v7v!EEZK6bG$r@4XaPTlw+e0!Z9}6I-#5Er*qHs|D)7V{g=XZCJROlr<!9b z@Of1|S^q`(xc)UikM%E3wSeFG&+EUoqPZ3wZ4zo_kVdn&EIljut2EI<(R8<WMAGDj zoZqGHk4u_}G4#gy_+*`43fqgj?*sd2)ZI7F-o&G%$iFvDFju3`G=+ERCTYnxPTGg* zE=G05g4DNcFkL5g$99wT@nstqH--8Ttbd*7TjPD!lMTX}b{a6>4W7GL%KTuljzs(O z8!lHLF21ZX)uzcH8`7TUTRGf!eg3pueO<1mv^8D^<8EI%-?}ABx+UF6^4b2cr9`~; zn_q0-qnCVREOn!B?LnAld<w_x&HNxAckF1?Si9tJpDO?W000000000000000008(i NegF|zL+t=4004?i&ZPhV literal 417 zcmV;S0bc$eiwFP!000001MSn@O2aS|0N`HtDMIhpHd|{&RFrLqpk8=8b`jgNHmvy} zNd@0X@G*QbQ=PCme?W#Jp6@1YbDDFSlL}rMHE>*j8KiL*r%AALsaNEAKhh#k*T+`@ z&4wdd4D%7)3sO27W@$k3-IubhvyK@;f`wtSTz(w))%+hNw)#J2rdF|2qTN-FzQKE0 z_F?~v(cAv#wDA2;cU8b={(1jrnw3jd!#sqFJFX--BGE9V`%x&Ffc|dnh(zHn7`uvk zJ}yznI@c&;{gYXA&2+;aA6jyz#N)jshyExDuqUc?>ZY@?1a}<G!wPI^xPDDH>BJ3j zT;7pZ)iswjStV=y%i0&$2Q|)VdypgxWo_Jmc1)|PUocrMo*3WCWTRLEta|x|>*?v_ zavF;&3R_-+v@lrk_Ic{!d_27xPy1-CEO*M>eK+5%0u8bPS#q%P`mZIJzqa#tw(reL zwp1GGj+tsBn8vgZ_OZ8vjd;IfTdVrsg};5S5JCtcgb+dqA%qY@2qA<JLI@#*(9d`R LTp(vb04M+eb_d4~ diff --git a/swh/loader/package/crates/tests/test_crates.py b/swh/loader/package/crates/tests/test_crates.py index 1ff76f72..2335d01b 100644 --- a/swh/loader/package/crates/tests/test_crates.py +++ b/swh/loader/package/crates/tests/test_crates.py @@ -2,112 +2,45 @@ # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information +import json +from pathlib import Path + import pytest +from swh.loader.package import __version__ from swh.loader.package.crates.loader import CratesLoader from swh.loader.tests import assert_last_visit_matches, check_snapshot, get_stats from swh.model.hashutil import hash_to_bytes from swh.model.model import ( + MetadataAuthority, + MetadataAuthorityType, + MetadataFetcher, ObjectType, Person, + RawExtrinsicMetadata, Release, Snapshot, SnapshotBranch, TargetType, TimestampWithTimezone, ) +from swh.model.swhids import CoreSWHID, ExtendedObjectType, ExtendedSWHID +from swh.model.swhids import ObjectType as OType +from swh.storage.interface import PagedResult -CRATES_EXTRA = [ - { - "url": "https://crates.io/api/v1/crates/hg-core", - "artifacts": [ - { - "checksums": { - "sha256": "48a45b46c2a8c38348adb1205b13c3c5eb0174e0c0fec52cc88e9fb1de14c54d", # noqa: B950 - }, - "filename": "hg-core-0.0.1.crate", - "url": "https://static.crates.io/crates/hg-core/hg-core-0.0.1.crate", - "version": "0.0.1", - }, - ], - }, - { - "url": "https://crates.io/api/v1/crates/micro-timer", - "artifacts": [ - { - "checksums": { - "sha256": "69ad8fd116f8af0298ae4e83e587b1600af12709022471e25581c3aeb1da77ce", # noqa: B950 - }, - "filename": "micro-timer-0.1.0.crate", - "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.1.0.crate", - "version": "0.1.0", - }, - { - "checksums": { - "sha256": "7b3f65fe0e109daad8d47e1938c9b5f9353efacd86bbe7ff013f84ae7ca758bf", # noqa: B950 - }, - "filename": "micro-timer-0.1.1.crate", - "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.1.1.crate", - "version": "0.1.1", - }, - { - "checksums": { - "sha256": "16439fea388f712c1df7737ceb8f784d407844624b4796faf1e1bf8bbaa97445", # noqa: B950 - }, - "filename": "micro-timer-0.1.2.crate", - "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.1.2.crate", - "version": "0.1.2", - }, - { - "checksums": { - "sha256": "336b4c0f071d16674747faa4643d742cc096fec2bf8cf01bb1a98d984bedcaf1", # noqa: B950 - }, - "filename": "micro-timer-0.2.0.crate", - "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.2.0.crate", - "version": "0.2.0", - }, - { - "checksums": { - "sha256": "987429cd6162a80ed5ff44fc790f5090b1c6d617ac73a2e272965ed91201d79b", # noqa: B950 - }, - "filename": "micro-timer-0.2.1.crate", - "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.2.1.crate", - "version": "0.2.1", - }, - { - "checksums": { - "sha256": "25b31d6cb9112984323d05d7a353f272ae5d7a307074f9ab9b25c00121b8c947", # noqa: B950 - }, - "filename": "micro-timer-0.3.0.crate", - "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.3.0.crate", - "version": "0.3.0", - }, - { - "checksums": { - "sha256": "2620153e1d903d26b72b89f0e9c48d8c4756cba941c185461dddc234980c298c", # noqa: B950 - }, - "filename": "micro-timer-0.3.1.crate", - "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.3.1.crate", - "version": "0.3.1", - }, - { - "checksums": { - "sha256": "5de32cb59a062672560d6f0842c4aa7714727457b9fe2daf8987d995a176a405", # noqa: B950 - }, - "filename": "micro-timer-0.4.0.crate", - "url": "https://static.crates.io/crates/micro-timer/micro-timer-0.4.0.crate", - "version": "0.4.0", - }, - ], - }, -] + +@pytest.fixture +def expected(datadir): + fp = datadir / Path("expected.json") + return json.loads(fp.read_bytes()) -def test_get_versions(requests_mock_datadir, swh_storage): +def test_get_versions(requests_mock_datadir, swh_storage, expected): loader = CratesLoader( swh_storage, - url=CRATES_EXTRA[1]["url"], - artifacts=CRATES_EXTRA[1]["artifacts"], + url=expected[1]["url"], + artifacts=expected[1]["artifacts"], + crates_metadata=expected[1]["crates_metadata"], ) assert loader.get_versions() == [ "0.1.0", @@ -121,11 +54,12 @@ def test_get_versions(requests_mock_datadir, swh_storage): ] -def test_get_default_version(requests_mock_datadir, swh_storage): +def test_get_default_version(requests_mock_datadir, swh_storage, expected): loader = CratesLoader( swh_storage, - url=CRATES_EXTRA[1]["url"], - artifacts=CRATES_EXTRA[1]["artifacts"], + url=expected[1]["url"], + artifacts=expected[1]["artifacts"], + crates_metadata=expected[1]["crates_metadata"], ) assert loader.get_default_version() == "0.4.0" @@ -137,9 +71,19 @@ def test_crate_invalid_origin_archive_not_found(swh_storage, requests_mock_datad url, artifacts=[ { + "version": "0.0.1", "filename": "nowhere-to-hide-0.0.1.crate", "url": "https://nowhere-to-run/nowhere-to-hide-0.0.1.crate", + "checksums": { + "sha256": "5de32cb59a062672560d6f0842c4aa7714727457b9fe2daf8987d995a176a405", # noqa: B950 + }, + }, + ], + crates_metadata=[ + { "version": "0.0.1", + "yanked": True, + "last_update": "1970-01-01T00:00:00.000000+00:00", }, ], ) @@ -151,18 +95,21 @@ def test_crate_invalid_origin_archive_not_found(swh_storage, requests_mock_datad ) -def test_crates_loader_load_one_version(datadir, requests_mock_datadir, swh_storage): +def test_crates_loader_load_one_version( + datadir, requests_mock_datadir, swh_storage, expected +): loader = CratesLoader( swh_storage, - url=CRATES_EXTRA[0]["url"], - artifacts=CRATES_EXTRA[0]["artifacts"], + url=expected[0]["url"], + artifacts=expected[0]["artifacts"], + crates_metadata=expected[0]["crates_metadata"], ) actual_load_status = loader.load() assert actual_load_status["status"] == "eventful" assert actual_load_status["snapshot_id"] is not None - expected_snapshot_id = "b3affb4949eb89b244f0e1d1fe235fc1d26bde76" - expected_release_id = "237c4cdd44a90e620795e5a07ebcc72bc82487f7" + expected_snapshot_id = "779fa94645a772fb9992c2bf80bf5e2ecfdb1b5c" + expected_release_id = "bb6f9b125867a8b4fa0b2febf890a317744e0140" assert expected_snapshot_id == actual_load_status["snapshot_id"] @@ -195,9 +142,7 @@ def test_crates_loader_load_one_version(datadir, requests_mock_datadir, swh_stor assert swh_storage.release_get([hash_to_bytes(expected_release_id)])[0] == Release( name=b"0.0.1", - message=b"Synthetic release for Crate source package hg-core version 0.0.1\n\n" - b"Mercurial pure Rust core library, with no assumption " - b"on Python bindings (FFI)\n", + message=b"Synthetic release for Crate source package hg-core version 0.0.1\n", target=hash_to_bytes("674c3b0b54628d55b93a79dc7adf304efc01b371"), target_type=ObjectType.DIRECTORY, synthetic=True, @@ -207,54 +152,59 @@ def test_crates_loader_load_one_version(datadir, requests_mock_datadir, swh_stor ) -def test_crates_loader_load_n_versions(datadir, requests_mock_datadir, swh_storage): - url = CRATES_EXTRA[1]["url"] +def test_crates_loader_load_n_versions( + datadir, requests_mock_datadir_visits, swh_storage, expected +): + url = expected[1]["url"] + loader = CratesLoader( swh_storage, - url=CRATES_EXTRA[1]["url"], - artifacts=CRATES_EXTRA[1]["artifacts"], + url=url, + artifacts=expected[1]["artifacts"], + crates_metadata=expected[1]["crates_metadata"], ) - actual_load_status = loader.load() + assert actual_load_status["status"] == "eventful" assert actual_load_status["snapshot_id"] is not None - expected_snapshot_id = "3f8ca5908a570fa32270b07a0946bcffa88babd5" + expected_snapshot_id = "d53bf516948733e422eaceb95dd1ef317e6f5df9" + assert expected_snapshot_id == actual_load_status["snapshot_id"] expected_snapshot = Snapshot( id=hash_to_bytes(expected_snapshot_id), branches={ - b"releases/0.4.0/micro-timer-0.4.0.crate": SnapshotBranch( - target=hash_to_bytes("b038a927244c852fb3794aecbebdc70f68ddf067"), + b"releases/0.1.0/micro-timer-0.1.0.crate": SnapshotBranch( + target=hash_to_bytes("bd8d093b4ad56ca7e49aa0f709d945483b831915"), target_type=TargetType.RELEASE, ), - b"releases/0.3.1/micro-timer-0.3.1.crate": SnapshotBranch( - target=hash_to_bytes("ea331a2ce755e6f0cd9d05c9be52accde68536c4"), + b"releases/0.1.1/micro-timer-0.1.1.crate": SnapshotBranch( + target=hash_to_bytes("a54bb00b38ae049ac4d7548a722bcd891811dbf6"), target_type=TargetType.RELEASE, ), - b"releases/0.3.0/micro-timer-0.3.0.crate": SnapshotBranch( - target=hash_to_bytes("7ea45f915ace083ed361bb12593625bf4cf1f5f2"), + b"releases/0.1.2/micro-timer-0.1.2.crate": SnapshotBranch( + target=hash_to_bytes("e234efbaef1999866d3b5c9a5676c42a5c4d1d3f"), target_type=TargetType.RELEASE, ), - b"releases/0.2.1/micro-timer-0.2.1.crate": SnapshotBranch( - target=hash_to_bytes("074f27605be8b759e5d7c638f026aac3709f58e5"), + b"releases/0.2.0/micro-timer-0.2.0.crate": SnapshotBranch( + target=hash_to_bytes("519bfdb898f91011eeb618d7d7aaa93097e57ab4"), target_type=TargetType.RELEASE, ), - b"releases/0.2.0/micro-timer-0.2.0.crate": SnapshotBranch( - target=hash_to_bytes("a1d642aaa54c5361f67e57adbd86e01f3a3276f8"), + b"releases/0.2.1/micro-timer-0.2.1.crate": SnapshotBranch( + target=hash_to_bytes("c1bdd5a5a0769c4fd10af92a43e66ce7a2394e8c"), target_type=TargetType.RELEASE, ), - b"releases/0.1.2/micro-timer-0.1.2.crate": SnapshotBranch( - target=hash_to_bytes("60f18ae067ce235bc60243bf5cdaaae474b11978"), + b"releases/0.3.0/micro-timer-0.3.0.crate": SnapshotBranch( + target=hash_to_bytes("d68b74713017c0b32a553f107d179be386bafdbc"), target_type=TargetType.RELEASE, ), - b"releases/0.1.1/micro-timer-0.1.1.crate": SnapshotBranch( - target=hash_to_bytes("fd6c55dfd016d58647a2d44b29a3fd4e3afa7671"), + b"releases/0.3.1/micro-timer-0.3.1.crate": SnapshotBranch( + target=hash_to_bytes("ee11442a44d32562aedf731932ae4a8a9cf23feb"), target_type=TargetType.RELEASE, ), - b"releases/0.1.0/micro-timer-0.1.0.crate": SnapshotBranch( - target=hash_to_bytes("3e07559a4b366a397b1ca154e72753ce27223ca1"), + b"releases/0.4.0/micro-timer-0.4.0.crate": SnapshotBranch( + target=hash_to_bytes("0018b25f87d0838f6bef3e94b3000043bc9c938d"), target_type=TargetType.RELEASE, ), b"HEAD": SnapshotBranch( @@ -285,3 +235,269 @@ def test_crates_loader_load_n_versions(datadir, requests_mock_datadir, swh_stora type="crates", snapshot=expected_snapshot.id, ) + + +def test_crates_loader_load_multiple_visits_no_changes( + datadir, requests_mock_datadir_visits, requests_mock_datadir, swh_storage, expected +): + + url = expected[0]["url"] + loader = CratesLoader( + swh_storage, + url=url, + artifacts=expected[0]["artifacts"], + crates_metadata=expected[0]["crates_metadata"], + ) + + visit_1_actual_load_status = loader.load() + assert visit_1_actual_load_status["status"] == "eventful" + assert visit_1_actual_load_status["snapshot_id"] is not None + + expected_snapshot_id = "779fa94645a772fb9992c2bf80bf5e2ecfdb1b5c" + expected_release_id = "bb6f9b125867a8b4fa0b2febf890a317744e0140" + + assert expected_snapshot_id == visit_1_actual_load_status["snapshot_id"] + + expected_snapshot = Snapshot( + id=hash_to_bytes(visit_1_actual_load_status["snapshot_id"]), + branches={ + b"releases/0.0.1/hg-core-0.0.1.crate": SnapshotBranch( + target=hash_to_bytes(expected_release_id), + target_type=TargetType.RELEASE, + ), + b"HEAD": SnapshotBranch( + target=b"releases/0.0.1/hg-core-0.0.1.crate", + target_type=TargetType.ALIAS, + ), + }, + ) + check_snapshot(expected_snapshot, swh_storage) + + assert_last_visit_matches( + swh_storage, url, status="full", type="crates", snapshot=expected_snapshot.id + ) + + stats = get_stats(swh_storage) + assert { + "content": 1, + "directory": 2, + "origin": 1, + "origin_visit": 1, + "release": 1, + "revision": 0, + "skipped_content": 0, + "snapshot": 1, + } == stats + + assert swh_storage.release_get([hash_to_bytes(expected_release_id)])[0] == Release( + name=b"0.0.1", + message=b"Synthetic release for Crate source package hg-core version 0.0.1\n", + target=hash_to_bytes("674c3b0b54628d55b93a79dc7adf304efc01b371"), + target_type=ObjectType.DIRECTORY, + synthetic=True, + author=Person.from_fullname(b"Georges Racinet <georges.racinet@octobus.net>"), + date=TimestampWithTimezone.from_iso8601("2019-04-16T18:48:11.404457+00:00"), + id=hash_to_bytes(expected_release_id), + ) + + loader_2 = CratesLoader( + swh_storage, + url=url, + artifacts=expected[0]["artifacts"], + crates_metadata=expected[0]["crates_metadata"], + ) + actual_load_status2 = loader_2.load() + assert actual_load_status2 == { + "status": "uneventful", + "snapshot_id": actual_load_status2["snapshot_id"], + } + + visit_status2 = assert_last_visit_matches( + swh_storage, url, status="full", type="crates" + ) + + stats2 = get_stats(swh_storage) + expected_stats2 = stats.copy() + expected_stats2["origin_visit"] = 1 + 1 + assert expected_stats2 == stats2 + + # same snapshot + assert visit_status2.snapshot == expected_snapshot.id + + +def test_crates_loader_load_multiple_version_incremental( + datadir, requests_mock_datadir_visits, swh_storage, expected +): + + url = expected[1]["url"] + # one version in artifacts + artifacts_0 = [ + artifact + for artifact in expected[1]["artifacts"] + if artifact["version"] in ["0.1.0"] + ] + crates_metadata_0 = [ + artifact + for artifact in expected[1]["crates_metadata"] + if artifact["version"] in ["0.1.0"] + ] + + # two versions in artifacts + artifacts_1 = [ + artifact + for artifact in expected[1]["artifacts"] + if artifact["version"] in ["0.1.0", "0.1.1"] + ] + crates_metadata_1 = [ + artifact + for artifact in expected[1]["crates_metadata"] + if artifact["version"] in ["0.1.0", "0.1.1"] + ] + + # Visit 1 + loader = CratesLoader( + swh_storage, + url=url, + artifacts=artifacts_0, + crates_metadata=crates_metadata_0, + ) + + visit1_actual_load_status = loader.load() + visit1_stats = get_stats(swh_storage) + expected_snapshot_id = hash_to_bytes("729d99ab2e68cde99c425251ac5725c478d686df") + + assert visit1_actual_load_status == { + "status": "eventful", + "snapshot_id": expected_snapshot_id.hex(), + } + + assert_last_visit_matches( + swh_storage, url, status="full", type="crates", snapshot=expected_snapshot_id + ) + + assert { + "content": 1, + "directory": 2, + "origin": 1, + "origin_visit": 1, + "release": 1, + "revision": 0, + "skipped_content": 0, + "snapshot": 1, + } == visit1_stats + + # Visit 2 + loader = CratesLoader( + swh_storage, + url=url, + artifacts=artifacts_1, + crates_metadata=crates_metadata_1, + ) + + visit2_actual_load_status = loader.load() + visit2_stats = get_stats(swh_storage) + + assert visit2_actual_load_status["status"] == "eventful", visit2_actual_load_status + expected_snapshot_id2 = hash_to_bytes("eff31762ab65175192d6e1f0570d8b2e4176ff0d") + assert visit2_actual_load_status == { + "status": "eventful", + "snapshot_id": expected_snapshot_id2.hex(), + } + + assert_last_visit_matches( + swh_storage, url, status="full", type="crates", snapshot=expected_snapshot_id2 + ) + expected_snapshot = Snapshot( + id=expected_snapshot_id2, + branches={ + b"releases/0.1.1/micro-timer-0.1.1.crate": SnapshotBranch( + target=hash_to_bytes("a54bb00b38ae049ac4d7548a722bcd891811dbf6"), + target_type=TargetType.RELEASE, + ), + b"releases/0.1.0/micro-timer-0.1.0.crate": SnapshotBranch( + target=hash_to_bytes("bd8d093b4ad56ca7e49aa0f709d945483b831915"), + target_type=TargetType.RELEASE, + ), + b"HEAD": SnapshotBranch( + target=b"releases/0.1.1/micro-timer-0.1.1.crate", + target_type=TargetType.ALIAS, + ), + }, + ) + + assert_last_visit_matches( + swh_storage, url, status="full", type="crates", snapshot=expected_snapshot.id + ) + + check_snapshot(expected_snapshot, swh_storage) + + assert { + "content": 1 + 1, + "directory": 2 + 2, + "origin": 1, + "origin_visit": 1 + 1, + "release": 1 + 1, + "revision": 0, + "skipped_content": 0, + "snapshot": 1 + 1, + } == visit2_stats + + +def test_crates_loader_raw_extrinsic_metadata( + datadir, requests_mock_datadir, swh_storage, expected +): + loader = CratesLoader( + swh_storage, + url=expected[0]["url"], + artifacts=expected[0]["artifacts"], + crates_metadata=expected[0]["crates_metadata"], + ) + actual_load_status = loader.load() + assert actual_load_status["status"] == "eventful" + assert actual_load_status["snapshot_id"] is not None + + expected_release_id = "bb6f9b125867a8b4fa0b2febf890a317744e0140" + + release = swh_storage.release_get([hash_to_bytes(expected_release_id)])[0] + + release_swhid = CoreSWHID( + object_type=OType.RELEASE, object_id=hash_to_bytes(expected_release_id) + ) + directory_swhid = ExtendedSWHID( + object_type=ExtendedObjectType.DIRECTORY, object_id=release.target + ) + metadata_authority = MetadataAuthority( + type=MetadataAuthorityType.FORGE, + url="https://crates.io/", + ) + expected_metadata = [ + RawExtrinsicMetadata( + target=directory_swhid, + authority=metadata_authority, + fetcher=MetadataFetcher( + name="swh.loader.package.crates.loader.CratesLoader", + version=__version__, + ), + discovery_date=loader.visit_date, + format="crates-package-json", + metadata=json.dumps( + [ + { + "version": "0.0.1", + "yanked": False, + "last_update": "2019-04-16T18:48:11.404457+00:00", + } + ] + ).encode(), + origin=expected[0]["url"], + release=release_swhid, + ), + ] + + assert swh_storage.raw_extrinsic_metadata_get( + directory_swhid, + metadata_authority, + ) == PagedResult( + next_page_token=None, + results=expected_metadata, + ) diff --git a/swh/loader/package/crates/tests/test_tasks.py b/swh/loader/package/crates/tests/test_tasks.py index 2c1b459c..8aba6ab3 100644 --- a/swh/loader/package/crates/tests/test_tasks.py +++ b/swh/loader/package/crates/tests/test_tasks.py @@ -24,7 +24,23 @@ def crates_listed_origin(crates_lister): url="some-url/api/v1/crates/some-package", visit_type="crates", extra_loader_arguments={ - "artifacts": [{"version": "0.0.1", "url": "some-package-0.0.1.crate"}], + "artifacts": [ + { + "version": "0.0.1", + "filename": "some-package-0.0.1.crate", + "url": "https://somewhere/some-package-0.0.1.crate", + "checksums": { + "sha256": "5de32cb59a062672560d6f0842c4aa7714727457b9fe2daf8987d995a176a405", # noqa: B950 + }, + }, + ], + "crates_metadata": [ + { + "version": "0.0.1", + "yanked": True, + "last_update": "1970-01-01T00:00:00.000000+00:00", + }, + ], }, ) -- GitLab