From 79bd559a6e1fefcb434314dcb7791d69ab33703d Mon Sep 17 00:00:00 2001 From: Angela Ko Date: Tue, 29 Apr 2025 16:08:12 -0400 Subject: [PATCH 01/41] Handle empty strings and repo urls in assertions --- tests/endtoend_tests/test_docker_client.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/endtoend_tests/test_docker_client.py b/tests/endtoend_tests/test_docker_client.py index 424768e..aaabb35 100644 --- a/tests/endtoend_tests/test_docker_client.py +++ b/tests/endtoend_tests/test_docker_client.py @@ -100,7 +100,7 @@ def test_get_image(docker_client, docker_client_built_image_name, docker_py_clie assert docker_py_image_list is not None # Check that every image in image_list is also in docker_py_image_list for image in image_list: - assert image.id in [img.id for img in docker_py_image_list] + assert image.id in [img.id for img in docker_py_image_list if img] def test_create_image( @@ -120,7 +120,7 @@ def test_create_image( try: image = docker_client.images.get(docker_client_built_image_name) assert image is not None - assert docker_client_built_image_name in image.tags + assert any(docker_client_built_image_name in tag for tag in image.tags) image_id_obj = docker_client.images.get(image.id) image_short_id_obj = docker_client.images.get(image.short_id) @@ -138,7 +138,7 @@ def test_create_image( image1 = docker_client.images.get(image1_name) image1_name = image1_name + ":latest" assert image1 is not None - assert image1_name in image1.tags + assert any(image1_name in tag for tag in image1.tags) assert image_exists(docker_client, image1_name) assert image_exists(docker_py_client, image1_name) @@ -155,6 +155,8 @@ def test_create_image( # Our docker client should be able to retrieve images with just the name image2 = docker_client.images.get(repo_url + image2_name) image2_no_url = docker_client.images.get(image2_name) + assert any(image2_name in tag for tag in image2.tags) + assert image2 is not None assert image2_no_url is not None assert image2.id == image2_py.id From 4ab50b33f4fd085dd1ed6a937bdebfa459b16ff1 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 30 Apr 2025 10:07:46 -0300 Subject: [PATCH 02/41] Handle more strings --- tests/endtoend_tests/test_docker_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/endtoend_tests/test_docker_client.py b/tests/endtoend_tests/test_docker_client.py index aaabb35..89b000e 100644 --- a/tests/endtoend_tests/test_docker_client.py +++ b/tests/endtoend_tests/test_docker_client.py @@ -52,7 +52,7 @@ def test_get_image(docker_client, docker_client_built_image_name, docker_py_clie # Get the image image = docker_client.images.get(docker_client_built_image_name) assert image is not None - assert docker_client_built_image_name in image.tags + assert any(docker_client_built_image_name in tag for tag in image.tags) docker_py_image = docker_py_client.images.get(docker_client_built_image_name) assert docker_py_image is not None From 6e0eb83abd9ace558908ededff1d40cfba879be4 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 30 Apr 2025 11:14:43 -0300 Subject: [PATCH 03/41] refactor logic to get an image --- tesseract_core/sdk/docker_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tesseract_core/sdk/docker_client.py b/tesseract_core/sdk/docker_client.py index 4bc8646..7dd7306 100644 --- a/tesseract_core/sdk/docker_client.py +++ b/tesseract_core/sdk/docker_client.py @@ -79,7 +79,7 @@ def is_image_id(s: str) -> bool: or image_id_or_name in image_obj.tags or ( any( - tag.split("/")[-1] == image_id_or_name for tag in image_obj.tags + tag.split("/")[-1] in image_id_or_name for tag in image_obj.tags ) ) ): From 265ed01cdbcf7c556d6c856a4c77e1e24c1ef878 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 30 Apr 2025 12:47:53 -0300 Subject: [PATCH 04/41] Handle image_name with URL --- tests/endtoend_tests/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/endtoend_tests/common.py b/tests/endtoend_tests/common.py index 6c4a46e..f58b2b2 100644 --- a/tests/endtoend_tests/common.py +++ b/tests/endtoend_tests/common.py @@ -74,5 +74,5 @@ def build_tesseract( assert result.exit_code == 0, result.exception image_tags = json.loads(result.stdout.strip()) - assert image_name in image_tags + assert [image_name in tag for tag in image_tags] return image_tags[0] From cbf14847dd0cec9847d5b57efee4af159a9bdee3 Mon Sep 17 00:00:00 2001 From: Angela Ko Date: Wed, 30 Apr 2025 12:20:24 -0400 Subject: [PATCH 05/41] Change to any --- tests/endtoend_tests/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/endtoend_tests/common.py b/tests/endtoend_tests/common.py index f58b2b2..8be57e3 100644 --- a/tests/endtoend_tests/common.py +++ b/tests/endtoend_tests/common.py @@ -74,5 +74,5 @@ def build_tesseract( assert result.exit_code == 0, result.exception image_tags = json.loads(result.stdout.strip()) - assert [image_name in tag for tag in image_tags] + assert any(image_name in tag for tag in image_tags) return image_tags[0] From e43a57348b4fe07e3c34fec554447311be2f0958 Mon Sep 17 00:00:00 2001 From: Angela Ko Date: Wed, 30 Apr 2025 12:35:49 -0400 Subject: [PATCH 06/41] Change image exists --- tests/endtoend_tests/test_examples.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/endtoend_tests/test_examples.py b/tests/endtoend_tests/test_examples.py index 1a07161..2e682d8 100644 --- a/tests/endtoend_tests/test_examples.py +++ b/tests/endtoend_tests/test_examples.py @@ -21,7 +21,7 @@ import numpy.typing as npt import pytest import requests -from common import build_tesseract +from common import build_tesseract, image_exists from typer.testing import CliRunner @@ -739,15 +739,6 @@ def unit_tesseract_config(unit_tesseract_names, unit_tesseract_path): return TEST_CASES[unit_tesseract_path.name] -def image_exists(client, image_name): - # Docker images may be prefixed with the registry URL - return any( - tag.split("/")[-1] == image_name - for img in client.images.list() - for tag in img.tags - ) - - def print_debug_info(result): """Print debug info from result of a CLI command if it failed.""" if result.exit_code == 0: From eec02a79b76e6e8587785de10915bd4949bbe1f3 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 30 Apr 2025 15:53:19 -0300 Subject: [PATCH 07/41] test: skip tesseract_serve_ports with range on Podman --- tests/endtoend_tests/test_endtoend.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/endtoend_tests/test_endtoend.py b/tests/endtoend_tests/test_endtoend.py index dde1069..92cec98 100644 --- a/tests/endtoend_tests/test_endtoend.py +++ b/tests/endtoend_tests/test_endtoend.py @@ -4,6 +4,7 @@ """End-to-end tests for Tesseract workflows.""" import json +import os from pathlib import Path import pytest @@ -325,6 +326,14 @@ def test_tesseract_serve_ports(built_image_name, port): cli_runner = CliRunner(mix_stderr=False) project_id = None + docker_host = os.environ.get("DOCKER_HOST", "") + + if "-" in port and "podman" in docker_host: + pytest.skip( + "Podman does not support port ranges in compose." + "See https://github.com/containers/podman/issues/15111" + ) + # Serve tesseract on specified ports. run_res = cli_runner.invoke( app, From 18adba4774b4270c2226a9586d2791bc14eac92f Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 30 Apr 2025 16:55:56 -0300 Subject: [PATCH 08/41] ci: run all E2E tests with Podman --- .github/workflows/run_tests.yml | 86 ++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 7146ab8..6954b56 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -159,7 +159,7 @@ jobs: subjobs=$(echo "$subjobs" | jq -c -r '. + ["base"]') printf 'matrix=%s' "$subjobs" >> $GITHUB_OUTPUT - tests-e2e: + tests-e2e-docker: needs: get-e2e-matrix strategy: @@ -236,3 +236,87 @@ jobs: slug: pasteurlabs/tesseract-core files: coverage*.xml fail_ci_if_error: true + + tests-e2e-podman: + needs: get-e2e-matrix + + strategy: + matrix: + os: [ubuntu-22.04] + python-version: ["3.12"] + + arch: ["x64"] + + unit-tesseract: ${{ fromJson(needs.get-e2e-matrix.outputs.matrix) }} + + include: + # Test on arm to ensure compatibility with Apple M1 chips + # (OSX runners don't have access to Docker so we use Linux ARM runners instead) + - os: "ubuntu-22.04" + python-version: "3.12" + arch: "arm" + unit-tesseract: "base" + + fail-fast: false + + runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-22.04' && 'ubuntu-22.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-22.04' && 'linux-arm64-ubuntu2204' }} + + defaults: + run: + shell: bash -el {0} + + steps: + - name: Set up Git repository + uses: actions/checkout@v4 + + # Use Conda to install Python (setup-python action doesn't support ARM) + - uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: true + python-version: ${{ matrix.python-version }} + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Restore UV environment + run: cp production.uv.lock uv.lock + + - name: Install package + run: | + uv sync --extra dev --frozen + + - name: Set up Podman + run: | + systemctl start --user podman.socket + + - name: Run test suite + run: | + if [ "${{ matrix.unit-tesseract }}" == "base" ]; then + DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ + uv run --no-sync pytest \ + --always-run-endtoend \ + --cov-report=term-missing:skip-covered \ + --cov-report=xml:coverage.xml \ + --cov=tesseract_core \ + tests/endtoend_tests \ + -k "not test_examples" + else + DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ + uv run --no-sync pytest \ + --always-run-endtoend \ + --cov-report=term-missing:skip-covered \ + --cov-report=xml:coverage.xml \ + --cov=tesseract_core \ + tests/endtoend_tests/test_examples.py \ + -k "[${{ matrix.unit-tesseract }}]" + fi + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v5.4.2 + with: + token: ${{ secrets.CODECOV_TOKEN }} + slug: pasteurlabs/tesseract-core + files: coverage*.xml + fail_ci_if_error: true From ee16afe079028bfcbd29a68ce5f51bcc957683d4 Mon Sep 17 00:00:00 2001 From: Angela Ko Date: Wed, 30 Apr 2025 17:26:33 -0400 Subject: [PATCH 09/41] Add proper substring matching for image names --- tesseract_core/sdk/docker_client.py | 34 ++++++++++++++++++---- tests/endtoend_tests/common.py | 5 ++-- tests/endtoend_tests/test_docker_client.py | 23 ++++++++++----- tests/endtoend_tests/test_endtoend.py | 4 ++- tests/endtoend_tests/test_examples.py | 4 +-- tests/endtoend_tests/test_tesseract_sdk.py | 4 ++- 6 files changed, 56 insertions(+), 18 deletions(-) diff --git a/tesseract_core/sdk/docker_client.py b/tesseract_core/sdk/docker_client.py index 7dd7306..9259ceb 100644 --- a/tesseract_core/sdk/docker_client.py +++ b/tesseract_core/sdk/docker_client.py @@ -42,6 +42,34 @@ def from_dict(cls, json_dict: dict) -> "Image": class Images: """Namespace for functions to interface with Tesseract docker images.""" + @staticmethod + def tag_exists(image_name: str, tags: list_) -> bool: + """Helper function to check if image name exists in the list of tags. + + Specially handling has to be done to achieve unfuzzy substring matching, i.e. + if image tag is foo/bar/image, we need to return true for foo/bar/image, bar/image, + and image, but not ar/image. + + There is no equivalent in docker-py. + + Params: + image_name: The image name to check. + tags: The list of tags to check against. + + Returns: + True if the image name exists in the list of tags, False otherwise. + """ + if ":" not in image_name: + image_name += ":latest" + + image_name_parts = image_name.strip("/").split("/") + for tag in tags: + tag_parts = tag.strip("/").split("/") + # Check if the last parts of the tag matches the subportion that image name specifies + if tag_parts[-len(image_name_parts) :] == image_name_parts: + return True + return False + @staticmethod def get(image_id_or_name: str | bytes, tesseract_only: bool = True) -> Image: """Returns the metadata for a specific image. @@ -77,11 +105,7 @@ def is_image_id(s: str) -> bool: image_obj.id == image_id_or_name or image_obj.short_id == image_id_or_name or image_id_or_name in image_obj.tags - or ( - any( - tag.split("/")[-1] in image_id_or_name for tag in image_obj.tags - ) - ) + or Images.tag_exists(image_id_or_name, image_obj.tags) ): return image_obj diff --git a/tests/endtoend_tests/common.py b/tests/endtoend_tests/common.py index 8be57e3..862124f 100644 --- a/tests/endtoend_tests/common.py +++ b/tests/endtoend_tests/common.py @@ -35,7 +35,7 @@ def print_debug_info(result): def build_tesseract( - sourcedir, image_name, config_override=None, tag=None, build_retries=3 + client, sourcedir, image_name, config_override=None, tag=None, build_retries=3 ): cli_runner = CliRunner(mix_stderr=False) @@ -74,5 +74,6 @@ def build_tesseract( assert result.exit_code == 0, result.exception image_tags = json.loads(result.stdout.strip()) - assert any(image_name in tag for tag in image_tags) + assert client.images.tag_exists(image_name, image_tags) + return image_tags[0] diff --git a/tests/endtoend_tests/test_docker_client.py b/tests/endtoend_tests/test_docker_client.py index 89b000e..2395535 100644 --- a/tests/endtoend_tests/test_docker_client.py +++ b/tests/endtoend_tests/test_docker_client.py @@ -52,7 +52,7 @@ def test_get_image(docker_client, docker_client_built_image_name, docker_py_clie # Get the image image = docker_client.images.get(docker_client_built_image_name) assert image is not None - assert any(docker_client_built_image_name in tag for tag in image.tags) + assert docker_client.images.tag_exists(docker_client_built_image_name, image.tags) docker_py_image = docker_py_client.images.get(docker_client_built_image_name) assert docker_py_image is not None @@ -120,7 +120,9 @@ def test_create_image( try: image = docker_client.images.get(docker_client_built_image_name) assert image is not None - assert any(docker_client_built_image_name in tag for tag in image.tags) + assert docker_client.images.tag_exists( + docker_client_built_image_name, image.tags + ) image_id_obj = docker_client.images.get(image.id) image_short_id_obj = docker_client.images.get(image.short_id) @@ -138,7 +140,7 @@ def test_create_image( image1 = docker_client.images.get(image1_name) image1_name = image1_name + ":latest" assert image1 is not None - assert any(image1_name in tag for tag in image1.tags) + assert docker_client.images.tag_exists(image1_name, image1.tags) assert image_exists(docker_client, image1_name) assert image_exists(docker_py_client, image1_name) @@ -155,16 +157,23 @@ def test_create_image( # Our docker client should be able to retrieve images with just the name image2 = docker_client.images.get(repo_url + image2_name) image2_no_url = docker_client.images.get(image2_name) - assert any(image2_name in tag for tag in image2.tags) + assert docker_client.images.tag_exists(image2_name, image2.tags) assert image2 is not None assert image2_no_url is not None assert image2.id == image2_py.id assert image2_no_url.id == image2_py.id - assert image_exists(docker_client, image2_name) - assert image_exists(docker_client, repo_url + image2_name) - assert image_exists(docker_py_client, repo_url + image2_name) + # Check we are not overmatching but we are getting all possible cases + for client in [docker_client, docker_py_client]: + assert not image_exists(client, "create_image") + assert image_exists(client, image2_name) + assert image_exists(client, f"/{image2_name}") + assert image_exists(client, f"bar/{image2_name}") + assert image_exists(client, f"bar/{image2_name}:latest") + assert not image_exists(client, f"ar/{image2_name}") + assert image_exists(client, f"foo/bar/{image2_name}") + assert image_exists(client, repo_url + image2_name) finally: # Clean up the images diff --git a/tests/endtoend_tests/test_endtoend.py b/tests/endtoend_tests/test_endtoend.py index 92cec98..53b5e90 100644 --- a/tests/endtoend_tests/test_endtoend.py +++ b/tests/endtoend_tests/test_endtoend.py @@ -19,7 +19,9 @@ @pytest.fixture(scope="module") def built_image_name(docker_client, shared_dummy_image_name, dummy_tesseract_location): """Build the dummy Tesseract image for the tests.""" - image_name = build_tesseract(dummy_tesseract_location, shared_dummy_image_name) + image_name = build_tesseract( + docker_client, dummy_tesseract_location, shared_dummy_image_name + ) assert image_exists(docker_client, image_name) yield image_name diff --git a/tests/endtoend_tests/test_examples.py b/tests/endtoend_tests/test_examples.py index 2e682d8..59f3c88 100644 --- a/tests/endtoend_tests/test_examples.py +++ b/tests/endtoend_tests/test_examples.py @@ -806,6 +806,7 @@ def test_unit_tesseract_endtoend( # Stage 1: Build img_name = build_tesseract( + docker_client, unit_tesseract_path, dummy_image_name, tag="sometag", @@ -914,9 +915,8 @@ def test_unit_tesseract_endtoend( # Cannot mix stderr if we want to load the json cli_runner = CliRunner(mix_stderr=False) + project_id = None try: - project_id = None - run_res = cli_runner.invoke( app, [ diff --git a/tests/endtoend_tests/test_tesseract_sdk.py b/tests/endtoend_tests/test_tesseract_sdk.py index 1f978e7..a4d44a9 100644 --- a/tests/endtoend_tests/test_tesseract_sdk.py +++ b/tests/endtoend_tests/test_tesseract_sdk.py @@ -25,7 +25,9 @@ @pytest.fixture(scope="module") def built_image_name(docker_client, shared_dummy_image_name, dummy_tesseract_location): """Build the dummy Tesseract image for the tests.""" - image_name = build_tesseract(dummy_tesseract_location, shared_dummy_image_name) + image_name = build_tesseract( + docker_client, dummy_tesseract_location, shared_dummy_image_name + ) assert image_exists(docker_client, image_name) yield image_name From 04fa690e782ec25f667877cac6784347d07de408 Mon Sep 17 00:00:00 2001 From: Angela Ko Date: Wed, 30 Apr 2025 17:30:14 -0400 Subject: [PATCH 10/41] Podman typeguard --- tesseract_core/sdk/docker_client.py | 2 +- tests/endtoend_tests/test_endtoend.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tesseract_core/sdk/docker_client.py b/tesseract_core/sdk/docker_client.py index 9259ceb..ed56404 100644 --- a/tesseract_core/sdk/docker_client.py +++ b/tesseract_core/sdk/docker_client.py @@ -735,7 +735,7 @@ class DockerException(Exception): class BuildError(DockerException): """Raised when a build fails.""" - def __init__(self, build_log: str) -> None: + def __init__(self, build_log: list_) -> None: self.build_log = build_log diff --git a/tests/endtoend_tests/test_endtoend.py b/tests/endtoend_tests/test_endtoend.py index 53b5e90..5e45e59 100644 --- a/tests/endtoend_tests/test_endtoend.py +++ b/tests/endtoend_tests/test_endtoend.py @@ -59,7 +59,11 @@ def test_build_from_init_endtoend( config_override["build_config.base_image"] = base_image image_name = build_tesseract( - tmp_path, dummy_image_name, config_override=config_override, tag=img_tag + docker_client, + tmp_path, + dummy_image_name, + config_override=config_override, + tag=img_tag, ) assert image_exists(docker_client, image_name) From 479d9e1573e1ce7e9158195605ba0954a6114d72 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Fri, 2 May 2025 11:06:48 -0300 Subject: [PATCH 11/41] ci: display status of podman socket for debugging --- .github/workflows/run_tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 6954b56..48b1fcc 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -290,6 +290,7 @@ jobs: - name: Set up Podman run: | systemctl start --user podman.socket + systemctl status --user podman.socket - name: Run test suite run: | From 507f6ecb46ad1a59f92c9783f4a94669dbfd8a2d Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 30 Apr 2025 15:53:19 -0300 Subject: [PATCH 12/41] Docker-py does not support partial string matching --- tesseract_core/sdk/docker_client.py | 10 ++++++++++ tests/endtoend_tests/test_docker_client.py | 23 ++++++++++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/tesseract_core/sdk/docker_client.py b/tesseract_core/sdk/docker_client.py index ed56404..915eb2d 100644 --- a/tesseract_core/sdk/docker_client.py +++ b/tesseract_core/sdk/docker_client.py @@ -74,6 +74,16 @@ def tag_exists(image_name: str, tags: list_) -> bool: def get(image_id_or_name: str | bytes, tesseract_only: bool = True) -> Image: """Returns the metadata for a specific image. + In docker-py, there is no substring matching and the image name is the + last tag in the list of tags, so if an image has multiple tags, only + one of the tags would be able to find the image. + + However, in podman, this is not the case. Podman has substring matching + by "/" segments to handle repository urls and returns images even if + partial name is specified, or if image has multiple tags. + + We chose to support podman's largest string matching functionality here. + Params: image_id_or_name: The image name or id to get. tesseract_only: If True, only retrieves Tesseract images. diff --git a/tests/endtoend_tests/test_docker_client.py b/tests/endtoend_tests/test_docker_client.py index 2395535..9f670e6 100644 --- a/tests/endtoend_tests/test_docker_client.py +++ b/tests/endtoend_tests/test_docker_client.py @@ -3,6 +3,7 @@ """End to end tests for docker cli wrapper.""" +import os import subprocess import textwrap from contextlib import closing @@ -165,14 +166,24 @@ def test_create_image( assert image2_no_url.id == image2_py.id # Check we are not overmatching but we are getting all possible cases + docker_host = os.environ.get("DOCKER_HOST", "") + + podman = False + if "podman" in docker_host: + podman = True + for client in [docker_client, docker_py_client]: assert not image_exists(client, "create_image") - assert image_exists(client, image2_name) - assert image_exists(client, f"/{image2_name}") - assert image_exists(client, f"bar/{image2_name}") - assert image_exists(client, f"bar/{image2_name}:latest") - assert not image_exists(client, f"ar/{image2_name}") - assert image_exists(client, f"foo/bar/{image2_name}") + + if podman and client == docker_py_client: + # Docker-py does not support partial string matching + assert image_exists(client, image2_name) + assert image_exists(client, f"/{image2_name}") + assert image_exists(client, f"bar/{image2_name}") + assert image_exists(client, f"bar/{image2_name}:latest") + assert not image_exists(client, f"ar/{image2_name}") + assert image_exists(client, f"foo/bar/{image2_name}") + assert image_exists(client, repo_url + image2_name) finally: From 5648db60e819d0b51554eaaf2346c87766dcb244 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Mon, 5 May 2025 10:44:18 -0300 Subject: [PATCH 13/41] ci(tests): test podman setup --- .github/workflows/run_tests.yml | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 48b1fcc..c135c1b 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -292,6 +292,9 @@ jobs: systemctl start --user podman.socket systemctl status --user podman.socket + podman -v + cat /etc/os-release + - name: Run test suite run: | if [ "${{ matrix.unit-tesseract }}" == "base" ]; then @@ -321,3 +324,55 @@ jobs: slug: pasteurlabs/tesseract-core files: coverage*.xml fail_ci_if_error: true + + + test-podman: + runs-on: [ubuntu-22.04, ubuntu-24.04] + + steps: + - name: Set up Git repository + uses: actions/checkout@v4 + + # Use Conda to install Python (setup-python action doesn't support ARM) + - uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: true + python-version: ${{ matrix.python-version }} + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Restore UV environment + run: cp production.uv.lock uv.lock + + - name: Install package + run: | + uv sync --extra dev --frozen + + - name: Set up Podman + run: | + systemctl start --user podman.socket + systemctl status --user podman.socket + + podman -v + cat /etc/os-release + + - name: Build vectoradd via `docker` + run: | + path=$(tesseract build --generate-only examples/vectoradd) + + DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ + docker buildx build --load --tag vectoradd --file "${path}/Dockerfile" "{path}" + + DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ + docker image ls + + - name: Build vectoradd via `podman` + run: | + path=$(tesseract build --generate-only examples/vectoradd) + + podman buildx build --load --tag vectoradd --file "${path}/Dockerfile" "{path}" + + podman image ls From 6136e432e428a7d6ebb9ba517210a113b1e41645 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Mon, 5 May 2025 11:22:59 -0300 Subject: [PATCH 14/41] Runs on multiple Ubuntus --- .github/workflows/run_tests.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index c135c1b..c480968 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -327,7 +327,10 @@ jobs: test-podman: - runs-on: [ubuntu-22.04, ubuntu-24.04] + strategy: + matrix: + os: [ubuntu-22.04, ubuntu-24.04] + runs-on: ${{ matrix.os }} steps: - name: Set up Git repository From 4bd82496890fd88f591945612f02cb5a61e0653b Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Mon, 5 May 2025 11:32:57 -0300 Subject: [PATCH 15/41] ci(test-podman): fix command to run `tesseract` --- .github/workflows/run_tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index c480968..462a08c 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -364,10 +364,10 @@ jobs: - name: Build vectoradd via `docker` run: | - path=$(tesseract build --generate-only examples/vectoradd) + path=$(uv run tesseract build --generate-only examples/vectoradd) DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ - docker buildx build --load --tag vectoradd --file "${path}/Dockerfile" "{path}" + docker buildx build --load --tag vectoradd --file "${path}/Dockerfile" "${path}" DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ docker image ls @@ -376,6 +376,6 @@ jobs: run: | path=$(tesseract build --generate-only examples/vectoradd) - podman buildx build --load --tag vectoradd --file "${path}/Dockerfile" "{path}" + podman buildx build --load --tag vectoradd --file "${path}/Dockerfile" "${path}" podman image ls From 73910ba8aa0f9fb4af9871907d15bb2988a2c670 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Mon, 5 May 2025 11:53:06 -0300 Subject: [PATCH 16/41] Fix the fixed command to build the images --- .github/workflows/run_tests.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 462a08c..512487a 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -362,6 +362,14 @@ jobs: podman -v cat /etc/os-release + - name: Build vectoradd via `podman` + run: | + path=$(uv run tesseract build --generate-only examples/vectoradd) + + podman buildx build --load --tag vectoradd --file "${path}/Dockerfile" "${path}" + + podman image ls + - name: Build vectoradd via `docker` run: | path=$(uv run tesseract build --generate-only examples/vectoradd) @@ -371,11 +379,3 @@ jobs: DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ docker image ls - - - name: Build vectoradd via `podman` - run: | - path=$(tesseract build --generate-only examples/vectoradd) - - podman buildx build --load --tag vectoradd --file "${path}/Dockerfile" "${path}" - - podman image ls From fc03af6e17a779488e3d4a248a84997e6c6160d1 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Mon, 5 May 2025 13:19:33 -0300 Subject: [PATCH 17/41] ci(tests): use Ubuntu24 for Podman tests --- .github/workflows/run_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 512487a..03a5106 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -242,7 +242,7 @@ jobs: strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] python-version: ["3.12"] arch: ["x64"] @@ -259,7 +259,7 @@ jobs: fail-fast: false - runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-22.04' && 'ubuntu-22.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-22.04' && 'linux-arm64-ubuntu2204' }} + runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-24.04' && 'ubuntu-24.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-24.04' && 'linux-arm64-ubuntu2204' }} defaults: run: From a9eabc3acf1d18ce6142b664b441dead4c33e286 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Tue, 6 May 2025 11:47:50 -0300 Subject: [PATCH 18/41] ci(tests): refactor to use a matrix for Podman --- .github/workflows/run_tests.yml | 152 ++++---------------------------- 1 file changed, 15 insertions(+), 137 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 03a5106..2fc4527 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -159,7 +159,7 @@ jobs: subjobs=$(echo "$subjobs" | jq -c -r '. + ["base"]') printf 'matrix=%s' "$subjobs" >> $GITHUB_OUTPUT - tests-e2e-docker: + tests-e2e: needs: get-e2e-matrix strategy: @@ -168,6 +168,7 @@ jobs: python-version: ["3.12"] arch: ["x64"] + docker-engine: ["docker"] unit-tesseract: ${{ fromJson(needs.get-e2e-matrix.outputs.matrix) }} @@ -177,89 +178,18 @@ jobs: - os: "ubuntu-22.04" python-version: "3.12" arch: "arm" + docker-engine: "docker" unit-tesseract: "base" - - fail-fast: false - - runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-22.04' && 'ubuntu-22.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-22.04' && 'linux-arm64-ubuntu2204' }} - - defaults: - run: - shell: bash -el {0} - - steps: - - name: Set up Git repository - uses: actions/checkout@v4 - - # Use Conda to install Python (setup-python action doesn't support ARM) - - uses: conda-incubator/setup-miniconda@v3 - with: - auto-update-conda: true - python-version: ${{ matrix.python-version }} - - - name: Set up uv - uses: astral-sh/setup-uv@v6 - with: - enable-cache: true - - - name: Restore UV environment - run: cp production.uv.lock uv.lock - - - name: Install package - run: | - uv sync --extra dev --frozen - - - name: Run test suite - run: | - if [ "${{ matrix.unit-tesseract }}" == "base" ]; then - uv run --no-sync pytest \ - --always-run-endtoend \ - --cov-report=term-missing:skip-covered \ - --cov-report=xml:coverage.xml \ - --cov=tesseract_core \ - tests/endtoend_tests \ - -k "not test_examples" - else - uv run --no-sync pytest \ - --always-run-endtoend \ - --cov-report=term-missing:skip-covered \ - --cov-report=xml:coverage.xml \ - --cov=tesseract_core \ - tests/endtoend_tests/test_examples.py \ - -k "[${{ matrix.unit-tesseract }}]" - fi - - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v5.4.2 - with: - token: ${{ secrets.CODECOV_TOKEN }} - slug: pasteurlabs/tesseract-core - files: coverage*.xml - fail_ci_if_error: true - - tests-e2e-podman: - needs: get-e2e-matrix - - strategy: - matrix: - os: [ubuntu-24.04] - python-version: ["3.12"] - - arch: ["x64"] - - unit-tesseract: ${{ fromJson(needs.get-e2e-matrix.outputs.matrix) }} - - include: - # Test on arm to ensure compatibility with Apple M1 chips - # (OSX runners don't have access to Docker so we use Linux ARM runners instead) - - os: "ubuntu-22.04" + # Run tests using Podman + - os: "ubuntu-24.04" python-version: "3.12" - arch: "arm" + arch: "x64" + docker-engine: "podman" unit-tesseract: "base" fail-fast: false - runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-24.04' && 'ubuntu-24.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-24.04' && 'linux-arm64-ubuntu2204' }} + runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-22.04' && 'ubuntu-22.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-22.04' && 'linux-arm64-ubuntu2204' }} defaults: run: @@ -288,17 +218,21 @@ jobs: uv sync --extra dev --frozen - name: Set up Podman + if: matrix.docker-engine == "podman" run: | systemctl start --user podman.socket systemctl status --user podman.socket - podman -v - cat /etc/os-release + podman --version + + export DOCKER_HOST=unix:///run/user/1001/podman/podman.sock + echo "${DOCKER_HOST}" >> $GITHUB_ENV - name: Run test suite run: | + env | grep DOCKER_HOST + if [ "${{ matrix.unit-tesseract }}" == "base" ]; then - DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ uv run --no-sync pytest \ --always-run-endtoend \ --cov-report=term-missing:skip-covered \ @@ -307,7 +241,6 @@ jobs: tests/endtoend_tests \ -k "not test_examples" else - DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ uv run --no-sync pytest \ --always-run-endtoend \ --cov-report=term-missing:skip-covered \ @@ -324,58 +257,3 @@ jobs: slug: pasteurlabs/tesseract-core files: coverage*.xml fail_ci_if_error: true - - - test-podman: - strategy: - matrix: - os: [ubuntu-22.04, ubuntu-24.04] - runs-on: ${{ matrix.os }} - - steps: - - name: Set up Git repository - uses: actions/checkout@v4 - - # Use Conda to install Python (setup-python action doesn't support ARM) - - uses: conda-incubator/setup-miniconda@v3 - with: - auto-update-conda: true - python-version: ${{ matrix.python-version }} - - - name: Set up uv - uses: astral-sh/setup-uv@v6 - with: - enable-cache: true - - - name: Restore UV environment - run: cp production.uv.lock uv.lock - - - name: Install package - run: | - uv sync --extra dev --frozen - - - name: Set up Podman - run: | - systemctl start --user podman.socket - systemctl status --user podman.socket - - podman -v - cat /etc/os-release - - - name: Build vectoradd via `podman` - run: | - path=$(uv run tesseract build --generate-only examples/vectoradd) - - podman buildx build --load --tag vectoradd --file "${path}/Dockerfile" "${path}" - - podman image ls - - - name: Build vectoradd via `docker` - run: | - path=$(uv run tesseract build --generate-only examples/vectoradd) - - DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ - docker buildx build --load --tag vectoradd --file "${path}/Dockerfile" "${path}" - - DOCKER_HOST=unix:///run/user/1001/podman/podman.sock \ - docker image ls From 3d4024d67711c68cc27a101c6b693fc497dac8d9 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Tue, 6 May 2025 11:58:52 -0300 Subject: [PATCH 19/41] use curly braces? --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 2fc4527..bad83fd 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -218,7 +218,7 @@ jobs: uv sync --extra dev --frozen - name: Set up Podman - if: matrix.docker-engine == "podman" + if: ${{matrix.docker-engine}} == "podman" run: | systemctl start --user podman.socket systemctl status --user podman.socket From b497733a8931826ccb3fa73c494694b488286aab Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Tue, 6 May 2025 12:04:46 -0300 Subject: [PATCH 20/41] Add spaces? --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index bad83fd..34c1446 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -218,7 +218,7 @@ jobs: uv sync --extra dev --frozen - name: Set up Podman - if: ${{matrix.docker-engine}} == "podman" + if: ${{ matrix.docker-engine }} == "podman" run: | systemctl start --user podman.socket systemctl status --user podman.socket From dc0496cbcb8420ed4ee7ce53a0da98c50ef9106c Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Tue, 6 May 2025 12:10:52 -0300 Subject: [PATCH 21/41] - -> _ --- .github/workflows/run_tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 34c1446..696b655 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -168,7 +168,7 @@ jobs: python-version: ["3.12"] arch: ["x64"] - docker-engine: ["docker"] + docker_engine: ["docker"] unit-tesseract: ${{ fromJson(needs.get-e2e-matrix.outputs.matrix) }} @@ -178,13 +178,13 @@ jobs: - os: "ubuntu-22.04" python-version: "3.12" arch: "arm" - docker-engine: "docker" + docker_engine: "docker" unit-tesseract: "base" # Run tests using Podman - os: "ubuntu-24.04" python-version: "3.12" arch: "x64" - docker-engine: "podman" + docker_engine: "podman" unit-tesseract: "base" fail-fast: false @@ -218,7 +218,7 @@ jobs: uv sync --extra dev --frozen - name: Set up Podman - if: ${{ matrix.docker-engine }} == "podman" + if: ${{ matrix.docker_engine }} == "podman" run: | systemctl start --user podman.socket systemctl status --user podman.socket From 605d82ea0aa8194ebe307cdf26a4800d9bb53cb0 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Tue, 6 May 2025 13:10:30 -0300 Subject: [PATCH 22/41] No more braces; use single quotes --- .github/workflows/run_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 696b655..6d08e87 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -218,7 +218,7 @@ jobs: uv sync --extra dev --frozen - name: Set up Podman - if: ${{ matrix.docker_engine }} == "podman" + if: matrix.docker_engine == 'podman' run: | systemctl start --user podman.socket systemctl status --user podman.socket @@ -226,7 +226,7 @@ jobs: podman --version export DOCKER_HOST=unix:///run/user/1001/podman/podman.sock - echo "${DOCKER_HOST}" >> $GITHUB_ENV + echo "DOCKER_HOST=${DOCKER_HOST}" >> $GITHUB_ENV - name: Run test suite run: | From 28697f1dc105de2fa36f8a03a2db6e153316a33d Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Tue, 6 May 2025 13:41:44 -0300 Subject: [PATCH 23/41] Try running Podman tests on all tessies --- .github/workflows/run_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 6d08e87..428aefc 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -185,7 +185,7 @@ jobs: python-version: "3.12" arch: "x64" docker_engine: "podman" - unit-tesseract: "base" + unit-tesseract: ${{ fromJson(needs.get-e2e-matrix.outputs.matrix) }} fail-fast: false @@ -230,7 +230,7 @@ jobs: - name: Run test suite run: | - env | grep DOCKER_HOST + env | grep DOCKER_HOST || true if [ "${{ matrix.unit-tesseract }}" == "base" ]; then uv run --no-sync pytest \ From d002512873a5354d4aff030dbef07c9a334f75ad Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Tue, 6 May 2025 13:48:44 -0300 Subject: [PATCH 24/41] Test all unit tessies with Podman --- .github/workflows/run_tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 428aefc..c15580a 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -185,7 +185,6 @@ jobs: python-version: "3.12" arch: "x64" docker_engine: "podman" - unit-tesseract: ${{ fromJson(needs.get-e2e-matrix.outputs.matrix) }} fail-fast: false From f5d400053e436b200f222c86dc9cafb045875f5f Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Tue, 6 May 2025 14:18:52 -0300 Subject: [PATCH 25/41] Run base E2E + vectoradd_jax tests with Podman --- .github/workflows/run_tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index c15580a..ddb633c 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -185,6 +185,12 @@ jobs: python-version: "3.12" arch: "x64" docker_engine: "podman" + unit-tesseract: "base" + - os: "ubuntu-24.04" + python-version: "3.12" + arch: "x64" + docker_engine: "podman" + unit-tesseract: "vectoradd_jax" fail-fast: false From 5564ef515acfdfe4046ea8ba0f6f44c3310f62e7 Mon Sep 17 00:00:00 2001 From: Angela Ko Date: Tue, 6 May 2025 14:49:40 -0400 Subject: [PATCH 26/41] Add ubuntu 24 to runs on --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index ddb633c..a9403b4 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -194,7 +194,7 @@ jobs: fail-fast: false - runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-22.04' && 'ubuntu-22.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-22.04' && 'linux-arm64-ubuntu2204' }} + runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-22.04' && 'ubuntu-22.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-22.04' && 'linux-arm64-ubuntu2204' || matrix.arch == 'x64' && matrix.os == 'ubuntu-24.04' && 'ubuntu-24.04'}} defaults: run: From a449c25b7c61c75e60ae4842612f1bc49ad6a18a Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Tue, 6 May 2025 16:19:55 -0300 Subject: [PATCH 27/41] Clear up some disk space --- .github/workflows/run_tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index a9403b4..849d9db 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -233,6 +233,12 @@ jobs: export DOCKER_HOST=unix:///run/user/1001/podman/podman.sock echo "DOCKER_HOST=${DOCKER_HOST}" >> $GITHUB_ENV + - name: Free some disk space + run: | + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ + /usr/local/share/powershell \ + /usr/share/swift /usr/local/.ghcup /usr/lib/jvm + - name: Run test suite run: | env | grep DOCKER_HOST || true From d899c23c585419ce3d44775715896240b64f5f0c Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 7 May 2025 12:54:53 -0300 Subject: [PATCH 28/41] ci(tests): replace docker_engine -> docker-engine --- .github/workflows/run_tests.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 849d9db..3c2aff0 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -168,7 +168,7 @@ jobs: python-version: ["3.12"] arch: ["x64"] - docker_engine: ["docker"] + docker-engine: ["docker"] unit-tesseract: ${{ fromJson(needs.get-e2e-matrix.outputs.matrix) }} @@ -178,18 +178,18 @@ jobs: - os: "ubuntu-22.04" python-version: "3.12" arch: "arm" - docker_engine: "docker" + docker-engine: "docker" unit-tesseract: "base" # Run tests using Podman - os: "ubuntu-24.04" python-version: "3.12" arch: "x64" - docker_engine: "podman" + docker-engine: "podman" unit-tesseract: "base" - os: "ubuntu-24.04" python-version: "3.12" arch: "x64" - docker_engine: "podman" + docker-engine: "podman" unit-tesseract: "vectoradd_jax" fail-fast: false @@ -223,7 +223,7 @@ jobs: uv sync --extra dev --frozen - name: Set up Podman - if: matrix.docker_engine == 'podman' + if: matrix.docker-engine == 'podman' run: | systemctl start --user podman.socket systemctl status --user podman.socket From 3fdd90e4ac86631220499559955c9ee1ef90c392 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 7 May 2025 12:56:18 -0300 Subject: [PATCH 29/41] ci(tests): use Ubuntu 24.04 to run E2E tests --- .github/workflows/run_tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 3c2aff0..a2b0610 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -164,7 +164,7 @@ jobs: strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] python-version: ["3.12"] arch: ["x64"] @@ -175,7 +175,7 @@ jobs: include: # Test on arm to ensure compatibility with Apple M1 chips # (OSX runners don't have access to Docker so we use Linux ARM runners instead) - - os: "ubuntu-22.04" + - os: "ubuntu-24.04" python-version: "3.12" arch: "arm" docker-engine: "docker" @@ -194,7 +194,7 @@ jobs: fail-fast: false - runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-22.04' && 'ubuntu-22.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-22.04' && 'linux-arm64-ubuntu2204' || matrix.arch == 'x64' && matrix.os == 'ubuntu-24.04' && 'ubuntu-24.04'}} + runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-24.04' && 'ubuntu-24.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-24.04' && 'linux-arm64-ubuntu2404' || matrix.arch == 'x64' && matrix.os == 'ubuntu-24.04' && 'ubuntu-24.04'}} defaults: run: From 365cba616ba1c02218baebc520972250de2a906e Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 7 May 2025 12:57:11 -0300 Subject: [PATCH 30/41] refactor(sdk): better type hint for DockerException --- tesseract_core/sdk/docker_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tesseract_core/sdk/docker_client.py b/tesseract_core/sdk/docker_client.py index 915eb2d..e02f949 100644 --- a/tesseract_core/sdk/docker_client.py +++ b/tesseract_core/sdk/docker_client.py @@ -745,7 +745,7 @@ class DockerException(Exception): class BuildError(DockerException): """Raised when a build fails.""" - def __init__(self, build_log: list_) -> None: + def __init__(self, build_log: list_[str]) -> None: self.build_log = build_log From 451e7050c447a1ef68788004424586b9095e256e Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 7 May 2025 15:33:07 -0300 Subject: [PATCH 31/41] Make method private Signed-off-by: Heitor Pascoal de Bittencourt --- tesseract_core/sdk/docker_client.py | 4 ++-- tests/endtoend_tests/common.py | 2 +- tests/endtoend_tests/test_docker_client.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tesseract_core/sdk/docker_client.py b/tesseract_core/sdk/docker_client.py index e02f949..bf3f691 100644 --- a/tesseract_core/sdk/docker_client.py +++ b/tesseract_core/sdk/docker_client.py @@ -43,7 +43,7 @@ class Images: """Namespace for functions to interface with Tesseract docker images.""" @staticmethod - def tag_exists(image_name: str, tags: list_) -> bool: + def _tag_exists(image_name: str, tags: list_) -> bool: """Helper function to check if image name exists in the list of tags. Specially handling has to be done to achieve unfuzzy substring matching, i.e. @@ -115,7 +115,7 @@ def is_image_id(s: str) -> bool: image_obj.id == image_id_or_name or image_obj.short_id == image_id_or_name or image_id_or_name in image_obj.tags - or Images.tag_exists(image_id_or_name, image_obj.tags) + or Images._tag_exists(image_id_or_name, image_obj.tags) ): return image_obj diff --git a/tests/endtoend_tests/common.py b/tests/endtoend_tests/common.py index 32a977c..6684ff7 100644 --- a/tests/endtoend_tests/common.py +++ b/tests/endtoend_tests/common.py @@ -88,6 +88,6 @@ def build_tesseract( image_tags = json.loads(result.stdout.strip()) - assert client.images.tag_exists(image_name, image_tags) + assert client.images._tag_exists(image_name, image_tags) return image_tags[0] diff --git a/tests/endtoend_tests/test_docker_client.py b/tests/endtoend_tests/test_docker_client.py index 9f670e6..a132117 100644 --- a/tests/endtoend_tests/test_docker_client.py +++ b/tests/endtoend_tests/test_docker_client.py @@ -53,7 +53,7 @@ def test_get_image(docker_client, docker_client_built_image_name, docker_py_clie # Get the image image = docker_client.images.get(docker_client_built_image_name) assert image is not None - assert docker_client.images.tag_exists(docker_client_built_image_name, image.tags) + assert docker_client.images._tag_exists(docker_client_built_image_name, image.tags) docker_py_image = docker_py_client.images.get(docker_client_built_image_name) assert docker_py_image is not None @@ -121,7 +121,7 @@ def test_create_image( try: image = docker_client.images.get(docker_client_built_image_name) assert image is not None - assert docker_client.images.tag_exists( + assert docker_client.images._tag_exists( docker_client_built_image_name, image.tags ) @@ -141,7 +141,7 @@ def test_create_image( image1 = docker_client.images.get(image1_name) image1_name = image1_name + ":latest" assert image1 is not None - assert docker_client.images.tag_exists(image1_name, image1.tags) + assert docker_client.images._tag_exists(image1_name, image1.tags) assert image_exists(docker_client, image1_name) assert image_exists(docker_py_client, image1_name) @@ -158,7 +158,7 @@ def test_create_image( # Our docker client should be able to retrieve images with just the name image2 = docker_client.images.get(repo_url + image2_name) image2_no_url = docker_client.images.get(image2_name) - assert docker_client.images.tag_exists(image2_name, image2.tags) + assert docker_client.images._tag_exists(image2_name, image2.tags) assert image2 is not None assert image2_no_url is not None From c822fba00ad5eb519f0055f2bd9c40086de78b7f Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 7 May 2025 16:00:33 -0300 Subject: [PATCH 32/41] Readd check for image name in image tags Signed-off-by: Heitor Pascoal de Bittencourt --- tests/endtoend_tests/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/endtoend_tests/common.py b/tests/endtoend_tests/common.py index 6684ff7..d0252e1 100644 --- a/tests/endtoend_tests/common.py +++ b/tests/endtoend_tests/common.py @@ -87,6 +87,7 @@ def build_tesseract( assert result.exit_code == 0, result.exception image_tags = json.loads(result.stdout.strip()) + assert any(image_name in tag for tag in image_tags) assert client.images._tag_exists(image_name, image_tags) From a6cf5f4690f7f4e346d35aefb64144580fa9c8f2 Mon Sep 17 00:00:00 2001 From: Heitor Pascoal de Bittencourt Date: Wed, 7 May 2025 16:02:21 -0300 Subject: [PATCH 33/41] Revert "Readd check for image name in image tags" This reverts commit c822fba00ad5eb519f0055f2bd9c40086de78b7f. --- tests/endtoend_tests/common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/endtoend_tests/common.py b/tests/endtoend_tests/common.py index d0252e1..6684ff7 100644 --- a/tests/endtoend_tests/common.py +++ b/tests/endtoend_tests/common.py @@ -87,7 +87,6 @@ def build_tesseract( assert result.exit_code == 0, result.exception image_tags = json.loads(result.stdout.strip()) - assert any(image_name in tag for tag in image_tags) assert client.images._tag_exists(image_name, image_tags) From 3ef4bedf8b68b26c6b42a08faba3a9bfc9fb176e Mon Sep 17 00:00:00 2001 From: Angela Ko Date: Wed, 7 May 2025 22:47:04 -0400 Subject: [PATCH 34/41] Add docker_cleanup --- tests/endtoend_tests/test_docker_client.py | 103 ++++++++++----------- tests/endtoend_tests/test_endtoend.py | 2 +- tests/sdk_tests/test_engine.py | 5 +- 3 files changed, 54 insertions(+), 56 deletions(-) diff --git a/tests/endtoend_tests/test_docker_client.py b/tests/endtoend_tests/test_docker_client.py index a132117..e834cef 100644 --- a/tests/endtoend_tests/test_docker_client.py +++ b/tests/endtoend_tests/test_docker_client.py @@ -274,65 +274,60 @@ def test_create_container( def test_container_volume_mounts( - docker_client, docker_client_built_image_name, tmp_path + docker_client, docker_cleanup, docker_client_built_image_name, tmp_path ): """Test container volume mounts.""" container1 = None - try: - # Pytest creates the tmp_path fixture with drwx------ mode, we need others - # to be able to read and execute the path so the Docker volume is readable - # from within the container - tmp_path.chmod(0o0707) - - dest = Path("/foo/") - bar_file = dest / "hello.txt" - stdout = docker_client.containers.run( - docker_client_built_image_name, - [f"touch {bar_file} && chmod 777 {bar_file} && echo hello"], - detach=False, - volumes={tmp_path: {"bind": dest, "mode": "rw"}}, - remove=True, - ) + # Pytest creates the tmp_path fixture with drwx------ mode, we need others + # to be able to read and execute the path so the Docker volume is readable + # from within the container + tmp_path.chmod(0o0707) + + dest = Path("/foo/") + bar_file = dest / "hello.txt" + stdout = docker_client.containers.run( + docker_client_built_image_name, + [f"touch {bar_file} && chmod 777 {bar_file} && echo hello"], + detach=False, + volumes={tmp_path: {"bind": dest, "mode": "rw"}}, + remove=True, + ) - assert stdout == "hello\n" - # Check file exists in tmp path - assert (tmp_path / "hello.txt").exists() - - # Check container is removed and there are no running containers associated with the test image - result = subprocess.run( - [ - "docker", - "ps", - "--filter", - f"ancestor={docker_client_built_image_name}", - "-q", - ], - capture_output=True, - text=True, - check=True, - ) - assert result.stdout == "" - - # Open tmp_path/hello.txt in write mode - with open(tmp_path / "hello.txt", "w") as f: - f.write("hello tesseract\n") - - # Check we can read it in another container - container1 = docker_client.containers.run( - docker_client_built_image_name, - [f"cat {dest}/hello.txt"], - detach=True, - volumes={tmp_path: {"bind": dest, "mode": "rw"}}, - ) - status = container1.wait() - assert status["StatusCode"] == 0 - stdout = container1.logs(stdout=True, stderr=False) - assert stdout == b"hello tesseract\n" + assert stdout == "hello\n" + # Check file exists in tmp path + assert (tmp_path / "hello.txt").exists() - finally: - # Clean up the container - if container1: - container1.remove(v=True, force=True) + # Check container is removed and there are no running containers associated with the test image + result = subprocess.run( + [ + "docker", + "ps", + "--filter", + f"ancestor={docker_client_built_image_name}", + "-q", + ], + capture_output=True, + text=True, + check=True, + ) + assert result.stdout == "" + + # Open tmp_path/hello.txt in write mode + with open(tmp_path / "hello.txt", "w") as f: + f.write("hello tesseract\n") + + # Check we can read it in another container + container1 = docker_client.containers.run( + docker_client_built_image_name, + [f"cat {dest}/hello.txt"], + detach=True, + volumes={tmp_path: {"bind": dest, "mode": "rw"}}, + ) + docker_cleanup["containers"].append(container1) + status = container1.wait() + assert status["StatusCode"] == 0 + stdout = container1.logs(stdout=True, stderr=False) + assert stdout == b"hello tesseract\n" def test_compose_up_down( diff --git a/tests/endtoend_tests/test_endtoend.py b/tests/endtoend_tests/test_endtoend.py index 75a64c9..149eeb4 100644 --- a/tests/endtoend_tests/test_endtoend.py +++ b/tests/endtoend_tests/test_endtoend.py @@ -69,7 +69,7 @@ def test_build_from_init_endtoend( tag=img_tag, ) assert image_exists(docker_client, image_name) - docker_cleanup["images"].append(image_name) + docker_cleanup["images"].append(image_name + ":" + img_tag if img_tag else "") # Test that the image can be run and that --help is forwarded correctly result = cli_runner.invoke( diff --git a/tests/sdk_tests/test_engine.py b/tests/sdk_tests/test_engine.py index 16b68f1..3fd345b 100644 --- a/tests/sdk_tests/test_engine.py +++ b/tests/sdk_tests/test_engine.py @@ -36,7 +36,9 @@ def test_prepare_build_context(tmp_path_factory): @pytest.mark.parametrize("generate_only", [True, False]) -def test_build_tesseract(dummy_tesseract_package, mocked_docker, generate_only, caplog): +def test_build_tesseract( + dummy_tesseract_package, mocked_docker, docker_cleanup, generate_only, caplog +): """Test we can build an image for a package and keep build directory.""" src_dir = dummy_tesseract_package image_name = "unit_vectoradd" @@ -48,6 +50,7 @@ def test_build_tesseract(dummy_tesseract_package, mocked_docker, generate_only, image_tag, generate_only=generate_only, ) + docker_cleanup["images"].append(image_name + ":" + image_tag) if generate_only: assert isinstance(out, Path) From e754a173359d95aa9f4b5afcf15d17ab8923844c Mon Sep 17 00:00:00 2001 From: Angela Ko Date: Thu, 8 May 2025 01:48:35 -0400 Subject: [PATCH 35/41] remove unnecessary tag check --- tesseract_core/sdk/docker_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tesseract_core/sdk/docker_client.py b/tesseract_core/sdk/docker_client.py index bf3f691..f3fc46f 100644 --- a/tesseract_core/sdk/docker_client.py +++ b/tesseract_core/sdk/docker_client.py @@ -114,7 +114,6 @@ def is_image_id(s: str) -> bool: if ( image_obj.id == image_id_or_name or image_obj.short_id == image_id_or_name - or image_id_or_name in image_obj.tags or Images._tag_exists(image_id_or_name, image_obj.tags) ): return image_obj From 5e153d3a5ec35a8b16b1e01bd517c9a05c33af84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dion=20H=C3=A4fner?= Date: Thu, 8 May 2025 09:54:04 +0200 Subject: [PATCH 36/41] convert port ranges to ports before passing to docker-compose --- tesseract_core/sdk/engine.py | 39 ++++++++++++++++++++++----- tests/endtoend_tests/test_endtoend.py | 9 ------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/tesseract_core/sdk/engine.py b/tesseract_core/sdk/engine.py index dafdd21..d113005 100644 --- a/tesseract_core/sdk/engine.py +++ b/tesseract_core/sdk/engine.py @@ -10,10 +10,12 @@ import os import random import shlex +import socket import string import tempfile import threading from collections.abc import Callable, Sequence +from contextlib import closing from pathlib import Path from shutil import copy, copytree, rmtree from typing import Any @@ -116,14 +118,31 @@ def wrapper_needs_docker(*args: Any, **kwargs: Any) -> None: return wrapper_needs_docker -def get_free_port() -> int: - """Find a free port to use for HTTP.""" - import socket - from contextlib import closing +def get_free_port(within_range: tuple[int, int] | None = None) -> int: + """Find a random free port to use for HTTP.""" + if within_range is None: + # Let OS pick a random free port + with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: + s.bind(("localhost", 0)) + return s.getsockname()[1] - with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: - s.bind(("localhost", 0)) - return s.getsockname()[1] + start, end = within_range + if start < 0 or end > 65535 or start >= end: + raise ValueError("Invalid port range") + + # Try random ports in the given range + portlist = list(range(start, end)) + random.shuffle(portlist) + for port in portlist: + with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: + try: + s.bind(("localhost", port)) + except OSError: + # Port is already in use + continue + else: + return port + raise RuntimeError(f"No free ports found in range {start}-{end}") def parse_requirements( @@ -560,6 +579,12 @@ def _create_docker_compose_template( if ports is None: ports = [str(get_free_port()) for _ in range(len(image_ids))] + # Convert port ranges to fixed ports + for i, port in enumerate(ports): + if "-" in port: + port_start, port_end = port.split("-") + ports[i] = str(get_free_port(within_range=(int(port_start), int(port_end)))) + gpu_settings = None if gpus: if (len(gpus) == 1) and (gpus[0] == "all"): diff --git a/tests/endtoend_tests/test_endtoend.py b/tests/endtoend_tests/test_endtoend.py index 149eeb4..3da7de7 100644 --- a/tests/endtoend_tests/test_endtoend.py +++ b/tests/endtoend_tests/test_endtoend.py @@ -4,7 +4,6 @@ """End-to-end tests for Tesseract workflows.""" import json -import os from pathlib import Path import pytest @@ -325,14 +324,6 @@ def test_tesseract_serve_ports(built_image_name, port, docker_cleanup): cli_runner = CliRunner(mix_stderr=False) project_id = None - docker_host = os.environ.get("DOCKER_HOST", "") - - if "-" in port and "podman" in docker_host: - pytest.skip( - "Podman does not support port ranges in compose." - "See https://github.com/containers/podman/issues/15111" - ) - # Serve tesseract on specified ports. run_res = cli_runner.invoke( app, From 597282d6c3421aeac8685f81f6075329edfad613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dion=20H=C3=A4fner?= Date: Thu, 8 May 2025 14:17:45 +0200 Subject: [PATCH 37/41] fix tests --- tests/conftest.py | 55 ++++++++++++++++++++------- tests/endtoend_tests/test_endtoend.py | 3 +- tests/sdk_tests/test_engine.py | 5 +-- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index b7c2bdd..58db7e8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,6 +7,8 @@ import string from pathlib import Path from shutil import copytree +from textwrap import indent +from traceback import format_exception from typing import Any import pytest @@ -190,51 +192,76 @@ def docker_client(): @pytest.fixture(scope="module") def docker_cleanup(docker_client, request): """Clean up all tesseracts created by the tests.""" - from tesseract_core.sdk.docker_client import ContainerError, ImageNotFound - # Shared object to track what objects need to be cleaned up in each test context = {"images": [], "project_ids": [], "containers": []} + def pprint_exc(e: BaseException) -> str: + """Pretty print exception.""" + return "".join( + indent(line, " ") for line in format_exception(type(e), e, e.__traceback__) + ) + def cleanup_func(): + failures = [] + # Teardown projects first for project_id in context["project_ids"]: - if docker_client.compose.exists(project_id): + try: docker_client.compose.down(project_id) + except Exception as e: + failures.append( + f"Failed to tear down project {project_id}: {pprint_exc(e)}" + ) # Remove containers for container in context["containers"]: try: - container_obj = docker_client.containers.get(container) - except ContainerError: - continue - container_obj.remove(v=True, force=True) + if isinstance(container, str): + container_obj = docker_client.containers.get(container.id) + else: + container_obj = container + + container_obj.remove(v=True, force=True) + except Exception as e: + failures.append( + f"Failed to remove container {container}: {pprint_exc(e)}" + ) # Remove images for image in context["images"]: try: - docker_client.images.remove(image) - except ImageNotFound: - continue + if isinstance(image, str): + image_obj = docker_client.images.get(image) + else: + image_obj = image + + docker_client.images.remove(image_obj.id) + except Exception as e: + failures.append(f"Failed to remove image {image}: {pprint_exc(e)}") + + if failures: + raise RuntimeError( + "Failed to clean up some Docker objects during test teardown:\n" + + "\n".join(failures) + ) request.addfinalizer(cleanup_func) return context @pytest.fixture -def dummy_image_name(docker_cleanup): +def dummy_image_name(): """Create a dummy image name, and clean up after the test.""" image_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=16)) image_name = f"tmp_tesseract_image_{image_id}" - docker_cleanup["images"].append(image_name) yield image_name @pytest.fixture(scope="module") -def shared_dummy_image_name(docker_cleanup): +def shared_dummy_image_name(): """Create a dummy image name, and clean up after all tests.""" image_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=16)) image_name = f"tmp_tesseract_image_{image_id}" - docker_cleanup["images"].append(image_name) yield image_name diff --git a/tests/endtoend_tests/test_endtoend.py b/tests/endtoend_tests/test_endtoend.py index 3da7de7..39599d5 100644 --- a/tests/endtoend_tests/test_endtoend.py +++ b/tests/endtoend_tests/test_endtoend.py @@ -67,8 +67,9 @@ def test_build_from_init_endtoend( config_override=config_override, tag=img_tag, ) + + docker_cleanup["images"].append(image_name) assert image_exists(docker_client, image_name) - docker_cleanup["images"].append(image_name + ":" + img_tag if img_tag else "") # Test that the image can be run and that --help is forwarded correctly result = cli_runner.invoke( diff --git a/tests/sdk_tests/test_engine.py b/tests/sdk_tests/test_engine.py index 3fd345b..16b68f1 100644 --- a/tests/sdk_tests/test_engine.py +++ b/tests/sdk_tests/test_engine.py @@ -36,9 +36,7 @@ def test_prepare_build_context(tmp_path_factory): @pytest.mark.parametrize("generate_only", [True, False]) -def test_build_tesseract( - dummy_tesseract_package, mocked_docker, docker_cleanup, generate_only, caplog -): +def test_build_tesseract(dummy_tesseract_package, mocked_docker, generate_only, caplog): """Test we can build an image for a package and keep build directory.""" src_dir = dummy_tesseract_package image_name = "unit_vectoradd" @@ -50,7 +48,6 @@ def test_build_tesseract( image_tag, generate_only=generate_only, ) - docker_cleanup["images"].append(image_name + ":" + image_tag) if generate_only: assert isinstance(out, Path) From 41bc3cf3df7263ca5d46fca6340e9b819f02c981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dion=20H=C3=A4fner?= Date: Thu, 8 May 2025 14:18:43 +0200 Subject: [PATCH 38/41] try w/o deletion --- .github/workflows/run_tests.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index a2b0610..aa0736a 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -233,11 +233,11 @@ jobs: export DOCKER_HOST=unix:///run/user/1001/podman/podman.sock echo "DOCKER_HOST=${DOCKER_HOST}" >> $GITHUB_ENV - - name: Free some disk space - run: | - sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ - /usr/local/share/powershell \ - /usr/share/swift /usr/local/.ghcup /usr/lib/jvm + # - name: Free some disk space + # run: | + # sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ + # /usr/local/share/powershell \ + # /usr/share/swift /usr/local/.ghcup /usr/lib/jvm - name: Run test suite run: | @@ -253,12 +253,12 @@ jobs: -k "not test_examples" else uv run --no-sync pytest \ - --always-run-endtoend \ - --cov-report=term-missing:skip-covered \ - --cov-report=xml:coverage.xml \ - --cov=tesseract_core \ - tests/endtoend_tests/test_examples.py \ - -k "[${{ matrix.unit-tesseract }}]" + --always-run-endtoend \ + --cov-report=term-missing:skip-covered \ + --cov-report=xml:coverage.xml \ + --cov=tesseract_core \ + tests/endtoend_tests/test_examples.py \ + -k "[${{ matrix.unit-tesseract }}]" fi - name: Upload coverage reports to Codecov From bed1ce5d35831ee947e025132e1636ba8147c3e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dion=20H=C3=A4fner?= Date: Thu, 8 May 2025 15:36:27 +0200 Subject: [PATCH 39/41] cleanup after every test by default --- tests/conftest.py | 11 +++++++++++ tests/endtoend_tests/test_endtoend.py | 7 +++++-- tests/endtoend_tests/test_tesseract_sdk.py | 7 +++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 58db7e8..f7057a6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -190,7 +190,18 @@ def docker_client(): @pytest.fixture(scope="module") +def docker_cleanup_module(docker_client, request): + """Clean up all tesseracts created by the tests after the module exits.""" + return _docker_cleanup(docker_client, request) + + +@pytest.fixture def docker_cleanup(docker_client, request): + """Clean up all tesseracts created by the tests after the test exits.""" + return _docker_cleanup(docker_client, request) + + +def _docker_cleanup(docker_client, request): """Clean up all tesseracts created by the tests.""" # Shared object to track what objects need to be cleaned up in each test context = {"images": [], "project_ids": [], "containers": []} diff --git a/tests/endtoend_tests/test_endtoend.py b/tests/endtoend_tests/test_endtoend.py index 39599d5..36774b9 100644 --- a/tests/endtoend_tests/test_endtoend.py +++ b/tests/endtoend_tests/test_endtoend.py @@ -17,14 +17,17 @@ @pytest.fixture(scope="module") def built_image_name( - docker_client, docker_cleanup, shared_dummy_image_name, dummy_tesseract_location + docker_client, + docker_cleanup_module, + shared_dummy_image_name, + dummy_tesseract_location, ): """Build the dummy Tesseract image for the tests.""" image_name = build_tesseract( docker_client, dummy_tesseract_location, shared_dummy_image_name ) assert image_exists(docker_client, image_name) - docker_cleanup["images"].append(image_name) + docker_cleanup_module["images"].append(image_name) yield image_name diff --git a/tests/endtoend_tests/test_tesseract_sdk.py b/tests/endtoend_tests/test_tesseract_sdk.py index a95cbda..b05b273 100644 --- a/tests/endtoend_tests/test_tesseract_sdk.py +++ b/tests/endtoend_tests/test_tesseract_sdk.py @@ -24,14 +24,17 @@ @pytest.fixture(scope="module") def built_image_name( - docker_client, shared_dummy_image_name, dummy_tesseract_location, docker_cleanup + docker_client, + shared_dummy_image_name, + dummy_tesseract_location, + docker_cleanup_module, ): """Build the dummy Tesseract image for the tests.""" image_name = build_tesseract( docker_client, dummy_tesseract_location, shared_dummy_image_name ) assert image_exists(docker_client, image_name) - docker_cleanup["images"].append(image_name) + docker_cleanup_module["images"].append(image_name) yield image_name From 9f2b81ca3a41874bf1b810d246e213c363253010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dion=20H=C3=A4fner?= Date: Thu, 8 May 2025 16:10:03 +0200 Subject: [PATCH 40/41] use cpu torch --- .../sdk/templates/pytorch/tesseract_requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tesseract_core/sdk/templates/pytorch/tesseract_requirements.txt b/tesseract_core/sdk/templates/pytorch/tesseract_requirements.txt index d0b29f2..0133df7 100644 --- a/tesseract_core/sdk/templates/pytorch/tesseract_requirements.txt +++ b/tesseract_core/sdk/templates/pytorch/tesseract_requirements.txt @@ -2,7 +2,8 @@ # Generated by tesseract {{version}} on {{timestamp}} # Add Python requirements like this: -torch --index-url https://download.pytorch.org/whl/cpu +--index-url https://download.pytorch.org/whl/cpu +torch # This may contain private dependencies via SSH URLs: # git+ssh://git@github.com/username/repo.git@branch From fe96ccb3e7bf3582a16c3b7258054a9d6103c3c1 Mon Sep 17 00:00:00 2001 From: Angela Ko Date: Thu, 8 May 2025 10:29:20 -0400 Subject: [PATCH 41/41] Address comments --- .github/workflows/run_tests.yml | 15 +-------------- tests/endtoend_tests/test_docker_client.py | 2 +- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 154cb97..fa537d8 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -186,15 +186,10 @@ jobs: arch: "x64" docker-engine: "podman" unit-tesseract: "base" - - os: "ubuntu-24.04" - python-version: "3.12" - arch: "x64" - docker-engine: "podman" - unit-tesseract: "vectoradd_jax" fail-fast: false - runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-24.04' && 'ubuntu-24.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-24.04' && 'linux-arm64-ubuntu2404' || matrix.arch == 'x64' && matrix.os == 'ubuntu-24.04' && 'ubuntu-24.04'}} + runs-on: ${{ matrix.arch == 'x64' && matrix.os == 'ubuntu-24.04' && 'ubuntu-24.04' || matrix.arch == 'arm' && matrix.os == 'ubuntu-24.04' && 'linux-arm64-ubuntu2404'}} defaults: run: @@ -233,16 +228,8 @@ jobs: export DOCKER_HOST=unix:///run/user/1001/podman/podman.sock echo "DOCKER_HOST=${DOCKER_HOST}" >> $GITHUB_ENV - # - name: Free some disk space - # run: | - # sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ - # /usr/local/share/powershell \ - # /usr/share/swift /usr/local/.ghcup /usr/lib/jvm - - name: Run test suite run: | - env | grep DOCKER_HOST || true - if [ "${{ matrix.unit-tesseract }}" == "base" ]; then uv run --no-sync pytest \ --always-run-endtoend \ diff --git a/tests/endtoend_tests/test_docker_client.py b/tests/endtoend_tests/test_docker_client.py index e834cef..6a8f239 100644 --- a/tests/endtoend_tests/test_docker_client.py +++ b/tests/endtoend_tests/test_docker_client.py @@ -175,7 +175,7 @@ def test_create_image( for client in [docker_client, docker_py_client]: assert not image_exists(client, "create_image") - if podman and client == docker_py_client: + if (podman and client == docker_py_client) or client == docker_client: # Docker-py does not support partial string matching assert image_exists(client, image2_name) assert image_exists(client, f"/{image2_name}")