Jaypore CI

> Jaypore CI: Minimal, Offline, Local CI system.
Log | Files | Refs | README

commit 8d52f98134c3069733081828f707d3f95c32d2e6
parent eba6687076924afb065aa98d7afcf38f15378781
Author: arjoonn <arjoonn@noreply.localhost>
Date:   Fri, 24 Feb 2023 03:57:49 +0000

Use python docker client instead of binary install (!51)

Branch auto created by JayporeCI

```jayporeci
╔ 🟢 : JayporeCI       [sha b1eb47c663]
┏━ build_and_test
┃
┃ 🟢 : JciEnv          [eaf81975]   0:32
┃ 🟢 : Jci             [9022a0e6]   0: 9            ❮-- ['JciEnv']
┃ 🟢 : black           [dc689d56]   0: 0            ❮-- ['JciEnv']
┃ 🟢 : pylint          [c7cd0e9e]   0: 7            ❮-- ['JciEnv']
┃ 🟢 : pytest          [752cfbd8]   1: 3 Cov: 87%   ❮-- ['JciEnv']
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Publish
┃
┃ 🟢 : DockerHubJci    [583c4c69]   5:38
┃ 🟢 : DockerHubJcienv [58baae70]   1: 8
┃ 🟢 : PublishDocs     [9bf44ae9]   0:18
┃ 🟢 : PublishPypi     [fbbe778b]   0: 6 v0.2.19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
```

Co-authored-by: arjoonn sharma <arjoonn.94@gmail.com>
Reviewed-on: https://gitea.midpathsoftware.com/midpath/jaypore_ci/pulls/51

Diffstat:
MDockerfile | 2--
Mcicd/run_tests.sh | 2+-
Mdocs/source/conf.py | 2+-
Mjaypore_ci/executors/__init__.py | 1-
Mjaypore_ci/executors/docker.py | 117+++++++++++++++++++++++++++++++++----------------------------------------------
Djaypore_ci/executors/mock.py | 138-------------------------------------------------------------------------------
Mjaypore_ci/jci.py | 5+----
Mjaypore_ci/remotes/git.py | 4++--
Mjaypore_ci/reporters/__init__.py | 1-
Djaypore_ci/reporters/mock.py | 6------
Mjaypore_ci/repos/__init__.py | 1-
Djaypore_ci/repos/mock.py | 20--------------------
Mpoetry.lock | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mpyproject.toml | 3++-
Mtests/conftest.py | 18+++++-------------
Atests/docker_mock.py | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/subprocess_mock.py | 43+++++++++++++++++++++++++++++++++++++++++++
17 files changed, 324 insertions(+), 320 deletions(-)

diff --git a/Dockerfile b/Dockerfile @@ -1,7 +1,5 @@ from python:3.11 as jcienv workdir /app -add cicd/install_docker.sh . -run bash install_docker.sh run python3 -m pip install --upgrade pip run python3 -m pip install poetry add pyproject.toml . diff --git a/cicd/run_tests.sh b/cicd/run_tests.sh @@ -6,7 +6,7 @@ set -o pipefail main() { - python -m coverage run --branch --source=. -m pytest -vv + python -m coverage run --branch --source=. -m pytest -xl --full-trace -vv coverage html coverage report echo "Cov: $(coverage report --format=total)%" > "/jaypore_ci/run/pytest.txt" diff --git a/docs/source/conf.py b/docs/source/conf.py @@ -34,7 +34,7 @@ html_sidebars = { html_theme = "alabaster" html_static_path = ["_static"] html_theme_options = { - "logo": "logo.png", + "nosidebar": True, "logo_name": "Jaypore CI", "touch_icon": "logo.png", "github_user": "theSage21", diff --git a/jaypore_ci/executors/__init__.py b/jaypore_ci/executors/__init__.py @@ -1,2 +1 @@ -from .mock import Mock from .docker import Docker diff --git a/jaypore_ci/executors/docker.py b/jaypore_ci/executors/docker.py @@ -1,30 +1,14 @@ """ A docker executor for Jaypore CI. """ -import json -import subprocess - import pendulum +import docker from rich import print as rprint from jaypore_ci.interfaces import Executor, TriggerFailed, JobStatus from jaypore_ci.logging import logger -def __check_output__(cmd): - """ - Common arguments that need to be provided while - calling subprocess.check_output - """ - proc = subprocess.run( - cmd, check=False, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE - ) - if proc.returncode != 0: - print(proc.stdout.decode()) - raise TriggerFailed(cmd) - return proc.stdout.decode().strip() - - class Docker(Executor): """ Run jobs via docker. @@ -39,6 +23,9 @@ class Docker(Executor): super().__init__() self.pipe_id = None self.pipeline = None + self.docker = docker.from_env() + self.client = docker.APIClient() + self.__execution_order__ = [] def logging(self): """ @@ -79,22 +66,13 @@ class Docker(Executor): """ assert self.pipe_id is not None, "Cannot create network if pipe is not set" for _ in range(3): - net_ls = subprocess.run( - f"docker network ls | grep {self.get_net()}", - shell=True, - check=False, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - if net_ls.returncode == 0: - self.logging().info( - "Found network", network_name=self.get_net(), subprocess=net_ls - ) - return net_ls + if len(self.docker.networks.list(names=[self.get_net()])) != 0: + self.logging().info("Found network", network_name=self.get_net()) + return self.logging().info( "Create network", - subprocess=__check_output__( - f"docker network create -d bridge {self.get_net()}" + subprocess=self.docker.networks.create( + name=self.get_net(), driver="bridge" ), ) raise TriggerFailed("Cannot create network") @@ -110,10 +88,9 @@ class Docker(Executor): job = None for job in self.pipeline.jobs.values(): if job.run_id is not None and not job.run_id.startswith("pyrun_"): - self.logging().info( - "Stop job:", - subprocess=__check_output__(f"docker stop -t 1 {job.run_id}"), - ) + container = self.docker.containers.get(job.run_id) + container.stop(timeout=1) + self.logging().info("Stop job:", run_id=job.run_id) job.check_job(with_update_report=False) if job is not None: job.check_job() @@ -124,14 +101,13 @@ class Docker(Executor): Delete the network for this executor. """ assert self.pipe_id is not None, "Cannot delete network if pipe is not set" - self.logging().info( - "Delete network", - subprocess=__check_output__( - f"docker network rm {self.get_net()} || echo 'No such net'" - ), - ) + try: + net = self.docker.networks.get(self.get_net()) + net.remove() + except docker.errors.NotFound: + self.logging().error("Delete network: Not found", netid=self.get_net()) - def get_job_name(self, job): + def get_job_name(self, job, tail=False): """ Generates a clean job name slug. """ @@ -139,6 +115,8 @@ class Docker(Executor): l if l in "abcdefghijklmnopqrstuvwxyz1234567890" else "-" for l in job.name.lower() ) + if tail: + return name return f"jayporeci__job__{self.pipe_id}__{name}" def run(self, job: "Job") -> str: @@ -147,39 +125,39 @@ class Docker(Executor): In case something goes wrong it will raise TriggerFailed """ assert self.pipe_id is not None, "Cannot run job if pipe id is not set" - env_vars = [f"--env {key}={val}" for key, val in job.get_env().items()] - trigger = [ - "docker run -d", - "-v /var/run/docker.sock:/var/run/docker.sock", - "-v /usr/bin/docker:/usr/bin/docker:ro", - f"-v /tmp/jayporeci__src__{self.pipeline.remote.sha}:/jaypore_ci/run", - *["--workdir /jaypore_ci/run" if not job.is_service else None], - f"--name {self.get_job_name(job)}", - f"--network {self.get_net()}", - *env_vars, - job.image, - job.command if not job.is_service else None, - ] + trigger = { + "detach": True, + "environment": job.get_env(), + "volumes": [ + "/var/run/docker.sock:/var/run/docker.sock", + "/usr/bin/docker:/usr/bin/docker:ro", + f"/tmp/jayporeci__src__{self.pipeline.remote.sha}:/jaypore_ci/run", + ], + "name": self.get_job_name(job), + "network": self.get_net(), + "image": job.image, + "command": job.command if not job.is_service else None, + } + if not job.is_service: + trigger["working_dir"] = "/jaypore_ci/run" if not job.is_service: assert job.command rprint(trigger) - trigger = " ".join(t for t in trigger if t is not None) - run_job = subprocess.run( - trigger, - shell=True, - check=False, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - if run_job.returncode == 0: - return run_job.stdout.decode().strip() - raise TriggerFailed(run_job) + try: + container = self.docker.containers.run(**trigger) + self.__execution_order__.append( + (self.get_job_name(job, tail=True), container.id, "Run") + ) + return container.id + except docker.errors.APIError as e: + self.logging().exception(e) + raise TriggerFailed(e) from e def get_status(self, run_id: str) -> JobStatus: """ Given a run_id, it will get the status for that run. """ - inspect = json.loads(__check_output__(f"docker inspect {run_id}"))[0] + inspect = self.client.inspect_container(run_id) status = JobStatus( is_running=inspect["State"]["Running"], exit_code=int(inspect["State"]["ExitCode"]), @@ -191,5 +169,8 @@ class Docker(Executor): ) # --- logs self.logging().debug("Check status", status=status) - logs = __check_output__(f"docker logs {run_id}") + logs = self.docker.containers.get(run_id).logs().decode() return status._replace(logs=logs) + + def get_execution_order(self): + return {name: i for i, (name, *_) in enumerate(self.__execution_order__)} diff --git a/jaypore_ci/executors/mock.py b/jaypore_ci/executors/mock.py @@ -1,138 +0,0 @@ -""" -A mock executor that actually does not run anything. -""" -import uuid - -from jaypore_ci.interfaces import Executor, JobStatus -from jaypore_ci.logging import logger - - -class Mock(Executor): - """ - Run jobs via docker. - - This will: - - Create a separate network for each run - - Run jobs as part of the network - - Clean up all jobs when the pipeline exits. - """ - - def __init__(self): - super().__init__() - self.pipe_id = None - self.pipeline = None - self.__log__ = [] - self.__status__ = {} - - def logging(self): - """ - Returns a logging instance that has executor specific - information bound to it. - """ - return logger.bind(pipe_id=self.pipe_id, network_name=self.get_net()) - - def set_pipeline(self, pipeline): - """ - Set executor's pipeline to the given one. - - This will clean up old networks and create new ones. - """ - if self.pipe_id is not None: - self.delete_network() - self.delete_all_jobs() - self.pipe_id = id(pipeline) - self.pipeline = pipeline - self.create_network() - - def __exit__(self, exc_type, exc_value, traceback): - self.delete_network() - self.delete_all_jobs() - - def get_net(self): - """ - Return a network name based on what the curent pipeline is. - """ - return f"jaypore_{self.pipe_id}" if self.pipe_id is not None else None - - def create_network(self): - """ - Will create a docker network. - - If it fails to do so in 3 attempts it will raise an - exception and fail. - """ - assert self.pipe_id is not None, "Cannot create network if pipe is not set" - return self.get_net() - - def delete_all_jobs(self): - """ - Deletes all jobs associated with the pipeline for this - executor. - - It will stop any jobs that are still running. - """ - assert self.pipe_id is not None, "Cannot delete jobs if pipe is not set" - job = None - for job in self.pipeline.jobs.values(): - if job.run_id is not None and not job.run_id.startswith("pyrun_"): - self.logging().info("Stop job:", run_id=job.run_id) - job.check_job(with_update_report=False) - if job is not None: - job.check_job() - self.logging().info("All jobs stopped") - - def delete_network(self): - """ - Delete the network for this executor. - """ - assert self.pipe_id is not None, "Cannot delete network if pipe is not set" - self.logging().info("Delete network", net=self.get_net()) - - def get_job_name(self, job): - """ - Generates a clean job name slug. - """ - name = "".join( - l - for l in job.name.lower().replace(" ", "_") - if l in "abcdefghijklmnopqrstuvwxyz_1234567890" - ) - return name - - def run(self, job: "Job") -> str: - """ - Run the given job and return a docker container ID. - """ - assert self.pipe_id is not None, "Cannot run job if pipe id is not set" - self.pipe_id = id(job.pipeline) if self.pipe_id is None else self.pipe_id - if not job.is_service: - assert job.command - name = self.get_job_name(job) - if name in self.__status__: - return None - run_id = uuid.uuid4().hex - self.__log__.append((name, run_id, "Run")) - self.__status__[name] = self.__status__[run_id] = True - self.logging().info( - "Run job", - run_id=run_id, - env_vars=job.get_env(), - is_service=job.is_service, - name=self.get_job_name(job), - net=self.get_net(), - image=job.image, - command=job.command if not job.is_service else None, - ) - return run_id - - def get_status(self, run_id: str) -> JobStatus: - """ - Given a run_id, it will get the status for that run. - """ - status = JobStatus(True, None, "", None, None) - if run_id in self.__status__: - status = JobStatus(False, 0, "fake logs", None, None) - return status - - def get_execution_order(self): - return {name: i for i, (name, *log) in enumerate(self.__log__)} diff --git a/jaypore_ci/jci.py b/jaypore_ci/jci.py @@ -149,14 +149,11 @@ class Job: # pylint: disable=too-many-instance-attributes self.run_id = self.pipeline.executor.run(self) self.logging().info("Trigger done") except TriggerFailed as e: - job_run = e.args[0] self.logging().error( "Trigger failed", - returncode=job_run.returncode, + error=e, job_name=self.name, ) - logs = job_run.stdout.decode() - self.logs["stdout"] = reporters.clean_logs(logs) self.status = Status.FAILED else: self.logging().info("Trigger called but job already running") diff --git a/jaypore_ci/remotes/git.py b/jaypore_ci/remotes/git.py @@ -5,7 +5,7 @@ import time import subprocess from jaypore_ci.interfaces import Remote -from jaypore_ci.repos import Git, Mock +from jaypore_ci.repos import Git from jaypore_ci.logging import logger @@ -26,7 +26,7 @@ class GitRemote(Remote): # pylint: disable=too-many-instance-attributes """ Creates a remote instance from the environment. """ - assert isinstance(repo, (Git, Mock)), "Git remote can only work in a git repo" + assert isinstance(repo, Git), "Git remote can only work in a git repo" return cls( repo=repo, branch=repo.branch, diff --git a/jaypore_ci/reporters/__init__.py b/jaypore_ci/reporters/__init__.py @@ -1,4 +1,3 @@ from .common import clean_logs from .markdown import Markdown -from .mock import Mock from .text import Text diff --git a/jaypore_ci/reporters/mock.py b/jaypore_ci/reporters/mock.py @@ -1,6 +0,0 @@ -from jaypore_ci.interfaces import Reporter - - -class Mock(Reporter): - def render(self, pipeline): - return f"{pipeline}" diff --git a/jaypore_ci/repos/__init__.py b/jaypore_ci/repos/__init__.py @@ -1,2 +1 @@ from .git import Git -from .mock import Mock diff --git a/jaypore_ci/repos/mock.py b/jaypore_ci/repos/mock.py @@ -1,20 +0,0 @@ -from typing import List - -from jaypore_ci.interfaces import Repo - - -class Mock(Repo): - def __init__(self, *, files_changed, **kwargs): - super().__init__(**kwargs) - self.files_changed = files_changed - - def files_changed(self, target: str) -> List[str]: - "Returns list of files changed between current sha and target" - return self.files_changed - - @classmethod - def from_env(cls, **kwargs) -> "Mock": - """ - Save whatever is provided to kwargs - """ - return cls(**kwargs) diff --git a/poetry.lock b/poetry.lock @@ -241,63 +241,63 @@ files = [ [[package]] name = "coverage" -version = "7.1.0" +version = "7.2.0" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "coverage-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b946bbcd5a8231383450b195cfb58cb01cbe7f8949f5758566b881df4b33baf"}, - {file = "coverage-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec8e767f13be637d056f7e07e61d089e555f719b387a7070154ad80a0ff31801"}, - {file = "coverage-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a5a5879a939cb84959d86869132b00176197ca561c664fc21478c1eee60d75"}, - {file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b643cb30821e7570c0aaf54feaf0bfb630b79059f85741843e9dc23f33aaca2c"}, - {file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32df215215f3af2c1617a55dbdfb403b772d463d54d219985ac7cd3bf124cada"}, - {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:33d1ae9d4079e05ac4cc1ef9e20c648f5afabf1a92adfaf2ccf509c50b85717f"}, - {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:29571503c37f2ef2138a306d23e7270687c0efb9cab4bd8038d609b5c2393a3a"}, - {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:63ffd21aa133ff48c4dff7adcc46b7ec8b565491bfc371212122dd999812ea1c"}, - {file = "coverage-7.1.0-cp310-cp310-win32.whl", hash = "sha256:4b14d5e09c656de5038a3f9bfe5228f53439282abcab87317c9f7f1acb280352"}, - {file = "coverage-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:8361be1c2c073919500b6601220a6f2f98ea0b6d2fec5014c1d9cfa23dd07038"}, - {file = "coverage-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:da9b41d4539eefd408c46725fb76ecba3a50a3367cafb7dea5f250d0653c1040"}, - {file = "coverage-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5b15ed7644ae4bee0ecf74fee95808dcc34ba6ace87e8dfbf5cb0dc20eab45a"}, - {file = "coverage-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d12d076582507ea460ea2a89a8c85cb558f83406c8a41dd641d7be9a32e1274f"}, - {file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2617759031dae1bf183c16cef8fcfb3de7617f394c813fa5e8e46e9b82d4222"}, - {file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4e4881fa9e9667afcc742f0c244d9364d197490fbc91d12ac3b5de0bf2df146"}, - {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9d58885215094ab4a86a6aef044e42994a2bd76a446dc59b352622655ba6621b"}, - {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ffeeb38ee4a80a30a6877c5c4c359e5498eec095878f1581453202bfacc8fbc2"}, - {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3baf5f126f30781b5e93dbefcc8271cb2491647f8283f20ac54d12161dff080e"}, - {file = "coverage-7.1.0-cp311-cp311-win32.whl", hash = "sha256:ded59300d6330be27bc6cf0b74b89ada58069ced87c48eaf9344e5e84b0072f7"}, - {file = "coverage-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:6a43c7823cd7427b4ed763aa7fb63901ca8288591323b58c9cd6ec31ad910f3c"}, - {file = "coverage-7.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a726d742816cb3a8973c8c9a97539c734b3a309345236cd533c4883dda05b8d"}, - {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc7c85a150501286f8b56bd8ed3aa4093f4b88fb68c0843d21ff9656f0009d6a"}, - {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b4198d85a3755d27e64c52f8c95d6333119e49fd001ae5798dac872c95e0f8"}, - {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb726cb861c3117a553f940372a495fe1078249ff5f8a5478c0576c7be12050"}, - {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51b236e764840a6df0661b67e50697aaa0e7d4124ca95e5058fa3d7cbc240b7c"}, - {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7ee5c9bb51695f80878faaa5598040dd6c9e172ddcf490382e8aedb8ec3fec8d"}, - {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c31b75ae466c053a98bf26843563b3b3517b8f37da4d47b1c582fdc703112bc3"}, - {file = "coverage-7.1.0-cp37-cp37m-win32.whl", hash = "sha256:3b155caf3760408d1cb903b21e6a97ad4e2bdad43cbc265e3ce0afb8e0057e73"}, - {file = "coverage-7.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2a60d6513781e87047c3e630b33b4d1e89f39836dac6e069ffee28c4786715f5"}, - {file = "coverage-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2cba5c6db29ce991029b5e4ac51eb36774458f0a3b8d3137241b32d1bb91f06"}, - {file = "coverage-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beeb129cacea34490ffd4d6153af70509aa3cda20fdda2ea1a2be870dfec8d52"}, - {file = "coverage-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c45948f613d5d18c9ec5eaa203ce06a653334cf1bd47c783a12d0dd4fd9c851"}, - {file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef382417db92ba23dfb5864a3fc9be27ea4894e86620d342a116b243ade5d35d"}, - {file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c7c0d0827e853315c9bbd43c1162c006dd808dbbe297db7ae66cd17b07830f0"}, - {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e5cdbb5cafcedea04924568d990e20ce7f1945a1dd54b560f879ee2d57226912"}, - {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9817733f0d3ea91bea80de0f79ef971ae94f81ca52f9b66500c6a2fea8e4b4f8"}, - {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:218fe982371ac7387304153ecd51205f14e9d731b34fb0568181abaf7b443ba0"}, - {file = "coverage-7.1.0-cp38-cp38-win32.whl", hash = "sha256:04481245ef966fbd24ae9b9e537ce899ae584d521dfbe78f89cad003c38ca2ab"}, - {file = "coverage-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8ae125d1134bf236acba8b83e74c603d1b30e207266121e76484562bc816344c"}, - {file = "coverage-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2bf1d5f2084c3932b56b962a683074a3692bce7cabd3aa023c987a2a8e7612f6"}, - {file = "coverage-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:98b85dd86514d889a2e3dd22ab3c18c9d0019e696478391d86708b805f4ea0fa"}, - {file = "coverage-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38da2db80cc505a611938d8624801158e409928b136c8916cd2e203970dde4dc"}, - {file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3164d31078fa9efe406e198aecd2a02d32a62fecbdef74f76dad6a46c7e48311"}, - {file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db61a79c07331e88b9a9974815c075fbd812bc9dbc4dc44b366b5368a2936063"}, - {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ccb092c9ede70b2517a57382a601619d20981f56f440eae7e4d7eaafd1d1d09"}, - {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:33ff26d0f6cc3ca8de13d14fde1ff8efe1456b53e3f0273e63cc8b3c84a063d8"}, - {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d47dd659a4ee952e90dc56c97d78132573dc5c7b09d61b416a9deef4ebe01a0c"}, - {file = "coverage-7.1.0-cp39-cp39-win32.whl", hash = "sha256:d248cd4a92065a4d4543b8331660121b31c4148dd00a691bfb7a5cdc7483cfa4"}, - {file = "coverage-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7ed681b0f8e8bcbbffa58ba26fcf5dbc8f79e7997595bf071ed5430d8c08d6f3"}, - {file = "coverage-7.1.0-pp37.pp38.pp39-none-any.whl", hash = "sha256:755e89e32376c850f826c425ece2c35a4fc266c081490eb0a841e7c1cb0d3bda"}, - {file = "coverage-7.1.0.tar.gz", hash = "sha256:10188fe543560ec4874f974b5305cd1a8bdcfa885ee00ea3a03733464c4ca265"}, + {file = "coverage-7.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90e7a4cbbb7b1916937d380beb1315b12957b8e895d7d9fb032e2038ac367525"}, + {file = "coverage-7.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:34d7211be69b215ad92298a962b2cd5a4ef4b17c7871d85e15d3d1b6dc8d8c96"}, + {file = "coverage-7.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971b49dbf713044c3e5f6451b39f65615d4d1c1d9a19948fa0f41b0245a98765"}, + {file = "coverage-7.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0557289260125a6c453ad5673ba79e5b6841d9a20c9e101f758bfbedf928a77"}, + {file = "coverage-7.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:049806ae2df69468c130f04f0fab4212c46b34ba5590296281423bb1ae379df2"}, + {file = "coverage-7.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:875b03d92ac939fbfa8ae74a35b2c468fc4f070f613d5b1692f9980099a3a210"}, + {file = "coverage-7.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c160e34e388277f10c50dc2c7b5e78abe6d07357d9fe7fcb2f3c156713fd647e"}, + {file = "coverage-7.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:32e6a730fd18b2556716039ab93278ccebbefa1af81e6aa0c8dba888cf659e6e"}, + {file = "coverage-7.2.0-cp310-cp310-win32.whl", hash = "sha256:f3ff4205aff999164834792a3949f82435bc7c7655c849226d5836c3242d7451"}, + {file = "coverage-7.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:93db11da6e728587e943dff8ae1b739002311f035831b6ecdb15e308224a4247"}, + {file = "coverage-7.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cd38140b56538855d3d5722c6d1b752b35237e7ea3f360047ce57f3fade82d98"}, + {file = "coverage-7.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9dbb21561b0e04acabe62d2c274f02df0d715e8769485353ddf3cf84727e31ce"}, + {file = "coverage-7.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:171dd3aa71a49274a7e4fc26f5bc167bfae5a4421a668bc074e21a0522a0af4b"}, + {file = "coverage-7.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4655ecd813f4ba44857af3e9cffd133ab409774e9d2a7d8fdaf4fdfd2941b789"}, + {file = "coverage-7.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1856a8c4aa77eb7ca0d42c996d0ca395ecafae658c1432b9da4528c429f2575c"}, + {file = "coverage-7.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd67df6b48db18c10790635060858e2ea4109601e84a1e9bfdd92e898dc7dc79"}, + {file = "coverage-7.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2d7daf3da9c7e0ed742b3e6b4de6cc464552e787b8a6449d16517b31bbdaddf5"}, + {file = "coverage-7.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf9e02bc3dee792b9d145af30db8686f328e781bd212fdef499db5e9e4dd8377"}, + {file = "coverage-7.2.0-cp311-cp311-win32.whl", hash = "sha256:3713a8ec18781fda408f0e853bf8c85963e2d3327c99a82a22e5c91baffcb934"}, + {file = "coverage-7.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:88ae5929f0ef668b582fd7cad09b5e7277f50f912183cf969b36e82a1c26e49a"}, + {file = "coverage-7.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5e29a64e9586194ea271048bc80c83cdd4587830110d1e07b109e6ff435e5dbc"}, + {file = "coverage-7.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d5302eb84c61e758c9d68b8a2f93a398b272073a046d07da83d77b0edc8d76b"}, + {file = "coverage-7.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c9fffbc39dc4a6277e1525cab06c161d11ee3995bbc97543dc74fcec33e045b"}, + {file = "coverage-7.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6ceeab5fca62bca072eba6865a12d881f281c74231d2990f8a398226e1a5d96"}, + {file = "coverage-7.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:28563a35ef4a82b5bc5160a01853ce62b9fceee00760e583ffc8acf9e3413753"}, + {file = "coverage-7.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bfa065307667f1c6e1f4c3e13f415b0925e34e56441f5fda2c84110a4a1d8bda"}, + {file = "coverage-7.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7f992b32286c86c38f07a8b5c3fc88384199e82434040a729ec06b067ee0d52c"}, + {file = "coverage-7.2.0-cp37-cp37m-win32.whl", hash = "sha256:2c15bd09fd5009f3a79c8b3682b52973df29761030b692043f9834fc780947c4"}, + {file = "coverage-7.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f332d61fbff353e2ef0f3130a166f499c3fad3a196e7f7ae72076d41a6bfb259"}, + {file = "coverage-7.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:577a8bc40c01ad88bb9ab1b3a1814f2f860ff5c5099827da2a3cafc5522dadea"}, + {file = "coverage-7.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9240a0335365c29c968131bdf624bb25a8a653a9c0d8c5dbfcabf80b59c1973c"}, + {file = "coverage-7.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:358d3bce1468f298b19a3e35183bdb13c06cdda029643537a0cc37e55e74e8f1"}, + {file = "coverage-7.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:932048364ff9c39030c6ba360c31bf4500036d4e15c02a2afc5a76e7623140d4"}, + {file = "coverage-7.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7efa21611ffc91156e6f053997285c6fe88cfef3fb7533692d0692d2cb30c846"}, + {file = "coverage-7.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:465ea431c3b78a87e32d7d9ea6d081a1003c43a442982375cf2c247a19971961"}, + {file = "coverage-7.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0f03c229f1453b936916f68a47b3dfb5e84e7ad48e160488168a5e35115320c8"}, + {file = "coverage-7.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:40785553d68c61e61100262b73f665024fd2bb3c6f0f8e2cd5b13e10e4df027b"}, + {file = "coverage-7.2.0-cp38-cp38-win32.whl", hash = "sha256:b09dd7bef59448c66e6b490cc3f3c25c14bc85d4e3c193b81a6204be8dd355de"}, + {file = "coverage-7.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:dc4f9a89c82faf6254d646180b2e3aa4daf5ff75bdb2c296b9f6a6cf547e26a7"}, + {file = "coverage-7.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c243b25051440386179591a8d5a5caff4484f92c980fb6e061b9559da7cc3f64"}, + {file = "coverage-7.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b8fd32f85b256fc096deeb4872aeb8137474da0c0351236f93cbedc359353d6"}, + {file = "coverage-7.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7f2a7df523791e6a63b40360afa6792a11869651307031160dc10802df9a252"}, + {file = "coverage-7.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da32526326e8da0effb452dc32a21ffad282c485a85a02aeff2393156f69c1c3"}, + {file = "coverage-7.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1153a6156715db9d6ae8283480ae67fb67452aa693a56d7dae9ffe8f7a80da"}, + {file = "coverage-7.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:74cd60fa00f46f28bd40048d6ca26bd58e9bee61d2b0eb4ec18cea13493c003f"}, + {file = "coverage-7.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:59a427f8a005aa7254074719441acb25ac2c2f60c1f1026d43f846d4254c1c2f"}, + {file = "coverage-7.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c3c4beddee01c8125a75cde3b71be273995e2e9ec08fbc260dd206b46bb99969"}, + {file = "coverage-7.2.0-cp39-cp39-win32.whl", hash = "sha256:08e3dd256b8d3e07bb230896c8c96ec6c5dffbe5a133ba21f8be82b275b900e8"}, + {file = "coverage-7.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad12c74c6ce53a027f5a5ecbac9be20758a41c85425c1bbab7078441794b04ee"}, + {file = "coverage-7.2.0-pp37.pp38.pp39-none-any.whl", hash = "sha256:ffa637a2d5883298449a5434b699b22ef98dd8e2ef8a1d9e60fa9cfe79813411"}, + {file = "coverage-7.2.0.tar.gz", hash = "sha256:9cc9c41aa5af16d845b53287051340c363dd03b7ef408e45eec3af52be77810d"}, ] [package.extras] @@ -319,6 +319,28 @@ files = [ graph = ["objgraph (>=1.7.2)"] [[package]] +name = "docker" +version = "6.0.1" +description = "A Python library for the Docker Engine API." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "docker-6.0.1-py3-none-any.whl", hash = "sha256:dbcb3bd2fa80dca0788ed908218bf43972772009b881ed1e20dfc29a65e49782"}, + {file = "docker-6.0.1.tar.gz", hash = "sha256:896c4282e5c7af5c45e8b683b0b0c33932974fe6e50fc6906a0a83616ab3da97"}, +] + +[package.dependencies] +packaging = ">=14.0" +pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""} +requests = ">=2.26.0" +urllib3 = ">=1.26.0" +websocket-client = ">=0.32.0" + +[package.extras] +ssh = ["paramiko (>=2.4.3)"] + +[[package]] name = "docutils" version = "0.19" description = "Docutils -- Python Documentation Utilities" @@ -530,24 +552,24 @@ files = [ [[package]] name = "markdown-it-py" -version = "2.1.0" +version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "markdown-it-py-2.1.0.tar.gz", hash = "sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da"}, - {file = "markdown_it_py-2.1.0-py3-none-any.whl", hash = "sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27"}, + {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, + {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, ] [package.dependencies] mdurl = ">=0.1,<1.0" [package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark (>=3.2,<4.0)"] -code-style = ["pre-commit (==2.6)"] -compare = ["commonmark (>=0.9.1,<0.10.0)", "markdown (>=3.3.6,<3.4.0)", "mistletoe (>=0.8.1,<0.9.0)", "mistune (>=2.0.2,<2.1.0)", "panflute (>=2.1.3,<2.2.0)"] -linkify = ["linkify-it-py (>=1.0,<2.0)"] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins"] profiling = ["gprof2dot"] rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] @@ -653,7 +675,7 @@ files = [ name = "packaging" version = "23.0" description = "Core utilities for Python packages" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -869,6 +891,30 @@ files = [ ] [[package]] +name = "pywin32" +version = "305" +description = "Python for Window Extensions" +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"}, +] + +[[package]] name = "requests" version = "2.28.2" description = "Python HTTP for Humans." @@ -1149,6 +1195,23 @@ secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "p socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] +name = "websocket-client" +version = "1.5.1" +description = "WebSocket client for Python with low level API options" +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"}, +] + +[package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] name = "wrapt" version = "1.14.1" description = "Module for decorators, wrappers and monkey patching." @@ -1241,4 +1304,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "9e58aabf16fd54e3083364bca0198366e2130799e0788894b56796eaf8554a39" +content-hash = "d524bec4d312a1d74020d921a33fd0b03ecdc93c87a161f71c6594c9985a2fc9" diff --git a/pyproject.toml b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "jaypore_ci" -version = "0.2.18" +version = "0.2.19" description = "" authors = ["arjoonn sharma <arjoonn.94@gmail.com>"] homepage = "https://www.jayporeci.in/" @@ -12,6 +12,7 @@ requests = "^2.28.1" click = "^8.1.3" pendulum = "^2.1.2" structlog = "^22.3.0" +docker = "^6.0.1" [tool.poetry.group.dev.dependencies] pylint = "^2.15.7" diff --git a/tests/conftest.py b/tests/conftest.py @@ -4,6 +4,7 @@ import unittest import pytest import tests.subprocess_mock # pylint: disable=unused-import +import tests.docker_mock # pylint: disable=unused-import from tests.requests_mock import Mock from jaypore_ci import jci, executors, remotes, reporters, repos @@ -64,7 +65,7 @@ def idfn(x): scope="function", params=list( jci.Pipeline.env_matrix( - reporter=[reporters.Text, reporters.Mock, reporters.Markdown], + reporter=[reporters.Text, reporters.Markdown], remote=[ remotes.Mock, remotes.Email, @@ -72,8 +73,8 @@ def idfn(x): remotes.Gitea, remotes.Github, ], - repo=[repos.Mock, repos.Git], - executor=[executors.Mock], + repo=[repos.Git], + executor=[executors.Docker], ) ), ids=idfn, @@ -85,16 +86,7 @@ def pipeline(request): os.environ["JAYPORE_EMAIL_PASSWORD"] = "fake_email_password" os.environ["JAYPORE_EMAIL_TO"] = "fake.to@mymailmail.com" kwargs = {} - if request.param["repo"] == repos.Mock: - kwargs["repo"] = repos.Mock.from_env( - files_changed=[], - branch="test_branch", - sha="fake_sha", - remote="https://fake_remote.com/fake_owner/fake_repo.git", - commit_message="fake_commit_message", - ) - else: - kwargs["repo"] = request.param["repo"].from_env() + kwargs["repo"] = request.param["repo"].from_env() # --- remote kwargs["remote"] = request.param["remote"].from_env(repo=kwargs["repo"]) if request.param["remote"] == remotes.Gitea and not Mock.gitea_added: diff --git a/tests/docker_mock.py b/tests/docker_mock.py @@ -0,0 +1,96 @@ +import random +from collections import defaultdict + +import pendulum +import docker + + +def cid(short=False): + n_chars = 12 if short else 64 + return "".join(random.sample("0123456789abcdef" * 10, n_chars)) + + +class Network: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def remove(self): + pass + + +class Networks: + nets = {} + + def list(self, names): + return list(filter(None, [self.nets.get(name) for name in names])) + + def create(self, **kwargs): + name = kwargs.get("name") + self.nets[name] = Network(**kwargs) + return name + + def get(self, name): + return self.nets[name] + + +class Container: + def __init__(self, **kwargs): + self.id = cid() + self.__dict__.update(kwargs) + self.FinishedAt = "0001-01-01T00:00:00Z" + self.ExitCode = 0 + + def logs(self): + return b"" + + def stop(self, **_): + self.FinishedAt = str(pendulum.now()) + self.ExitCode = 0 + + +class Containers: + boxes = {} + + def get(self, container_id): + return self.boxes[container_id] + + def run(self, **kwargs): + kwargs["StartedAt"] = str(pendulum.now()) + c = Container(**kwargs) + self.boxes[c.id] = c + return c + + +class Docker: + networks = Networks() + containers = Containers() + + +class APIClient: + max_running = {} + reported_running = defaultdict(int) + + def inspect_container(self, container_id): + if container_id not in self.max_running: + self.max_running[container_id] = random.choice(range(3, 11)) + self.reported_running[container_id] += 1 + is_running = ( + self.reported_running[container_id] <= self.max_running[container_id] + ) + container = Containers.boxes[container_id] + return { + "State": { + "Running": is_running, + "ExitCode": container.ExitCode, + "StartedAt": container.StartedAt, + "FinishedAt": container.FinishedAt, + } + } + + +def from_env(): + return Docker() + + +docker.from_env = from_env +docker.APIClient = APIClient diff --git a/tests/subprocess_mock.py b/tests/subprocess_mock.py @@ -1,5 +1,11 @@ import random import subprocess +from typing import NamedTuple + + +class ProcMock(NamedTuple): + returncode: int + stdout: str def sha(): @@ -34,4 +40,41 @@ def check_output(cmd, **_): return text.encode() +networks = {} +names = {} +containers = {} + + +def cid(short=False): + n_chars = 12 if short else 64 + return random.sample("0123456789abcdef" * 10, n_chars) + + +def run(cmd, **_): + code, text = 0, "" + if "docker network create" in cmd: + name = cmd.split()[-1] + networks[name] = True + elif "docker network ls" in cmd: + name = cmd.split("grep")[1] + if name in networks: + text = f"{cid(short=True)} {name} bridge local" + else: + code = 1 + elif "docker network rm" in cmd: + name = text = cmd.split(" rm ")[1].split("|")[0].strip() + if name not in networks: + text = "No such net" + elif "docker stop -t 1" in cmd: + name = text = cmd.split()[-1] + if name not in containers and name not in names: + cmd = 1 + text = f"Error response from daemon: No such container: {name}" + elif "docker run -d" in cmd: + name = cmd.split("--name")[1].strip().split()[0] + containers[name] = text = cid() + return ProcMock(returncode=code, stdout=text.encode()) + + subprocess.check_output = check_output +subprocess.run = run