From 2dd251c97f3ae2316d097d0ec6ed4d0a45835bc3 Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Sun, 21 May 2023 21:19:53 +0200 Subject: [PATCH 01/10] Updated Python and Poetry versions --- .gitignore | 1 + build/constants.py | 168 ++++++++++++++++++--------------------------- 2 files changed, 67 insertions(+), 102 deletions(-) diff --git a/.gitignore b/.gitignore index 03897d7..e49a264 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ .venv .env .idea +.vscode .pytest_cache __pycache__ diff --git a/build/constants.py b/build/constants.py index df8482b..fe9984f 100644 --- a/build/constants.py +++ b/build/constants.py @@ -1,67 +1,61 @@ PYTHON_POETRY_IMAGE_NAME: str = "pfeiffermax/python-poetry" TARGET_ARCHITECTURES: list[str] = [ - "poetry1.2.2-python3.7.16-bullseye", - "poetry1.2.2-python3.7.16-slim-bullseye", - "poetry1.2.2-python3.8.16-bullseye", - "poetry1.2.2-python3.8.16-slim-bullseye", - "poetry1.2.2-python3.9.16-bullseye", - "poetry1.2.2-python3.9.16-slim-bullseye", - "poetry1.2.2-python3.10.10-bullseye", - "poetry1.2.2-python3.10.10-slim-bullseye", - "poetry1.3.2-python3.7.16-bullseye", - "poetry1.3.2-python3.7.16-slim-bullseye", - "poetry1.3.2-python3.8.16-bullseye", - "poetry1.3.2-python3.8.16-slim-bullseye", "poetry1.3.2-python3.9.16-bullseye", "poetry1.3.2-python3.9.16-slim-bullseye", - "poetry1.3.2-python3.10.10-bullseye", - "poetry1.3.2-python3.10.10-slim-bullseye", - "poetry1.4.1-python3.7.16-bullseye", - "poetry1.4.1-python3.7.16-slim-bullseye", - "poetry1.4.1-python3.8.16-bullseye", - "poetry1.4.1-python3.8.16-slim-bullseye", - "poetry1.4.1-python3.9.16-bullseye", - "poetry1.4.1-python3.9.16-slim-bullseye", - "poetry1.4.1-python3.10.10-bullseye", - "poetry1.4.1-python3.10.10-slim-bullseye", + "poetry1.3.2-python3.10.11-bullseye", + "poetry1.3.2-python3.10.11-slim-bullseye", + "poetry1.3.2-python3.11.3-bullseye", + "poetry1.3.2-python3.11.3-slim-bullseye", + "poetry1.4.2-python3.9.16-bullseye", + "poetry1.4.2-python3.9.16-slim-bullseye", + "poetry1.4.2-python3.10.11-bullseye", + "poetry1.4.2-python3.10.11-slim-bullseye", + "poetry1.4.2-python3.11.3-bullseye", + "poetry1.4.2-python3.11.3-slim-bullseye", + "poetry1.5.0-python3.9.16-bullseye", + "poetry1.5.0-python3.9.16-slim-bullseye", + "poetry1.5.0-python3.10.11-bullseye", + "poetry1.5.0-python3.10.11-slim-bullseye", + "poetry1.5.0-python3.11.3-bullseye", + "poetry1.5.0-python3.11.3-slim-bullseye", ] BASE_IMAGES: dict = { TARGET_ARCHITECTURES[ 0 - ]: "python:3.7.16-bullseye@sha256:bf85a74f4ace82f3503c2199aaae10e7a8e370bc7e42fd246c5774891f1fab0b", + ]: "python:3.9.16-bullseye@sha256:b8ddeb68904299c09a39aff59d4a713862253b137fdd7ace3a3b7ba0391971b1", TARGET_ARCHITECTURES[ 1 - ]: "python:3.7.16-slim-bullseye@sha256:aa949f5f10e9b28e1f9561fff73d1a359fa8517d4e543451a714d1a4ecc61c56", + ]: "python:3.9.16-slim-bullseye@sha256:9e0b4391fc41bc35c16caef4740736b6b349f6626fd14eba32793ae3c7b01908", TARGET_ARCHITECTURES[ 2 - ]: "python:3.8.16-bullseye@sha256:3a519327ab069a4e356a8aa279e80b7ef6270e17c5df1493dd0a5b281755e95a", + ]: "python:3.10.11-bullseye@sha256:7f8f3cf6668c563e44a2285ce2eca9f8b82f96038449f07183126c95979f7d21", TARGET_ARCHITECTURES[ 3 - ]: "python:3.8.16-slim-bullseye@sha256:75b74d058401381b056d00f903dff58262d884025f772ed635a68e9699c36b87", + ]: "python:3.10.11-slim-bullseye@sha256:12af6fa557c55d85754107e59d0e21530d7a253757e128b3682d138e58712e54", TARGET_ARCHITECTURES[ 4 - ]: "python:3.9.16-bullseye@sha256:b8ddeb68904299c09a39aff59d4a713862253b137fdd7ace3a3b7ba0391971b1", + ]: "python:3.11.3-bullseye@sha256:89cbc1829d74f72436c96302c49218291eb464705c726cc27d71c32fec1d9082", TARGET_ARCHITECTURES[ 5 - ]: "python:3.9.16-slim-bullseye@sha256:9e0b4391fc41bc35c16caef4740736b6b349f6626fd14eba32793ae3c7b01908", + ]: "python:3.11.3-slim-bullseye@sha256:551c9529e77896518ac5693d7e98ee5e12051d625de450ac2a68da1eae15ec87", TARGET_ARCHITECTURES[ 6 - ]: "python:3.10.10-bullseye@sha256:e37b60978ae806a23be492b0003ea081ff699dcf3375cd2eadce5a5bea48156d", + ]: "python:3.9.16-bullseye@sha256:b8ddeb68904299c09a39aff59d4a713862253b137fdd7ace3a3b7ba0391971b1", TARGET_ARCHITECTURES[ 7 - ]: "python:3.10.10-slim-bullseye@sha256:86c8669ed0c407d423b4235da869e1e5275ea59a8429df3ae62d3c415ad0d171", + ]: "python:3.9.16-slim-bullseye@sha256:9e0b4391fc41bc35c16caef4740736b6b349f6626fd14eba32793ae3c7b01908", TARGET_ARCHITECTURES[ 8 - ]: "python:3.7.16-bullseye@sha256:bf85a74f4ace82f3503c2199aaae10e7a8e370bc7e42fd246c5774891f1fab0b", + ]: "python:3.10.11-bullseye@sha256:7f8f3cf6668c563e44a2285ce2eca9f8b82f96038449f07183126c95979f7d21", TARGET_ARCHITECTURES[ 9 - ]: "python:3.7.16-slim-bullseye@sha256:aa949f5f10e9b28e1f9561fff73d1a359fa8517d4e543451a714d1a4ecc61c56", + ]: "python:3.10.11-slim-bullseye@sha256:12af6fa557c55d85754107e59d0e21530d7a253757e128b3682d138e58712e54", TARGET_ARCHITECTURES[ 10 - ]: "python:3.8.16-bullseye@sha256:3a519327ab069a4e356a8aa279e80b7ef6270e17c5df1493dd0a5b281755e95a", + ]: "python:3.11.3-bullseye@sha256:89cbc1829d74f72436c96302c49218291eb464705c726cc27d71c32fec1d9082", TARGET_ARCHITECTURES[ 11 - ]: "python:3.8.16-slim-bullseye@sha256:75b74d058401381b056d00f903dff58262d884025f772ed635a68e9699c36b87", + ]: "python:3.11.3-slim-bullseye@sha256:551c9529e77896518ac5693d7e98ee5e12051d625de450ac2a68da1eae15ec87", TARGET_ARCHITECTURES[ 12 ]: "python:3.9.16-bullseye@sha256:b8ddeb68904299c09a39aff59d4a713862253b137fdd7ace3a3b7ba0391971b1", @@ -70,84 +64,54 @@ ]: "python:3.9.16-slim-bullseye@sha256:9e0b4391fc41bc35c16caef4740736b6b349f6626fd14eba32793ae3c7b01908", TARGET_ARCHITECTURES[ 14 - ]: "python:3.10.10-bullseye@sha256:e37b60978ae806a23be492b0003ea081ff699dcf3375cd2eadce5a5bea48156d", + ]: "python:3.10.11-bullseye@sha256:7f8f3cf6668c563e44a2285ce2eca9f8b82f96038449f07183126c95979f7d21", TARGET_ARCHITECTURES[ 15 - ]: "python:3.10.10-slim-bullseye@sha256:86c8669ed0c407d423b4235da869e1e5275ea59a8429df3ae62d3c415ad0d171", + ]: "python:3.10.11-slim-bullseye@sha256:12af6fa557c55d85754107e59d0e21530d7a253757e128b3682d138e58712e54", TARGET_ARCHITECTURES[ 16 - ]: "python:3.7.16-bullseye@sha256:bf85a74f4ace82f3503c2199aaae10e7a8e370bc7e42fd246c5774891f1fab0b", + ]: "python:3.11.3-bullseye@sha256:89cbc1829d74f72436c96302c49218291eb464705c726cc27d71c32fec1d9082", TARGET_ARCHITECTURES[ 17 - ]: "python:3.7.16-slim-bullseye@sha256:aa949f5f10e9b28e1f9561fff73d1a359fa8517d4e543451a714d1a4ecc61c56", - TARGET_ARCHITECTURES[ - 18 - ]: "python:3.8.16-bullseye@sha256:3a519327ab069a4e356a8aa279e80b7ef6270e17c5df1493dd0a5b281755e95a", - TARGET_ARCHITECTURES[ - 19 - ]: "python:3.8.16-slim-bullseye@sha256:75b74d058401381b056d00f903dff58262d884025f772ed635a68e9699c36b87", - TARGET_ARCHITECTURES[ - 20 - ]: "python:3.9.16-bullseye@sha256:b8ddeb68904299c09a39aff59d4a713862253b137fdd7ace3a3b7ba0391971b1", - TARGET_ARCHITECTURES[ - 21 - ]: "python:3.9.16-slim-bullseye@sha256:9e0b4391fc41bc35c16caef4740736b6b349f6626fd14eba32793ae3c7b01908", - TARGET_ARCHITECTURES[ - 22 - ]: "python:3.10.10-bullseye@sha256:e37b60978ae806a23be492b0003ea081ff699dcf3375cd2eadce5a5bea48156d", - TARGET_ARCHITECTURES[ - 23 - ]: "python:3.10.10-slim-bullseye@sha256:86c8669ed0c407d423b4235da869e1e5275ea59a8429df3ae62d3c415ad0d171", + ]: "python:3.11.3-slim-bullseye@sha256:551c9529e77896518ac5693d7e98ee5e12051d625de450ac2a68da1eae15ec87", } PYTHON_VERSIONS: dict = { - TARGET_ARCHITECTURES[0]: "3.7.16", - TARGET_ARCHITECTURES[1]: "3.7.16", - TARGET_ARCHITECTURES[2]: "3.8.16", - TARGET_ARCHITECTURES[3]: "3.8.16", - TARGET_ARCHITECTURES[4]: "3.9.16", - TARGET_ARCHITECTURES[5]: "3.9.16", - TARGET_ARCHITECTURES[6]: "3.10.10", - TARGET_ARCHITECTURES[7]: "3.10.10", - TARGET_ARCHITECTURES[8]: "3.7.16", - TARGET_ARCHITECTURES[9]: "3.7.16", - TARGET_ARCHITECTURES[10]: "3.8.16", - TARGET_ARCHITECTURES[11]: "3.8.16", + TARGET_ARCHITECTURES[0]: "3.9.16", + TARGET_ARCHITECTURES[1]: "3.9.16", + TARGET_ARCHITECTURES[2]: "3.10.11", + TARGET_ARCHITECTURES[3]: "3.10.11", + TARGET_ARCHITECTURES[4]: "3.11.3", + TARGET_ARCHITECTURES[5]: "3.11.3", + TARGET_ARCHITECTURES[6]: "3.9.16", + TARGET_ARCHITECTURES[7]: "3.9.16", + TARGET_ARCHITECTURES[8]: "3.10.11", + TARGET_ARCHITECTURES[9]: "3.10.11", + TARGET_ARCHITECTURES[10]: "3.11.3", + TARGET_ARCHITECTURES[11]: "3.11.3", TARGET_ARCHITECTURES[12]: "3.9.16", TARGET_ARCHITECTURES[13]: "3.9.16", - TARGET_ARCHITECTURES[14]: "3.10.10", - TARGET_ARCHITECTURES[15]: "3.10.10", - TARGET_ARCHITECTURES[16]: "3.7.16", - TARGET_ARCHITECTURES[17]: "3.7.16", - TARGET_ARCHITECTURES[18]: "3.8.16", - TARGET_ARCHITECTURES[19]: "3.8.16", - TARGET_ARCHITECTURES[20]: "3.9.16", - TARGET_ARCHITECTURES[21]: "3.9.16", - TARGET_ARCHITECTURES[22]: "3.10.10", - TARGET_ARCHITECTURES[23]: "3.10.10", + TARGET_ARCHITECTURES[14]: "3.10.11", + TARGET_ARCHITECTURES[15]: "3.10.11", + TARGET_ARCHITECTURES[16]: "3.11.3", + TARGET_ARCHITECTURES[17]: "3.11.3", } POETRY_VERSIONS: dict = { - TARGET_ARCHITECTURES[0]: "1.2.2", - TARGET_ARCHITECTURES[1]: "1.2.2", - TARGET_ARCHITECTURES[2]: "1.2.2", - TARGET_ARCHITECTURES[3]: "1.2.2", - TARGET_ARCHITECTURES[4]: "1.2.2", - TARGET_ARCHITECTURES[5]: "1.2.2", - TARGET_ARCHITECTURES[6]: "1.2.2", - TARGET_ARCHITECTURES[7]: "1.2.2", - TARGET_ARCHITECTURES[8]: "1.3.2", - TARGET_ARCHITECTURES[9]: "1.3.2", - TARGET_ARCHITECTURES[10]: "1.3.2", - TARGET_ARCHITECTURES[11]: "1.3.2", - TARGET_ARCHITECTURES[12]: "1.3.2", - TARGET_ARCHITECTURES[13]: "1.3.2", - TARGET_ARCHITECTURES[14]: "1.3.2", - TARGET_ARCHITECTURES[15]: "1.3.2", - TARGET_ARCHITECTURES[16]: "1.4.1", - TARGET_ARCHITECTURES[17]: "1.4.1", - TARGET_ARCHITECTURES[18]: "1.4.1", - TARGET_ARCHITECTURES[19]: "1.4.1", - TARGET_ARCHITECTURES[20]: "1.4.1", - TARGET_ARCHITECTURES[21]: "1.4.1", - TARGET_ARCHITECTURES[22]: "1.4.1", - TARGET_ARCHITECTURES[23]: "1.4.1", + TARGET_ARCHITECTURES[0]: "1.3.2", + TARGET_ARCHITECTURES[1]: "1.3.2", + TARGET_ARCHITECTURES[2]: "1.3.2", + TARGET_ARCHITECTURES[3]: "1.3.2", + TARGET_ARCHITECTURES[4]: "1.3.2", + TARGET_ARCHITECTURES[5]: "1.3.2", + TARGET_ARCHITECTURES[6]: "1.4.2", + TARGET_ARCHITECTURES[7]: "1.4.2", + TARGET_ARCHITECTURES[8]: "1.4.2", + TARGET_ARCHITECTURES[9]: "1.4.2", + TARGET_ARCHITECTURES[10]: "1.4.2", + TARGET_ARCHITECTURES[11]: "1.4.2", + TARGET_ARCHITECTURES[12]: "1.5.0", + TARGET_ARCHITECTURES[13]: "1.5.0", + TARGET_ARCHITECTURES[14]: "1.5.0", + TARGET_ARCHITECTURES[15]: "1.5.0", + TARGET_ARCHITECTURES[16]: "1.5.0", + TARGET_ARCHITECTURES[17]: "1.5.0", } From 81123d1d97b46ce4a96f3e0059773322eb72649f Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Sun, 21 May 2023 21:21:29 +0200 Subject: [PATCH 02/10] Updated docs, changed dependabot config Those weekly dependabot check were going on my nerves --- .github/dependabot.yml | 2 +- README.md | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5273e26..cfca83a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,4 +8,4 @@ updates: - package-ecosystem: "pip" directory: "/" schedule: - interval: "weekly" + interval: "monthly" diff --git a/README.md b/README.md index 2625428..d17237f 100644 --- a/README.md +++ b/README.md @@ -17,15 +17,14 @@ and [Debian images](https://hub.docker.com/_/debian). ## Docker Image Features **Poetry versions:** -* v1.2.2 * v1.3.2 -* v1.4.1 +* v1.4.2 +* v1.5.0 **Python versions:** -* v3.7 -* v3.8 * v3.9 * v3.10 +* v3.11 **Operating system:** * [Debian Bullseye v10.13](https://www.debian.org/releases/bullseye/) From dcc02cae969d7120613d327d3efc2b9bfddf2077 Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Sun, 21 May 2023 23:02:06 +0200 Subject: [PATCH 03/10] Added tests for publising the image I had to restructure the whole test setup a bit to make it work --- build/images.py | 48 ++-- build/publish.py | 62 ++-- poetry.lock | 266 +++++++++++------- pyproject.toml | 6 +- .../__init__.py | 0 tests/build_image/conftest.py | 29 ++ .../test_build_version.py | 0 .../test_poetry_version.py | 0 .../test_python_version.py | 0 tests/configuration/__init__.py | 0 tests/conftest.py | 32 +-- tests/constants.py | 2 + tests/publish_image/conftest.py | 17 ++ tests/publish_image/test_cli.py | 90 ++++++ tests/publish_image/test_cli_and_env.py | 10 + tests/publish_image/test_env.py | 76 +++++ tests/registry_container.py | 60 ++++ 17 files changed, 530 insertions(+), 168 deletions(-) rename tests/{build_versions => build_image}/__init__.py (100%) create mode 100644 tests/build_image/conftest.py rename tests/{build_versions => build_image}/test_build_version.py (100%) rename tests/{configuration => build_image}/test_poetry_version.py (100%) rename tests/{configuration => build_image}/test_python_version.py (100%) delete mode 100644 tests/configuration/__init__.py create mode 100644 tests/publish_image/conftest.py create mode 100644 tests/publish_image/test_cli.py create mode 100644 tests/publish_image/test_cli_and_env.py create mode 100644 tests/publish_image/test_env.py create mode 100644 tests/registry_container.py diff --git a/build/images.py b/build/images.py index ebfdfca..7944430 100644 --- a/build/images.py +++ b/build/images.py @@ -1,9 +1,8 @@ from pathlib import Path from typing import Optional -import docker from docker.models.images import Image - +from docker.client import DockerClient from build.constants import ( PYTHON_POETRY_IMAGE_NAME, BASE_IMAGES, @@ -12,41 +11,48 @@ class DockerImage: - def __init__(self, docker_client: docker.client): - self.docker_client: docker.client = docker_client - self.absolute_module_directory_path: Path = Path( - __file__ - ).parent.resolve() + def __init__( + self, + docker_client: DockerClient, + target_architecture: str, + version: str, + ): + self.docker_client: DockerClient = docker_client + self.dockerfile_name: str = "Dockerfile" self.image_name: Optional[str] = None self.image_tag: Optional[str] = None - self.version_tag: Optional[str] = None - self.dockerfile_name: str = "Dockerfile" + self.version: Optional[str] = version + self.target_architecture: str = target_architecture class PythonPoetryImage(DockerImage): - def __init__(self, docker_client: docker.client): - super().__init__(docker_client) - self.absolute_docker_image_directory_path: Path = ( - self.absolute_module_directory_path - ) + def __init__( + self, + docker_client: DockerClient, + target_architecture: str, + version: str, + ): + super().__init__(docker_client, target_architecture, version) + self.dockerfile_directory: Path = Path(__file__).parent.resolve() # An image name is made up of slash-separated name components, optionally prefixed by a registry hostname. # see: https://docs.docker.com/engine/reference/commandline/tag/ self.image_name = PYTHON_POETRY_IMAGE_NAME - def build(self, target_architecture: str, version: str = None) -> Image: - self.version_tag = version + def build(self) -> Image: + self.image_tag = ( + f"{self.version}-{self.target_architecture}" + ) buildargs: dict[str, str] = { - "OFFICIAL_PYTHON_IMAGE": BASE_IMAGES[target_architecture], - "POETRY_VERSION": POETRY_VERSIONS[target_architecture], + "OFFICIAL_PYTHON_IMAGE": BASE_IMAGES[self.target_architecture], + "POETRY_VERSION": POETRY_VERSIONS[self.target_architecture], } - tag: str = f"{self.image_name}:{self.version_tag}-{target_architecture}" image: Image = self.docker_client.images.build( - path=str(self.absolute_docker_image_directory_path), + path=str(self.dockerfile_directory), dockerfile=self.dockerfile_name, - tag=tag, + tag=f"{self.image_name}:{self.image_tag}", buildargs=buildargs, )[0] return image diff --git a/build/publish.py b/build/publish.py index 8623827..cdc3e46 100644 --- a/build/publish.py +++ b/build/publish.py @@ -1,27 +1,39 @@ -import os - +import click import docker -from dotenv import load_dotenv - from build.constants import ( TARGET_ARCHITECTURES, PYTHON_POETRY_IMAGE_NAME, ) from build.images import PythonPoetryImage -environment_variables_loaded: bool = load_dotenv() - -docker_hub_username: str = os.getenv("DOCKER_HUB_USERNAME") -docker_hub_password: str = os.getenv("DOCKER_HUB_PASSWORD") -version_tag: str = os.getenv("GIT_TAG_NAME") +from docker.client import DockerClient - -def main() -> None: - docker_client: docker.client = docker.from_env() +@click.command() +@click.option( + "--docker-hub-username", + envvar="DOCKER_HUB_USERNAME", + help="Docker Hub username", +) +@click.option( + "--docker-hub-password", + envvar="DOCKER_HUB_PASSWORD", + help="Docker Hub password", +) +@click.option( + "--version-tag", envvar="GIT_TAG_NAME", required=True, help="Version Tag" +) +@click.option("--registry", envvar="REGISTRY", help="Docker registry") +def main( + docker_hub_username: str, + docker_hub_password: str, + version_tag: str, + registry: str, +) -> None: + docker_client: DockerClient = docker.from_env() for target_architecture in TARGET_ARCHITECTURES: new_python_poetry_image: PythonPoetryImage = PythonPoetryImage( - docker_client + docker_client, target_architecture, version_tag ) # Delete old existing images @@ -31,16 +43,30 @@ def main() -> None: for tag in old_image.tags: docker_client.images.remove(tag, force=True) - new_python_poetry_image.build(target_architecture, version=version_tag) + new_python_poetry_image.build() # https://docs.docker.com/engine/reference/commandline/push/ # https://docs.docker.com/engine/reference/commandline/tag/ # https://docs.docker.com/engine/reference/commandline/image_tag/ - docker_client.login( - username=docker_hub_username, password=docker_hub_password - ) + if docker_hub_username and docker_hub_password: + login_kwargs: dict = { + "username": docker_hub_username, + "password": docker_hub_password, + } + if registry: + login_kwargs["registry"] = registry + + docker_client.login(**login_kwargs) + + if registry: + repository: str = ( + f"{registry}/{new_python_poetry_image.image_name}" + ) + else: + repository: str = new_python_poetry_image.image_name + for line in docker_client.images.push( - PYTHON_POETRY_IMAGE_NAME, + repository, tag=new_python_poetry_image.image_tag, stream=True, decode=True, diff --git a/poetry.lock b/poetry.lock index 2ae627f..a79c421 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,15 +1,15 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] name = "astroid" -version = "2.15.4" +version = "2.15.5" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false python-versions = ">=3.7.2" files = [ - {file = "astroid-2.15.4-py3-none-any.whl", hash = "sha256:a1b8543ef9d36ea777194bc9b17f5f8678d2c56ee6a45b2c2f17eec96f242347"}, - {file = "astroid-2.15.4.tar.gz", hash = "sha256:c81e1c7fbac615037744d067a9bb5f9aeb655edf59b63ee8b59585475d6f80d8"}, + {file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"}, + {file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"}, ] [package.dependencies] @@ -17,6 +17,41 @@ lazy-object-proxy = ">=1.4.0" typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} wrapt = {version = ">=1.11,<2", markers = "python_version < \"3.11\""} +[[package]] +name = "bcrypt" +version = "4.0.1" +description = "Modern password hashing for your software and your servers" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, + {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, + {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, + {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, +] + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] +typecheck = ["mypy"] + [[package]] name = "black" version = "23.3.0" @@ -69,14 +104,14 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2022.12.7" +version = "2023.5.7" description = "Python package for providing Mozilla's CA Bundle." -category = "dev" +category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, ] [[package]] @@ -95,7 +130,7 @@ files = [ name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -180,7 +215,7 @@ files = [ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -195,7 +230,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -270,6 +305,21 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "deprecation" +version = "2.1.0" +description = "A library to handle automated deprecations" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"}, + {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, +] + +[package.dependencies] +packaging = "*" + [[package]] name = "dill" version = "0.3.6" @@ -301,7 +351,7 @@ files = [ name = "docker" version = "6.1.1" description = "A Python library for the Docker Engine API." -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -336,30 +386,30 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.10.4" +version = "3.12.0" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "filelock-3.10.4-py3-none-any.whl", hash = "sha256:6d332dc5c896f18ba93a21d987155e97c434a96d3fe4042ca70d0b3b46e3b470"}, - {file = "filelock-3.10.4.tar.gz", hash = "sha256:9fc1734dbddcdcd4aaa02c160dd94db5272b92dfa859b44ec8df28e160b751f0"}, + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, ] [package.extras] -docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.2)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "identify" -version = "2.5.22" +version = "2.5.24" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "identify-2.5.22-py2.py3-none-any.whl", hash = "sha256:f0faad595a4687053669c112004178149f6c326db71ee999ae4636685753ad2f"}, - {file = "identify-2.5.22.tar.gz", hash = "sha256:f7a93d6cf98e29bd07663c60728e7a4057615068d7a639d132dc883b2d54d31e"}, + {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, + {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, ] [package.extras] @@ -369,7 +419,7 @@ license = ["ukkonen"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -479,14 +529,14 @@ files = [ [[package]] name = "nodeenv" -version = "1.7.0" +version = "1.8.0" description = "Node.js virtual environment builder" category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] @@ -494,14 +544,14 @@ setuptools = "*" [[package]] name = "packaging" -version = "23.0" +version = "23.1" description = "Core utilities for Python packages" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] [[package]] @@ -518,19 +568,19 @@ files = [ [[package]] name = "platformdirs" -version = "3.1.1" +version = "3.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.1.1-py3-none-any.whl", hash = "sha256:e5986afb596e4bb5bde29a79ac9061aa955b94fca2399b7aaac4090860920dd8"}, - {file = "platformdirs-3.1.1.tar.gz", hash = "sha256:024996549ee88ec1a9aa99ff7f8fc819bb59e2c3477b410d90a16d32d6e707aa"}, + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, ] [package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" @@ -636,43 +686,28 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] -[[package]] -name = "python-dotenv" -version = "1.0.0" -description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - [[package]] name = "pywin32" -version = "305" +version = "306" description = "Python for Window Extensions" -category = "dev" +category = "main" optional = false python-versions = "*" files = [ - {file = "pywin32-305-cp310-cp310-win32.whl", hash = "sha256:421f6cd86e84bbb696d54563c48014b12a23ef95a14e0bdba526be756d89f116"}, - {file = "pywin32-305-cp310-cp310-win_amd64.whl", hash = "sha256:73e819c6bed89f44ff1d690498c0a811948f73777e5f97c494c152b850fad478"}, - {file = "pywin32-305-cp310-cp310-win_arm64.whl", hash = "sha256:742eb905ce2187133a29365b428e6c3b9001d79accdc30aa8969afba1d8470f4"}, - {file = "pywin32-305-cp311-cp311-win32.whl", hash = "sha256:19ca459cd2e66c0e2cc9a09d589f71d827f26d47fe4a9d09175f6aa0256b51c2"}, - {file = "pywin32-305-cp311-cp311-win_amd64.whl", hash = "sha256:326f42ab4cfff56e77e3e595aeaf6c216712bbdd91e464d167c6434b28d65990"}, - {file = "pywin32-305-cp311-cp311-win_arm64.whl", hash = "sha256:4ecd404b2c6eceaca52f8b2e3e91b2187850a1ad3f8b746d0796a98b4cea04db"}, - {file = "pywin32-305-cp36-cp36m-win32.whl", hash = "sha256:48d8b1659284f3c17b68587af047d110d8c44837736b8932c034091683e05863"}, - {file = "pywin32-305-cp36-cp36m-win_amd64.whl", hash = "sha256:13362cc5aa93c2beaf489c9c9017c793722aeb56d3e5166dadd5ef82da021fe1"}, - {file = "pywin32-305-cp37-cp37m-win32.whl", hash = "sha256:a55db448124d1c1484df22fa8bbcbc45c64da5e6eae74ab095b9ea62e6d00496"}, - {file = "pywin32-305-cp37-cp37m-win_amd64.whl", hash = "sha256:109f98980bfb27e78f4df8a51a8198e10b0f347257d1e265bb1a32993d0c973d"}, - {file = "pywin32-305-cp38-cp38-win32.whl", hash = "sha256:9dd98384da775afa009bc04863426cb30596fd78c6f8e4e2e5bbf4edf8029504"}, - {file = "pywin32-305-cp38-cp38-win_amd64.whl", hash = "sha256:56d7a9c6e1a6835f521788f53b5af7912090674bb84ef5611663ee1595860fc7"}, - {file = "pywin32-305-cp39-cp39-win32.whl", hash = "sha256:9d968c677ac4d5cbdaa62fd3014ab241718e619d8e36ef8e11fb930515a1e918"}, - {file = "pywin32-305-cp39-cp39-win_amd64.whl", hash = "sha256:50768c6b7c3f0b38b7fb14dd4104da93ebced5f1a50dc0e834594bff6fbe1271"}, + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, ] [[package]] @@ -727,21 +762,21 @@ files = [ [[package]] name = "requests" -version = "2.28.2" +version = "2.30.0" description = "Python HTTP for Humans." -category = "dev" +category = "main" optional = false -python-versions = ">=3.7, <4" +python-versions = ">=3.7" files = [ - {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, - {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, + {file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"}, + {file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" +urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] @@ -761,21 +796,55 @@ files = [ [[package]] name = "setuptools" -version = "67.6.0" +version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.6.0-py3-none-any.whl", hash = "sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"}, - {file = "setuptools-67.6.0.tar.gz", hash = "sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077"}, + {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, + {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +[[package]] +name = "testcontainers" +version = "3.7.1" +description = "Library provides lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "testcontainers-3.7.1-py2.py3-none-any.whl", hash = "sha256:7f48cef4bf0ccd78f1a4534d4b701a003a3bace851f24eae58a32f9e3f0aeba0"}, +] + +[package.dependencies] +deprecation = "*" +docker = ">=4.0.0" +wrapt = "*" + +[package.extras] +arangodb = ["python-arango"] +azurite = ["azure-storage-blob"] +clickhouse = ["clickhouse-driver"] +docker-compose = ["docker-compose"] +google-cloud-pubsub = ["google-cloud-pubsub (<2)"] +kafka = ["kafka-python"] +keycloak = ["python-keycloak"] +mongo = ["pymongo"] +mssqlserver = ["pymssql"] +mysql = ["pymysql", "sqlalchemy"] +neo4j = ["neo4j"] +oracle = ["cx-Oracle", "sqlalchemy"] +postgresql = ["psycopg2-binary", "sqlalchemy"] +rabbitmq = ["pika"] +redis = ["redis"] +selenium = ["selenium"] + [[package]] name = "tomli" version = "2.0.1" @@ -790,14 +859,14 @@ files = [ [[package]] name = "tomlkit" -version = "0.11.6" +version = "0.11.8" description = "Style preserving TOML library" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, - {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, + {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, + {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, ] [[package]] @@ -814,52 +883,53 @@ files = [ [[package]] name = "urllib3" -version = "1.26.15" +version = "2.0.2" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7" files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, + {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, + {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.21.0" +version = "20.23.0" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.21.0-py3-none-any.whl", hash = "sha256:31712f8f2a17bd06234fa97fdf19609e789dd4e3e4bf108c3da71d710651adbc"}, - {file = "virtualenv-20.21.0.tar.gz", hash = "sha256:f50e3e60f990a0757c9b68333c9fdaa72d7188caa417f96af9e52407831a3b68"}, + {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, + {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, ] [package.dependencies] distlib = ">=0.3.6,<1" -filelock = ">=3.4.1,<4" -platformdirs = ">=2.4,<4" +filelock = ">=3.11,<4" +platformdirs = ">=3.2,<4" [package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -test = ["covdefaults (>=2.2.2)", "coverage (>=7.1)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23)", "pytest (>=7.2.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] [[package]] name = "websocket-client" -version = "1.5.1" +version = "1.5.2" description = "WebSocket client for Python with low level API options" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, - {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, + {file = "websocket-client-1.5.2.tar.gz", hash = "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b"}, + {file = "websocket_client-1.5.2-py3-none-any.whl", hash = "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"}, ] [package.extras] @@ -955,4 +1025,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "3.9.*" -content-hash = "61cde192b57c94f4f9606acdaa981ed8988d4002f0c613bf163abd2cff87dd5f" +content-hash = "0777f39986d9524a9364c303e035f542db64ff2c67d5f9716f93ca8d6656e07a" diff --git a/pyproject.toml b/pyproject.toml index b7bb283..f83f39a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,17 +7,19 @@ license = "MIT" [tool.poetry.dependencies] python = "3.9.*" -python-dotenv = "1.0.0" +click = "8.1.3" +docker = "6.1.1" [tool.poetry.dev-dependencies] pylint = "2.17.4" pytest = "7.3.1" pytest-cov = "4.0.0" coverage = "7.2.5" -docker = "6.1.1" black = "23.3.0" pre-commit = "3.3.1" semver = "3.0.0" +testcontainers = "3.7.1" +bcrypt = "4.0.1" # https://docs.pytest.org/en/latest/reference/customize.html [tool.pytest.ini_options] diff --git a/tests/build_versions/__init__.py b/tests/build_image/__init__.py similarity index 100% rename from tests/build_versions/__init__.py rename to tests/build_image/__init__.py diff --git a/tests/build_image/conftest.py b/tests/build_image/conftest.py new file mode 100644 index 0000000..13282ae --- /dev/null +++ b/tests/build_image/conftest.py @@ -0,0 +1,29 @@ +import pytest +from docker.client import DockerClient +from docker.models.images import Image + +from build.constants import TARGET_ARCHITECTURES +from build.images import PythonPoetryImage + + +@pytest.fixture(scope="session", params=TARGET_ARCHITECTURES) +def python_poetry_image( + docker_client: DockerClient, version: str, request +) -> str: + target_architecture: str = request.param + + python_poetry_image: Image = PythonPoetryImage(docker_client).build( + target_architecture, version=version + ) + image_tag: str = python_poetry_image.tags[0] + yield image_tag + docker_client.images.remove(image_tag, force=True) + + +@pytest.fixture(scope="function") +def cleaned_up_test_container(docker_client: DockerClient, request) -> None: + test_container_name: str = request.param + yield test_container_name + test_container = docker_client.containers.get(test_container_name) + test_container.stop() + test_container.remove() diff --git a/tests/build_versions/test_build_version.py b/tests/build_image/test_build_version.py similarity index 100% rename from tests/build_versions/test_build_version.py rename to tests/build_image/test_build_version.py diff --git a/tests/configuration/test_poetry_version.py b/tests/build_image/test_poetry_version.py similarity index 100% rename from tests/configuration/test_poetry_version.py rename to tests/build_image/test_poetry_version.py diff --git a/tests/configuration/test_python_version.py b/tests/build_image/test_python_version.py similarity index 100% rename from tests/configuration/test_python_version.py rename to tests/build_image/test_python_version.py diff --git a/tests/configuration/__init__.py b/tests/configuration/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/conftest.py b/tests/conftest.py index eda28c8..615b336 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,18 +2,13 @@ import docker import pytest -from docker.models.images import Image +from docker.client import DockerClient from semver import VersionInfo -from build.constants import ( - TARGET_ARCHITECTURES, -) -from build.images import PythonPoetryImage - @pytest.fixture(scope="session") -def docker_client() -> docker.client: - return docker.client.from_env() +def docker_client() -> DockerClient: + return docker.from_env() @pytest.fixture(scope="session") @@ -23,24 +18,3 @@ def version() -> str: ) version_string: str = str(version) return version_string - - -@pytest.fixture(scope="session", params=TARGET_ARCHITECTURES) -def python_poetry_image(docker_client, version, request) -> str: - target_architecture: str = request.param - - python_poetry_image: Image = PythonPoetryImage(docker_client).build( - target_architecture, version=version - ) - image_tag: str = python_poetry_image.tags[0] - yield image_tag - docker_client.images.remove(image_tag, force=True) - - -@pytest.fixture(scope="function") -def cleaned_up_test_container(docker_client, request) -> None: - test_container_name: str = request.param - yield test_container_name - test_container = docker_client.containers.get(test_container_name) - test_container.stop() - test_container.remove() diff --git a/tests/constants.py b/tests/constants.py index dc076f3..aab9a85 100644 --- a/tests/constants.py +++ b/tests/constants.py @@ -1 +1,3 @@ SLEEP_TIME: float = 3.0 +REGISTRY_USERNAME: str = "foo" +REGISTRY_PASSWORD: str = "bar" diff --git a/tests/publish_image/conftest.py b/tests/publish_image/conftest.py new file mode 100644 index 0000000..3fb45ef --- /dev/null +++ b/tests/publish_image/conftest.py @@ -0,0 +1,17 @@ +import pytest +from click.testing import CliRunner +from docker.client import DockerClient + + +@pytest.fixture(scope="package") +def cli_runner() -> CliRunner: + runner = CliRunner() + return runner + + +@pytest.fixture(scope="package") +def cleanup_images(docker_client: DockerClient): + yield + for old_image in docker_client.images.list("pfeiffermax/python-poetry"): + for tag in old_image.tags: + docker_client.images.remove(tag, force=True) diff --git a/tests/publish_image/test_cli.py b/tests/publish_image/test_cli.py new file mode 100644 index 0000000..31addff --- /dev/null +++ b/tests/publish_image/test_cli.py @@ -0,0 +1,90 @@ +from click.testing import CliRunner, Result +from docker.errors import APIError + +from build.publish import main +from tests.constants import REGISTRY_USERNAME, REGISTRY_PASSWORD +from tests.registry_container import DockerRegistryContainer +import pytest + + +@pytest.mark.usefixtures("cleanup_images") +def test_registry(cli_runner: CliRunner, version: str): + with DockerRegistryContainer().with_bind_ports( + 5000, 5000 + ) as docker_registry: + result: Result = cli_runner.invoke( + main, + args=[ + "--version-tag", + version, + "--registry", + docker_registry.get_registry(), + ], + ) + assert result.exit_code == 0 + + +@pytest.mark.usefixtures("cleanup_images") +def test_registry_with_unnecessary_credentials( + cli_runner: CliRunner, version: str +): + with DockerRegistryContainer().with_bind_ports( + 5000, 5000 + ) as docker_registry: + result: Result = cli_runner.invoke( + main, + args=[ + "--docker-hub-username", + "bang", + "--docker-hub-password", + "boom", + "--version-tag", + version, + "--registry", + docker_registry.get_registry(), + ], + ) + assert result.exit_code == 0 + + +@pytest.mark.usefixtures("cleanup_images") +def test_registry_with_credentials(cli_runner: CliRunner, version: str): + with DockerRegistryContainer( + username=REGISTRY_USERNAME, password=REGISTRY_PASSWORD + ).with_bind_ports(5000, 5000) as docker_registry: + result: Result = cli_runner.invoke( + main, + args=[ + "--docker-hub-username", + REGISTRY_USERNAME, + "--docker-hub-password", + REGISTRY_PASSWORD, + "--version-tag", + version, + "--registry", + docker_registry.get_registry(), + ], + ) + assert result.exit_code == 0 + + +@pytest.mark.usefixtures("cleanup_images") +def test_registry_with_wrong_credentials(cli_runner: CliRunner, version: str): + with DockerRegistryContainer( + username=REGISTRY_USERNAME, password=REGISTRY_PASSWORD + ).with_bind_ports(5000, 5000) as docker_registry: + result: Result = cli_runner.invoke( + main, + args=[ + "--docker-hub-username", + "bang", + "--docker-hub-password", + "boom", + "--version-tag", + version, + "--registry", + docker_registry.get_registry(), + ], + ) + assert result.exit_code == 1 + assert isinstance(result.exception, APIError) diff --git a/tests/publish_image/test_cli_and_env.py b/tests/publish_image/test_cli_and_env.py new file mode 100644 index 0000000..f582d8c --- /dev/null +++ b/tests/publish_image/test_cli_and_env.py @@ -0,0 +1,10 @@ +import pytest +from click.testing import CliRunner, Result + +from build.publish import main + + +@pytest.mark.usefixtures("cleanup_images") +def test_missing_options_and_env(cli_runner: CliRunner): + result: Result = cli_runner.invoke(main) + assert result.exit_code == 2 diff --git a/tests/publish_image/test_env.py b/tests/publish_image/test_env.py new file mode 100644 index 0000000..cbf0a18 --- /dev/null +++ b/tests/publish_image/test_env.py @@ -0,0 +1,76 @@ +import pytest +from click.testing import CliRunner, Result +from docker.errors import APIError + +from build.publish import main +from tests.constants import REGISTRY_USERNAME, REGISTRY_PASSWORD +from tests.registry_container import DockerRegistryContainer + + +@pytest.mark.usefixtures("cleanup_images") +def test_registry(cli_runner: CliRunner, version: str): + with DockerRegistryContainer().with_bind_ports( + 5000, 5000 + ) as docker_registry: + result: Result = cli_runner.invoke( + main, + env={ + "GIT_TAG_NAME": version, + "REGISTRY": docker_registry.get_registry(), + }, + ) + assert result.exit_code == 0 + + +@pytest.mark.usefixtures("cleanup_images") +def test_registry_with_unnecessary_credentials( + cli_runner: CliRunner, version: str +): + with DockerRegistryContainer().with_bind_ports( + 5000, 5000 + ) as docker_registry: + result: Result = cli_runner.invoke( + main, + env={ + "DOCKER_HUB_USERNAME": REGISTRY_USERNAME, + "DOCKER_HUB_PASSWORD": REGISTRY_PASSWORD, + "GIT_TAG_NAME": version, + "REGISTRY": docker_registry.get_registry(), + }, + ) + assert result.exit_code == 0 + + +@pytest.mark.usefixtures("cleanup_images") +def test_registry_with_credentials(cli_runner: CliRunner, version: str): + with DockerRegistryContainer( + username=REGISTRY_USERNAME, password=REGISTRY_PASSWORD + ).with_bind_ports(5000, 5000) as docker_registry: + result: Result = cli_runner.invoke( + main, + env={ + "DOCKER_HUB_USERNAME": REGISTRY_USERNAME, + "DOCKER_HUB_PASSWORD": REGISTRY_PASSWORD, + "GIT_TAG_NAME": version, + "REGISTRY": docker_registry.get_registry(), + }, + ) + assert result.exit_code == 0 + + +@pytest.mark.usefixtures("cleanup_images") +def test_registry_with_wrong_credentials(cli_runner: CliRunner, version: str): + with DockerRegistryContainer( + username=REGISTRY_USERNAME, password=REGISTRY_PASSWORD + ).with_bind_ports(5000, 5000) as docker_registry: + result: Result = cli_runner.invoke( + main, + env={ + "DOCKER_HUB_USERNAME": "boom", + "DOCKER_HUB_PASSWORD": "bang", + "GIT_TAG_NAME": version, + "REGISTRY": docker_registry.get_registry(), + }, + ) + assert result.exit_code == 1 + assert isinstance(result.exception, APIError) diff --git a/tests/registry_container.py b/tests/registry_container.py new file mode 100644 index 0000000..46cf313 --- /dev/null +++ b/tests/registry_container.py @@ -0,0 +1,60 @@ +from pathlib import Path +from tempfile import NamedTemporaryFile +from typing import Optional + +import bcrypt +from testcontainers.core.container import DockerContainer + + +# https://docs.docker.com/registry/ +class DockerRegistryContainer(DockerContainer): + def __init__( + self, + image: str = "registry:latest", + port: int = 5000, + username: str = None, + password: str = None, + **kwargs, + ) -> None: + super().__init__(image=image, **kwargs) + self.port_to_expose = port + self.username: Optional[str] = username + self.password: Optional[str] = password + self.htpasswd_file: Optional[Path] = None + self.with_exposed_ports(self.port_to_expose) + + def start(self): + # Create the password file + if self.username and self.password: + hashed_password: str = bcrypt.hashpw( + self.password.encode("utf-8"), + bcrypt.gensalt(rounds=12, prefix=b"2a"), + ).decode("utf-8") + content = f"{self.username}:{hashed_password}" + + with NamedTemporaryFile(delete=False) as file: + file.write(content.encode("utf-8")) + self.htpasswd_file = Path(file.name) + + host_dir: str = str(self.htpasswd_file.parent.resolve()) + tmp_file: str = self.htpasswd_file.name + + self.with_volume_mapping(host_dir, "/htpasswd") + self.with_env("REGISTRY_AUTH_HTPASSWD_REALM", "local-registry") + self.with_env( + "REGISTRY_AUTH_HTPASSWD_PATH", f"/htpasswd/{tmp_file}" + ) + + super().start() + return self + + def stop(self, **kwargs): + super().stop(**kwargs) + # Remove the password file + if self.htpasswd_file: + self.htpasswd_file.unlink() + + def get_registry(self) -> str: + host = self.get_container_host_ip() + port = self.get_exposed_port(self.port_to_expose) + return f"{host}:{port}" From 76348830df328fa405cb0df3aef6b8cb26d6e6bc Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Sun, 21 May 2023 23:16:49 +0200 Subject: [PATCH 04/10] Code formatting and added a pylint exception --- build/constants.py | 18 +++++++++--------- build/images.py | 4 +--- build/publish.py | 10 +++++----- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/build/constants.py b/build/constants.py index fe9984f..e2d6ac7 100644 --- a/build/constants.py +++ b/build/constants.py @@ -5,19 +5,19 @@ "poetry1.3.2-python3.10.11-bullseye", "poetry1.3.2-python3.10.11-slim-bullseye", "poetry1.3.2-python3.11.3-bullseye", - "poetry1.3.2-python3.11.3-slim-bullseye", + "poetry1.3.2-python3.11.3-slim-bullseye", "poetry1.4.2-python3.9.16-bullseye", "poetry1.4.2-python3.9.16-slim-bullseye", "poetry1.4.2-python3.10.11-bullseye", "poetry1.4.2-python3.10.11-slim-bullseye", "poetry1.4.2-python3.11.3-bullseye", - "poetry1.4.2-python3.11.3-slim-bullseye", + "poetry1.4.2-python3.11.3-slim-bullseye", "poetry1.5.0-python3.9.16-bullseye", "poetry1.5.0-python3.9.16-slim-bullseye", "poetry1.5.0-python3.10.11-bullseye", "poetry1.5.0-python3.10.11-slim-bullseye", "poetry1.5.0-python3.11.3-bullseye", - "poetry1.5.0-python3.11.3-slim-bullseye", + "poetry1.5.0-python3.11.3-slim-bullseye", ] BASE_IMAGES: dict = { TARGET_ARCHITECTURES[ @@ -37,7 +37,7 @@ ]: "python:3.11.3-bullseye@sha256:89cbc1829d74f72436c96302c49218291eb464705c726cc27d71c32fec1d9082", TARGET_ARCHITECTURES[ 5 - ]: "python:3.11.3-slim-bullseye@sha256:551c9529e77896518ac5693d7e98ee5e12051d625de450ac2a68da1eae15ec87", + ]: "python:3.11.3-slim-bullseye@sha256:551c9529e77896518ac5693d7e98ee5e12051d625de450ac2a68da1eae15ec87", TARGET_ARCHITECTURES[ 6 ]: "python:3.9.16-bullseye@sha256:b8ddeb68904299c09a39aff59d4a713862253b137fdd7ace3a3b7ba0391971b1", @@ -55,7 +55,7 @@ ]: "python:3.11.3-bullseye@sha256:89cbc1829d74f72436c96302c49218291eb464705c726cc27d71c32fec1d9082", TARGET_ARCHITECTURES[ 11 - ]: "python:3.11.3-slim-bullseye@sha256:551c9529e77896518ac5693d7e98ee5e12051d625de450ac2a68da1eae15ec87", + ]: "python:3.11.3-slim-bullseye@sha256:551c9529e77896518ac5693d7e98ee5e12051d625de450ac2a68da1eae15ec87", TARGET_ARCHITECTURES[ 12 ]: "python:3.9.16-bullseye@sha256:b8ddeb68904299c09a39aff59d4a713862253b137fdd7ace3a3b7ba0391971b1", @@ -73,7 +73,7 @@ ]: "python:3.11.3-bullseye@sha256:89cbc1829d74f72436c96302c49218291eb464705c726cc27d71c32fec1d9082", TARGET_ARCHITECTURES[ 17 - ]: "python:3.11.3-slim-bullseye@sha256:551c9529e77896518ac5693d7e98ee5e12051d625de450ac2a68da1eae15ec87", + ]: "python:3.11.3-slim-bullseye@sha256:551c9529e77896518ac5693d7e98ee5e12051d625de450ac2a68da1eae15ec87", } PYTHON_VERSIONS: dict = { TARGET_ARCHITECTURES[0]: "3.9.16", @@ -87,13 +87,13 @@ TARGET_ARCHITECTURES[8]: "3.10.11", TARGET_ARCHITECTURES[9]: "3.10.11", TARGET_ARCHITECTURES[10]: "3.11.3", - TARGET_ARCHITECTURES[11]: "3.11.3", + TARGET_ARCHITECTURES[11]: "3.11.3", TARGET_ARCHITECTURES[12]: "3.9.16", TARGET_ARCHITECTURES[13]: "3.9.16", TARGET_ARCHITECTURES[14]: "3.10.11", TARGET_ARCHITECTURES[15]: "3.10.11", TARGET_ARCHITECTURES[16]: "3.11.3", - TARGET_ARCHITECTURES[17]: "3.11.3", + TARGET_ARCHITECTURES[17]: "3.11.3", } POETRY_VERSIONS: dict = { TARGET_ARCHITECTURES[0]: "1.3.2", @@ -113,5 +113,5 @@ TARGET_ARCHITECTURES[14]: "1.5.0", TARGET_ARCHITECTURES[15]: "1.5.0", TARGET_ARCHITECTURES[16]: "1.5.0", - TARGET_ARCHITECTURES[17]: "1.5.0", + TARGET_ARCHITECTURES[17]: "1.5.0", } diff --git a/build/images.py b/build/images.py index 7944430..6baaaeb 100644 --- a/build/images.py +++ b/build/images.py @@ -40,9 +40,7 @@ def __init__( self.image_name = PYTHON_POETRY_IMAGE_NAME def build(self) -> Image: - self.image_tag = ( - f"{self.version}-{self.target_architecture}" - ) + self.image_tag = f"{self.version}-{self.target_architecture}" buildargs: dict[str, str] = { "OFFICIAL_PYTHON_IMAGE": BASE_IMAGES[self.target_architecture], diff --git a/build/publish.py b/build/publish.py index cdc3e46..5110934 100644 --- a/build/publish.py +++ b/build/publish.py @@ -8,6 +8,7 @@ from docker.client import DockerClient + @click.command() @click.option( "--docker-hub-username", @@ -55,16 +56,14 @@ def main( } if registry: login_kwargs["registry"] = registry - + docker_client.login(**login_kwargs) if registry: - repository: str = ( - f"{registry}/{new_python_poetry_image.image_name}" - ) + repository: str = f"{registry}/{new_python_poetry_image.image_name}" else: repository: str = new_python_poetry_image.image_name - + for line in docker_client.images.push( repository, tag=new_python_poetry_image.image_tag, @@ -76,4 +75,5 @@ def main( if __name__ == "__main__": + # pylint: disable=no-value-for-parameter main() From 1747e7b0ddac02e61ee8f789285d078d7c9b7433 Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Sun, 21 May 2023 23:30:33 +0200 Subject: [PATCH 05/10] Fixed issue in build tests, updated pre commit config --- .pre-commit-config.yaml | 3 +++ tests/build_image/conftest.py | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e6bebb..b80a5d4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,3 +17,6 @@ repos: entry: pylint language: system types: [python] + args: + - build + - tests diff --git a/tests/build_image/conftest.py b/tests/build_image/conftest.py index 13282ae..5656014 100644 --- a/tests/build_image/conftest.py +++ b/tests/build_image/conftest.py @@ -12,9 +12,7 @@ def python_poetry_image( ) -> str: target_architecture: str = request.param - python_poetry_image: Image = PythonPoetryImage(docker_client).build( - target_architecture, version=version - ) + python_poetry_image: Image = PythonPoetryImage(docker_client, target_architecture, version).build() image_tag: str = python_poetry_image.tags[0] yield image_tag docker_client.images.remove(image_tag, force=True) From 76fe4f05c7358e3ae2f34d9fe8f77863d4c61a5b Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Mon, 22 May 2023 21:11:24 +0200 Subject: [PATCH 06/10] Code formatting --- tests/build_image/conftest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/build_image/conftest.py b/tests/build_image/conftest.py index 5656014..6e9cea4 100644 --- a/tests/build_image/conftest.py +++ b/tests/build_image/conftest.py @@ -12,7 +12,9 @@ def python_poetry_image( ) -> str: target_architecture: str = request.param - python_poetry_image: Image = PythonPoetryImage(docker_client, target_architecture, version).build() + python_poetry_image: Image = PythonPoetryImage( + docker_client, target_architecture, version + ).build() image_tag: str = python_poetry_image.tags[0] yield image_tag docker_client.images.remove(image_tag, force=True) From 41971f421b4887b48522fafc70522bba0c36564e Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Tue, 23 May 2023 00:14:02 +0200 Subject: [PATCH 07/10] Fixed test scopes and adjusted pre commit config --- .pre-commit-config.yaml | 7 ++++--- tests/build_image/conftest.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b80a5d4..c847cc6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,22 +1,23 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.4.0 hooks: - id: check-ast - id: check-merge-conflict - id: detect-private-key - id: check-added-large-files - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.3.0 hooks: - id: black - repo: local hooks: - id: pylint name: pylint - entry: pylint + entry: .venv/bin/pylint language: system types: [python] args: - build - tests + \ No newline at end of file diff --git a/tests/build_image/conftest.py b/tests/build_image/conftest.py index 6e9cea4..17b9399 100644 --- a/tests/build_image/conftest.py +++ b/tests/build_image/conftest.py @@ -6,7 +6,7 @@ from build.images import PythonPoetryImage -@pytest.fixture(scope="session", params=TARGET_ARCHITECTURES) +@pytest.fixture(scope="package", params=TARGET_ARCHITECTURES) def python_poetry_image( docker_client: DockerClient, version: str, request ) -> str: From 59f71c124de1054eec377af9917e0197f80b92fb Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Tue, 23 May 2023 21:07:53 +0200 Subject: [PATCH 08/10] Fixed pipeline Tests were executed two times. --- .github/workflows/pipeline.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index de999db..0da6024 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -52,10 +52,9 @@ jobs: run: | source .venv/bin/activate export DOCKER_BUILDKIT=1 - pytest - pytest --cov=./ --cov-report=xml + pytest --cov - name: Upload coverage to Codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} From 2655d18ac9efb021ed8aa08a0d8cffd1de9d3bee Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Tue, 23 May 2023 21:34:20 +0200 Subject: [PATCH 09/10] Added caching of virtual environment to pipeline This will speed up the pipeline a bit. Also fixed some minor issues there. --- .github/workflows/pipeline.yml | 40 ++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 0da6024..b0aae65 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -17,10 +17,16 @@ jobs: with: version: 1.4.1 virtualenvs-in-project: true + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: | - poetry config virtualenvs.in-project true - poetry install --no-root + poetry install --no-interaction --no-root - name: Run pylint and black run: | source .venv/bin/activate @@ -33,8 +39,10 @@ jobs: steps: - name: Checkout Repository uses: actions/checkout@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.9 - name: Install Poetry @@ -42,12 +50,16 @@ jobs: with: version: 1.4.1 virtualenvs-in-project: true - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: | - poetry config virtualenvs.in-project true - poetry install --no-root + poetry install --no-interaction --no-root - name: Run all tests with pytest run: | source .venv/bin/activate @@ -68,6 +80,8 @@ jobs: uses: actions/checkout@v3 - name: Get Git Commit Tag Name uses: olegtarasov/get-tag@v2.1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 - name: Setup Python uses: actions/setup-python@v4 with: @@ -77,12 +91,16 @@ jobs: with: version: 1.4.1 virtualenvs-in-project: true - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: | - poetry config virtualenvs.in-project true - poetry install --no-root + poetry install --no-interaction --no-root - name: Publish Image to Docker Hub env: DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }} From 8501c23ac51a00489c296b311dcc84ad40b24760 Mon Sep 17 00:00:00 2001 From: Max Pfeiffer Date: Tue, 23 May 2023 23:26:42 +0200 Subject: [PATCH 10/10] Removed obsolet from pipeline --- .github/workflows/pipeline.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index b0aae65..b4f21ea 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -63,7 +63,6 @@ jobs: - name: Run all tests with pytest run: | source .venv/bin/activate - export DOCKER_BUILDKIT=1 pytest --cov - name: Upload coverage to Codecov uses: codecov/codecov-action@v3