diff --git a/.copier-answers.yml b/.copier-answers.yml index 0ab4a4b0c..715ad8ad7 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -5,7 +5,7 @@ author_email: helmut@aignostics.com author_github_username: helmut-hoffer-von-ankershoffen author_name: Helmut Hoffer von Ankershoffen codeql_enabled: true -docker_io_enabled: true +docker_io_enabled: false docker_io_image_name: aignostics-python-sdk docker_io_owner: helmuthva github_repository_name: python-sdk diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 5884a8369..000000000 --- a/.dockerignore +++ /dev/null @@ -1,96 +0,0 @@ -# .dockerignore of project Aignostics Python SDK - -# .dockerfile specifics - -**/.git -**/.gitignore -Dockerfile -docker-compose.yml - -# Environment -**/.env -**/.env.backup -**/.env.bak -**/ENV/ -**/env/ -**/.envrc - -## secrets -**/.secret -**/.secrets -**/.secrets.toml -**/.secrets.yaml -**/.secrets.yml -**/.secrets.json -**/.act-env-secret - -# More secrets -**/.ssh -**/.aws - -# Python virtual environment -**/venv/ -**/.venv/ - -# Python temps -**/*.py[cdo] -**/__pycache__/ -**/*.so -**/*.egg -**/*.egg-info/ -**/*.log -**/dist/ -**/build/ -**/eggs/ -**/parts/ -**/sdist/ -**/develop-eggs/ -**/.installed.cfg -**/.Python -**/.pytest_cache/ -**/.ruff_cache/ -**/.nox -**/.dmypy.json -**/.mypy_cache/ -**/.coverage -**/.coverage.* - - -# Build Report -**/reports/* -**/!reports/.keep -**/!reports/README.md - -# IDE -**/.idea/ -**/*.swp -**/*.swo - -# macOS -**/.DS_Store - -# Other OS -**/lib/ -**/lib64/ - -# Data -**/var/ -**/tmp/ - -# Node temps -**/node_modules/ - -# AI workflow -**/.fixme - -# Copier -**/*.rej - -# Vercel -**/.vercel - -# Scalene -profile.json -profile.html - -# Application specific diff --git a/.github/CLAUDE.md b/.github/CLAUDE.md index e384561be..d1d550b5b 100644 --- a/.github/CLAUDE.md +++ b/.github/CLAUDE.md @@ -13,7 +13,7 @@ workflows (`> …`, prefixed `_`) are called via `uses:`. | Entry point | Trigger | Calls | |-------------|---------|-------| -| `ci-cd.yml` | push (main, release/v*, tag v*.*.*), PR (main, release/v*), release created, workflow_dispatch | `_lint`, `_docs`, `_audit`, `_test`, `_codeql`, `_ketryx_report_and_check`, `_package-publish`, `_docker-publish` | +| `ci-cd.yml` | push (main, release/v*, tag v*.*.*), PR (main, release/v*), release created, workflow_dispatch | `_lint`, `_docs`, `_audit`, `_test`, `_codeql`, `_ketryx_report_and_check`, `_package-publish` | | `prepare-release.yml` | workflow_dispatch | — | | `publish-release.yml` | workflow_dispatch | — | | `merge-release.yml` | workflow_dispatch | — | @@ -30,9 +30,9 @@ workflows (`> …`, prefixed `_`) are called via `uses:`. | `labels-sync.yml` | push to label config | — | Reusable: `_lint`, `_docs`, `_audit`, `_test`, `_codeql`, -`_ketryx_report_and_check`, `_package-publish`, `_docker-publish`, -`_build-native-only`, `_claude-code`, `_scheduled-audit`, -`_scheduled-test-hourly`, `_scheduled-test-daily`, `_scheduled-test-stress`. +`_ketryx_report_and_check`, `_package-publish`, `_build-native-only`, +`_claude-code`, `_scheduled-audit`, `_scheduled-test-hourly`, +`_scheduled-test-daily`, `_scheduled-test-stress`. (`stress-testing-staging.yml.paused` is currently disabled.) ## Main pipeline (`ci-cd.yml`) @@ -46,7 +46,7 @@ Reusable: `_lint`, `_docs`, `_audit`, `_test`, `_codeql`, **Jobs**: `get-commit-message` (extracts commit message + release version from branch), `lint`, `docs`, `audit`, `test`, `codeql`, `sonarcloud`, -`ketryx_report_and_check`, `package_publish`, `docker_publish`. +`ketryx_report_and_check`, `package_publish`. **Dependency graph**: @@ -54,11 +54,10 @@ branch), `lint`, `docs`, `audit`, `test`, `codeql`, `sonarcloud`, get-commit-message ──> lint, docs, audit, test, codeql test ──> sonarcloud lint, audit, test, codeql, sonarcloud, docs ──> ketryx_report_and_check - ├──> package_publish (tags only) - └──> docker_publish (tags only) + └──> package_publish (tags only) ``` -`package_publish`/`docker_publish` run only on `refs/tags/v*`. +`package_publish` runs only on `refs/tags/v*`. `ketryx_report_and_check` is skipped for `dependabot[bot]`. ## Test execution (`_test.yml`) @@ -177,8 +176,7 @@ GitHub secrets: `ANTHROPIC_API_KEY`, `AIGNOSTICS_REFRESH_TOKEN_{STAGING,PRODUCTION}`, `GCP_CREDENTIALS_{STAGING,PRODUCTION}` (base64 JSON), the BetterStack URLs above, `CODECOV_TOKEN`, `SONAR_TOKEN`, `SENTRY_DSN`/`SENTRY_AUTH_TOKEN`, -`UV_PUBLISH_TOKEN`, `DOCKER_USERNAME`/`DOCKER_PASSWORD`, -`KETRYX_PROJECT`/`KETRYX_API_KEY`, `SLACK_*_RELEASE_ANNOUNCEMENT`. +`UV_PUBLISH_TOKEN`, `KETRYX_PROJECT`/`KETRYX_API_KEY`, `SLACK_*_RELEASE_ANNOUNCEMENT`. Local `.env` for E2E: diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3e38c276a..721dc6e63 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -86,9 +86,8 @@ make lint # Ruff formatting + MyPy type checking - Add dependencies: `uv add ` **Testing:** -- Pytest with markers: `sequential`, `long_running`, `scheduled`, `docker`, `skip_with_act` +- Pytest with markers: `sequential`, `long_running`, `scheduled`, `skip_with_act` - Run specific tests: `uv run pytest tests/path/test.py::test_function` -- Docker integration: `make test-docker` ## Code Patterns & Standards diff --git a/.github/workflows/_build-native-only.yml b/.github/workflows/_build-native-only.yml index 2bc288a44..a647d9f38 100644 --- a/.github/workflows/_build-native-only.yml +++ b/.github/workflows/_build-native-only.yml @@ -35,7 +35,7 @@ jobs: attestations: write contents: read id-token: write - packages: write + packages: read steps: - name: Checkout diff --git a/.github/workflows/_docker-publish.yml b/.github/workflows/_docker-publish.yml deleted file mode 100644 index 8c3431914..000000000 --- a/.github/workflows/_docker-publish.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: "> Docker Publish" - -on: - workflow_call: - secrets: - DOCKER_USERNAME: - required: false - DOCKER_PASSWORD: - required: false - -jobs: - docker_publish: - runs-on: ubuntu-latest - permissions: - attestations: write - contents: read - id-token: write - packages: write - - env: - DOCKER_IO_REGISTRY: docker.io - DOCKER_IO_IMAGE_NAME_ALL: helmuthva/aignostics-python-sdk - DOCKER_IO_IMAGE_NAME_SLIM: helmuthva/aignostics-python-sdk-slim - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - name: Install dev tools - shell: bash - run: .github/workflows/_install_dev_tools.bash - - - name: Set up QEMU - uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - - - name: Log in to Docker Hub - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Log in to GitHub container registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: "(all target): Extract metadata (tags, labels) for Docker" - id: meta-all - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 - with: - images: | - ${{ env.DOCKER_IO_IMAGE_NAME_ALL }} - ghcr.io/${{ github.repository }} - tags: | - # set latest tag for releases - type=raw,value=latest - # set semver tags from git tags (v1.2.3 -> 1.2.3, 1.2, 1) - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: "(slim target): Extract metadata (tags, labels) for Docker" - id: meta-slim - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 - with: - images: | - ${{ env.DOCKER_IO_IMAGE_NAME_SLIM }} - ghcr.io/${{ github.repository }}-slim - tags: | - # set latest tag for releases - type=raw,value=latest - # set semver tags from git tags (v1.2.3 -> 1.2.3, 1.2, 1) - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: "(all target): Build and push Docker image" - id: build-and-push-all - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 - with: - context: . - file: ./Dockerfile - target: all - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.meta-all.outputs.tags }} - labels: ${{ steps.meta-all.outputs.labels }} - - - name: "(slim target): Build and push Docker image" - id: build-and-push-slim - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 - with: - context: . - file: ./Dockerfile - target: slim - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.meta-slim.outputs.tags }} - labels: ${{ steps.meta-slim.outputs.labels }} diff --git a/.github/workflows/_package-publish.yml b/.github/workflows/_package-publish.yml index f3d0a182c..ae6815cfa 100644 --- a/.github/workflows/_package-publish.yml +++ b/.github/workflows/_package-publish.yml @@ -44,7 +44,7 @@ jobs: attestations: write contents: read id-token: write - packages: write + packages: read steps: - name: Checkout diff --git a/.github/workflows/_scheduled-test-daily.yml b/.github/workflows/_scheduled-test-daily.yml index 106c586df..bb06e57a7 100644 --- a/.github/workflows/_scheduled-test-daily.yml +++ b/.github/workflows/_scheduled-test-daily.yml @@ -45,7 +45,7 @@ jobs: attestations: write contents: read id-token: write - packages: write + packages: read steps: - name: Checkout diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 3096c9ea8..4ca6e4b03 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -69,7 +69,7 @@ jobs: attestations: write contents: read id-token: write - packages: write + packages: read steps: - name: Checkout diff --git a/.github/workflows/build-native-only.yml b/.github/workflows/build-native-only.yml index 11d686ea2..78d4cc75e 100644 --- a/.github/workflows/build-native-only.yml +++ b/.github/workflows/build-native-only.yml @@ -81,4 +81,4 @@ jobs: attestations: write contents: write id-token: write - packages: write + packages: read diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 798d0bfd3..e1bcb88ab 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -134,7 +134,7 @@ jobs: attestations: write contents: read id-token: write - packages: write + packages: read secrets: AIGNOSTICS_CLIENT_ID_DEVICE_STAGING: ${{ secrets.AIGNOSTICS_CLIENT_ID_DEVICE_STAGING }} AIGNOSTICS_REFRESH_TOKEN_STAGING: ${{ secrets.AIGNOSTICS_REFRESH_TOKEN_STAGING }} @@ -195,7 +195,7 @@ jobs: attestations: write contents: write id-token: write - packages: write + packages: read secrets: KETRYX_PROJECT: ${{ secrets.KETRYX_PROJECT }} KETRYX_API_KEY: ${{ secrets.KETRYX_API_KEY }} @@ -214,26 +214,9 @@ jobs: attestations: write contents: write id-token: write - packages: write + packages: read secrets: UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }} SLACK_WEBHOOK_URL_RELEASE_ANNOUNCEMENT: ${{ secrets.SLACK_WEBHOOK_URL_RELEASE_ANNOUNCEMENT }} SLACK_CHANNEL_ID_RELEASE_ANNOUNCEMENT: ${{ secrets.SLACK_CHANNEL_ID_RELEASE_ANNOUNCEMENT }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - - docker_publish: - needs: [get-commit-message, ketryx_report_and_check] - if: | - (startsWith(github.ref, 'refs/tags/v') && (!contains(needs.get-commit-message.outputs.commit_message, 'skip:ci'))) && - (!contains(needs.get-commit-message.outputs.commit_message, 'build:native:only')) && - (!contains(github.event.pull_request.labels.*.name, 'skip:ci')) && - (!contains(github.event.pull_request.labels.*.name, 'build:native:only')) - uses: ./.github/workflows/_docker-publish.yml - permissions: - attestations: write - contents: read - id-token: write - packages: write - secrets: - DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/scheduled-testing-production-daily.yml b/.github/workflows/scheduled-testing-production-daily.yml index a00b0b774..4f27d4b05 100644 --- a/.github/workflows/scheduled-testing-production-daily.yml +++ b/.github/workflows/scheduled-testing-production-daily.yml @@ -21,7 +21,7 @@ jobs: attestations: write contents: read id-token: write - packages: write + packages: read secrets: AIGNOSTICS_CLIENT_ID_DEVICE_STAGING: ${{ secrets.AIGNOSTICS_CLIENT_ID_DEVICE_STAGING }} AIGNOSTICS_REFRESH_TOKEN_STAGING: ${{ secrets.AIGNOSTICS_REFRESH_TOKEN_STAGING }} diff --git a/.github/workflows/scheduled-testing-staging-daily.yml b/.github/workflows/scheduled-testing-staging-daily.yml index 64d9b6f2d..c38a7e9d8 100644 --- a/.github/workflows/scheduled-testing-staging-daily.yml +++ b/.github/workflows/scheduled-testing-staging-daily.yml @@ -21,7 +21,7 @@ jobs: attestations: write contents: read id-token: write - packages: write + packages: read secrets: AIGNOSTICS_CLIENT_ID_DEVICE_STAGING: ${{ secrets.AIGNOSTICS_CLIENT_ID_DEVICE_STAGING }} AIGNOSTICS_REFRESH_TOKEN_STAGING: ${{ secrets.AIGNOSTICS_REFRESH_TOKEN_STAGING }} diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md index 4177f1ddd..cc6c4f3f4 100644 --- a/ATTRIBUTIONS.md +++ b/ATTRIBUTIONS.md @@ -29803,38 +29803,6 @@ DEALINGS IN THE SOFTWARE. ``` -## pytest-docker (3.2.5) - MIT License - -Simple pytest fixtures for Docker and Docker Compose based tests - -* URL: https://github.com/avast/pytest-docker -* Author(s): Max K., Andre Caron - -### License Text - -``` -Copyright (c) 2017 pytest-docker contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -``` - ## pytest-durations (1.9.0) - MIT License Pytest plugin reporting fixtures and test functions execution time. diff --git a/CLAUDE.md b/CLAUDE.md index d278164b5..9d9a2eb0e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -558,7 +558,7 @@ The other markers gate execution: `long_running` / `very_long_running` (excluded from `make test`; run via `make test_long_running` etc.; skippable/enable-able via PR labels `skip:test:long_running` / `enable:test:very_long_running`), `scheduled` / `scheduled_only`, `stress` / `stress_only`, `sequential`, -`docker`, `skip_with_act`, `no_extras`. +`skip_with_act`, `no_extras`. Tests live under `tests/aignostics//` mirroring the source layout; see `tests/CLAUDE.md`. @@ -1053,7 +1053,7 @@ This triggers `merge-release.yml`, which: ``` make prepare-release → push to release/vX.Y.Z → lint + test + audit + Ketryx -make publish-release → push vX.Y.Z tag → full CI + PyPI + Docker + GitHub release +make publish-release → push vX.Y.Z tag → full CI + PyPI + GitHub release make merge-release → push to main → full CI pipeline ``` diff --git a/CODE_STYLE.md b/CODE_STYLE.md index c19ae9ce3..bdceabd27 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -114,10 +114,9 @@ We use [pytest](https://docs.pytest.org/en/stable/) for testing Python code. 3. We leverage several pytest plugins: 1. `pytest-asyncio` for testing async code 2. `pytest-cov` for coverage reporting - 3. `pytest-docker` for integration tests with containers - 4. `pytest-env` for environment variable management - 5. `pytest-regressions` for regression testing - 6. `pytest-xdist` for parallel test execution + 3. `pytest-env` for environment variable management + 4. `pytest-regressions` for regression testing + 5. `pytest-xdist` for parallel test execution 4. Test execution is automated through the nox test session which runs across the Python versions indicated in the `pyproject.toml`. @@ -132,7 +131,6 @@ Apart from unit tests we provide integration tests and end-to-end tests: 1. We smoke test as part of the CI/CD pipeline. 2. We facilitate exploratory testing to ensure comprehensive coverage. -3. We use `pytest-docker` for integration tests with containers. ## Error Handling diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 350fe0b5c..e42cd143f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -165,7 +165,7 @@ Point the Ketryx release to the `release/vX.Y.Z` branch and collect required app make publish-release ``` -Generates `CHANGELOG.md`, creates the `vX.Y.Z` tag, and pushes — triggering CI/CD which publishes to PyPI, Docker registries, and creates a GitHub release (Ketryx check must pass first). +Generates `CHANGELOG.md`, creates the `vX.Y.Z` tag, and pushes — triggering CI/CD which publishes to PyPI and creates a GitHub release (Ketryx check must pass first). **Phase 4 — Merge back to main:** @@ -196,54 +196,6 @@ Notes: 1. Workflow defined in `.github/workflows/*.yml` 2. ci-cd.yml calls all build steps defined in noxfile.py -### Docker - -Build and run the Docker image with plain Docker - -```shell -# Build from Dockerimage -make docker_build # builds targets all and slim - -# Run the CLI -docker run --env THE_VAR=THE_VALUE -t aignostics --target all --help # target with all extras -docker run --env THE_VAR=THE_VALUE -t aignostics --target slim --help # slim flavor, no extras -``` - -Build and run the Docker image with docker compose: - -```shell -echo "Building the Docker image with docker compose and running CLI..." -docker compose run --build aignostics --help -echo "Building the Docker image with docker compose and running API container as a daemon ..." -docker compose up --build -d -echo "Waiting for the API server to start..." -sleep 5 -echo "Checking health of v1 API ..." -curl http://127.0.0.1:8000/api/v1/healthz -echo "" -echo "Saying hello world with v1 API ..." -curl http://127.0.0.1:8000/api/v1/hello/world -echo "" -echo "Swagger docs of v1 API ..." -curl http://127.0.0.1:8000/api/v1/docs -echo "" -echo "Checking health of v2 API ..." -curl http://127.0.0.1:8000/api/v2/healthz -echo "" -echo "Saying hello world with v1 API ..." -curl http://127.0.0.1:8000/api/v2/hello/world -echo "" -echo "Swagger docs of v2 API ..." -curl http://127.0.0.1:8000/api/v2/docs -echo "" -echo "Shutting down the API container ..." -docker compose down -``` - -Notes: - -1. The API service is run based on the slim Docker image. Change in compose.yaml if you need the API service to run on the fat image. - ### Pinning GitHub Actions ```shell diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index ab056b388..000000000 --- a/Dockerfile +++ /dev/null @@ -1,136 +0,0 @@ -# We share the base in the builder and targets -FROM python:3.14.3-slim-trixie AS base - -# The base of our builder -FROM base AS builder - -# Copy in UV -COPY --from=ghcr.io/astral-sh/uv:0.11.7 /uv /bin/uv - -# We use the system interpreter managed by uv -ENV UV_PYTHON_DOWNLOADS=0 - -# Enable bytecode compilation -ENV UV_COMPILE_BYTECODE=1 - -# Give more time with Python 3.14 on GHA with Qemu for arm -ENV UV_COMPILE_BYTECODE_TIMEOUT=300 - -# Copy from the cache instead of linking since it's a mounted volume -ENV UV_LINK_MODE=copy - -# Create and set workdir -WORKDIR /app - - -# The slim builder does not take in the extras -FROM builder AS builder-slim - -# Install the project's dependencies using the lockfile and settings -RUN --mount=type=cache,target=/root/.cache/uv \ - --mount=type=bind,source=uv.lock,target=uv.lock \ - --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ - uv sync --frozen --no-install-project --no-dev --no-editable - -# Then, add the rest of the project source code and install it -# Installing separately from its dependencies allows optimal layer caching -COPY pyproject.toml /app -COPY .python-version /app -COPY uv.lock /app -COPY src /app/src -COPY LICENSE /app -COPY *.md /app - -COPY .env.example /app/.env.example -COPY tests /app/tests -COPY examples /app/examples - -# Install project specifics -# Nothing yet - -RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --frozen --no-dev --no-editable - - -# The all builder takes in all extras -FROM builder AS builder-all - -# Install the project's dependencies using the lockfile and settings -RUN --mount=type=cache,target=/root/.cache/uv \ - --mount=type=bind,source=uv.lock,target=uv.lock \ - --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ - uv sync --frozen --no-install-project --all-extras --no-dev --no-editable - -# Then, add the rest of the project source code and install it -# Installing separately from its dependencies allows optimal layer caching -COPY pyproject.toml /app -COPY .python-version /app -COPY uv.lock /app -COPY src /app/src -COPY LICENSE /app -COPY *.md /app - -COPY .env.example /app/.env.example -COPY tests /app/tests -COPY examples /app/examples - -# Install project specifics -COPY codegen/out/aignx /app/codegen/out/aignx - -RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --frozen --all-extras --no-dev --no-editable - - -# Base of our build targets -FROM base AS target - -ENV AIGNOSTICS_RUNNING_IN_CONTAINER=1 - -# We don't want to run the app as root -RUN <Python wheel] - DOCKER[🐳 Docker Images
Slim + Full variants] DOCS[📚 Documentation
Sphinx + API docs] end subgraph "Release & Deploy" PYPI[🐍 PyPI Release] - REGISTRY[🗂️ Container Registry] RTD[📖 Read the Docs] MONITOR[📊 Monitoring
Sentry + Logfire] end @@ -293,11 +291,9 @@ flowchart TD TEST --> BUILD SEC --> BUILD - BUILD --> DOCKER BUILD --> DOCS BUILD --> PYPI - DOCKER --> REGISTRY DOCS --> RTD PYPI --> MONITOR @@ -305,7 +301,6 @@ flowchart TD PRECOMMIT -.->|"Runs automatically
on git commit"| LINT TEST -.->|"85% coverage
requirement"| BUILD SEC -.->|"Scans dependencies
& secrets"| BUILD - DOCKER -.->|"Multi-arch builds
ARM64 + AMD64"| REGISTRY classDef dev fill:#e3f2fd,stroke:#1976d2,stroke-width:2px classDef quality fill:#fff3e0,stroke:#f57c00,stroke-width:2px @@ -314,8 +309,8 @@ flowchart TD class DEV,CODE,PRECOMMIT dev class LINT,TYPE,TEST,SEC quality - class BUILD,DOCKER,DOCS build - class PYPI,REGISTRY,RTD,MONITOR deploy + class BUILD,DOCS build + class PYPI,RTD,MONITOR deploy ``` **Key Pipeline Features:** @@ -325,7 +320,6 @@ flowchart TD - **Multi-environment Testing**: Matrix testing across Python versions and operating systems - **Security Scanning**: `pip-audit` dependency vulnerability scanning and secret detection - **Documentation Generation**: Automated API docs and user guides using Sphinx -- **Multi-platform Builds**: Docker images for both ARM64 and AMD64 architectures - **Compliance Integration**: Automated reporting to compliance platforms **Code Quality & Analysis:** @@ -355,7 +349,7 @@ flowchart TD - **API Documentation**: Interactive OpenAPI specification with Swagger UI - **Version Management**: `bump-my-version` for semantic versioning - **Changelog Generation**: `git-cliff` for automated release notes -- **Multi-format Publishing**: PyPI packages, Docker images, and Read The Docs +- **Multi-format Publishing**: PyPI packages and Read The Docs **Monitoring & Observability:** @@ -363,13 +357,6 @@ flowchart TD - **Logging & Metrics**: Logfire integration for structured logging - **Uptime Monitoring**: Prepared integration with monitoring services -**Deployment & Distribution:** - -- **Multi-stage Docker Builds**: Fat (all extras) and slim (core only) variants -- **Multi-architecture Support**: ARM64 and AMD64 container images -- **Container Security**: Non-root execution within immutable containers -- **Registry Publishing**: Docker.io and GitHub Container Registry with attestations - **Development Environment:** - **Dev Containers**: One-click development environments with GitHub Codespaces @@ -843,7 +830,6 @@ Comprehensive documentation requirements: Multiple distribution methods: - **PyPI Package**: Standard Python package installation via pip/uv -- **Docker Images**: Containerized deployment with multiple variants - **Source Installation**: Direct installation from GitHub repository - **Development Setup**: One-click development environment setup diff --git a/compose.yaml b/compose.yaml deleted file mode 100644 index 95f1b5dfc..000000000 --- a/compose.yaml +++ /dev/null @@ -1,39 +0,0 @@ -services: - aignostics: - build: - context: . - target: all - env_file: - - path: .env - required: false - develop: - watch: - - path: src - action: rebuild - restart: no - profiles: - - manual - tty: true - stdin_open: true - aignostics-notebook: - build: - context: . - target: all - env_file: - - path: .env - required: false - develop: - watch: - - path: src - action: rebuild - command: notebook --host=0.0.0.0 --port=8001 - restart: always - ports: - - "8001:8001" - healthcheck: - test: [ "CMD", "curl", "-f", "http://127.0.0.1:8001/healthz" ] - interval: 5s - timeout: 2s - retries: 3 - start_period: 5s - start_interval: 1s diff --git a/docs/source/index.rst b/docs/source/index.rst index 861da3a1a..316e10bd4 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -39,7 +39,5 @@ :github: :pypi: aignostics - Docker - ghcr.io SonarQube Codecov diff --git a/noxfile.py b/noxfile.py index 27ccaceca..5aa888ecd 100644 --- a/noxfile.py +++ b/noxfile.py @@ -861,25 +861,6 @@ def _generate_coverage_report(session: nox.Session) -> None: _inject_headline("# Coverage report", coverage_report_file_name) -def _cleanup_test_execution(session: nox.Session) -> None: - """Clean up post test execution. - - - Docker containers created by pytest-docker removed - - Args: - session: The nox session instance - """ - session.run( - "bash", - "-c", - ( - "docker compose ls --format json | jq -r '.[].Name' | " - "grep ^pytest | xargs -I {} docker compose -p {} down --remove-orphans" - ), - external=True, - ) - - def _run_test_suite(session: nox.Session, marker: str = "", cov_append: bool = False) -> None: """Run test suite with specified marker. @@ -916,8 +897,8 @@ def _run_test_suite(session: nox.Session, marker: str = "", cov_append: bool = F # Determine report type from python version and custom marker report_type = _get_report_type(session, custom_marker) - # Run parallel and sequential tests, collecting failures so that coverage and cleanup - # always execute even when some tests fail. This ensures Codecov always receives a + # Run parallel and sequential tests, collecting failures so that coverage always + # executes even when some tests fail. This ensures Codecov always receives a # complete report (all JUnit XML files, full accumulated coverage) regardless of # individual test failures. failure: CommandFailed | None = None @@ -940,9 +921,6 @@ def _run_test_suite(session: nox.Session, marker: str = "", cov_append: bool = F # Note: This will be called multiple times, which is fine as it updates the same report _generate_coverage_report(session) - # Clean up post test execution - _cleanup_test_execution(session) - # Re-raise to propagate the failure to nox / make / CI if failure is not None: raise failure diff --git a/pyproject.toml b/pyproject.toml index 970a32897..633e7a372 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,6 @@ keywords = [ "cyclonedx", "devcontainer", "detect-secrets", - "docker", "git-cliff", "jupyter", "marimo", @@ -191,7 +190,6 @@ dev = [ "pytest-asyncio>=1.3.0,<2", "pytest-cov>=7.0.0,<8", "coverage>=7.12.0,<7.13", # Pin: coverage 7.13.x introduced a regression where pytest-cov fails with "INTERNALERROR: Expected current collector to be ..." when tests spawn subprocesses (e.g., marimo server). Re-evaluate when coverage releases a fix. - "pytest-docker>=3.2.5,<4", "pytest-durations>=1.6.1,<2", "pytest-env>=1.2.0,<2", "pytest-md-report>=0.7.0,<1", @@ -384,12 +382,11 @@ markers = [ "skip_with_act: Don't run with act.", "stress: Tests to run as part of 5 minute stress test schedule.", "stress_only: Tests to run as part of 5 minute stress test schedule only.", - "docker: That require Docker.", "long_running: Tests with a test timeout >=5 min and < 60 min. CI/CD will run long running tests with one Python version only. When calling `make` long running tests are excluded - use `make test_long_running` instead.", "very_long_running: Tests with a test timeout >= 60 min. CI/CD will run very long running tests with one Python version only. When calling `make` very long runing tests are excluded - use `make test_very_long_running` instead.", # Test Categories (following Martin Fowler's Solitary vs Sociable unit test distinction) "unit: Solitary unit tests - test a layer of a module in isolation with all dependencies mocked, except interaction with shared utils and the systems module. Unit tests must be able to pass offline, i.e. not calls to external services. The timeout should not be bigger than the default 10s, and must be <5 min.", - "integration: Sociable integration tests - test interactions across architectural layers (e.g. CLI/GUI→Service, Service→Utils) or between modules (e.g. Application→Platform), using real SDK collaborators, real file I/O, real subprocesses, and real Docker containers. Integration test must be able to pass offline, i.e. mock external services (Aignostics Platform API, Auth0, S3/GCS buckets, IDC). The timeout should not be bigger than the default 10s, and must be <5 min.", + "integration: Sociable integration tests - test interactions across architectural layers (e.g. CLI/GUI→Service, Service→Utils) or between modules (e.g. Application→Platform), using real SDK collaborators, real file I/O, and real subprocesses. Integration test must be able to pass offline, i.e. mock external services (Aignostics Platform API, Auth0, S3/GCS buckets, IDC). The timeout should not be bigger than the default 10s, and must be <5 min.", "e2e: End-to-end tests - test complete workflows with real external network services (Aignostics Platform API, cloud storage, IDC, etc). If the test timeout is >= 5 min and < 60 min, additionally mark as `long_running`, if >= 60min mark as 'very_long_running'.", ] md_report = true diff --git a/specifications/SPEC-BUILD-CHAIN-CICD-SERVICE.md b/specifications/SPEC-BUILD-CHAIN-CICD-SERVICE.md index 3a1afafcf..7f64e803a 100644 --- a/specifications/SPEC-BUILD-CHAIN-CICD-SERVICE.md +++ b/specifications/SPEC-BUILD-CHAIN-CICD-SERVICE.md @@ -5,7 +5,7 @@ itemType: Software Item Spec itemFulfills: TBD _(System service requirements to be defined)_ Layer: Infrastructure Service Version: 0.2.140 -Date: 2025-09-11 +Date: 2026-10-07 --- ## 1. Description @@ -22,16 +22,15 @@ The Build Chain and CI/CD Module shall: - **[FR-02]** Run comprehensive test suites across multiple Python versions and operating systems with coverage reporting - **[FR-03]** Generate and publish documentation automatically including API references and user guides - **[FR-04]** Build and distribute Python packages to PyPI with semantic versioning -- **[FR-05]** Create and publish multi-architecture Docker images for both slim and full variants - **[FR-06]** Generate compliance artifacts including SBOM, license reports, and vulnerability assessments - **[FR-07]** Provide local development environment consistency through pre-commit hooks and development tools -- **[FR-08]** Support multiple distribution channels including PyPI, Docker registries, and GitHub releases +- **[FR-08]** Support multiple distribution channels including PyPI and GitHub releases - **[FR-09]** Enable local CI/CD testing through Act integration for GitHub Actions workflows - **[FR-10]** Implement automated dependency monitoring and security vulnerability detection ### 1.3 Non-Functional Requirements -- **Performance**: Build pipeline optimized for fast feedback with parallel execution across multiple platforms, efficient caching strategies for dependencies and Docker layers +- **Performance**: Build pipeline optimized for fast feedback with parallel execution across multiple platforms, efficient caching strategies for dependencies - **Security**: Secrets managed through GitHub secrets, vulnerability scanning integrated, OIDC token-based authentication for secure service communication - **Reliability**: Manual retry capabilities through GitHub Actions UI, comprehensive error reporting through job summaries and artifacts - **Usability**: Clear feedback through GitHub status checks, detailed test reports in job summaries, one-command local development setup @@ -40,7 +39,6 @@ The Build Chain and CI/CD Module shall: ### 1.4 Constraints and Limitations - GitHub Actions runner limitations for concurrent jobs and execution time -- Docker registry rate limits requiring authenticated access for heavy usage - Platform-specific testing constraints (e.g., macOS GitHub Actions limitations) - Secret management restricted to repository administrators and configured environments @@ -58,7 +56,6 @@ The Build Chain and CI/CD Module shall: │ ├── _test.yml # Multi-platform testing pipeline │ ├── _audit.yml # Security and compliance scanning │ ├── _package-publish.yml # PyPI package publishing -│ ├── _docker-publish.yml # Container image publishing │ ├── _codeql.yml # GitHub CodeQL security analysis │ └── _ketryx_report_and_check.yml # Compliance reporting └── copilot-instructions.md # AI pair programming guidelines @@ -78,7 +75,6 @@ pyproject.toml # Project configuration and dependencies | `_test.yml` | Workflow | Multi-platform testing with coverage | Pytest execution across matrix | UV, test dependencies | | `_audit.yml` | Workflow | Security and license compliance scanning | pip-audit, pip-licenses | Python environment | | `_package-publish.yml` | Workflow | PyPI package building and publishing | UV build tools, PyPI API | GitHub release tags | -| `_docker-publish.yml` | Workflow | Container image building and publishing | Docker Buildx, registries | Docker Hub, GHCR | | `Makefile` | Build System | Local development task orchestration | Command-line interface | Nox, UV, system tools | | `noxfile.py` | Task Runner | Python environment and session management | Python API and CLI | UV, pytest, ruff, mypy | @@ -109,7 +105,6 @@ pyproject.toml # Project configuration and dependencies | Output Type | Destination | Data Type/Format | Success Criteria | Error Conditions | | ---------------- | ---------------- | ---------------------------------- | ----------------------------- | --------------------------------------- | | Python Package | PyPI | Wheel and source distribution | Successful upload | Authentication/validation failures | -| Docker Images | Docker Hub, GHCR | Multi-arch container images | Multi-platform build success | Registry authentication failures | | Documentation | Read The Docs | HTML/PDF documentation | Successful deployment | Build/rendering failures | | Test Reports | GitHub Actions | JUnit XML, coverage reports | All tests pass, coverage >85% | Test failures, coverage below threshold | | Security Reports | Artifacts | JSON vulnerability/license reports | Clean vulnerability scan | Critical vulnerabilities detected | @@ -159,9 +154,6 @@ release_assets: - type: python_package format: [wheel, sdist] platform: any - - type: docker_image - variants: [slim, full] - architectures: [amd64, arm64] - type: documentation formats: [html, pdf] - type: compliance_reports @@ -183,7 +175,6 @@ graph LR D --> I[Documentation Generation] F --> J[PyPI] - F --> K[Docker Registries] F --> L[GitHub Releases] ``` @@ -206,7 +197,6 @@ graph LR - `codeql`: GitHub CodeQL security analysis - `ketryx_report_and_check`: Compliance reporting and validation - `package_publish`: PyPI package publishing (tags only) - - `docker_publish`: Container image publishing (tags only) **Input/Output Contracts**: @@ -250,7 +240,6 @@ uv run nox -s dist # Package building | `make test` | Execute test suite | Optional: Python version | JUnit XML, coverage reports | | `make lint` | Code quality checks | None | Console output, exit codes | | `make audit` | Security/compliance scan | None | JSON reports, console output | -| `make docker_build` | Build container images | None | Docker images (local) | | `make dist` | Build Python packages | None | Wheel/sdist in dist/ | **Common Options**: @@ -267,10 +256,9 @@ uv run nox -s dist # Package building | Workflow | Method | Purpose | Permissions Required | Secrets Used | | ---------------------- | --------------- | ----------------------- | ---------------------------------- | ------------------ | | `_lint.yml` | `workflow_call` | Code quality validation | `contents: read` | None | -| `_test.yml` | `workflow_call` | Multi-platform testing | `contents: read, packages: write` | Test credentials | +| `_test.yml` | `workflow_call` | Multi-platform testing | `contents: read, packages: read` | Test credentials | | `_audit.yml` | `workflow_call` | Security scanning | `contents: read` | None | -| `_package-publish.yml` | `workflow_call` | PyPI publishing | `contents: write, packages: write` | `UV_PUBLISH_TOKEN` | -| `_docker-publish.yml` | `workflow_call` | Container publishing | `packages: write` | Docker credentials | +| `_package-publish.yml` | `workflow_call` | PyPI publishing | `contents: write, packages: read` | `UV_PUBLISH_TOKEN` | **Environment Variables**: @@ -297,7 +285,6 @@ uv run nox -s dist # Package building | ------------------ | ----------- | ----------------------------- | ----------------- | ---------------------- | | GitHub Actions | N/A | CI/CD execution platform | Required | Local testing with Act | | UV Package Manager | Latest | Python environment management | Required | Fallback to pip/venv | -| Docker | 20.10+ | Container image building | Required | Skip container builds | | Ruff | 0.1.0+ | Code formatting and linting | Required | Pipeline failure | | MyPy | 1.0+ | Static type checking | Required | Pipeline failure | | pytest | 7.0+ | Test execution framework | Required | Pipeline failure | @@ -307,7 +294,6 @@ uv run nox -s dist # Package building - **Aignostics Platform API**: Authentication and service integration for E2E tests - **PyPI**: Package publishing and distribution -- **Docker Hub/GHCR**: Container image registry publication - **Read The Docs**: Automated documentation deployment - **Codecov**: Test coverage reporting and analysis - **SonarQube**: Code quality and security analysis @@ -334,8 +320,6 @@ uv run nox -s dist # Package building | ----------------------------- | ------------------------------ | --------------------- | | `GITHUB_TOKEN` | GitHub API authentication | `ghp_xxxxxxxxxxxx` | | `UV_PUBLISH_TOKEN` | PyPI publishing authentication | `pypi-xxxxxxx` | -| `DOCKER_USERNAME` | Docker Hub authentication | `username` | -| `DOCKER_PASSWORD` | Docker Hub token | `dckr_pat_xxxxx` | | `CODECOV_TOKEN` | Coverage reporting | `xxxxxxxx-xxxx-xxxx` | | `SONAR_TOKEN` | SonarQube authentication | `squ_xxxxxxxx` | | `AIGNOSTICS_CLIENT_ID_DEVICE` | Platform API testing | `client_id_value` | @@ -362,7 +346,6 @@ uv run nox -s dist # Package building - **Commit Messages**: Validated for conventional commits format - **Environment Variables**: Presence and format validation for required secrets - **Python Code**: Syntax validation through AST parsing before execution -- **Docker Configuration**: Dockerfile and compose file validation ### 7.3 Graceful Degradation @@ -387,7 +370,6 @@ uv run nox -s dist # Package building - **Input Sanitization**: All external inputs validated and sanitized before use - **Dependency Scanning**: pip-audit integration for vulnerability detection in dependencies - **Secret Detection**: detect-secrets pre-commit hook prevents credential leakage -- **Container Security**: Multi-stage docker builds with minimal base images, non-root execution - **Supply Chain Security**: SBOM generation in CycloneDX and SPDX formats, dependency vulnerability tracking - **Code Analysis**: GitHub CodeQL and SonarQube integration for security analysis @@ -407,14 +389,14 @@ uv run nox -s dist # Package building - **State Type**: Stateless pipelines with artifact-based state passing between jobs - **Data Persistence**: Artifacts stored in GitHub Actions with configurable retention - **Session Management**: Isolated environments per job with clean setup/teardown -- **Cache Strategy**: Multi-level caching (UV dependencies, Docker layers, build artifacts) +- **Cache Strategy**: Multi-level caching (UV dependencies, build artifacts) ### 9.3 Performance and Scalability Considerations - **Performance Characteristics**: - Matrix testing: Parallel execution across 6+ platforms - - Caching: UV dependency and Docker layer caching implemented - - Optimization: Efficient resource usage through UV package manager and container builds + - Caching: UV dependency caching implemented + - Optimization: Efficient resource usage through UV package manager - **Scalability Patterns**: Horizontal scaling through GitHub Actions matrix builds - **Resource Management**: Memory and CPU optimization through UV and container limits - **Concurrency Model**: Parallel job execution with dependency-based ordering diff --git a/tests/CLAUDE.md b/tests/CLAUDE.md index cd83a90be..ae8458daa 100644 --- a/tests/CLAUDE.md +++ b/tests/CLAUDE.md @@ -29,7 +29,7 @@ Real fixtures live here, not in `fixtures/`: Every test needs at least one of `unit`, `integration`, `e2e` or it will not run in CI. The authoritative marker list and descriptions live in `pyproject.toml` `[tool.pytest.ini_options]` (also `long_running`, `very_long_running`, -`scheduled`, `scheduled_only`, `sequential`, `stress`, `stress_only`, `docker`, +`scheduled`, `scheduled_only`, `sequential`, `stress`, `stress_only`, `no_extras`, `skip_with_act`). See `.github/CLAUDE.md` for how CI selects them and the `Makefile` for the `test_*` targets. diff --git a/tests/aignostics/docker_test.py b/tests/aignostics/docker_test.py deleted file mode 100644 index b64146756..000000000 --- a/tests/aignostics/docker_test.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Tests to verify the CLI functionality of OE Python Template works with Docker.""" - -import os -import platform - -import pytest - -BUILT_WITH_LOVE = "built with love in Berlin" - - -@pytest.mark.e2e # Calls container registry (ghcr.io) -@pytest.mark.long_running -@pytest.mark.docker -@pytest.mark.skipif( - platform.system() == "Windows", reason="Docker CLI tests are not supported on Windows due to path issues." -) -@pytest.mark.skipif( - platform.system() == "Darwin" and os.getenv("GITHUB_ACTIONS") == "true", - reason="Docker CLI tests are not supported on Mac when running on GitHub actions.", -) -@pytest.mark.skip_with_act -@pytest.mark.xdist_group(name="docker") -@pytest.mark.timeout(timeout=60 * 5) -def test_core_docker_cli_help_with_love(docker_services) -> None: - """Test the CLI help command with docker services returns expected output.""" - out = docker_services._docker_compose.execute("run aignostics --help") - out_str = out.decode("utf-8") - assert "built with love in Berlin" in out_str diff --git a/tests/conftest.py b/tests/conftest.py index 648f8d166..57ff31a18 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -282,43 +282,6 @@ def silent_logging(caplog) -> Generator[None, None, None]: yield -@pytest.fixture(scope="session") -def docker_compose_file(pytestconfig) -> str: - """Get the path to the docker compose file. - - Args: - pytestconfig: The pytest configuration object. - - Returns: - str: The path to the docker compose file. - """ - # We want to test the compose.yaml file in the root of the project. - return str(Path(pytestconfig.rootdir) / "compose.yaml") - - -@pytest.fixture(scope="session") -def docker_setup() -> list[str] | str: - """Commands to run when spinning up services. - - Returns: - list[str] | str: The commands to run. - """ - # You can consider to return an empty list so you can decide on the - # commands to run in the test itself - return ["up --build -d"] - - -def docker_compose_project_name() -> str: - """Generate a project name using the current process PID. - - Returns: - str: The project name. - """ - # You can consider to override this with a project name to reuse the stack - # across test executions. - return f"aignostics-pytest-{os.getpid()}" - - def pytest_sessionfinish(session, exitstatus) -> None: """Run after the test session ends. diff --git a/uv.lock b/uv.lock index 93503a292..68cb24f62 100644 --- a/uv.lock +++ b/uv.lock @@ -156,7 +156,6 @@ dev = [ { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-cov" }, - { name = "pytest-docker" }, { name = "pytest-durations" }, { name = "pytest-env" }, { name = "pytest-md-report" }, @@ -293,7 +292,6 @@ dev = [ { name = "pytest", specifier = ">=9.0.3,<10" }, { name = "pytest-asyncio", specifier = ">=1.3.0,<2" }, { name = "pytest-cov", specifier = ">=7.0.0,<8" }, - { name = "pytest-docker", specifier = ">=3.2.5,<4" }, { name = "pytest-durations", specifier = ">=1.6.1,<2" }, { name = "pytest-env", specifier = ">=1.2.0,<2" }, { name = "pytest-md-report", specifier = ">=0.7.0,<1" }, @@ -6267,19 +6265,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/7a/33895863aec26ac3bb5068a73583f935680d6ab6af2a9567d409430c3ee1/pytest_datadir-1.8.0-py3-none-any.whl", hash = "sha256:5c677bc097d907ac71ca418109adc3abe34cf0bddfe6cf78aecfbabd96a15cf0", size = 6512, upload-time = "2025-07-30T13:52:11.525Z" }, ] -[[package]] -name = "pytest-docker" -version = "3.2.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/05/b7e47dc3e01b505838372e296bd780180b3b699a9a134bb8d6be85f3d567/pytest_docker-3.2.5.tar.gz", hash = "sha256:c9662567522911280b394af4da2edd57facaf644494601fac962ff1e396d7ab6", size = 13717, upload-time = "2025-11-12T13:42:19.641Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/e4/3a76a393f808edb0ee08ecc25e5c00bce0522a45b8fcf8693ec9441739c8/pytest_docker-3.2.5-py3-none-any.whl", hash = "sha256:79f3d209f928f45d4385cb825944861bc8a8cccd309804d9c9cd63bcef03edba", size = 8724, upload-time = "2025-11-12T13:42:18.631Z" }, -] - [[package]] name = "pytest-durations" version = "1.9.0"