From 2facad2c7c3d9453a427600d80a9f0b3af17ada4 Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Fri, 18 Apr 2025 16:28:43 +0300 Subject: [PATCH 01/14] chore: Update Dockerfile to use Ubuntu base image + install Python based on .python_version file --- Dockerfile | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index 162779d..f15cbbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,12 @@ -FROM python:3.12-bookworm +FROM ubuntu:24.04 AS base ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 ENV JAVA_HOME=/usr/lib/jvm/java-openjdk # https://docs.docker.com/build/cache/optimize/#use-cache-mounts -RUN --mount=type=cache,target=/var/cache/apt \ - --mount=type=cache,target=/var/lib/apt \ - --mount=type=cache,target=/root/.cache/pip \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt-get update \ && apt-get install -y --no-install-recommends --no-install-suggests \ # Required for pyre vscode extension @@ -24,8 +23,7 @@ RUN --mount=type=cache,target=/var/cache/apt \ git \ jq \ zsh \ - && pip install --no-cache-dir -U pip setuptools wheel \ - && pip install --no-cache-dir uv \ + && curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR="/usr/local/bin" sh \ # Install Pulumi: && curl -fsSL https://get.pulumi.com | sh \ # Install reviewdog: @@ -55,13 +53,12 @@ RUN --mount=type=cache,target=/var/cache/apt \ # Copy from the cache instead of linking since it's a mounted volume ENV UV_LINK_MODE=copy -ENV UV_SYSTEM_PYTHON=true -ENV UV_BREAK_SYSTEM_PACKAGES=true -ENV UV_PROJECT_ENVIRONMENT=/usr/local - +ENV PATH="/root/.local/bin:$PATH" # Install the project's dependencies using the lockfile and settings ONBUILD 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 + --mount=type=bind,source=uv.lock,target=uv.lock,readonly \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml,readonly \ + --mount=type=bind,source=.python-version,target=.python-version,readonly \ + uv python install --preview --default \ + && uv sync --frozen --no-install-project From 9715cf9a53472693c07d86acc1ec9d937b9e8ebc Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Sat, 19 Apr 2025 01:24:31 +0300 Subject: [PATCH 02/14] refactor: Improve Dockerfile --- Dockerfile | 65 +++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/Dockerfile b/Dockerfile index f15cbbe..684ebfa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,58 +7,63 @@ ENV JAVA_HOME=/usr/lib/jvm/java-openjdk # https://docs.docker.com/build/cache/optimize/#use-cache-mounts RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ - apt-get update \ + set -eux \ + && apt-get update \ && apt-get install -y --no-install-recommends --no-install-suggests \ - # Required for pyre vscode extension - watchman \ - # Required for sonarqube vscode extension - openjdk-17-jre-headless \ - nodejs \ - # Required for shellcheck vscode extension - shellcheck \ - # Required for general purpose compilation - gcc \ - # General purpose tools - curl \ - git \ - jq \ - zsh \ + # Required for pyre vscode extension + watchman \ + # Required for sonarqube vscode extension + openjdk-17-jre-headless \ + nodejs \ + # Required for shellcheck vscode extension + shellcheck \ + # Required for general purpose compilation + gcc \ + # General purpose tools + curl \ + git \ + jq \ + zsh \ && curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR="/usr/local/bin" sh \ # Install Pulumi: && curl -fsSL https://get.pulumi.com | sh \ # Install reviewdog: && curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh \ - | sh -s -- -b /usr/local/bin \ + | sh -s -- -b /usr/local/bin \ # Make sure java runtime is found for sonarqube: && ln -s "$(dirname "$(dirname "$(readlink -f "$(which java)")")")" "$JAVA_HOME" \ # Install other tools: && export ACTIONLINT_VERSION=$(curl -s https://api.github.com/repos/rhysd/actionlint/releases/latest | jq -r '.tag_name' | sed "s/v//") \ && export HADOLINT_VERSION=$(curl -s https://api.github.com/repos/hadolint/hadolint/releases/latest | jq -r '.tag_name') \ && if [ "$(uname -m)" = "aarch64" ]; then \ - curl -o /usr/local/bin/snyk -L https://static.snyk.io/cli/latest/snyk-linux-arm64 \ - && curl -o /usr/local/bin/hadolint -L https://github.com/hadolint/hadolint/releases/download/${HADOLINT_VERSION}/hadolint-Linux-arm64 \ - && curl -o /usr/local/bin/shfmt https://github.com/patrickvane/shfmt/releases/download/master/shfmt_linux_arm \ - && curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_arm64.tar.gz" | tar -xzf - -C /usr/local/bin actionlint ; \ + curl -o /usr/local/bin/snyk -L https://static.snyk.io/cli/latest/snyk-linux-arm64 \ + && curl -o /usr/local/bin/hadolint -L https://github.com/hadolint/hadolint/releases/download/${HADOLINT_VERSION}/hadolint-Linux-arm64 \ + && curl -o /usr/local/bin/shfmt https://github.com/patrickvane/shfmt/releases/download/master/shfmt_linux_arm \ + && curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_arm64.tar.gz" | tar -xzf - -C /usr/local/bin actionlint ; \ else \ - curl -o /usr/local/bin/snyk -L https://static.snyk.io/cli/latest/snyk-linux \ - && curl -o /usr/local/bin/hadolint -L https://github.com/hadolint/hadolint/releases/download/${HADOLINT_VERSION}/hadolint-Linux-x86_64 \ - && curl -o /usr/local/bin/shfmt https://github.com/patrickvane/shfmt/releases/download/master/shfmt_linux_amd64 \ - && curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | tar -xzf - -C /usr/local/bin actionlint ; \ + curl -o /usr/local/bin/snyk -L https://static.snyk.io/cli/latest/snyk-linux \ + && curl -o /usr/local/bin/hadolint -L https://github.com/hadolint/hadolint/releases/download/${HADOLINT_VERSION}/hadolint-Linux-x86_64 \ + && curl -o /usr/local/bin/shfmt https://github.com/patrickvane/shfmt/releases/download/master/shfmt_linux_amd64 \ + && curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | tar -xzf - -C /usr/local/bin actionlint ; \ fi \ && chmod +x /usr/local/bin/snyk \ && chmod +x /usr/local/bin/hadolint \ && chmod +x /usr/local/bin/shfmt \ && chmod +x /usr/local/bin/actionlint +WORKDIR /app +ENV PATH="/opt/venv/bin:$PATH" # Copy from the cache instead of linking since it's a mounted volume ENV UV_LINK_MODE=copy -ENV PATH="/root/.local/bin:$PATH" +ENV UV_PROJECT_ENVIRONMENT=/opt/venv +ENV UV_PYTHON_INSTALL_DIR=/opt/pythons # Install the project's dependencies using the lockfile and settings ONBUILD RUN --mount=type=cache,target=/root/.cache/uv \ - --mount=type=bind,source=uv.lock,target=uv.lock,readonly \ - --mount=type=bind,source=pyproject.toml,target=pyproject.toml,readonly \ - --mount=type=bind,source=.python-version,target=.python-version,readonly \ - uv python install --preview --default \ - && uv sync --frozen --no-install-project + --mount=type=bind,source=uv.lock,target=uv.lock,readonly \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml,readonly \ + --mount=type=bind,source=.python-version,target=.python-version,readonly \ + set -eux \ + && uv venv \ + && uv sync --frozen --no-install-project From b452396300803c459c6c7e357b174c5b872a0593 Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 01:24:59 +0300 Subject: [PATCH 03/14] feat: Add testing and validation for Docker image with multi-platform support --- .github/workflows/publish.yaml | 13 +++++ .vscode/extensions.json | 12 +++++ .vscode/settings.json | 13 +++++ Brewfile | 5 ++ Dockerfile | 16 +++--- README.md | 53 ++++++++++++++++--- test.sh | 38 +++++++++++++ tests/specs.yaml | 52 ++++++++++++++++++ tests/test-data/build-context/.python-version | 1 + tests/test-data/build-context/Dockerfile | 4 ++ tests/test-data/build-context/pyproject.toml | 9 ++++ tests/test-data/build-context/uv.lock | 39 ++++++++++++++ 12 files changed, 241 insertions(+), 14 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 Brewfile create mode 100755 test.sh create mode 100644 tests/specs.yaml create mode 100644 tests/test-data/build-context/.python-version create mode 100644 tests/test-data/build-context/Dockerfile create mode 100644 tests/test-data/build-context/pyproject.toml create mode 100644 tests/test-data/build-context/uv.lock diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index b59f3a2..8185a91 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -49,9 +49,22 @@ jobs: IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" echo "IMAGE_LOWERCASE=$(echo $IMAGE | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + + - name: Run tests + run: ./test.sh + build_and_push_image: needs: - prep + - test env: IMAGE_LOWERCASE: ${{ needs.prep.outputs.IMAGE_LOWERCASE }} strategy: diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..534704c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + "timonwong.shellcheck", + "ms-azuretools.vscode-docker", + "exiasr.hadolint", + "foxundermoon.shell-format", + "davidanson.vscode-markdownlint", + "github.vscode-github-actions", + "redhat.vscode-yaml", + "docker.docker" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..268d16e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "[shellscript]": { + "editor.defaultFormatter": "foxundermoon.shell-format" + }, + "[dockerfile]": { + "editor.defaultFormatter": "ms-azuretools.vscode-docker" + }, + "[markdown]": { + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint" + }, + "editor.formatOnSave": true, + "yaml.format.enable": true, +} \ No newline at end of file diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000..7e9d01f --- /dev/null +++ b/Brewfile @@ -0,0 +1,5 @@ +tap "homebrew/bundle" +brew "container-structure-test" +brew "hadolint" +brew "shellcheck" +brew "shfmt" diff --git a/Dockerfile b/Dockerfile index 684ebfa..dffbf95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,11 +4,13 @@ ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 ENV JAVA_HOME=/usr/lib/jvm/java-openjdk +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + # https://docs.docker.com/build/cache/optimize/#use-cache-mounts RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ - set -eux \ - && apt-get update \ + apt-get update \ + && apt-get upgrade -y \ && apt-get install -y --no-install-recommends --no-install-suggests \ # Required for pyre vscode extension watchman \ @@ -24,9 +26,11 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ git \ jq \ zsh \ + # Install uv: && curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR="/usr/local/bin" sh \ # Install Pulumi: && curl -fsSL https://get.pulumi.com | sh \ + && mv /root/.pulumi/bin/pulumi /usr/local/bin \ # Install reviewdog: && curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh \ | sh -s -- -b /usr/local/bin \ @@ -35,15 +39,16 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ # Install other tools: && export ACTIONLINT_VERSION=$(curl -s https://api.github.com/repos/rhysd/actionlint/releases/latest | jq -r '.tag_name' | sed "s/v//") \ && export HADOLINT_VERSION=$(curl -s https://api.github.com/repos/hadolint/hadolint/releases/latest | jq -r '.tag_name') \ + && export SHFMT_VERSION=$(curl -s https://api.github.com/repos/mvdan/sh/releases/latest | jq -r '.tag_name') \ && if [ "$(uname -m)" = "aarch64" ]; then \ curl -o /usr/local/bin/snyk -L https://static.snyk.io/cli/latest/snyk-linux-arm64 \ && curl -o /usr/local/bin/hadolint -L https://github.com/hadolint/hadolint/releases/download/${HADOLINT_VERSION}/hadolint-Linux-arm64 \ - && curl -o /usr/local/bin/shfmt https://github.com/patrickvane/shfmt/releases/download/master/shfmt_linux_arm \ + && curl -o /usr/local/bin/shfmt -L https://github.com/mvdan/sh/releases/download/${SHFMT_VERSION}/shfmt_${SHFMT_VERSION}_linux_arm64 \ && curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_arm64.tar.gz" | tar -xzf - -C /usr/local/bin actionlint ; \ else \ curl -o /usr/local/bin/snyk -L https://static.snyk.io/cli/latest/snyk-linux \ && curl -o /usr/local/bin/hadolint -L https://github.com/hadolint/hadolint/releases/download/${HADOLINT_VERSION}/hadolint-Linux-x86_64 \ - && curl -o /usr/local/bin/shfmt https://github.com/patrickvane/shfmt/releases/download/master/shfmt_linux_amd64 \ + && curl -o /usr/local/bin/shfmt -L https://github.com/mvdan/sh/releases/download/${SHFMT_VERSION}/shfmt_${SHFMT_VERSION}_linux_amd64 \ && curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | tar -xzf - -C /usr/local/bin actionlint ; \ fi \ && chmod +x /usr/local/bin/snyk \ @@ -64,6 +69,5 @@ ONBUILD RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock,readonly \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml,readonly \ --mount=type=bind,source=.python-version,target=.python-version,readonly \ - set -eux \ - && uv venv \ + uv venv \ && uv sync --frozen --no-install-project diff --git a/README.md b/README.md index cf1b3fe..d1fc046 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,83 @@ # Python base image for development purpose -Specially created Docker image for Python to work as a devcontainer for development purposes. +Specially created Docker image for Python to work as a devcontainer for development purposes. + +NOTE: This image is NOT meant to be used as a production image. ## Contains It contains: -- `python` - Python sourced from official Python image on Docker Hub. We are using now Python version 3.12 as it is now the latest Python version supported by the type checkers we use. - `uv` - for Python package management +- `python` - Python version that is defined in `.python-version` file in the child image. -It contains the neccesary dependencies for running various linters and type checkers: +It contains the necessary dependencies for running various linters and type checkers: -- `watchman` - for running Pyre Python type checker +- `watchman` - for running Pyre Python type checker - `shfmt` - for shell script formatting +- `shellcheck` - for shell script linting - `reviewdog` - for code review - `hadolint` - for linting Dockerfile - `actionlint` - static checker for GitHub Actions workflow files +Other tools: + +- `pulumi` - Pulumi CLI for infrastructure as code + ## Usage We host the image on [Github packages](https://github.com/NextGenContributions/python-dev-image/pkgs/container/python-dev-image). You can use it the like this: +### As a standalone image + Command line: + ```shell -docker pull ghcr.io/nextgencontributions/python-dev-image +docker run --rm ghcr.io/nextgencontributions/python-dev-image ``` +### As a base image + In your project's `Dockerfile`: + ```Dockerfile FROM ghcr.io/nextgencontributions/python-dev-image # Do your own customizations here... ``` +NOTE: When using this as a base image, you should have the following files available in your build context: + +- `pyproject.toml` +- `uv.lock` +- `.python-version` - this will be used to install the Python version in the container. + +If you don't have these files, you can create them by running the following commands: + +```shell +uv init +uv lock +uv python pin 3.12 # or replace with any other Python version you want +``` + +Or in a single command in your project's root directory: + +```shell +docker run --rm -v $(pwd):/app ghcr.io/nextgencontributions/python-dev-image \ + sh -c "cd /app && uv init && uv lock && uv python pin 3.12" +``` + +### As a devcontainer + With VSCode in `.devcontainer/devcontainer.json`: + ```jsonc // For format details, see https://aka.ms/devcontainer.json. { - "name": "Python 3", - "image": "ghcr.io/nextgencontributions/python-dev-image" - // Do your own customizations here... + "name": "Python 3", + "image": "ghcr.io/nextgencontributions/python-dev-image" + // Do your own customizations here... } ``` diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..6331410 --- /dev/null +++ b/test.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# +# This script builds and tests a Docker image for multiple platforms (amd64 and arm64). +# It uses Docker Buildx to build the image and container-structure-test to run tests on the built image. +# +# Usage: ./test.sh + +set -euxo pipefail + +TEST_IMAGE_NAME="python-dev-test-image" + +# Build and test function that takes platform as parameter +build_and_test() { + local platform=$1 + local tag=$2 + + echo "Building and testing for platform: $platform with tag: $tag" + + # Build the base image + docker buildx build --platform "$platform" -t python-dev-image:"$tag" -f Dockerfile . + + # Build the test image + docker buildx build --platform "$platform" -t $TEST_IMAGE_NAME:"$tag" -f tests/test-data/build-context/Dockerfile tests/test-data/build-context + + docker run --platform "$platform" --rm $TEST_IMAGE_NAME:"$tag" uname -m + + # Run the tests + container-structure-test test --platform "$platform" --image $TEST_IMAGE_NAME:"$tag" --config tests/specs.yaml + + # Clean up + docker rmi $TEST_IMAGE_NAME:"$tag" || true +} + +# Run for amd64 +build_and_test "linux/amd64" "latest" + +# Run for arm64 +build_and_test "linux/arm64" "latest" diff --git a/tests/specs.yaml b/tests/specs.yaml new file mode 100644 index 0000000..b673241 --- /dev/null +++ b/tests/specs.yaml @@ -0,0 +1,52 @@ +schemaVersion: "2.0.0" + +metadataTest: + exposedPorts: [] + +commandTests: + - name: "python is installed in the path" + command: "python" + args: ["--version"] + expectedOutput: ["Python 3.12"] + + - name: "uv is installed in path" + command: "uv" + args: ["--version"] + expectedOutput: ["uv"] + + - name: "shellcheck is installed in path" + command: "shellcheck" + args: ["--version"] + expectedOutput: ["shellcheck"] + + - name: "hadolint is installed in path" + command: "hadolint" + args: ["--version"] + + - name: "shfmt is installed in path" + command: "shfmt" + args: ["--version"] + + - name: "snyk is installed in path" + command: "snyk" + args: ["--version"] + + - name: "pulumi is installed in path" + command: "pulumi" + args: ["version"] + + - name: "reviewdog is installed in path" + command: "reviewdog" + args: ["--version"] + + - name: "actionlint is installed in path" + command: "actionlint" + args: ["--version"] + + - name: "watchman is installed in path" + command: "watchman" + args: ["--version"] + + - name: "ONBUILD was executed" + command: "ruff" + args: ["--version"] diff --git a/tests/test-data/build-context/.python-version b/tests/test-data/build-context/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/tests/test-data/build-context/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/tests/test-data/build-context/Dockerfile b/tests/test-data/build-context/Dockerfile new file mode 100644 index 0000000..66acb5b --- /dev/null +++ b/tests/test-data/build-context/Dockerfile @@ -0,0 +1,4 @@ +FROM python-dev-image + + + diff --git a/tests/test-data/build-context/pyproject.toml b/tests/test-data/build-context/pyproject.toml new file mode 100644 index 0000000..5bd76fa --- /dev/null +++ b/tests/test-data/build-context/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "python-dev-image" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = "==3.12.*" +dependencies = [ + "ruff", +] diff --git a/tests/test-data/build-context/uv.lock b/tests/test-data/build-context/uv.lock new file mode 100644 index 0000000..a3a88b7 --- /dev/null +++ b/tests/test-data/build-context/uv.lock @@ -0,0 +1,39 @@ +version = 1 +revision = 1 +requires-python = "==3.12.*" + +[[package]] +name = "python-dev-image" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [{ name = "ruff" }] + +[[package]] +name = "ruff" +version = "0.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/11/bcef6784c7e5d200b8a1f5c2ddf53e5da0efec37e6e5a44d163fb97e04ba/ruff-0.11.6.tar.gz", hash = "sha256:bec8bcc3ac228a45ccc811e45f7eb61b950dbf4cf31a67fa89352574b01c7d79", size = 4010053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/1f/8848b625100ebcc8740c8bac5b5dd8ba97dd4ee210970e98832092c1635b/ruff-0.11.6-py3-none-linux_armv6l.whl", hash = "sha256:d84dcbe74cf9356d1bdb4a78cf74fd47c740bf7bdeb7529068f69b08272239a1", size = 10248105 }, + { url = "https://files.pythonhosted.org/packages/e0/47/c44036e70c6cc11e6ee24399c2a1e1f1e99be5152bd7dff0190e4b325b76/ruff-0.11.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9bc583628e1096148011a5d51ff3c836f51899e61112e03e5f2b1573a9b726de", size = 11001494 }, + { url = "https://files.pythonhosted.org/packages/ed/5b/170444061650202d84d316e8f112de02d092bff71fafe060d3542f5bc5df/ruff-0.11.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2959049faeb5ba5e3b378709e9d1bf0cab06528b306b9dd6ebd2a312127964a", size = 10352151 }, + { url = "https://files.pythonhosted.org/packages/ff/91/f02839fb3787c678e112c8865f2c3e87cfe1744dcc96ff9fc56cfb97dda2/ruff-0.11.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63c5d4e30d9d0de7fedbfb3e9e20d134b73a30c1e74b596f40f0629d5c28a193", size = 10541951 }, + { url = "https://files.pythonhosted.org/packages/9e/f3/c09933306096ff7a08abede3cc2534d6fcf5529ccd26504c16bf363989b5/ruff-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a4b9a4e1439f7d0a091c6763a100cef8fbdc10d68593df6f3cfa5abdd9246e", size = 10079195 }, + { url = "https://files.pythonhosted.org/packages/e0/0d/a87f8933fccbc0d8c653cfbf44bedda69c9582ba09210a309c066794e2ee/ruff-0.11.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5edf270223dd622218256569636dc3e708c2cb989242262fe378609eccf1308", size = 11698918 }, + { url = "https://files.pythonhosted.org/packages/52/7d/8eac0bd083ea8a0b55b7e4628428203441ca68cd55e0b67c135a4bc6e309/ruff-0.11.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f55844e818206a9dd31ff27f91385afb538067e2dc0beb05f82c293ab84f7d55", size = 12319426 }, + { url = "https://files.pythonhosted.org/packages/c2/dc/d0c17d875662d0c86fadcf4ca014ab2001f867621b793d5d7eef01b9dcce/ruff-0.11.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d8f782286c5ff562e4e00344f954b9320026d8e3fae2ba9e6948443fafd9ffc", size = 11791012 }, + { url = "https://files.pythonhosted.org/packages/f9/f3/81a1aea17f1065449a72509fc7ccc3659cf93148b136ff2a8291c4bc3ef1/ruff-0.11.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01c63ba219514271cee955cd0adc26a4083df1956d57847978383b0e50ffd7d2", size = 13949947 }, + { url = "https://files.pythonhosted.org/packages/61/9f/a3e34de425a668284e7024ee6fd41f452f6fa9d817f1f3495b46e5e3a407/ruff-0.11.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15adac20ef2ca296dd3d8e2bedc6202ea6de81c091a74661c3666e5c4c223ff6", size = 11471753 }, + { url = "https://files.pythonhosted.org/packages/df/c5/4a57a86d12542c0f6e2744f262257b2aa5a3783098ec14e40f3e4b3a354a/ruff-0.11.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4dd6b09e98144ad7aec026f5588e493c65057d1b387dd937d7787baa531d9bc2", size = 10417121 }, + { url = "https://files.pythonhosted.org/packages/58/3f/a3b4346dff07ef5b862e2ba06d98fcbf71f66f04cf01d375e871382b5e4b/ruff-0.11.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:45b2e1d6c0eed89c248d024ea95074d0e09988d8e7b1dad8d3ab9a67017a5b03", size = 10073829 }, + { url = "https://files.pythonhosted.org/packages/93/cc/7ed02e0b86a649216b845b3ac66ed55d8aa86f5898c5f1691797f408fcb9/ruff-0.11.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bd40de4115b2ec4850302f1a1d8067f42e70b4990b68838ccb9ccd9f110c5e8b", size = 11076108 }, + { url = "https://files.pythonhosted.org/packages/39/5e/5b09840fef0eff1a6fa1dea6296c07d09c17cb6fb94ed5593aa591b50460/ruff-0.11.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:77cda2dfbac1ab73aef5e514c4cbfc4ec1fbef4b84a44c736cc26f61b3814cd9", size = 11512366 }, + { url = "https://files.pythonhosted.org/packages/6f/4c/1cd5a84a412d3626335ae69f5f9de2bb554eea0faf46deb1f0cb48534042/ruff-0.11.6-py3-none-win32.whl", hash = "sha256:5151a871554be3036cd6e51d0ec6eef56334d74dfe1702de717a995ee3d5b287", size = 10485900 }, + { url = "https://files.pythonhosted.org/packages/42/46/8997872bc44d43df986491c18d4418f1caff03bc47b7f381261d62c23442/ruff-0.11.6-py3-none-win_amd64.whl", hash = "sha256:cce85721d09c51f3b782c331b0abd07e9d7d5f775840379c640606d3159cae0e", size = 11558592 }, + { url = "https://files.pythonhosted.org/packages/d7/6a/65fecd51a9ca19e1477c3879a7fda24f8904174d1275b419422ac00f6eee/ruff-0.11.6-py3-none-win_arm64.whl", hash = "sha256:3567ba0d07fb170b1b48d944715e3294b77f5b7679e8ba258199a250383ccb79", size = 10682766 }, +] From 24328c87a0dfced30e4cf2f974dc916082c0bc61 Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 01:40:52 +0300 Subject: [PATCH 04/14] fix: Add --load flag to Docker build commands to make it work on Github --- test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.sh b/test.sh index 6331410..3dc6b94 100755 --- a/test.sh +++ b/test.sh @@ -17,10 +17,10 @@ build_and_test() { echo "Building and testing for platform: $platform with tag: $tag" # Build the base image - docker buildx build --platform "$platform" -t python-dev-image:"$tag" -f Dockerfile . + docker buildx build --load --platform "$platform" -t python-dev-image:"$tag" -f Dockerfile . # Build the test image - docker buildx build --platform "$platform" -t $TEST_IMAGE_NAME:"$tag" -f tests/test-data/build-context/Dockerfile tests/test-data/build-context + docker buildx build --load --platform "$platform" -t $TEST_IMAGE_NAME:"$tag" -f tests/test-data/build-context/Dockerfile tests/test-data/build-context docker run --platform "$platform" --rm $TEST_IMAGE_NAME:"$tag" uname -m From 1c083fd6378db78dce5f5303ac15a895b4c2a24e Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 01:50:52 +0300 Subject: [PATCH 05/14] fix: ? --- test.sh | 2 +- tests/test-data/build-context/Dockerfile | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test.sh b/test.sh index 3dc6b94..68c7537 100755 --- a/test.sh +++ b/test.sh @@ -20,7 +20,7 @@ build_and_test() { docker buildx build --load --platform "$platform" -t python-dev-image:"$tag" -f Dockerfile . # Build the test image - docker buildx build --load --platform "$platform" -t $TEST_IMAGE_NAME:"$tag" -f tests/test-data/build-context/Dockerfile tests/test-data/build-context + docker buildx build --load --platform "$platform" -t $TEST_IMAGE_NAME:"$tag" --build-arg BASE_IMAGE=python-dev-image:"$tag" -f tests/test-data/build-context/Dockerfile tests/test-data/build-context docker run --platform "$platform" --rm $TEST_IMAGE_NAME:"$tag" uname -m diff --git a/tests/test-data/build-context/Dockerfile b/tests/test-data/build-context/Dockerfile index 66acb5b..bbafc9a 100644 --- a/tests/test-data/build-context/Dockerfile +++ b/tests/test-data/build-context/Dockerfile @@ -1,4 +1,5 @@ -FROM python-dev-image +ARG BASE_IMAGE=python-dev-image:latest +FROM ${BASE_IMAGE} From 9aaf1853585e979b98f170daefae4e195a3d0e23 Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 01:58:35 +0300 Subject: [PATCH 06/14] fix: ? --- test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.sh b/test.sh index 68c7537..93210c9 100755 --- a/test.sh +++ b/test.sh @@ -19,8 +19,8 @@ build_and_test() { # Build the base image docker buildx build --load --platform "$platform" -t python-dev-image:"$tag" -f Dockerfile . - # Build the test image - docker buildx build --load --platform "$platform" -t $TEST_IMAGE_NAME:"$tag" --build-arg BASE_IMAGE=python-dev-image:"$tag" -f tests/test-data/build-context/Dockerfile tests/test-data/build-context + # Build the test image (use classic docker build for local-only workaround) + docker build --platform "$platform" -t $TEST_IMAGE_NAME:"$tag" --build-arg BASE_IMAGE=python-dev-image:"$tag" -f tests/test-data/build-context/Dockerfile tests/test-data/build-context docker run --platform "$platform" --rm $TEST_IMAGE_NAME:"$tag" uname -m From 8359442d13ff24d353b0b65dc6da4f1501399a68 Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 02:02:49 +0300 Subject: [PATCH 07/14] fix? --- tests/test-data/build-context/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-data/build-context/Dockerfile b/tests/test-data/build-context/Dockerfile index bbafc9a..54af815 100644 --- a/tests/test-data/build-context/Dockerfile +++ b/tests/test-data/build-context/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=python-dev-image:latest +ARG BASE_IMAGE=python-dev-image:latest AS scratch FROM ${BASE_IMAGE} From 9ebfaa41046f524d41eaad5142f4200813d2980b Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 02:04:51 +0300 Subject: [PATCH 08/14] ?f --- tests/test-data/build-context/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-data/build-context/Dockerfile b/tests/test-data/build-context/Dockerfile index 54af815..c067152 100644 --- a/tests/test-data/build-context/Dockerfile +++ b/tests/test-data/build-context/Dockerfile @@ -1,5 +1,5 @@ -ARG BASE_IMAGE=python-dev-image:latest AS scratch -FROM ${BASE_IMAGE} +ARG BASE_IMAGE=python-dev-image:latest +FROM ${BASE_IMAGE} AS scratch From ce1b165e4a556ce3247cc83f21ffdb33ef484ad7 Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 02:15:46 +0300 Subject: [PATCH 09/14] fix: Install container-structure-test and update test script execution --- .github/workflows/publish.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 8185a91..fb112c2 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -58,8 +58,13 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + - name: Install container-structure-test + run: | + curl -LO https://github.com/GoogleContainerTools/container-structure-test/releases/latest/download/container-structure-test-linux-amd64 && chmod +x container-structure-test-linux-amd64 && mkdir -p $HOME/bin && export PATH=$PATH:$HOME/bin && mv container-structure-test-linux-amd64 $HOME/bin/container-structure-test + - name: Run tests - run: ./test.sh + run: | + ./test.sh build_and_push_image: needs: From 0c750ffb1009b8ff6dd9c647988af90a4d86616d Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 02:16:00 +0300 Subject: [PATCH 10/14] fix: Remove unnecessary blank lines from Dockerfile --- tests/test-data/build-context/Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test-data/build-context/Dockerfile b/tests/test-data/build-context/Dockerfile index c067152..1e93d76 100644 --- a/tests/test-data/build-context/Dockerfile +++ b/tests/test-data/build-context/Dockerfile @@ -1,5 +1,2 @@ ARG BASE_IMAGE=python-dev-image:latest FROM ${BASE_IMAGE} AS scratch - - - From debe2158105e4efada069d805739b94a31b3491b Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 02:19:38 +0300 Subject: [PATCH 11/14] fix: Update installation path for container-structure-test and clarify Dockerfile usage --- .github/workflows/publish.yaml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index fb112c2..812a395 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -60,7 +60,7 @@ jobs: - name: Install container-structure-test run: | - curl -LO https://github.com/GoogleContainerTools/container-structure-test/releases/latest/download/container-structure-test-linux-amd64 && chmod +x container-structure-test-linux-amd64 && mkdir -p $HOME/bin && export PATH=$PATH:$HOME/bin && mv container-structure-test-linux-amd64 $HOME/bin/container-structure-test + curl -LO https://github.com/GoogleContainerTools/container-structure-test/releases/latest/download/container-structure-test-linux-amd64 && chmod +x container-structure-test-linux-amd64 && sudo mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test - name: Run tests run: | diff --git a/README.md b/README.md index d1fc046..c55b138 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ docker run --rm ghcr.io/nextgencontributions/python-dev-image In your project's `Dockerfile`: ```Dockerfile -FROM ghcr.io/nextgencontributions/python-dev-image +FROM ghcr.io/nextgencontributions/python-dev-image # AS scratch # Do your own customizations here... ``` From f5ef43cab1840d41f0eddca2fe3fed839c4ed0d2 Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 04:14:11 +0300 Subject: [PATCH 12/14] fix: Enhance multi-platform testing in CI workflow and update test script for platform argument --- .github/workflows/publish.yaml | 21 ++++++++++++++++++--- test.sh | 15 +++++++++------ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 812a395..ffdb23e 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -50,7 +50,18 @@ jobs: echo "IMAGE_LOWERCASE=$(echo $IMAGE | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT test: - runs-on: ubuntu-latest + strategy: + fail-fast: false + # This job runs on two different runners: one for x86_64 and one for ARM. + # The ARM runner is used to test the image on an ARM architecture. + # The x86_64 runner is used to test the image on an x86_64 architecture. + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-latest + runs-on: ${{ matrix.runner }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -60,11 +71,15 @@ jobs: - name: Install container-structure-test run: | - curl -LO https://github.com/GoogleContainerTools/container-structure-test/releases/latest/download/container-structure-test-linux-amd64 && chmod +x container-structure-test-linux-amd64 && sudo mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test + if [[ "${{ matrix.platform }}" == *"arm64"* ]]; then + curl -LO https://github.com/GoogleContainerTools/container-structure-test/releases/latest/download/container-structure-test-linux-arm64 && chmod +x container-structure-test-linux-arm64 && sudo mv container-structure-test-linux-arm64 /usr/local/bin/container-structure-test + else + curl -LO https://github.com/GoogleContainerTools/container-structure-test/releases/latest/download/container-structure-test-linux-amd64 && chmod +x container-structure-test-linux-amd64 && sudo mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test + fi - name: Run tests run: | - ./test.sh + ./test.sh ${{ matrix.platform }} build_and_push_image: needs: diff --git a/test.sh b/test.sh index 93210c9..02b885f 100755 --- a/test.sh +++ b/test.sh @@ -3,12 +3,16 @@ # This script builds and tests a Docker image for multiple platforms (amd64 and arm64). # It uses Docker Buildx to build the image and container-structure-test to run tests on the built image. # -# Usage: ./test.sh +# Usage: ./test.sh [platform] +# +# If no platform is specified, it defaults to "linux/amd64 linux/arm64". set -euxo pipefail TEST_IMAGE_NAME="python-dev-test-image" +platforms="${1:-linux/amd64 linux/arm64}" + # Build and test function that takes platform as parameter build_and_test() { local platform=$1 @@ -31,8 +35,7 @@ build_and_test() { docker rmi $TEST_IMAGE_NAME:"$tag" || true } -# Run for amd64 -build_and_test "linux/amd64" "latest" - -# Run for arm64 -build_and_test "linux/arm64" "latest" +for platform in $platforms; do + # Build and test for each platform + build_and_test "$platform" "latest" +done From 438769f2c85484da43e067c4614a8ce01a5e3131 Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 04:48:39 +0300 Subject: [PATCH 13/14] fix: Update test script and Dockerfile for multi-platform support and architecture validation --- test.sh | 22 ++++++++++++++++------ tests/amd64.yaml | 7 +++++++ tests/arm64.yaml | 7 +++++++ tests/test-data/build-context/Dockerfile | 5 +++-- 4 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 tests/amd64.yaml create mode 100644 tests/arm64.yaml diff --git a/test.sh b/test.sh index 02b885f..977892c 100755 --- a/test.sh +++ b/test.sh @@ -9,7 +9,8 @@ set -euxo pipefail -TEST_IMAGE_NAME="python-dev-test-image" +BASE_IMAGE="python-dev-image" +TEST_IMAGE="python-dev-test-image" platforms="${1:-linux/amd64 linux/arm64}" @@ -18,21 +19,30 @@ build_and_test() { local platform=$1 local tag=$2 + BASE_PLATFORM_IMAGE="$BASE_IMAGE-$platform:$tag" + TEST_PLATFORM_IMAGE="$TEST_IMAGE-$platform:$tag" + echo "Building and testing for platform: $platform with tag: $tag" # Build the base image - docker buildx build --load --platform "$platform" -t python-dev-image:"$tag" -f Dockerfile . + docker build --load --platform "$platform" -t "$BASE_PLATFORM_IMAGE" -f Dockerfile . # Build the test image (use classic docker build for local-only workaround) - docker build --platform "$platform" -t $TEST_IMAGE_NAME:"$tag" --build-arg BASE_IMAGE=python-dev-image:"$tag" -f tests/test-data/build-context/Dockerfile tests/test-data/build-context + docker build --load --platform "$platform" -t "$TEST_PLATFORM_IMAGE" --build-arg BASE_IMAGE="$BASE_PLATFORM_IMAGE" --build-arg PLATFORM="$platform" -f tests/test-data/build-context/Dockerfile tests/test-data/build-context + + docker run --platform "$platform" --rm "$TEST_PLATFORM_IMAGE" uname -m - docker run --platform "$platform" --rm $TEST_IMAGE_NAME:"$tag" uname -m + if [ "$platform" == "linux/amd64" ]; then + container-structure-test test --platform "$platform" --image "$TEST_PLATFORM_IMAGE" --config tests/amd64.yaml + else + container-structure-test test --platform "$platform" --image "$TEST_PLATFORM_IMAGE" --config tests/arm64.yaml + fi # Run the tests - container-structure-test test --platform "$platform" --image $TEST_IMAGE_NAME:"$tag" --config tests/specs.yaml + container-structure-test test --platform "$platform" --image "$TEST_PLATFORM_IMAGE" --config tests/specs.yaml # Clean up - docker rmi $TEST_IMAGE_NAME:"$tag" || true + docker rmi $TEST_IMAGE:"$tag" || true } for platform in $platforms; do diff --git a/tests/amd64.yaml b/tests/amd64.yaml new file mode 100644 index 0000000..3db2193 --- /dev/null +++ b/tests/amd64.yaml @@ -0,0 +1,7 @@ +schemaVersion: "2.0.0" + +commandTests: + - name: "is correct architecture" + command: "uname" + args: ["-m"] + expectedOutput: ["x86_64"] diff --git a/tests/arm64.yaml b/tests/arm64.yaml new file mode 100644 index 0000000..f1dc963 --- /dev/null +++ b/tests/arm64.yaml @@ -0,0 +1,7 @@ +schemaVersion: "2.0.0" + +commandTests: + - name: "is correct architecture" + command: "uname" + args: ["-m"] + expectedOutput: ["aarch64"] diff --git a/tests/test-data/build-context/Dockerfile b/tests/test-data/build-context/Dockerfile index 1e93d76..6d9651c 100644 --- a/tests/test-data/build-context/Dockerfile +++ b/tests/test-data/build-context/Dockerfile @@ -1,2 +1,3 @@ -ARG BASE_IMAGE=python-dev-image:latest -FROM ${BASE_IMAGE} AS scratch +ARG BASE_IMAGE +ARG PLATFORM +FROM --platform=${PLATFORM} ${BASE_IMAGE} AS scratch From 3212e4460ed81646e34863f2c36da60394fd6420 Mon Sep 17 00:00:00 2001 From: Jukka Hassinen Date: Wed, 23 Apr 2025 04:50:47 +0300 Subject: [PATCH 14/14] fix: Update CI workflow to use specific Ubuntu 24.04 runners for multi-platform testing --- .github/workflows/publish.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index ffdb23e..dd50232 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -58,9 +58,9 @@ jobs: matrix: include: - platform: linux/amd64 - runner: ubuntu-latest + runner: ubuntu-24.04 - platform: linux/arm64 - runner: ubuntu-latest + runner: ubuntu-24.04-arm runs-on: ${{ matrix.runner }} steps: - name: Checkout repository