From 6a9dfb8bcb614493e561a6a45ea91aac5fa4d32c Mon Sep 17 00:00:00 2001 From: Filipe Brandenburger Date: Thu, 5 Sep 2024 20:19:04 -0400 Subject: [PATCH 01/37] Add support for filename, content-type and headers when uploading files The requests library has support for passing additional information, such as the intended filename on upload, the content-type and additional headers, by passing a tuple with 2, 3 or 4 elements instead of just a file object. See "POST a Multipart-Encoded File" in requests documentation for details: https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file Extend aptly_api files API to also be able to take similar tuples when uploading files to Aptly. One useful use case is to pass a proper package filename, in cases where packages are generated simply as .deb by upstream projects (usually via non native Debian build systems) but should more properly be stored as _:_.deb. Renaming files locally is a possibility, but potentially runs into permission issues. Being able to specify the filename to the API solves this in a more elegant way, without having to modify the local filesystem. The package information can be easily derived using debian.debfile.DebFile() to inspect a package file, in specific gencontrol() returns the fields of the control file which can be used to derive the expected filename. Tested locally by uploading files to aptly using the modified API. Also added a test (even though it mostly relies on mocks.) Confirmed mypy is happy with all the type annotation. --- aptly_api/parts/files.py | 23 ++++++++++++++++------- aptly_api/tests/test_files.py | 8 ++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/aptly_api/parts/files.py b/aptly_api/parts/files.py index c71b9e6..d3f5904 100644 --- a/aptly_api/parts/files.py +++ b/aptly_api/parts/files.py @@ -4,10 +4,16 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import os -from typing import Sequence, List, Tuple, BinaryIO, cast, Optional # noqa: F401 +from typing import Sequence, List, Tuple, TextIO, BinaryIO, cast, Optional, Union, Dict # noqa: F401 from aptly_api.base import BaseAPIClient, AptlyAPIException +_tuplefiletype = Union[ + Tuple[str, Union[TextIO, BinaryIO, str, bytes]], + Tuple[str, Union[TextIO, BinaryIO, str, bytes], str], + Tuple[str, Union[TextIO, BinaryIO, str, bytes], str, Dict[str, str]] +] + class FilesAPISection(BaseAPIClient): def list(self, directory: Optional[str] = None) -> Sequence[str]: @@ -18,13 +24,16 @@ def list(self, directory: Optional[str] = None) -> Sequence[str]: return cast(List[str], resp.json()) - def upload(self, destination: str, *files: str) -> Sequence[str]: - to_upload = [] # type: List[Tuple[str, BinaryIO]] + def upload(self, destination: str, *files: Union[str, _tuplefiletype]) -> Sequence[str]: + to_upload = [] # type: List[Tuple[str, Union[BinaryIO, _tuplefiletype]]] for f in files: - if not os.path.exists(f) or not os.access(f, os.R_OK): + if isinstance(f, tuple): + to_upload.append((f[0], f),) + elif not os.path.exists(f) or not os.access(f, os.R_OK): raise AptlyAPIException("File to upload %s can't be opened or read" % f) - fh = open(f, mode="rb") - to_upload.append((f, fh),) + else: + fh = open(f, mode="rb") + to_upload.append((f, fh),) try: resp = self.do_post("api/files/%s" % destination, @@ -33,7 +42,7 @@ def upload(self, destination: str, *files: str) -> Sequence[str]: raise finally: for fn, to_close in to_upload: - if not to_close.closed: + if not isinstance(to_close, tuple) and not to_close.closed: to_close.close() return cast(List[str], resp.json()) diff --git a/aptly_api/tests/test_files.py b/aptly_api/tests/test_files.py index 76f571b..f814534 100644 --- a/aptly_api/tests/test_files.py +++ b/aptly_api/tests/test_files.py @@ -45,6 +45,14 @@ def test_upload_failed(self, *, rmock: requests_mock.Mocker) -> None: with self.assertRaises(AptlyAPIException): self.fapi.upload("test", os.path.join(os.path.dirname(__file__), "testpkg.deb")) + def test_upload_with_tuples(self, *, rmock: requests_mock.Mocker) -> None: + rmock.post("http://test/api/files/test", text='["test/otherpkg.deb", "test/binpkg.deb"]') + with open(os.path.join(os.path.dirname(__file__), "testpkg.deb"), "rb") as pkgf: + self.assertSequenceEqual( + self.fapi.upload("test", ("otherpkg.deb", pkgf), ("binpkg.deb", b"dpkg-contents")), + ['test/otherpkg.deb', 'test/binpkg.deb'], + ) + def test_delete(self, *, rmock: requests_mock.Mocker) -> None: rmock.delete("http://test/api/files/test", text='{}') From 9bc5183005ddf8a781a11eb3dfc1fee6a896026b Mon Sep 17 00:00:00 2001 From: Xaoc7 Date: Wed, 23 Nov 2022 17:52:24 +0200 Subject: [PATCH 02/37] Add support for working with Aptly Mirrors API --- aptly_api/client.py | 3 + aptly_api/parts/mirrors.py | 177 +++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 aptly_api/parts/mirrors.py diff --git a/aptly_api/client.py b/aptly_api/client.py index a4930e0..7d41ef7 100644 --- a/aptly_api/client.py +++ b/aptly_api/client.py @@ -12,6 +12,7 @@ from aptly_api.parts.repos import ReposAPISection from aptly_api.parts.files import FilesAPISection from aptly_api.parts.snapshots import SnapshotAPISection +from aptly_api.parts.mirrors import MirrorsAPISection class Client: @@ -31,6 +32,8 @@ def __init__(self, aptly_server_url: str, ssl_verify: Union[str, bool, None] = N ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) self.snapshots = SnapshotAPISection(base_url=self.__aptly_server_url, ssl_verify=ssl_verify, ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) + self.mirrors = MirrorsAPISection( + base_url=self.__aptly_server_url, ssl_verify=ssl_verify, ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) @property def aptly_server_url(self) -> str: diff --git a/aptly_api/parts/mirrors.py b/aptly_api/parts/mirrors.py new file mode 100644 index 0000000..30e6a0f --- /dev/null +++ b/aptly_api/parts/mirrors.py @@ -0,0 +1,177 @@ +# -* encoding: utf-8 *- + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +from typing import NamedTuple, Sequence, Dict, Union, cast, Optional, List +from urllib.parse import quote + +from aptly_api.base import BaseAPIClient, AptlyAPIException +from aptly_api.parts.packages import Package, PackageAPISection + + +Mirror = NamedTuple('Mirror', [ + ('uuid', str), + ('name', str), + ('archiveroot', str), + ('distribution', str), + ('components', Sequence[str]), + ('architectures', Sequence[str]), + ('meta', Sequence[Dict[str, str]]), + ('downloaddate', str), + ('filter', str), + ('status', str), + ('worker_pid', int), + ('filter_with_deps', bool), + ('skip_component_check', bool), + ('skip_architecture_check', bool), + ('download_sources', bool), + ('download_udebs', bool), + ('download_installer', bool) +]) + + +class MirrorsAPISection(BaseAPIClient): + @staticmethod + def mirror_from_response(api_response: Dict[str, str]) -> Mirror: + return Mirror( + uuid=cast(str, api_response["UUID"]), + name=cast(str, api_response["Name"]), + archiveroot=cast(str, api_response["ArchiveRoot"]), + distribution=cast(str, api_response["Distribution"]), + components=cast(List[str], api_response["Components"]), + architectures=cast(List[str], api_response["Architectures"]), + meta=cast(List[Dict[str, str]], api_response["Meta"]), + downloaddate=cast(str, api_response["LastDownloadDate"]), + filter=cast(str, api_response["Filter"]), + status=cast(str, api_response["Status"]), + worker_pid=cast(int, api_response["WorkerPID"]), + filter_with_deps=cast(bool, api_response["FilterWithDeps"]), + skip_component_check=cast( + bool, api_response["SkipComponentCheck"]), + skip_architecture_check=cast( + bool, api_response["SkipArchitectureCheck"]), + download_sources=cast(bool, api_response["DownloadSources"]), + download_udebs=cast(bool, api_response["DownloadUdebs"]), + download_installer=cast(bool, api_response["DownloadInstaller"]) + ) + + def list(self) -> Sequence[Mirror]: + resp = self.do_get("api/mirrors") + + mirrors = [] + for mirr in resp.json(): + mirrors.append( + self.mirror_from_response(mirr) + ) + return mirrors + + def update(self, name: str, ignore_signatures: bool = False) -> Sequence[Mirror]: + body = {} + if ignore_signatures: + body["IgnoreSignatures"] = ignore_signatures + resp = self.do_put("api/mirrors/%s" % (quote(name)), json=body) + return resp + + def edit(self, name: str, newname: Optional[str] = None, archiveroot: Optional[str] = None, + filter: Optional[str] = None, architectures: Optional[List[str]] = None, + components: Optional[List[str]] = None, keyrings: Optional[List[str]] = None, + filter_with_deps: bool = False, skip_existing_packages: bool = False, + download_sources: bool = False, download_udebs: bool = False, + skip_component_check: bool = False, ignore_checksums: bool = False, + ignore_signatures: bool = False, force_update: bool = False) -> Mirror: + + body = {} + if newname: + body["Name"] = newname + if archiveroot: + body["ArchiveURL"] = archiveroot + if filter: + body["Filter"] = filter + if architectures: + body["Architectures"] = architectures + if components: + body["Components"] = components + if keyrings: + body["Keyrings"] = keyrings + if filter_with_deps: + body["FilterWithDeps"] = filter_with_deps + if download_sources: + body["DownloadSources"] = download_sources + if download_udebs: + body["DownloadUdebs"] = download_udebs + if skip_component_check: + body["SkipComponentCheck"] = skip_component_check + if ignore_checksums: + body["IgnoreChecksums"] = ignore_checksums + if ignore_signatures: + body["IgnoreSignatures"] = ignore_signatures + if skip_existing_packages: + body["SkipExistingPackages"] = skip_existing_packages + if force_update: + body["ForceUpdate"] = force_update + + resp = self.do_put("api/mirrors/%s" % (quote(name)), json=body) + return resp + + def show(self, name: str) -> Mirror: + resp = self.do_get("api/mirrors/%s" % (quote(name))) + + return self.mirror_from_response(resp.json()) + + def list_packages(self, name: str, query: Optional[str] = None, with_deps: bool = False, + detailed: bool = False) -> Sequence[Package]: + params = {} + if query is not None: + params["q"] = query + if with_deps: + params["withDeps"] = "1" + if detailed: + params["format"] = "details" + + resp = self.do_get("api/mirrors/%s/packages" % + quote(name), params=params) + ret = [] + for rpkg in resp.json(): + ret.append(PackageAPISection.package_from_response(rpkg)) + return ret + + def delete(self, name: str) -> Sequence[Mirror]: + resp = self.do_delete("api/mirrors/%s" % quote(name)) + return resp + + def create(self, name: str, archiveroot: str, distribution: Optional[str] = None, + filter: Optional[str] = None, components: Optional[List[str]] = None, + architectures: Optional[List[str]] = None, keyrings: Optional[List[str]] = None, + download_sources: bool = False, download_udebs: bool = False, + download_installer: bool = False, filter_with_deps: bool = False, + skip_component_check: bool = False, ignore_signatures: bool = False) -> Mirror: + data = { + "Name": name, + "ArchiveURL": archiveroot + } + + if ignore_signatures: + data["IgnoreSignatures"] = ignore_signatures + if keyrings: + data["Keyrings"] = keyrings + if filter: + data["Filter"] = filter + if distribution: + data["Distribution"] = distribution + if components: + data["Components"] = components + if architectures: + data["Architectures"] = architectures + if download_sources: + data["DownloadSources"] = download_sources + if download_udebs: + data["DownloadUdebs"] = download_udebs + if download_installer: + data["DownloadInstaller"] = download_installer + if skip_component_check: + data["SkipComponentCheck"] = skip_component_check + + resp = self.do_post("api/mirrors", json=data) + + return self.mirror_from_response(resp.json()) From b8c2d7fd6d0793e5dfc9c97cd1bc9637244b57cb Mon Sep 17 00:00:00 2001 From: Xaoc7 Date: Wed, 23 Nov 2022 17:53:52 +0200 Subject: [PATCH 03/37] Add support for making snapshots from mirrors --- aptly_api/parts/snapshots.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/aptly_api/parts/snapshots.py b/aptly_api/parts/snapshots.py index 7831d4d..3cf9376 100644 --- a/aptly_api/parts/snapshots.py +++ b/aptly_api/parts/snapshots.py @@ -12,6 +12,7 @@ from aptly_api.base import BaseAPIClient, AptlyAPIException from aptly_api.parts.packages import Package, PackageAPISection +from aptly_api.parts.mirrors import Mirror Snapshot = NamedTuple('Snapshot', [ ('name', str), @@ -49,7 +50,16 @@ def create_from_repo(self, reponame: str, snapshotname: str, description: Option if description is not None: body["Description"] = description - resp = self.do_post("api/repos/%s/snapshots" % quote(reponame), json=body) + resp = self.do_post("api/repos/%s/snapshots" % + quote(reponame), json=body) + return self.snapshot_from_response(resp.json()) + + def create_from_mirror(self, mirrorname: str, snapshotname: str, description: Optional[str] = None) -> Snapshot: + body = { + "Name": snapshotname + } + resp = self.do_post("api/mirrors/%s/snapshots" % + quote(mirrorname), json=body) return self.snapshot_from_response(resp.json()) def create_from_packages(self, snapshotname: str, description: Optional[str] = None, @@ -99,7 +109,8 @@ def list_packages(self, snapshotname: str, query: Optional[str] = None, with_dep if detailed: params["format"] = "details" - resp = self.do_get("api/snapshots/%s/packages" % quote(snapshotname), params=params) + resp = self.do_get("api/snapshots/%s/packages" % + quote(snapshotname), params=params) ret = [] for rpkg in resp.json(): ret.append(PackageAPISection.package_from_response(rpkg)) @@ -115,5 +126,6 @@ def delete(self, snapshotname: str, force: bool = False) -> None: self.do_delete("api/snapshots/%s" % quote(snapshotname), params=params) def diff(self, snapshot1: str, snapshot2: str) -> Sequence[Dict[str, str]]: - resp = self.do_get("api/snapshots/%s/diff/%s" % (quote(snapshot1), quote(snapshot2),)) + resp = self.do_get("api/snapshots/%s/diff/%s" % + (quote(snapshot1), quote(snapshot2),)) return cast(List[Dict[str, str]], resp.json()) From 921c52a9800a6c0b8aa1e536b363b325a62db02c Mon Sep 17 00:00:00 2001 From: Xaoc7 Date: Thu, 24 Nov 2022 14:34:07 +0200 Subject: [PATCH 04/37] Adding unit-tests to cover mirrors API functionality: --- aptly_api/parts/mirrors.py | 44 +++--- aptly_api/tests/__init__.py | 1 + aptly_api/tests/test_mirrors.py | 240 ++++++++++++++++++++++++++++++ aptly_api/tests/test_snapshots.py | 29 +++- 4 files changed, 292 insertions(+), 22 deletions(-) create mode 100644 aptly_api/tests/test_mirrors.py diff --git a/aptly_api/parts/mirrors.py b/aptly_api/parts/mirrors.py index 30e6a0f..5ce1b75 100644 --- a/aptly_api/parts/mirrors.py +++ b/aptly_api/parts/mirrors.py @@ -13,14 +13,14 @@ Mirror = NamedTuple('Mirror', [ ('uuid', str), ('name', str), - ('archiveroot', str), + ('archiveurl', str), ('distribution', str), ('components', Sequence[str]), ('architectures', Sequence[str]), ('meta', Sequence[Dict[str, str]]), ('downloaddate', str), ('filter', str), - ('status', str), + ('status', int), ('worker_pid', int), ('filter_with_deps', bool), ('skip_component_check', bool), @@ -35,17 +35,27 @@ class MirrorsAPISection(BaseAPIClient): @staticmethod def mirror_from_response(api_response: Dict[str, str]) -> Mirror: return Mirror( - uuid=cast(str, api_response["UUID"]), + uuid=cast(str, api_response["UUID"] + ) if "UUID" in api_response else None, name=cast(str, api_response["Name"]), - archiveroot=cast(str, api_response["ArchiveRoot"]), - distribution=cast(str, api_response["Distribution"]), - components=cast(List[str], api_response["Components"]), - architectures=cast(List[str], api_response["Architectures"]), - meta=cast(List[Dict[str, str]], api_response["Meta"]), - downloaddate=cast(str, api_response["LastDownloadDate"]), - filter=cast(str, api_response["Filter"]), - status=cast(str, api_response["Status"]), - worker_pid=cast(int, api_response["WorkerPID"]), + archiveurl=cast( + str, api_response["ArchiveRoot"]), + distribution=cast( + str, api_response["Distribution"]) if "Distribution" in api_response else None, + components=cast(List[str], api_response["Components"] + )if "Components" in api_response else None, + architectures=cast(List[str], api_response["Architectures"] + ) if "Architectures" in api_response else None, + meta=cast(List[Dict[str, str]], api_response["Meta"] + ) if "Meta" in api_response else None, + downloaddate=cast( + str, api_response["LastDownloadDate"]) if "LastDownloadDate" in api_response else None, + filter=cast(str, api_response["Filter"] + ) if "Filter" in api_response else None, + status=cast(int, api_response["Status"] + )if "Status" in api_response else None, + worker_pid=cast( + int, api_response["WorkerPID"])if "WorkerPID" in api_response else None, filter_with_deps=cast(bool, api_response["FilterWithDeps"]), skip_component_check=cast( bool, api_response["SkipComponentCheck"]), @@ -73,7 +83,7 @@ def update(self, name: str, ignore_signatures: bool = False) -> Sequence[Mirror] resp = self.do_put("api/mirrors/%s" % (quote(name)), json=body) return resp - def edit(self, name: str, newname: Optional[str] = None, archiveroot: Optional[str] = None, + def edit(self, name: str, newname: Optional[str] = None, archiveurl: Optional[str] = None, filter: Optional[str] = None, architectures: Optional[List[str]] = None, components: Optional[List[str]] = None, keyrings: Optional[List[str]] = None, filter_with_deps: bool = False, skip_existing_packages: bool = False, @@ -84,8 +94,8 @@ def edit(self, name: str, newname: Optional[str] = None, archiveroot: Optional[s body = {} if newname: body["Name"] = newname - if archiveroot: - body["ArchiveURL"] = archiveroot + if archiveurl: + body["ArchiveURL"] = archiveurl if filter: body["Filter"] = filter if architectures: @@ -140,7 +150,7 @@ def delete(self, name: str) -> Sequence[Mirror]: resp = self.do_delete("api/mirrors/%s" % quote(name)) return resp - def create(self, name: str, archiveroot: str, distribution: Optional[str] = None, + def create(self, name: str, archiveurl: str, distribution: Optional[str] = None, filter: Optional[str] = None, components: Optional[List[str]] = None, architectures: Optional[List[str]] = None, keyrings: Optional[List[str]] = None, download_sources: bool = False, download_udebs: bool = False, @@ -148,7 +158,7 @@ def create(self, name: str, archiveroot: str, distribution: Optional[str] = None skip_component_check: bool = False, ignore_signatures: bool = False) -> Mirror: data = { "Name": name, - "ArchiveURL": archiveroot + "ArchiveURL": archiveurl } if ignore_signatures: diff --git a/aptly_api/tests/__init__.py b/aptly_api/tests/__init__.py index 83c4e19..a66487b 100644 --- a/aptly_api/tests/__init__.py +++ b/aptly_api/tests/__init__.py @@ -12,3 +12,4 @@ from .test_publish import * # noqa from .test_repos import * # noqa from .test_snapshots import * # noqa +from .test_mirrors import * diff --git a/aptly_api/tests/test_mirrors.py b/aptly_api/tests/test_mirrors.py new file mode 100644 index 0000000..d20f5ae --- /dev/null +++ b/aptly_api/tests/test_mirrors.py @@ -0,0 +1,240 @@ +# -* encoding: utf-8 *- + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +from typing import Any +from unittest.case import TestCase + +import requests_mock + +from aptly_api.base import AptlyAPIException +from aptly_api.parts.packages import Package +from aptly_api.parts.mirrors import MirrorsAPISection, Mirror + + +@requests_mock.Mocker(kw='rmock') +class MirrorsAPISectionTests(TestCase): + def __init__(self, *args: Any) -> None: + super().__init__(*args) + self.mapi = MirrorsAPISection("http://test/") + + def test_create(self, *, rmock: requests_mock.Mocker) -> None: + rmock.post("http://test/api/mirrors", + text='{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", "Name": "aptly-mirror", "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", "Distribution": "bionic", "Components": ["main"], "Architectures": ["amd64"], "Meta": {"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", "Components": "main", "Date": "Tue, 06 Apr 2021 21:05:41 UTC","Description": " Apt Repository for the Node.JS 10.x Branch", "Label": "Node Source", "Origin": "Node Source"}, "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "test", "Status": 0, "WorkerPID": 0, "FilterWithDeps": true, "SkipComponentCheck": true, "SkipArchitectureCheck": true, "DownloadSources": true, "DownloadUdebs": true, "DownloadInstaller": true}' + ) + self.assertEqual( + self.mapi.create(name="aptly-mirror", archiveurl='https://deb.nodesource.com/node_10.x/', + distribution='bionic', components=["main"], + architectures=["amd64"], + filter="test", download_udebs=True, + download_sources=True, download_installer=True, + skip_component_check=True, filter_with_deps=True, + keyrings="/path/to/keyring", ignore_signatures=True), + Mirror( + uuid='2cb5985a-a23f-4a1f-8eb6-d5409193b4eb', + name="aptly-mirror", + archiveurl="https://deb.nodesource.com/node_10.x/", + distribution='bionic', + components=["main"], + architectures=["amd64"], + downloaddate='0001-01-01T00:00:00Z', + meta={"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", + "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}, + filter="test", + status=0, + worker_pid=0, + filter_with_deps=True, + skip_component_check=True, + skip_architecture_check=True, + download_sources=True, + download_udebs=True, + download_installer=True + + ) + ) + + def test_list(self, *, rmock: requests_mock.Mocker) -> None: + rmock.get("http://test/api/mirrors", + text='[{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", "Name": "aptly-mirror", "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", "Distribution": "bionic", "Components": ["main"], "Architectures": ["amd64"], "Meta": {"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", "Components": "main", "Date": "Tue, 06 Apr 2021 21:05:41 UTC","Description": " Apt Repository for the Node.JS 10.x Branch", "Label": "Node Source", "Origin": "Node Source"}, "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, "SkipComponentCheck": false, "SkipArchitectureCheck": false, "DownloadSources": false, "DownloadUdebs": false, "DownloadInstaller": false}]' + ) + self.assertSequenceEqual( + self.mapi.list(), + [ + Mirror( + uuid='2cb5985a-a23f-4a1f-8eb6-d5409193b4eb', + name="aptly-mirror", + archiveurl="https://deb.nodesource.com/node_10.x/", + distribution='bionic', + components=["main"], + architectures=["amd64"], + downloaddate='0001-01-01T00:00:00Z', + meta={"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", + "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}, + filter="", + status=0, + worker_pid=0, + filter_with_deps=False, + skip_component_check=False, + skip_architecture_check=False, + download_sources=False, + download_udebs=False, + download_installer=False + + ) + ] + ) + + def test_show(self, *, rmock: requests_mock.Mocker) -> None: + rmock.get("http://test/api/mirrors/aptly-mirror", + text='{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", "Name": "aptly-mirror", "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", "Distribution": "bionic", "Components": ["main"], "Architectures": ["amd64"], "Meta": {"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", "Components": "main", "Date": "Tue, 06 Apr 2021 21:05:41 UTC","Description": " Apt Repository for the Node.JS 10.x Branch", "Label": "Node Source", "Origin": "Node Source"}, "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, "SkipComponentCheck": false, "SkipArchitectureCheck": false, "DownloadSources": false, "DownloadUdebs": false, "DownloadInstaller": false}' + ) + self.assertEqual( + self.mapi.show(name="aptly-mirror"), + Mirror( + uuid='2cb5985a-a23f-4a1f-8eb6-d5409193b4eb', + name="aptly-mirror", + archiveurl="https://deb.nodesource.com/node_10.x/", + distribution='bionic', + components=["main"], + architectures=["amd64"], + downloaddate='0001-01-01T00:00:00Z', + meta={"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", + "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}, + filter="", + status=0, + worker_pid=0, + filter_with_deps=False, + skip_component_check=False, + skip_architecture_check=False, + download_sources=False, + download_udebs=False, + download_installer=False + + ) + ) + + def test_list_packages(self, *, rmock: requests_mock.Mocker) -> None: + rmock.get("http://test/api/mirrors/aptly-mirror/packages", + text='["Pamd64 nodejs 10.24.1-1nodesource1 1f74a6abf6acc572"]') + self.assertSequenceEqual( + self.mapi.list_packages( + name="aptly-mirror", query=("nodejs"), with_deps=True), + [ + Package( + key="Pamd64 nodejs 10.24.1-1nodesource1 1f74a6abf6acc572", + short_key=None, + files_hash=None, + fields=None, + ) + ], + ) + + def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: + rmock.get( + "http://test/api/mirrors/aptly-mirror/packages?format=details", + text="""[{ + "Architecture":"amd64", + "Conflicts": "nodejs-dev, nodejs-legacy, npm", + "Depends":"1libc6 (>= 2.9), libgcc1 (>= 1:3.4), libstdc++6 (>= 4.4.0), python-minimal, ca-certificates", + "Description":"Node.js event-based server-side javascript engine\\n", + "Filename":"nodejs_10.24.1-1nodesource1_amd64.deb", + "FilesHash":"1f74a6abf6acc572", + "Homepage":"https://nodejs.org", + "Installed-Size":"78630", + "Key":"Pamd64 nodejs 10.24.1-1nodesource1 1f74a6abf6acc572", + "License":"unknown", + "MD5sum":"6d9f0e30396cb6c20945ff6de2f9f322", + "Maintainer":"Ivan Iguaran ", + "Package":"nodejs", + "Priority":"optional", + "Provides":"nodejs-dev, nodejs-legacy, npm", + "SHA1":"a3bc5a29614eab366bb3644abb1e602b5c8953d5", + "SHA256":"4b374d16b536cf1a3963ddc4575ed2b68b28b0b5ea6eefe93c942dfc0ed35177", + "SHA512":"bf203bb319de0c5f7ed3b6ba69de39b1ea8b5086b872561379bd462dd93f07969ca64fa01ade01ff08fa13a4e5e28625b59292ba44bc01ba876ec95875630460", + "Section":"web", + "ShortKey":"Pamd64 nodejs 10.24.1-1nodesource1", + "Size":"15949164", + "Version":"10.24.1-1nodesource1" + }]""" + ) + self.assertEqual( + self.mapi.list_packages( + "aptly-mirror", detailed=True, with_deps=True, query="nodejs"), + [ + Package( + key='Pamd64 nodejs 10.24.1-1nodesource1 1f74a6abf6acc572', + short_key='Pamd64 nodejs 10.24.1-1nodesource1', + files_hash='1f74a6abf6acc572', + fields={ + 'Architecture': 'amd64', + 'Conflicts': 'nodejs-dev, nodejs-legacy, npm', + 'Depends': '1libc6 (>= 2.9), libgcc1 (>= 1:3.4), libstdc++6 (>= 4.4.0), python-minimal, ca-certificates', + 'Description': 'Node.js event-based server-side javascript engine\n', + 'Filename': 'nodejs_10.24.1-1nodesource1_amd64.deb', + 'FilesHash': '1f74a6abf6acc572', + 'Homepage': 'https://nodejs.org', + 'Installed-Size': '78630', + 'Key': 'Pamd64 nodejs 10.24.1-1nodesource1 1f74a6abf6acc572', + 'License': 'unknown', + 'MD5sum': '6d9f0e30396cb6c20945ff6de2f9f322', + 'Maintainer': 'Ivan Iguaran ', + 'Package': 'nodejs', + 'Priority': 'optional', + 'Provides': 'nodejs-dev, nodejs-legacy, npm', + 'SHA1': 'a3bc5a29614eab366bb3644abb1e602b5c8953d5', + 'SHA256': '4b374d16b536cf1a3963ddc4575ed2b68b28b0b5ea6eefe93c942dfc0ed35177', + 'SHA512': 'bf203bb319de0c5f7ed3b6ba69de39b1ea8b5086b872561379bd462dd93f0796' + '9ca64fa01ade01ff08fa13a4e5e28625b59292ba44bc01ba876ec95875630460', + 'Section': 'web', + 'ShortKey': 'Pamd64 nodejs 10.24.1-1nodesource1', + 'Size': '15949164', + 'Version': '10.24.1-1nodesource1' + } + ) + ] + ) + + def test_delete(self, *, rmock: requests_mock.Mocker) -> None: + with self.assertRaises(requests_mock.NoMockAddress): + self.mapi.delete(name="aptly-mirror") + + def test_update(self, *, rmock: requests_mock.Mocker) -> None: + with self.assertRaises(requests_mock.NoMockAddress): + self.mapi.update(name="aptly-mirror", ignore_signatures=True) + + def test_edit(self, *, rmock: requests_mock.Mocker) -> None: + with self.assertRaises(requests_mock.NoMockAddress): + self.mapi.edit(name="aptly-mirror", newname="aptly-mirror-renamed", + archiveurl='https://deb.nodesource.com/node_10.x/', + architectures=["i386", "amd64"], filter="test", + components=["main"], keyrings="/path/to/keyring", + skip_existing_packages=True, ignore_checksums=True, + download_udebs=True, download_sources=True, + skip_component_check=True, filter_with_deps=True, + ignore_signatures=True, force_update=True), + + def test_delete_validation(self, *, rmock: requests_mock.Mocker) -> None: + rmock.delete("http://test/api/mirrors/aptly-mirror") + self.mapi.delete(name="aptly-mirror") + + def test_update_validation(self, *, rmock: requests_mock.Mocker) -> None: + rmock.put("http://test/api/mirrors/aptly-mirror") + self.mapi.update(name="aptly-mirror") + + def test_edit_validation(self, *, rmock: requests_mock.Mocker) -> None: + rmock.put("http://test/api/mirrors/aptly-mirror", + text='{"Name":"aptly-mirror-bla", "IgnoreSignatures": true}') + self.mapi.edit(name="aptly-mirror", newname="aptly-mirror-renamed") diff --git a/aptly_api/tests/test_snapshots.py b/aptly_api/tests/test_snapshots.py index 4ef06d4..507ad20 100644 --- a/aptly_api/tests/test_snapshots.py +++ b/aptly_api/tests/test_snapshots.py @@ -34,13 +34,15 @@ def test_list(self, *, rmock: requests_mock.Mocker) -> None: name='stretch-security-1', description='Snapshot from mirror [stretch-security]: http://security.debian.org/debian-security/ ' 'stretch/updates', - created_at=iso8601.parse_date('2017-06-03T21:36:22.2692213Z') + created_at=iso8601.parse_date( + '2017-06-03T21:36:22.2692213Z') ), Snapshot( name='stretch-updates-1', description='Snapshot from mirror [stretch-updates]: http://ftp-stud.hs-esslingen.de/debian/ ' 'stretch-updates', - created_at=iso8601.parse_date('2017-06-03T21:36:22.431767659Z') + created_at=iso8601.parse_date( + '2017-06-03T21:36:22.431767659Z') ) ] ) @@ -136,8 +138,10 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: self.assertIsNotNone(expected.fields) self.assertDictEqual( - parsed.fields if parsed.fields else {}, # make sure that mypy doesn't error on this being potentially None - expected.fields if expected.fields else {}, # this can't happen unless Package.__init__ is fubared + # make sure that mypy doesn't error on this being potentially None + parsed.fields if parsed.fields else {}, + # this can't happen unless Package.__init__ is fubared + expected.fields if expected.fields else {}, ) def test_show(self, *, rmock: requests_mock.Mocker) -> None: @@ -159,7 +163,8 @@ def test_update(self, *, rmock: requests_mock.Mocker) -> None: text='{"Name":"aptly-repo-2","CreatedAt":"2017-06-03T23:43:40.275605639Z",' '"Description":"test"}') self.assertEqual( - self.sapi.update("aptly-repo-1", newname="aptly-repo-2", newdescription="test"), + self.sapi.update( + "aptly-repo-1", newname="aptly-repo-2", newdescription="test"), Snapshot( name='aptly-repo-2', description='test', @@ -200,3 +205,17 @@ def test_create_from_packages(self, *, rmock: requests_mock.Mocker) -> None: created_at=iso8601.parse_date('2017-06-07T14:19:07.706408213Z') ) ) + + def test_create_from_mirror(self, *, rmock: requests_mock.Mocker) -> None: + rmock.post("http://test/api/mirrors/aptly-mirror/snapshots", + text='{"Name":"aptly-mirror-snap","CreatedAt":"2022-11-29T21:43:45.275605639Z",' + '"Description":"Snapshot from local mirror [aptly-mirror]"}') + self.assertEqual( + self.sapi.create_from_mirror(mirrorname="aptly-mirror", snapshotname="aptly-mirror-snap", + description='Snapshot from local repo [aptly-repo]'), + Snapshot( + name='aptly-mirror-snap', + description='Snapshot from local mirror [aptly-mirror]', + created_at=iso8601.parse_date('2022-11-29T21:43:45.275605639Z') + ) + ) From 5e2e2b0254162d1881afe322c3c448639e73fd43 Mon Sep 17 00:00:00 2001 From: Xaoc7 Date: Fri, 2 Dec 2022 18:31:26 +0200 Subject: [PATCH 05/37] Fixed code style according flake8: * ensure that there are no lines longer than 120 characters * fix imported but not used Union * fix imported but not used AptlyAPIException * fix imported but not used Mirror * removed trailing whitespaces * removed black lines at the end of files --- aptly_api/client.py | 3 +- aptly_api/parts/mirrors.py | 13 ++++--- aptly_api/parts/snapshots.py | 1 - aptly_api/tests/__init__.py | 2 +- aptly_api/tests/test_mirrors.py | 66 +++++++++++++++++++++++++++------ 5 files changed, 66 insertions(+), 19 deletions(-) diff --git a/aptly_api/client.py b/aptly_api/client.py index 7d41ef7..bfce19b 100644 --- a/aptly_api/client.py +++ b/aptly_api/client.py @@ -33,7 +33,8 @@ def __init__(self, aptly_server_url: str, ssl_verify: Union[str, bool, None] = N self.snapshots = SnapshotAPISection(base_url=self.__aptly_server_url, ssl_verify=ssl_verify, ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) self.mirrors = MirrorsAPISection( - base_url=self.__aptly_server_url, ssl_verify=ssl_verify, ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) + base_url=self.__aptly_server_url, ssl_verify=ssl_verify, + ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) @property def aptly_server_url(self) -> str: diff --git a/aptly_api/parts/mirrors.py b/aptly_api/parts/mirrors.py index 5ce1b75..3bedc35 100644 --- a/aptly_api/parts/mirrors.py +++ b/aptly_api/parts/mirrors.py @@ -3,10 +3,10 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -from typing import NamedTuple, Sequence, Dict, Union, cast, Optional, List +from typing import NamedTuple, Sequence, Dict, cast, Optional, List from urllib.parse import quote -from aptly_api.base import BaseAPIClient, AptlyAPIException +from aptly_api.base import BaseAPIClient from aptly_api.parts.packages import Package, PackageAPISection @@ -41,7 +41,8 @@ def mirror_from_response(api_response: Dict[str, str]) -> Mirror: archiveurl=cast( str, api_response["ArchiveRoot"]), distribution=cast( - str, api_response["Distribution"]) if "Distribution" in api_response else None, + str, api_response["Distribution"]) + if "Distribution" in api_response else None, components=cast(List[str], api_response["Components"] )if "Components" in api_response else None, architectures=cast(List[str], api_response["Architectures"] @@ -49,13 +50,15 @@ def mirror_from_response(api_response: Dict[str, str]) -> Mirror: meta=cast(List[Dict[str, str]], api_response["Meta"] ) if "Meta" in api_response else None, downloaddate=cast( - str, api_response["LastDownloadDate"]) if "LastDownloadDate" in api_response else None, + str, api_response["LastDownloadDate"]) + if "LastDownloadDate" in api_response else None, filter=cast(str, api_response["Filter"] ) if "Filter" in api_response else None, status=cast(int, api_response["Status"] )if "Status" in api_response else None, worker_pid=cast( - int, api_response["WorkerPID"])if "WorkerPID" in api_response else None, + int, api_response["WorkerPID"]) + if "WorkerPID" in api_response else None, filter_with_deps=cast(bool, api_response["FilterWithDeps"]), skip_component_check=cast( bool, api_response["SkipComponentCheck"]), diff --git a/aptly_api/parts/snapshots.py b/aptly_api/parts/snapshots.py index 3cf9376..0bff9a7 100644 --- a/aptly_api/parts/snapshots.py +++ b/aptly_api/parts/snapshots.py @@ -12,7 +12,6 @@ from aptly_api.base import BaseAPIClient, AptlyAPIException from aptly_api.parts.packages import Package, PackageAPISection -from aptly_api.parts.mirrors import Mirror Snapshot = NamedTuple('Snapshot', [ ('name', str), diff --git a/aptly_api/tests/__init__.py b/aptly_api/tests/__init__.py index a66487b..e551fe6 100644 --- a/aptly_api/tests/__init__.py +++ b/aptly_api/tests/__init__.py @@ -12,4 +12,4 @@ from .test_publish import * # noqa from .test_repos import * # noqa from .test_snapshots import * # noqa -from .test_mirrors import * +from .test_mirrors import * # noqa diff --git a/aptly_api/tests/test_mirrors.py b/aptly_api/tests/test_mirrors.py index d20f5ae..240bcfe 100644 --- a/aptly_api/tests/test_mirrors.py +++ b/aptly_api/tests/test_mirrors.py @@ -8,7 +8,6 @@ import requests_mock -from aptly_api.base import AptlyAPIException from aptly_api.parts.packages import Package from aptly_api.parts.mirrors import MirrorsAPISection, Mirror @@ -21,7 +20,21 @@ def __init__(self, *args: Any) -> None: def test_create(self, *, rmock: requests_mock.Mocker) -> None: rmock.post("http://test/api/mirrors", - text='{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", "Name": "aptly-mirror", "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", "Distribution": "bionic", "Components": ["main"], "Architectures": ["amd64"], "Meta": {"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", "Components": "main", "Date": "Tue, 06 Apr 2021 21:05:41 UTC","Description": " Apt Repository for the Node.JS 10.x Branch", "Label": "Node Source", "Origin": "Node Source"}, "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "test", "Status": 0, "WorkerPID": 0, "FilterWithDeps": true, "SkipComponentCheck": true, "SkipArchitectureCheck": true, "DownloadSources": true, "DownloadUdebs": true, "DownloadInstaller": true}' + text="""{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", + "Name": "aptly-mirror", + "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", + "Distribution": "bionic", "Components": ["main"], + "Architectures": ["amd64"], + "Meta": {"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}, + "LastDownloadDate": "0001-01-01T00:00:00Z", + "Filter": "test", "Status": 0, "WorkerPID": 0, + "FilterWithDeps": true, "SkipComponentCheck": true, + "SkipArchitectureCheck": true, "DownloadSources": true, + "DownloadUdebs": true, "DownloadInstaller": true}""" ) self.assertEqual( self.mapi.create(name="aptly-mirror", archiveurl='https://deb.nodesource.com/node_10.x/', @@ -60,7 +73,21 @@ def test_create(self, *, rmock: requests_mock.Mocker) -> None: def test_list(self, *, rmock: requests_mock.Mocker) -> None: rmock.get("http://test/api/mirrors", - text='[{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", "Name": "aptly-mirror", "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", "Distribution": "bionic", "Components": ["main"], "Architectures": ["amd64"], "Meta": {"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", "Components": "main", "Date": "Tue, 06 Apr 2021 21:05:41 UTC","Description": " Apt Repository for the Node.JS 10.x Branch", "Label": "Node Source", "Origin": "Node Source"}, "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, "SkipComponentCheck": false, "SkipArchitectureCheck": false, "DownloadSources": false, "DownloadUdebs": false, "DownloadInstaller": false}]' + text="""[{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", + "Name": "aptly-mirror", + "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", + "Distribution": "bionic", "Components": ["main"], + "Architectures": ["amd64"], + "Meta": {"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}, + "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", + "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, + "SkipComponentCheck": false, "SkipArchitectureCheck": false, + "DownloadSources": false, "DownloadUdebs": false, + "DownloadInstaller": false}]""" ) self.assertSequenceEqual( self.mapi.list(), @@ -95,7 +122,21 @@ def test_list(self, *, rmock: requests_mock.Mocker) -> None: def test_show(self, *, rmock: requests_mock.Mocker) -> None: rmock.get("http://test/api/mirrors/aptly-mirror", - text='{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", "Name": "aptly-mirror", "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", "Distribution": "bionic", "Components": ["main"], "Architectures": ["amd64"], "Meta": {"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", "Components": "main", "Date": "Tue, 06 Apr 2021 21:05:41 UTC","Description": " Apt Repository for the Node.JS 10.x Branch", "Label": "Node Source", "Origin": "Node Source"}, "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, "SkipComponentCheck": false, "SkipArchitectureCheck": false, "DownloadSources": false, "DownloadUdebs": false, "DownloadInstaller": false}' + text="""{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", + "Name": "aptly-mirror", + "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", + "Distribution": "bionic", "Components": ["main"], + "Architectures": ["amd64"], + "Meta": {"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}, + "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", + "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, + "SkipComponentCheck": false, "SkipArchitectureCheck": false, + "DownloadSources": false, "DownloadUdebs": false, + "DownloadInstaller": false}""" ) self.assertEqual( self.mapi.show(name="aptly-mirror"), @@ -148,8 +189,9 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: text="""[{ "Architecture":"amd64", "Conflicts": "nodejs-dev, nodejs-legacy, npm", - "Depends":"1libc6 (>= 2.9), libgcc1 (>= 1:3.4), libstdc++6 (>= 4.4.0), python-minimal, ca-certificates", - "Description":"Node.js event-based server-side javascript engine\\n", + "Depends":"1libc6 (>= 2.9), libgcc1 (>= 1:3.4),""" + """ libstdc++6 (>= 4.4.0), python-minimal, ca-certificates", + "Description":" Node.js event-based server-side javascript engine\\n", "Filename":"nodejs_10.24.1-1nodesource1_amd64.deb", "FilesHash":"1f74a6abf6acc572", "Homepage":"https://nodejs.org", @@ -163,7 +205,8 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: "Provides":"nodejs-dev, nodejs-legacy, npm", "SHA1":"a3bc5a29614eab366bb3644abb1e602b5c8953d5", "SHA256":"4b374d16b536cf1a3963ddc4575ed2b68b28b0b5ea6eefe93c942dfc0ed35177", - "SHA512":"bf203bb319de0c5f7ed3b6ba69de39b1ea8b5086b872561379bd462dd93f07969ca64fa01ade01ff08fa13a4e5e28625b59292ba44bc01ba876ec95875630460", + "SHA512":"bf203bb319de0c5f7ed3b6ba69de39b1ea8b5086b872561379bd462dd93f0796""" + """9ca64fa01ade01ff08fa13a4e5e28625b59292ba44bc01ba876ec95875630460", "Section":"web", "ShortKey":"Pamd64 nodejs 10.24.1-1nodesource1", "Size":"15949164", @@ -179,10 +222,11 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: short_key='Pamd64 nodejs 10.24.1-1nodesource1', files_hash='1f74a6abf6acc572', fields={ - 'Architecture': 'amd64', + "Architecture": "amd64", 'Conflicts': 'nodejs-dev, nodejs-legacy, npm', - 'Depends': '1libc6 (>= 2.9), libgcc1 (>= 1:3.4), libstdc++6 (>= 4.4.0), python-minimal, ca-certificates', - 'Description': 'Node.js event-based server-side javascript engine\n', + 'Depends': '1libc6 (>= 2.9), libgcc1 (>= 1:3.4), ' + 'libstdc++6 (>= 4.4.0), python-minimal, ca-certificates', + 'Description': ' Node.js event-based server-side javascript engine\n', 'Filename': 'nodejs_10.24.1-1nodesource1_amd64.deb', 'FilesHash': '1f74a6abf6acc572', 'Homepage': 'https://nodejs.org', @@ -197,7 +241,7 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: 'SHA1': 'a3bc5a29614eab366bb3644abb1e602b5c8953d5', 'SHA256': '4b374d16b536cf1a3963ddc4575ed2b68b28b0b5ea6eefe93c942dfc0ed35177', 'SHA512': 'bf203bb319de0c5f7ed3b6ba69de39b1ea8b5086b872561379bd462dd93f0796' - '9ca64fa01ade01ff08fa13a4e5e28625b59292ba44bc01ba876ec95875630460', + '9ca64fa01ade01ff08fa13a4e5e28625b59292ba44bc01ba876ec95875630460', 'Section': 'web', 'ShortKey': 'Pamd64 nodejs 10.24.1-1nodesource1', 'Size': '15949164', From 26898ddf6fda60c8266ba25d0ae75713ba5ee404 Mon Sep 17 00:00:00 2001 From: Xaoc7 Date: Tue, 6 Dec 2022 08:11:47 +0200 Subject: [PATCH 06/37] Apply changes to mitigate mypy reported errors: * Incompatible type assignments * Incompatible types for arguments * Incompatible return values --- aptly_api/parts/mirrors.py | 42 +++++++------- aptly_api/tests/test_mirrors.py | 98 ++++++++++++++++----------------- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/aptly_api/parts/mirrors.py b/aptly_api/parts/mirrors.py index 3bedc35..d833bbb 100644 --- a/aptly_api/parts/mirrors.py +++ b/aptly_api/parts/mirrors.py @@ -3,7 +3,7 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -from typing import NamedTuple, Sequence, Dict, cast, Optional, List +from typing import NamedTuple, Sequence, Dict, cast, Optional, List, Union from urllib.parse import quote from aptly_api.base import BaseAPIClient @@ -11,17 +11,17 @@ Mirror = NamedTuple('Mirror', [ - ('uuid', str), + ('uuid', Optional[str]), ('name', str), ('archiveurl', str), - ('distribution', str), - ('components', Sequence[str]), - ('architectures', Sequence[str]), - ('meta', Sequence[Dict[str, str]]), - ('downloaddate', str), - ('filter', str), - ('status', int), - ('worker_pid', int), + ('distribution', Optional[str]), + ('components', Optional[Sequence[str]]), + ('architectures', Optional[Sequence[str]]), + ('meta', Optional[Sequence[Dict[str, str]]]), + ('downloaddate', Optional[str]), + ('filter', Optional[str]), + ('status', Optional[int]), + ('worker_pid', Optional[int]), ('filter_with_deps', bool), ('skip_component_check', bool), ('skip_architecture_check', bool), @@ -30,6 +30,9 @@ ('download_installer', bool) ]) +T_BodyDict = Dict[str, Union[str, bool, Sequence[Dict[str, str]], + Sequence[str], Dict[str, Union[bool, str]]]] + class MirrorsAPISection(BaseAPIClient): @staticmethod @@ -79,12 +82,11 @@ def list(self) -> Sequence[Mirror]: ) return mirrors - def update(self, name: str, ignore_signatures: bool = False) -> Sequence[Mirror]: + def update(self, name: str, ignore_signatures: bool = False) -> None: body = {} if ignore_signatures: body["IgnoreSignatures"] = ignore_signatures - resp = self.do_put("api/mirrors/%s" % (quote(name)), json=body) - return resp + self.do_put("api/mirrors/%s" % (quote(name)), json=body) def edit(self, name: str, newname: Optional[str] = None, archiveurl: Optional[str] = None, filter: Optional[str] = None, architectures: Optional[List[str]] = None, @@ -92,9 +94,9 @@ def edit(self, name: str, newname: Optional[str] = None, archiveurl: Optional[st filter_with_deps: bool = False, skip_existing_packages: bool = False, download_sources: bool = False, download_udebs: bool = False, skip_component_check: bool = False, ignore_checksums: bool = False, - ignore_signatures: bool = False, force_update: bool = False) -> Mirror: + ignore_signatures: bool = False, force_update: bool = False) -> None: - body = {} + body = {} # type: T_BodyDict if newname: body["Name"] = newname if archiveurl: @@ -124,8 +126,7 @@ def edit(self, name: str, newname: Optional[str] = None, archiveurl: Optional[st if force_update: body["ForceUpdate"] = force_update - resp = self.do_put("api/mirrors/%s" % (quote(name)), json=body) - return resp + self.do_put("api/mirrors/%s" % (quote(name)), json=body) def show(self, name: str) -> Mirror: resp = self.do_get("api/mirrors/%s" % (quote(name))) @@ -149,9 +150,8 @@ def list_packages(self, name: str, query: Optional[str] = None, with_deps: bool ret.append(PackageAPISection.package_from_response(rpkg)) return ret - def delete(self, name: str) -> Sequence[Mirror]: - resp = self.do_delete("api/mirrors/%s" % quote(name)) - return resp + def delete(self, name: str) -> None: + self.do_delete("api/mirrors/%s" % quote(name)) def create(self, name: str, archiveurl: str, distribution: Optional[str] = None, filter: Optional[str] = None, components: Optional[List[str]] = None, @@ -162,7 +162,7 @@ def create(self, name: str, archiveurl: str, distribution: Optional[str] = None, data = { "Name": name, "ArchiveURL": archiveurl - } + } # type: T_BodyDict if ignore_signatures: data["IgnoreSignatures"] = ignore_signatures diff --git a/aptly_api/tests/test_mirrors.py b/aptly_api/tests/test_mirrors.py index 240bcfe..07c1103 100644 --- a/aptly_api/tests/test_mirrors.py +++ b/aptly_api/tests/test_mirrors.py @@ -16,7 +16,7 @@ class MirrorsAPISectionTests(TestCase): def __init__(self, *args: Any) -> None: super().__init__(*args) - self.mapi = MirrorsAPISection("http://test/") + self.miapi = MirrorsAPISection("http://test/") def test_create(self, *, rmock: requests_mock.Mocker) -> None: rmock.post("http://test/api/mirrors", @@ -25,25 +25,25 @@ def test_create(self, *, rmock: requests_mock.Mocker) -> None: "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", "Distribution": "bionic", "Components": ["main"], "Architectures": ["amd64"], - "Meta": {"Architectures": "i386 amd64 armhf arm64", + "Meta": [{"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", "Components": "main", "Date": "Tue, 06 Apr 2021 21:05:41 UTC", "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}, + "Label": "Node Source", "Origin": "Node Source"}], "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "test", "Status": 0, "WorkerPID": 0, "FilterWithDeps": true, "SkipComponentCheck": true, "SkipArchitectureCheck": true, "DownloadSources": true, "DownloadUdebs": true, "DownloadInstaller": true}""" ) - self.assertEqual( - self.mapi.create(name="aptly-mirror", archiveurl='https://deb.nodesource.com/node_10.x/', - distribution='bionic', components=["main"], - architectures=["amd64"], - filter="test", download_udebs=True, - download_sources=True, download_installer=True, - skip_component_check=True, filter_with_deps=True, - keyrings="/path/to/keyring", ignore_signatures=True), + self.assertSequenceEqual( + self.miapi.create(name="aptly-mirror", archiveurl='https://deb.nodesource.com/node_10.x/', + distribution='bionic', components=["main"], + architectures=["amd64"], + filter="test", download_udebs=True, + download_sources=True, download_installer=True, + skip_component_check=True, filter_with_deps=True, + keyrings=["/path/to/keyring"], ignore_signatures=True), Mirror( uuid='2cb5985a-a23f-4a1f-8eb6-d5409193b4eb', name="aptly-mirror", @@ -52,12 +52,12 @@ def test_create(self, *, rmock: requests_mock.Mocker) -> None: components=["main"], architectures=["amd64"], downloaddate='0001-01-01T00:00:00Z', - meta={"Architectures": "i386 amd64 armhf arm64", + meta=[{"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", - "Components": "main", - "Date": "Tue, 06 Apr 2021 21:05:41 UTC", - "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}, + "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}], filter="test", status=0, worker_pid=0, @@ -78,11 +78,11 @@ def test_list(self, *, rmock: requests_mock.Mocker) -> None: "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", "Distribution": "bionic", "Components": ["main"], "Architectures": ["amd64"], - "Meta": {"Architectures": "i386 amd64 armhf arm64", + "Meta": [{"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", "Components": "main", "Date": "Tue, 06 Apr 2021 21:05:41 UTC", "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}, + "Label": "Node Source", "Origin": "Node Source"}], "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, "SkipComponentCheck": false, "SkipArchitectureCheck": false, @@ -90,7 +90,7 @@ def test_list(self, *, rmock: requests_mock.Mocker) -> None: "DownloadInstaller": false}]""" ) self.assertSequenceEqual( - self.mapi.list(), + self.miapi.list(), [ Mirror( uuid='2cb5985a-a23f-4a1f-8eb6-d5409193b4eb', @@ -100,12 +100,12 @@ def test_list(self, *, rmock: requests_mock.Mocker) -> None: components=["main"], architectures=["amd64"], downloaddate='0001-01-01T00:00:00Z', - meta={"Architectures": "i386 amd64 armhf arm64", + meta=[{"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", - "Components": "main", - "Date": "Tue, 06 Apr 2021 21:05:41 UTC", - "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}, + "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}], filter="", status=0, worker_pid=0, @@ -127,19 +127,19 @@ def test_show(self, *, rmock: requests_mock.Mocker) -> None: "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", "Distribution": "bionic", "Components": ["main"], "Architectures": ["amd64"], - "Meta": {"Architectures": "i386 amd64 armhf arm64", + "Meta": [{"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", "Components": "main", "Date": "Tue, 06 Apr 2021 21:05:41 UTC", "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}, + "Label": "Node Source", "Origin": "Node Source"}], "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, "SkipComponentCheck": false, "SkipArchitectureCheck": false, "DownloadSources": false, "DownloadUdebs": false, "DownloadInstaller": false}""" ) - self.assertEqual( - self.mapi.show(name="aptly-mirror"), + self.assertSequenceEqual( + self.miapi.show(name="aptly-mirror"), Mirror( uuid='2cb5985a-a23f-4a1f-8eb6-d5409193b4eb', name="aptly-mirror", @@ -148,12 +148,12 @@ def test_show(self, *, rmock: requests_mock.Mocker) -> None: components=["main"], architectures=["amd64"], downloaddate='0001-01-01T00:00:00Z', - meta={"Architectures": "i386 amd64 armhf arm64", + meta=[{"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", - "Components": "main", - "Date": "Tue, 06 Apr 2021 21:05:41 UTC", - "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}, + "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}], filter="", status=0, worker_pid=0, @@ -171,7 +171,7 @@ def test_list_packages(self, *, rmock: requests_mock.Mocker) -> None: rmock.get("http://test/api/mirrors/aptly-mirror/packages", text='["Pamd64 nodejs 10.24.1-1nodesource1 1f74a6abf6acc572"]') self.assertSequenceEqual( - self.mapi.list_packages( + self.miapi.list_packages( name="aptly-mirror", query=("nodejs"), with_deps=True), [ Package( @@ -213,8 +213,8 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: "Version":"10.24.1-1nodesource1" }]""" ) - self.assertEqual( - self.mapi.list_packages( + self.assertSequenceEqual( + self.miapi.list_packages( "aptly-mirror", detailed=True, with_deps=True, query="nodejs"), [ Package( @@ -253,32 +253,32 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: def test_delete(self, *, rmock: requests_mock.Mocker) -> None: with self.assertRaises(requests_mock.NoMockAddress): - self.mapi.delete(name="aptly-mirror") + self.miapi.delete(name="aptly-mirror") def test_update(self, *, rmock: requests_mock.Mocker) -> None: with self.assertRaises(requests_mock.NoMockAddress): - self.mapi.update(name="aptly-mirror", ignore_signatures=True) + self.miapi.update(name="aptly-mirror", ignore_signatures=True) def test_edit(self, *, rmock: requests_mock.Mocker) -> None: with self.assertRaises(requests_mock.NoMockAddress): - self.mapi.edit(name="aptly-mirror", newname="aptly-mirror-renamed", - archiveurl='https://deb.nodesource.com/node_10.x/', - architectures=["i386", "amd64"], filter="test", - components=["main"], keyrings="/path/to/keyring", - skip_existing_packages=True, ignore_checksums=True, - download_udebs=True, download_sources=True, - skip_component_check=True, filter_with_deps=True, - ignore_signatures=True, force_update=True), + self.miapi.edit(name="aptly-mirror", newname="aptly-mirror-renamed", + archiveurl='https://deb.nodesource.com/node_10.x/', + architectures=["i386", "amd64"], filter="test", + components=["main"], keyrings=["/path/to/keyring"], + skip_existing_packages=True, ignore_checksums=True, + download_udebs=True, download_sources=True, + skip_component_check=True, filter_with_deps=True, + ignore_signatures=True, force_update=True) def test_delete_validation(self, *, rmock: requests_mock.Mocker) -> None: rmock.delete("http://test/api/mirrors/aptly-mirror") - self.mapi.delete(name="aptly-mirror") + self.miapi.delete(name="aptly-mirror") def test_update_validation(self, *, rmock: requests_mock.Mocker) -> None: rmock.put("http://test/api/mirrors/aptly-mirror") - self.mapi.update(name="aptly-mirror") + self.miapi.update(name="aptly-mirror") def test_edit_validation(self, *, rmock: requests_mock.Mocker) -> None: rmock.put("http://test/api/mirrors/aptly-mirror", text='{"Name":"aptly-mirror-bla", "IgnoreSignatures": true}') - self.mapi.edit(name="aptly-mirror", newname="aptly-mirror-renamed") + self.miapi.edit(name="aptly-mirror", newname="aptly-mirror-renamed") From 747dc4b74dc6cd4fd73914828d8332fe90da1020 Mon Sep 17 00:00:00 2001 From: Xaoc7 Date: Tue, 10 Jan 2023 10:44:39 +0200 Subject: [PATCH 07/37] Fixed code style Fixed code style1 --- aptly_api/parts/snapshots.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aptly_api/parts/snapshots.py b/aptly_api/parts/snapshots.py index 0bff9a7..0dba393 100644 --- a/aptly_api/parts/snapshots.py +++ b/aptly_api/parts/snapshots.py @@ -49,8 +49,7 @@ def create_from_repo(self, reponame: str, snapshotname: str, description: Option if description is not None: body["Description"] = description - resp = self.do_post("api/repos/%s/snapshots" % - quote(reponame), json=body) + resp = self.do_post("api/repos/%s/snapshots" % quote(reponame), json=body) return self.snapshot_from_response(resp.json()) def create_from_mirror(self, mirrorname: str, snapshotname: str, description: Optional[str] = None) -> Snapshot: From 54a42c0b89a46896b318bc511dfb94cd49066d4e Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Tue, 13 Aug 2024 14:11:44 +0200 Subject: [PATCH 08/37] fix code formatting --- aptly_api/parts/mirrors.py | 45 +++++++++++--------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/aptly_api/parts/mirrors.py b/aptly_api/parts/mirrors.py index d833bbb..e9f7714 100644 --- a/aptly_api/parts/mirrors.py +++ b/aptly_api/parts/mirrors.py @@ -38,35 +38,19 @@ class MirrorsAPISection(BaseAPIClient): @staticmethod def mirror_from_response(api_response: Dict[str, str]) -> Mirror: return Mirror( - uuid=cast(str, api_response["UUID"] - ) if "UUID" in api_response else None, - name=cast(str, api_response["Name"]), - archiveurl=cast( - str, api_response["ArchiveRoot"]), - distribution=cast( - str, api_response["Distribution"]) - if "Distribution" in api_response else None, - components=cast(List[str], api_response["Components"] - )if "Components" in api_response else None, - architectures=cast(List[str], api_response["Architectures"] - ) if "Architectures" in api_response else None, - meta=cast(List[Dict[str, str]], api_response["Meta"] - ) if "Meta" in api_response else None, - downloaddate=cast( - str, api_response["LastDownloadDate"]) - if "LastDownloadDate" in api_response else None, - filter=cast(str, api_response["Filter"] - ) if "Filter" in api_response else None, - status=cast(int, api_response["Status"] - )if "Status" in api_response else None, - worker_pid=cast( - int, api_response["WorkerPID"]) - if "WorkerPID" in api_response else None, + uuid=cast(str, api_response["UUID"]) if "UUID" in api_response else None, + name=cast(str, api_response["Name"]), archiveurl=cast(str, api_response["ArchiveRoot"]), + distribution=cast(str, api_response["Distribution"]) if "Distribution" in api_response else None, + components=cast(List[str], api_response["Components"])if "Components" in api_response else None, + architectures=cast(List[str], api_response["Architectures"]) if "Architectures" in api_response else None, + meta=cast(List[Dict[str, str]], api_response["Meta"]) if "Meta" in api_response else None, + downloaddate=cast(str, api_response["LastDownloadDate"]) if "LastDownloadDate" in api_response else None, + filter=cast(str, api_response["Filter"]) if "Filter" in api_response else None, + status=cast(int, api_response["Status"])if "Status" in api_response else None, + worker_pid=cast(int, api_response["WorkerPID"]) if "WorkerPID" in api_response else None, filter_with_deps=cast(bool, api_response["FilterWithDeps"]), - skip_component_check=cast( - bool, api_response["SkipComponentCheck"]), - skip_architecture_check=cast( - bool, api_response["SkipArchitectureCheck"]), + skip_component_check=cast(bool, api_response["SkipComponentCheck"]), + skip_architecture_check=cast(bool, api_response["SkipArchitectureCheck"]), download_sources=cast(bool, api_response["DownloadSources"]), download_udebs=cast(bool, api_response["DownloadUdebs"]), download_installer=cast(bool, api_response["DownloadInstaller"]) @@ -77,9 +61,7 @@ def list(self) -> Sequence[Mirror]: mirrors = [] for mirr in resp.json(): - mirrors.append( - self.mirror_from_response(mirr) - ) + mirrors.append(self.mirror_from_response(mirr)) return mirrors def update(self, name: str, ignore_signatures: bool = False) -> None: @@ -130,7 +112,6 @@ def edit(self, name: str, newname: Optional[str] = None, archiveurl: Optional[st def show(self, name: str) -> Mirror: resp = self.do_get("api/mirrors/%s" % (quote(name))) - return self.mirror_from_response(resp.json()) def list_packages(self, name: str, query: Optional[str] = None, with_deps: bool = False, From d0ec838e17c3b5d3a8061f3d47feaa139e0b5836 Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Sat, 17 Aug 2024 13:56:49 +0200 Subject: [PATCH 09/37] More formatting alignment I understand that my formatting lacks rhyme or reason, but now this is consistent with the rest of the files. --- aptly_api/client.py | 5 +- aptly_api/tests/test_mirrors.py | 153 +++++++++++++++----------------- 2 files changed, 75 insertions(+), 83 deletions(-) diff --git a/aptly_api/client.py b/aptly_api/client.py index bfce19b..b9a9851 100644 --- a/aptly_api/client.py +++ b/aptly_api/client.py @@ -32,9 +32,8 @@ def __init__(self, aptly_server_url: str, ssl_verify: Union[str, bool, None] = N ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) self.snapshots = SnapshotAPISection(base_url=self.__aptly_server_url, ssl_verify=ssl_verify, ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) - self.mirrors = MirrorsAPISection( - base_url=self.__aptly_server_url, ssl_verify=ssl_verify, - ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) + self.mirrors = MirrorsAPISection(base_url=self.__aptly_server_url, ssl_verify=ssl_verify, + ssl_cert=ssl_cert, http_auth=http_auth, timeout=timeout) @property def aptly_server_url(self) -> str: diff --git a/aptly_api/tests/test_mirrors.py b/aptly_api/tests/test_mirrors.py index 07c1103..4a84895 100644 --- a/aptly_api/tests/test_mirrors.py +++ b/aptly_api/tests/test_mirrors.py @@ -21,29 +21,30 @@ def __init__(self, *args: Any) -> None: def test_create(self, *, rmock: requests_mock.Mocker) -> None: rmock.post("http://test/api/mirrors", text="""{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", - "Name": "aptly-mirror", - "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", - "Distribution": "bionic", "Components": ["main"], - "Architectures": ["amd64"], - "Meta": [{"Architectures": "i386 amd64 armhf arm64", - "Codename": "bionic", "Components": "main", - "Date": "Tue, 06 Apr 2021 21:05:41 UTC", - "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}], - "LastDownloadDate": "0001-01-01T00:00:00Z", - "Filter": "test", "Status": 0, "WorkerPID": 0, - "FilterWithDeps": true, "SkipComponentCheck": true, - "SkipArchitectureCheck": true, "DownloadSources": true, - "DownloadUdebs": true, "DownloadInstaller": true}""" - ) + "Name": "aptly-mirror", + "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", + "Distribution": "bionic", "Components": ["main"], + "Architectures": ["amd64"], + "Meta": [{"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}], + "LastDownloadDate": "0001-01-01T00:00:00Z", + "Filter": "test", "Status": 0, "WorkerPID": 0, + "FilterWithDeps": true, "SkipComponentCheck": true, + "SkipArchitectureCheck": true, "DownloadSources": true, + "DownloadUdebs": true, "DownloadInstaller": true}""") self.assertSequenceEqual( - self.miapi.create(name="aptly-mirror", archiveurl='https://deb.nodesource.com/node_10.x/', - distribution='bionic', components=["main"], - architectures=["amd64"], - filter="test", download_udebs=True, - download_sources=True, download_installer=True, - skip_component_check=True, filter_with_deps=True, - keyrings=["/path/to/keyring"], ignore_signatures=True), + self.miapi.create( + name="aptly-mirror", archiveurl='https://deb.nodesource.com/node_10.x/', + distribution='bionic', components=["main"], + architectures=["amd64"], + filter="test", download_udebs=True, + download_sources=True, download_installer=True, + skip_component_check=True, filter_with_deps=True, + keyrings=["/path/to/keyring"], ignore_signatures=True + ), Mirror( uuid='2cb5985a-a23f-4a1f-8eb6-d5409193b4eb', name="aptly-mirror", @@ -67,28 +68,26 @@ def test_create(self, *, rmock: requests_mock.Mocker) -> None: download_sources=True, download_udebs=True, download_installer=True - ) ) def test_list(self, *, rmock: requests_mock.Mocker) -> None: rmock.get("http://test/api/mirrors", text="""[{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", - "Name": "aptly-mirror", - "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", - "Distribution": "bionic", "Components": ["main"], - "Architectures": ["amd64"], - "Meta": [{"Architectures": "i386 amd64 armhf arm64", - "Codename": "bionic", "Components": "main", - "Date": "Tue, 06 Apr 2021 21:05:41 UTC", - "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}], - "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", - "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, - "SkipComponentCheck": false, "SkipArchitectureCheck": false, - "DownloadSources": false, "DownloadUdebs": false, - "DownloadInstaller": false}]""" - ) + "Name": "aptly-mirror", + "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", + "Distribution": "bionic", "Components": ["main"], + "Architectures": ["amd64"], + "Meta": [{"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}], + "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", + "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, + "SkipComponentCheck": false, "SkipArchitectureCheck": false, + "DownloadSources": false, "DownloadUdebs": false, + "DownloadInstaller": false}]""") self.assertSequenceEqual( self.miapi.list(), [ @@ -123,21 +122,20 @@ def test_list(self, *, rmock: requests_mock.Mocker) -> None: def test_show(self, *, rmock: requests_mock.Mocker) -> None: rmock.get("http://test/api/mirrors/aptly-mirror", text="""{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", - "Name": "aptly-mirror", - "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", - "Distribution": "bionic", "Components": ["main"], - "Architectures": ["amd64"], - "Meta": [{"Architectures": "i386 amd64 armhf arm64", - "Codename": "bionic", "Components": "main", - "Date": "Tue, 06 Apr 2021 21:05:41 UTC", - "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}], - "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", - "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, - "SkipComponentCheck": false, "SkipArchitectureCheck": false, - "DownloadSources": false, "DownloadUdebs": false, - "DownloadInstaller": false}""" - ) + "Name": "aptly-mirror", + "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", + "Distribution": "bionic", "Components": ["main"], + "Architectures": ["amd64"], + "Meta": [{"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", "Origin": "Node Source"}], + "LastDownloadDate": "0001-01-01T00:00:00Z", "Filter": "", + "Status": 0, "WorkerPID": 0, "FilterWithDeps": false, + "SkipComponentCheck": false, "SkipArchitectureCheck": false, + "DownloadSources": false, "DownloadUdebs": false, + "DownloadInstaller": false}""") self.assertSequenceEqual( self.miapi.show(name="aptly-mirror"), Mirror( @@ -186,33 +184,28 @@ def test_list_packages(self, *, rmock: requests_mock.Mocker) -> None: def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: rmock.get( "http://test/api/mirrors/aptly-mirror/packages?format=details", - text="""[{ - "Architecture":"amd64", - "Conflicts": "nodejs-dev, nodejs-legacy, npm", - "Depends":"1libc6 (>= 2.9), libgcc1 (>= 1:3.4),""" - """ libstdc++6 (>= 4.4.0), python-minimal, ca-certificates", - "Description":" Node.js event-based server-side javascript engine\\n", - "Filename":"nodejs_10.24.1-1nodesource1_amd64.deb", - "FilesHash":"1f74a6abf6acc572", - "Homepage":"https://nodejs.org", - "Installed-Size":"78630", - "Key":"Pamd64 nodejs 10.24.1-1nodesource1 1f74a6abf6acc572", - "License":"unknown", - "MD5sum":"6d9f0e30396cb6c20945ff6de2f9f322", - "Maintainer":"Ivan Iguaran ", - "Package":"nodejs", - "Priority":"optional", - "Provides":"nodejs-dev, nodejs-legacy, npm", - "SHA1":"a3bc5a29614eab366bb3644abb1e602b5c8953d5", - "SHA256":"4b374d16b536cf1a3963ddc4575ed2b68b28b0b5ea6eefe93c942dfc0ed35177", - "SHA512":"bf203bb319de0c5f7ed3b6ba69de39b1ea8b5086b872561379bd462dd93f0796""" - """9ca64fa01ade01ff08fa13a4e5e28625b59292ba44bc01ba876ec95875630460", - "Section":"web", - "ShortKey":"Pamd64 nodejs 10.24.1-1nodesource1", - "Size":"15949164", - "Version":"10.24.1-1nodesource1" - }]""" - ) + text='[{"Architecture":"amd64",' + '"Conflicts": "nodejs-dev, nodejs-legacy, npm",' + '"Depends":"1libc6 (>= 2.9), libgcc1 (>= 1:3.4),' + 'libstdc++6 (>= 4.4.0), python-minimal, ca-certificates",' + '"Description":" Node.js event-based server-side javascript engine\\n",' + '"Filename":"nodejs_10.24.1-1nodesource1_amd64.deb",' + '"FilesHash":"1f74a6abf6acc572",' + '"Homepage":"https://nodejs.org",' + '"Installed-Size":"78630", "Key":"Pamd64 nodejs 10.24.1-1nodesource11f74a6abf6acc572",' + '"License":"unknown",' + '"MD5sum":"6d9f0e30396cb6c20945ff6de2f9f322","Maintainer":"Ivan Iguaran ",' + '"Package":"nodejs",' + '"Priority":"optional",' + '"Provides":"nodejs-dev, nodejs-legacy, npm",' + '"SHA1":"a3bc5a29614eab366bb3644abb1e602b5c8953d5",' + '"SHA256":"4b374d16b536cf1a3963ddc4575ed2b68b28b0b5ea6eefe93c942dfc0ed35177",' + '"SHA512":"bf203bb319de0c5f7ed3b6ba69de39b1ea8b5086b872561379bd462dd93f0796' + '9ca64fa01ade01ff08fa13a4e5e28625b59292ba44bc01ba876ec95875630460",' + '"Section":"web",' + '"ShortKey":"Pamd64 nodejs 10.24.1-1nodesource1",' + '"Size":"15949164",' + 'Version":"10.24.1-1nodesource1"}]') self.assertSequenceEqual( self.miapi.list_packages( "aptly-mirror", detailed=True, with_deps=True, query="nodejs"), From 38f170c792a9b0c4111451f13bd68466b21267dc Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Sat, 17 Aug 2024 14:01:23 +0200 Subject: [PATCH 10/37] typo fixes --- aptly_api/tests/test_mirrors.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aptly_api/tests/test_mirrors.py b/aptly_api/tests/test_mirrors.py index 4a84895..637abbe 100644 --- a/aptly_api/tests/test_mirrors.py +++ b/aptly_api/tests/test_mirrors.py @@ -185,8 +185,8 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: rmock.get( "http://test/api/mirrors/aptly-mirror/packages?format=details", text='[{"Architecture":"amd64",' - '"Conflicts": "nodejs-dev, nodejs-legacy, npm",' - '"Depends":"1libc6 (>= 2.9), libgcc1 (>= 1:3.4),' + '"Conflicts":"nodejs-dev, nodejs-legacy, npm",' + '"Depends":"1libc6 (>= 2.9), libgcc1 (>= 1:3.4), ' 'libstdc++6 (>= 4.4.0), python-minimal, ca-certificates",' '"Description":" Node.js event-based server-side javascript engine\\n",' '"Filename":"nodejs_10.24.1-1nodesource1_amd64.deb",' @@ -205,7 +205,7 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: '"Section":"web",' '"ShortKey":"Pamd64 nodejs 10.24.1-1nodesource1",' '"Size":"15949164",' - 'Version":"10.24.1-1nodesource1"}]') + '"Version":"10.24.1-1nodesource1"}]') self.assertSequenceEqual( self.miapi.list_packages( "aptly-mirror", detailed=True, with_deps=True, query="nodejs"), From bf527858d6246e379f771dfa5bfc7b0201add492 Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Sat, 17 Aug 2024 14:04:06 +0200 Subject: [PATCH 11/37] typo fixes --- aptly_api/tests/test_mirrors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aptly_api/tests/test_mirrors.py b/aptly_api/tests/test_mirrors.py index 637abbe..35d70e7 100644 --- a/aptly_api/tests/test_mirrors.py +++ b/aptly_api/tests/test_mirrors.py @@ -192,7 +192,7 @@ def test_list_packages_details(self, *, rmock: requests_mock.Mocker) -> None: '"Filename":"nodejs_10.24.1-1nodesource1_amd64.deb",' '"FilesHash":"1f74a6abf6acc572",' '"Homepage":"https://nodejs.org",' - '"Installed-Size":"78630", "Key":"Pamd64 nodejs 10.24.1-1nodesource11f74a6abf6acc572",' + '"Installed-Size":"78630", "Key":"Pamd64 nodejs 10.24.1-1nodesource1 1f74a6abf6acc572",' '"License":"unknown",' '"MD5sum":"6d9f0e30396cb6c20945ff6de2f9f322","Maintainer":"Ivan Iguaran ",' '"Package":"nodejs",' From 8df58badf17ecbfae7f1d83df6c2250450432fe3 Mon Sep 17 00:00:00 2001 From: Greg Land Date: Mon, 29 Jul 2024 18:34:49 -0400 Subject: [PATCH 12/37] Corrected error in mirror.create filter_with_deps * Fixed Mirror.Create. It didn't make use of filter_with_deps argument. * Organized Mirror.create statements to make them easier to follow. * Created unit tests for Mirror.Create to make sure post request is created correctly. * Renamed misleading test named test_create to test_mirror_from_response and cleaned up the function. (cherry picked from commit 71e19b409e5f945b4ff66a14c9e7f4cb7b65a552) --- aptly_api/parts/mirrors.py | 40 +++++++----- aptly_api/tests/test_mirrors.py | 108 ++++++++++++++++++++++++-------- 2 files changed, 105 insertions(+), 43 deletions(-) diff --git a/aptly_api/parts/mirrors.py b/aptly_api/parts/mirrors.py index e9f7714..bfaeea0 100644 --- a/aptly_api/parts/mirrors.py +++ b/aptly_api/parts/mirrors.py @@ -39,21 +39,22 @@ class MirrorsAPISection(BaseAPIClient): def mirror_from_response(api_response: Dict[str, str]) -> Mirror: return Mirror( uuid=cast(str, api_response["UUID"]) if "UUID" in api_response else None, - name=cast(str, api_response["Name"]), archiveurl=cast(str, api_response["ArchiveRoot"]), + name=cast(str, api_response["Name"]), + archiveurl=cast(str, api_response["ArchiveRoot"]), distribution=cast(str, api_response["Distribution"]) if "Distribution" in api_response else None, - components=cast(List[str], api_response["Components"])if "Components" in api_response else None, + components=cast(List[str], api_response["Components"]) if "Components" in api_response else None, architectures=cast(List[str], api_response["Architectures"]) if "Architectures" in api_response else None, meta=cast(List[Dict[str, str]], api_response["Meta"]) if "Meta" in api_response else None, downloaddate=cast(str, api_response["LastDownloadDate"]) if "LastDownloadDate" in api_response else None, filter=cast(str, api_response["Filter"]) if "Filter" in api_response else None, - status=cast(int, api_response["Status"])if "Status" in api_response else None, - worker_pid=cast(int, api_response["WorkerPID"]) if "WorkerPID" in api_response else None, - filter_with_deps=cast(bool, api_response["FilterWithDeps"]), - skip_component_check=cast(bool, api_response["SkipComponentCheck"]), - skip_architecture_check=cast(bool, api_response["SkipArchitectureCheck"]), - download_sources=cast(bool, api_response["DownloadSources"]), - download_udebs=cast(bool, api_response["DownloadUdebs"]), - download_installer=cast(bool, api_response["DownloadInstaller"]) + status=cast(int, api_response["Status"]) if "Status" in api_response else None, + worker_pid=cast( int, api_response["WorkerPID"]) if "WorkerPID" in api_response else None, + filter_with_deps=cast(bool, api_response["FilterWithDeps"]) if "FilterWithDeps" in api_response else False, + skip_component_check=cast(bool, api_response["SkipComponentCheck"]) if "SkipComponentCheck" in api_response else False, + skip_architecture_check=cast(bool, api_response["SkipArchitectureCheck"]) if "SkipArchitectureCheck" in api_response else False, + download_sources=cast(bool, api_response["DownloadSources"]) if "DownloadSources" in api_response else False, + download_udebs=cast(bool, api_response["DownloadUdebs"]) if "DownloadUdebs" in api_response else False, + download_installer=cast(bool, api_response["DownloadInstaller"]) if "DownloadInstaller" in api_response else False, ) def list(self) -> Sequence[Mirror]: @@ -139,32 +140,37 @@ def create(self, name: str, archiveurl: str, distribution: Optional[str] = None, architectures: Optional[List[str]] = None, keyrings: Optional[List[str]] = None, download_sources: bool = False, download_udebs: bool = False, download_installer: bool = False, filter_with_deps: bool = False, - skip_component_check: bool = False, ignore_signatures: bool = False) -> Mirror: + skip_component_check: bool = False, skip_architecture_check: bool = False, + ignore_signatures: bool = False) -> Mirror: data = { "Name": name, "ArchiveURL": archiveurl } # type: T_BodyDict - if ignore_signatures: - data["IgnoreSignatures"] = ignore_signatures - if keyrings: - data["Keyrings"] = keyrings - if filter: - data["Filter"] = filter if distribution: data["Distribution"] = distribution + if filter: + data["Filter"] = filter if components: data["Components"] = components if architectures: data["Architectures"] = architectures + if keyrings: + data["Keyrings"] = keyrings if download_sources: data["DownloadSources"] = download_sources if download_udebs: data["DownloadUdebs"] = download_udebs if download_installer: data["DownloadInstaller"] = download_installer + if filter_with_deps: + data["FilterWithDeps"] = filter_with_deps if skip_component_check: data["SkipComponentCheck"] = skip_component_check + if skip_architecture_check: + data["SkipArchitectureCheck"] = skip_architecture_check + if ignore_signatures: + data["IgnoreSignatures"] = ignore_signatures resp = self.do_post("api/mirrors", json=data) diff --git a/aptly_api/tests/test_mirrors.py b/aptly_api/tests/test_mirrors.py index 35d70e7..b92be6d 100644 --- a/aptly_api/tests/test_mirrors.py +++ b/aptly_api/tests/test_mirrors.py @@ -5,6 +5,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from typing import Any from unittest.case import TestCase +from inspect import signature +import json import requests_mock @@ -19,39 +21,93 @@ def __init__(self, *args: Any) -> None: self.miapi = MirrorsAPISection("http://test/") def test_create(self, *, rmock: requests_mock.Mocker) -> None: - rmock.post("http://test/api/mirrors", - text="""{"UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", - "Name": "aptly-mirror", - "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", - "Distribution": "bionic", "Components": ["main"], - "Architectures": ["amd64"], - "Meta": [{"Architectures": "i386 amd64 armhf arm64", - "Codename": "bionic", "Components": "main", - "Date": "Tue, 06 Apr 2021 21:05:41 UTC", - "Description": " Apt Repository for the Node.JS 10.x Branch", - "Label": "Node Source", "Origin": "Node Source"}], - "LastDownloadDate": "0001-01-01T00:00:00Z", - "Filter": "test", "Status": 0, "WorkerPID": 0, - "FilterWithDeps": true, "SkipComponentCheck": true, - "SkipArchitectureCheck": true, "DownloadSources": true, - "DownloadUdebs": true, "DownloadInstaller": true}""") + expected = {"Name": "testname", "ArchiveURL": "http://randomurl.url"} + + rmock.post("http://test/api/mirrors", text="""{"Name":"nocheck", "ArchiveRoot":"nocheck"}""") + self.miapi.create(expected["Name"], expected["ArchiveURL"]) + + self.assertEqual(rmock.request_history[0].method, "POST") + self.assertEqual(len(rmock.request_history[0].json()), len(expected)) + self.assertEqual(rmock.request_history[0].json(), expected) + + def test_create_all_args(self, *, rmock: requests_mock.Mocker) -> None: + expected = { + "Name": "aptly-mirror", + "ArchiveURL": "https://deb.nodesource.com/node_10.x/", + "Distribution": "bionic", + "Filter": "test", + "Components": ["main"], + "Architectures": ["amd64"], + "Keyrings": ["/path/to/keyring"], + "DownloadSources": True, + "DownloadUdebs": True, + "DownloadInstaller": True, + "FilterWithDeps": True, + "SkipComponentCheck": True, + "SkipArchitectureCheck": True, + "IgnoreSignatures": True, + } + # Keep us from getting out of lockstep with the number of args to create + self.assertEqual(len(signature(self.miapi.create).parameters), len(expected)) + + rmock.post("http://test/api/mirrors", text="""{"Name":"nocheck", "ArchiveRoot":"nocheck"}""") + self.miapi.create( + name="aptly-mirror", + archiveurl="https://deb.nodesource.com/node_10.x/", + distribution="bionic", + filter="test", + components=["main"], + architectures=["amd64"], + keyrings=["/path/to/keyring"], + download_sources=True, + download_udebs=True, + download_installer=True, + filter_with_deps=True, + skip_component_check=True, + skip_architecture_check=True, + ignore_signatures=True, + ) + + self.assertEqual(rmock.request_history[0].method, "POST") + self.assertEqual(len(rmock.request_history[0].json()), len(expected)) + self.assertEqual(rmock.request_history[0].json(), expected) + + def test_mirror_from_response(self, *, rmock: requests_mock.Mocker) -> None: self.assertSequenceEqual( - self.miapi.create( - name="aptly-mirror", archiveurl='https://deb.nodesource.com/node_10.x/', - distribution='bionic', components=["main"], - architectures=["amd64"], - filter="test", download_udebs=True, - download_sources=True, download_installer=True, - skip_component_check=True, filter_with_deps=True, - keyrings=["/path/to/keyring"], ignore_signatures=True + self.miapi.mirror_from_response( + json.loads("""{ + "UUID": "2cb5985a-a23f-4a1f-8eb6-d5409193b4eb", + "Name": "aptly-mirror", + "ArchiveRoot": "https://deb.nodesource.com/node_10.x/", + "Distribution": "bionic", + "Components": ["main"], + "Architectures": ["amd64"], + "LastDownloadDate": "0001-01-01T00:00:00Z", + "Meta": [{"Architectures": "i386 amd64 armhf arm64", + "Codename": "bionic", + "Components": "main", + "Date": "Tue, 06 Apr 2021 21:05:41 UTC", + "Description": " Apt Repository for the Node.JS 10.x Branch", + "Label": "Node Source", + "Origin": "Node Source"}], + "Filter": "test", + "Status": 0, + "WorkerPID": 0, + "FilterWithDeps": true, + "SkipComponentCheck": true, + "SkipArchitectureCheck": true, + "DownloadSources": true, + "DownloadUdebs": true, + "DownloadInstaller": true + }""") ), Mirror( uuid='2cb5985a-a23f-4a1f-8eb6-d5409193b4eb', name="aptly-mirror", archiveurl="https://deb.nodesource.com/node_10.x/", distribution='bionic', - components=["main"], - architectures=["amd64"], + components=['main'], + architectures=['amd64'], downloaddate='0001-01-01T00:00:00Z', meta=[{"Architectures": "i386 amd64 armhf arm64", "Codename": "bionic", From 0456a4552d4d487fc1d86d013d9c9f78fc7c90c6 Mon Sep 17 00:00:00 2001 From: Greg Land Date: Mon, 29 Jul 2024 20:50:55 -0400 Subject: [PATCH 13/37] Fixed description argument for create_from_mirror * Fixed Snapshot.create_from_mirror. Description argument was not being set in the post. * Fixed test_create_from_mirror to check the post request fields to make sure they match expected values. (cherry picked from commit 292be7de8435924af72276b9f9cb0f3373e89c9b) --- aptly_api/parts/snapshots.py | 3 +++ aptly_api/tests/test_snapshots.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/aptly_api/parts/snapshots.py b/aptly_api/parts/snapshots.py index 0dba393..655179c 100644 --- a/aptly_api/parts/snapshots.py +++ b/aptly_api/parts/snapshots.py @@ -56,6 +56,9 @@ def create_from_mirror(self, mirrorname: str, snapshotname: str, description: Op body = { "Name": snapshotname } + if description is not None: + body["Description"] = description + resp = self.do_post("api/mirrors/%s/snapshots" % quote(mirrorname), json=body) return self.snapshot_from_response(resp.json()) diff --git a/aptly_api/tests/test_snapshots.py b/aptly_api/tests/test_snapshots.py index 507ad20..605a497 100644 --- a/aptly_api/tests/test_snapshots.py +++ b/aptly_api/tests/test_snapshots.py @@ -207,6 +207,7 @@ def test_create_from_packages(self, *, rmock: requests_mock.Mocker) -> None: ) def test_create_from_mirror(self, *, rmock: requests_mock.Mocker) -> None: + expected = {'Name': 'aptly-mirror-snap', 'Description': 'Snapshot from local repo [aptly-repo]'} rmock.post("http://test/api/mirrors/aptly-mirror/snapshots", text='{"Name":"aptly-mirror-snap","CreatedAt":"2022-11-29T21:43:45.275605639Z",' '"Description":"Snapshot from local mirror [aptly-mirror]"}') @@ -219,3 +220,4 @@ def test_create_from_mirror(self, *, rmock: requests_mock.Mocker) -> None: created_at=iso8601.parse_date('2022-11-29T21:43:45.275605639Z') ) ) + self.assertEqual(rmock.request_history[0].json(), expected) From ff39a552935511bd8a4be936b82d5b78d1179c97 Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Tue, 17 Sep 2024 00:05:37 +0200 Subject: [PATCH 14/37] keep lines to max 120 chars --- aptly_api/parts/mirrors.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/aptly_api/parts/mirrors.py b/aptly_api/parts/mirrors.py index bfaeea0..1cae53f 100644 --- a/aptly_api/parts/mirrors.py +++ b/aptly_api/parts/mirrors.py @@ -50,11 +50,16 @@ def mirror_from_response(api_response: Dict[str, str]) -> Mirror: status=cast(int, api_response["Status"]) if "Status" in api_response else None, worker_pid=cast( int, api_response["WorkerPID"]) if "WorkerPID" in api_response else None, filter_with_deps=cast(bool, api_response["FilterWithDeps"]) if "FilterWithDeps" in api_response else False, - skip_component_check=cast(bool, api_response["SkipComponentCheck"]) if "SkipComponentCheck" in api_response else False, - skip_architecture_check=cast(bool, api_response["SkipArchitectureCheck"]) if "SkipArchitectureCheck" in api_response else False, - download_sources=cast(bool, api_response["DownloadSources"]) if "DownloadSources" in api_response else False, - download_udebs=cast(bool, api_response["DownloadUdebs"]) if "DownloadUdebs" in api_response else False, - download_installer=cast(bool, api_response["DownloadInstaller"]) if "DownloadInstaller" in api_response else False, + skip_component_check=cast(bool, api_response["SkipComponentCheck"] + ) if "SkipComponentCheck" in api_response else False, + skip_architecture_check=cast(bool, api_response["SkipArchitectureCheck"] + ) if "SkipArchitectureCheck" in api_response else False, + download_sources=cast(bool, api_response["DownloadSources"] + ) if "DownloadSources" in api_response else False, + download_udebs=cast(bool, api_response["DownloadUdebs"] + ) if "DownloadUdebs" in api_response else False, + download_installer=cast(bool, api_response["DownloadInstaller"] + ) if "DownloadInstaller" in api_response else False, ) def list(self) -> Sequence[Mirror]: From 2ff1052fdbe061aaf385e4f0bdad157d85c87f2a Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Tue, 17 Sep 2024 00:07:35 +0200 Subject: [PATCH 15/37] remove superfluous space --- aptly_api/parts/mirrors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aptly_api/parts/mirrors.py b/aptly_api/parts/mirrors.py index 1cae53f..36fdac0 100644 --- a/aptly_api/parts/mirrors.py +++ b/aptly_api/parts/mirrors.py @@ -48,7 +48,7 @@ def mirror_from_response(api_response: Dict[str, str]) -> Mirror: downloaddate=cast(str, api_response["LastDownloadDate"]) if "LastDownloadDate" in api_response else None, filter=cast(str, api_response["Filter"]) if "Filter" in api_response else None, status=cast(int, api_response["Status"]) if "Status" in api_response else None, - worker_pid=cast( int, api_response["WorkerPID"]) if "WorkerPID" in api_response else None, + worker_pid=cast(int, api_response["WorkerPID"]) if "WorkerPID" in api_response else None, filter_with_deps=cast(bool, api_response["FilterWithDeps"]) if "FilterWithDeps" in api_response else False, skip_component_check=cast(bool, api_response["SkipComponentCheck"] ) if "SkipComponentCheck" in api_response else False, From 6456b9e44ffdb2e6b12f047e8828be4442fd1c9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 19:07:08 +0000 Subject: [PATCH 16/37] Bump pytest from 8.3.2 to 8.3.3 Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.2 to 8.3.3. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.3.2...8.3.3) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 4e59df2..5336b14 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -6,5 +6,5 @@ pep257==0.7.0 doc8==1.1.1 Pygments==2.18.0 mypy==1.11.1 -pytest==8.3.2 +pytest==8.3.3 pytest-cov==5.0.0 From 879970ecfbfa215d01d4474f3e87b721cfead072 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:29:48 +0000 Subject: [PATCH 17/37] Bump doc8 from 1.1.1 to 1.1.2 Bumps [doc8](https://github.com/pycqa/doc8) from 1.1.1 to 1.1.2. - [Release notes](https://github.com/pycqa/doc8/releases) - [Commits](https://github.com/pycqa/doc8/compare/v1.1.1...v1.1.2) --- updated-dependencies: - dependency-name: doc8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 5336b14..3fd358d 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,7 +3,7 @@ coverage==7.6.1 coveralls==4.0.1 flake8==7.1.1 pep257==0.7.0 -doc8==1.1.1 +doc8==1.1.2 Pygments==2.18.0 mypy==1.11.1 pytest==8.3.3 From be863057e40d545ec24cf6ccc29e6cebf855674d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:39:13 +0000 Subject: [PATCH 18/37] Bump mypy from 1.11.1 to 1.11.2 Bumps [mypy](https://github.com/python/mypy) from 1.11.1 to 1.11.2. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.11.1...v1.11.2) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 3fd358d..84c7c90 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -5,6 +5,6 @@ flake8==7.1.1 pep257==0.7.0 doc8==1.1.2 Pygments==2.18.0 -mypy==1.11.1 +mypy==1.11.2 pytest==8.3.3 pytest-cov==5.0.0 From de656e5c635f8480fd5d0b702c53c98a8d984546 Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Tue, 17 Sep 2024 00:10:59 +0200 Subject: [PATCH 19/37] bump setup-python action version --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cf79ec5..8089d00 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install Dependencies From 569bb1bb8650325fccc4c64011d7c634ae9257a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 19:25:49 +0000 Subject: [PATCH 20/37] Bump mypy from 1.11.2 to 1.12.0 Bumps [mypy](https://github.com/python/mypy) from 1.11.2 to 1.12.0. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.11.2...v1.12.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 84c7c90..4c779c5 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -5,6 +5,6 @@ flake8==7.1.1 pep257==0.7.0 doc8==1.1.2 Pygments==2.18.0 -mypy==1.11.2 +mypy==1.12.0 pytest==8.3.3 pytest-cov==5.0.0 From b80ec43d541774b62ddbd3326826131efa096c64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 19:25:43 +0000 Subject: [PATCH 21/37] Bump coverage from 7.6.1 to 7.6.3 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.6.1 to 7.6.3. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.6.1...7.6.3) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 4c779c5..23faa6e 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ requests-mock==1.12.1 -coverage==7.6.1 +coverage==7.6.3 coveralls==4.0.1 flake8==7.1.1 pep257==0.7.0 From 85e920841a7632bad7ca09c9251a55dd3afb83bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:23:20 +0000 Subject: [PATCH 22/37] Bump coverage from 7.6.3 to 7.6.7 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.6.3 to 7.6.7. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.6.3...7.6.7) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 23faa6e..1951f44 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ requests-mock==1.12.1 -coverage==7.6.3 +coverage==7.6.7 coveralls==4.0.1 flake8==7.1.1 pep257==0.7.0 From 4e4e156a00cba69476eec10a091e66acc433214c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:19:48 +0000 Subject: [PATCH 23/37] Bump pytest-cov from 5.0.0 to 6.0.0 Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 5.0.0 to 6.0.0. - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v5.0.0...v6.0.0) --- updated-dependencies: - dependency-name: pytest-cov dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 1951f44..f45aebb 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -7,4 +7,4 @@ doc8==1.1.2 Pygments==2.18.0 mypy==1.12.0 pytest==8.3.3 -pytest-cov==5.0.0 +pytest-cov==6.0.0 From ef6ef1426afa0a24826f7545fcf2848aa7ed96ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:05:48 +0000 Subject: [PATCH 24/37] Bump mypy from 1.12.0 to 1.13.0 Bumps [mypy](https://github.com/python/mypy) from 1.12.0 to 1.13.0. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.12.0...v1.13.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index f45aebb..e0b6cf7 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -5,6 +5,6 @@ flake8==7.1.1 pep257==0.7.0 doc8==1.1.2 Pygments==2.18.0 -mypy==1.12.0 +mypy==1.13.0 pytest==8.3.3 pytest-cov==6.0.0 From d2d9ddcef33a744dc71b083acc33e6ff519c8a2c Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Sat, 23 Nov 2024 22:06:13 +0100 Subject: [PATCH 25/37] bump version for release --- aptly_api/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aptly_api/__init__.py b/aptly_api/__init__.py index 0b4b7ed..db01f51 100644 --- a/aptly_api/__init__.py +++ b/aptly_api/__init__.py @@ -13,7 +13,7 @@ from aptly_api.parts.repos import Repo as Repo, FileReport as FileReport from aptly_api.parts.snapshots import Snapshot as Snapshot -version = "0.2.4" +version = "0.3.0" __all__ = ['Client', 'AptlyAPIException', 'version', 'Package', 'PublishEndpoint', 'Repo', 'FileReport', From 2957c2661c5ff1799b56dd34bc616a939ee01d43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 19:56:18 +0000 Subject: [PATCH 26/37] Bump flake8 from 7.1.1 to 7.1.2 Bumps [flake8](https://github.com/pycqa/flake8) from 7.1.1 to 7.1.2. - [Commits](https://github.com/pycqa/flake8/compare/7.1.1...7.1.2) --- updated-dependencies: - dependency-name: flake8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index e0b6cf7..750c6cc 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ requests-mock==1.12.1 coverage==7.6.7 coveralls==4.0.1 -flake8==7.1.1 +flake8==7.1.2 pep257==0.7.0 doc8==1.1.2 Pygments==2.18.0 From 7fd5b6c17145ad962001f1efa9c42ecf6516aa77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 19:39:34 +0000 Subject: [PATCH 27/37] Bump coverage from 7.6.7 to 7.6.12 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.6.7 to 7.6.12. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.6.7...7.6.12) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 750c6cc..a30c5cf 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ requests-mock==1.12.1 -coverage==7.6.7 +coverage==7.6.12 coveralls==4.0.1 flake8==7.1.2 pep257==0.7.0 From a1503a429a6c02c6ba6ce379eb173e85704d122f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 19:11:41 +0000 Subject: [PATCH 28/37] Bump mypy from 1.13.0 to 1.15.0 Bumps [mypy](https://github.com/python/mypy) from 1.13.0 to 1.15.0. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.13.0...v1.15.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index a30c5cf..d340f88 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -5,6 +5,6 @@ flake8==7.1.2 pep257==0.7.0 doc8==1.1.2 Pygments==2.18.0 -mypy==1.13.0 +mypy==1.15.0 pytest==8.3.3 pytest-cov==6.0.0 From 8908cdea3b490b091fd7afc45339154d3d64c392 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:14:36 +0000 Subject: [PATCH 29/37] Bump pygments from 2.18.0 to 2.19.1 Bumps [pygments](https://github.com/pygments/pygments) from 2.18.0 to 2.19.1. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](https://github.com/pygments/pygments/compare/2.18.0...2.19.1) --- updated-dependencies: - dependency-name: pygments dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index d340f88..9527615 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -4,7 +4,7 @@ coveralls==4.0.1 flake8==7.1.2 pep257==0.7.0 doc8==1.1.2 -Pygments==2.18.0 +Pygments==2.19.2 mypy==1.15.0 pytest==8.3.3 pytest-cov==6.0.0 From 0b950f8dd2103226db70285bf6d8622b19dca5ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:15:21 +0000 Subject: [PATCH 30/37] Bump pytest from 8.3.3 to 8.3.4 Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.3 to 8.3.4. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.3.3...8.3.4) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 9527615..7ed0d12 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -6,5 +6,5 @@ pep257==0.7.0 doc8==1.1.2 Pygments==2.19.2 mypy==1.15.0 -pytest==8.3.3 +pytest==8.4.1 pytest-cov==6.0.0 From 78533ca0e24f429fd94d91fcf92b59d2a008abc8 Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Tue, 29 Jul 2025 12:26:08 +0200 Subject: [PATCH 31/37] add Python 3.13 target and manual dispatching --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8089d00..bd9f933 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,7 @@ on: branches: [ "master" ] pull_request: branches: [ "master" ] + workflow_dispatch: jobs: build: @@ -12,7 +13,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: ["3.11", "3.12"] + python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 From c5e4f2dfe47d7a584bcb1528da021bfa8b5435eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:29:53 +0000 Subject: [PATCH 32/37] Bump flake8 from 7.1.2 to 7.3.0 Bumps [flake8](https://github.com/pycqa/flake8) from 7.1.2 to 7.3.0. - [Commits](https://github.com/pycqa/flake8/compare/7.1.2...7.3.0) --- updated-dependencies: - dependency-name: flake8 dependency-version: 7.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 7ed0d12..8335842 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ requests-mock==1.12.1 coverage==7.6.12 coveralls==4.0.1 -flake8==7.1.2 +flake8==7.3.0 pep257==0.7.0 doc8==1.1.2 Pygments==2.19.2 From 656bd5c44126184c5e167b6e89861d8a9b223df9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:29:50 +0000 Subject: [PATCH 33/37] Bump coverage from 7.6.12 to 7.10.1 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.6.12 to 7.10.1. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.6.12...7.10.1) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.10.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 8335842..a6ecba3 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ requests-mock==1.12.1 -coverage==7.6.12 +coverage==7.10.1 coveralls==4.0.1 flake8==7.3.0 pep257==0.7.0 From aff06a9666ce066a61ae96d4d8f617f9718508a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:29:46 +0000 Subject: [PATCH 34/37] Bump pytest-cov from 6.0.0 to 6.2.1 Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 6.0.0 to 6.2.1. - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v6.0.0...v6.2.1) --- updated-dependencies: - dependency-name: pytest-cov dependency-version: 6.2.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index a6ecba3..7cbb1ca 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -7,4 +7,4 @@ doc8==1.1.2 Pygments==2.19.2 mypy==1.15.0 pytest==8.4.1 -pytest-cov==6.0.0 +pytest-cov==6.2.1 From 004cbf9fd384ec2a1c8bec287a05f2addd2c4a8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:29:43 +0000 Subject: [PATCH 35/37] Bump mypy from 1.15.0 to 1.17.0 Bumps [mypy](https://github.com/python/mypy) from 1.15.0 to 1.17.0. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.15.0...v1.17.0) --- updated-dependencies: - dependency-name: mypy dependency-version: 1.17.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 7cbb1ca..8601b26 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -5,6 +5,6 @@ flake8==7.3.0 pep257==0.7.0 doc8==1.1.2 Pygments==2.19.2 -mypy==1.15.0 +mypy==1.17.0 pytest==8.4.1 pytest-cov==6.2.1 From 5d8f82254ebc24f872ae41bd2aab25452704ce8d Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Tue, 29 Jul 2025 12:32:42 +0200 Subject: [PATCH 36/37] coveralls still doesn't support Python 3.13 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bd9f933..7aa581f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: ["3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12"] steps: - uses: actions/checkout@v4 From edcee223f5141b69351cd09d086827a3a33196c5 Mon Sep 17 00:00:00 2001 From: Filipe Brandenburger Date: Thu, 5 Sep 2024 20:19:04 -0400 Subject: [PATCH 37/37] Add support for filename, content-type and headers when uploading files The requests library has support for passing additional information, such as the intended filename on upload, the content-type and additional headers, by passing a tuple with 2, 3 or 4 elements instead of just a file object. See "POST a Multipart-Encoded File" in requests documentation for details: https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file Extend aptly_api files API to also be able to take similar tuples when uploading files to Aptly. One useful use case is to pass a proper package filename, in cases where packages are generated simply as .deb by upstream projects (usually via non native Debian build systems) but should more properly be stored as _:_.deb. Renaming files locally is a possibility, but potentially runs into permission issues. Being able to specify the filename to the API solves this in a more elegant way, without having to modify the local filesystem. The package information can be easily derived using debian.debfile.DebFile() to inspect a package file, in specific gencontrol() returns the fields of the control file which can be used to derive the expected filename. Tested locally by uploading files to aptly using the modified API. Also added a test (even though it mostly relies on mocks.) Confirmed mypy is happy with all the type annotation. --- aptly_api/parts/files.py | 23 ++++++++++++++++------- aptly_api/tests/test_files.py | 8 ++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/aptly_api/parts/files.py b/aptly_api/parts/files.py index c71b9e6..d3f5904 100644 --- a/aptly_api/parts/files.py +++ b/aptly_api/parts/files.py @@ -4,10 +4,16 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import os -from typing import Sequence, List, Tuple, BinaryIO, cast, Optional # noqa: F401 +from typing import Sequence, List, Tuple, TextIO, BinaryIO, cast, Optional, Union, Dict # noqa: F401 from aptly_api.base import BaseAPIClient, AptlyAPIException +_tuplefiletype = Union[ + Tuple[str, Union[TextIO, BinaryIO, str, bytes]], + Tuple[str, Union[TextIO, BinaryIO, str, bytes], str], + Tuple[str, Union[TextIO, BinaryIO, str, bytes], str, Dict[str, str]] +] + class FilesAPISection(BaseAPIClient): def list(self, directory: Optional[str] = None) -> Sequence[str]: @@ -18,13 +24,16 @@ def list(self, directory: Optional[str] = None) -> Sequence[str]: return cast(List[str], resp.json()) - def upload(self, destination: str, *files: str) -> Sequence[str]: - to_upload = [] # type: List[Tuple[str, BinaryIO]] + def upload(self, destination: str, *files: Union[str, _tuplefiletype]) -> Sequence[str]: + to_upload = [] # type: List[Tuple[str, Union[BinaryIO, _tuplefiletype]]] for f in files: - if not os.path.exists(f) or not os.access(f, os.R_OK): + if isinstance(f, tuple): + to_upload.append((f[0], f),) + elif not os.path.exists(f) or not os.access(f, os.R_OK): raise AptlyAPIException("File to upload %s can't be opened or read" % f) - fh = open(f, mode="rb") - to_upload.append((f, fh),) + else: + fh = open(f, mode="rb") + to_upload.append((f, fh),) try: resp = self.do_post("api/files/%s" % destination, @@ -33,7 +42,7 @@ def upload(self, destination: str, *files: str) -> Sequence[str]: raise finally: for fn, to_close in to_upload: - if not to_close.closed: + if not isinstance(to_close, tuple) and not to_close.closed: to_close.close() return cast(List[str], resp.json()) diff --git a/aptly_api/tests/test_files.py b/aptly_api/tests/test_files.py index 76f571b..f814534 100644 --- a/aptly_api/tests/test_files.py +++ b/aptly_api/tests/test_files.py @@ -45,6 +45,14 @@ def test_upload_failed(self, *, rmock: requests_mock.Mocker) -> None: with self.assertRaises(AptlyAPIException): self.fapi.upload("test", os.path.join(os.path.dirname(__file__), "testpkg.deb")) + def test_upload_with_tuples(self, *, rmock: requests_mock.Mocker) -> None: + rmock.post("http://test/api/files/test", text='["test/otherpkg.deb", "test/binpkg.deb"]') + with open(os.path.join(os.path.dirname(__file__), "testpkg.deb"), "rb") as pkgf: + self.assertSequenceEqual( + self.fapi.upload("test", ("otherpkg.deb", pkgf), ("binpkg.deb", b"dpkg-contents")), + ['test/otherpkg.deb', 'test/binpkg.deb'], + ) + def test_delete(self, *, rmock: requests_mock.Mocker) -> None: rmock.delete("http://test/api/files/test", text='{}')