From 3b5755fad4dfe0f140a3f7289531efabbb855ebe Mon Sep 17 00:00:00 2001 From: Adrien Mannocci Date: Mon, 22 Apr 2024 12:34:22 +0100 Subject: [PATCH] feat: support for multi os (#21) Signed-off-by: amannocci --- .github/workflows/ci.yml | 55 +++++++++++----- .github/workflows/env-setup/action.yml | 23 +++++++ .python-version | 2 +- Containerfile | 22 +++---- scripts/build.py | 86 ++++++++++++++++++-------- scripts/utils.py | 11 ++-- 6 files changed, 138 insertions(+), 61 deletions(-) create mode 100644 .github/workflows/env-setup/action.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1bf052..4ff9cbb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,10 @@ --- - name: CI on: pull_request: + branches: + - main push: branches: - main @@ -11,32 +12,52 @@ on: permissions: contents: read +## Concurrency only allowed in the main branch. +## So old builds running for old commits within the same Pull Request are cancelled +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + jobs: - test: + lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - uses: actions/checkout@v4 - - name: Install python - uses: actions/setup-python@v4 - with: - python-version-file: '.python-version' - - - name: Install poetry - run: | - curl -sSL https://install.python-poetry.org | python3 - - poetry install --no-interaction + - name: Setup environment + uses: ./.github/workflows/env-setup - name: Lint run: poetry run poe lint + build: + needs: + - lint + strategy: + matrix: + os: + - macos-14 + - macos-13 + - ubuntu-latest + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Setup environment + uses: ./.github/workflows/env-setup + - name: Build run: poetry run poe build + test: + needs: + - build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup environment + uses: ./.github/workflows/env-setup + - name: Test run: poetry run poe test diff --git a/.github/workflows/env-setup/action.yml b/.github/workflows/env-setup/action.yml new file mode 100644 index 0000000..ae8d403 --- /dev/null +++ b/.github/workflows/env-setup/action.yml @@ -0,0 +1,23 @@ +--- +name: env-setup +description: Setup environment + +runs: + using: "composite" + steps: + - name: Set up QEMU + if: matrix.os == 'ubuntu-latest' + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + if: matrix.os == 'ubuntu-latest' + uses: docker/setup-buildx-action@v3 + + - uses: actions/setup-python@v5 + with: + python-version-file: ".python-version" + + - name: Install poetry and dependencies + run: | + pip3 install --no-cache-dir --upgrade pip poetry + poetry install --no-interaction + shell: "bash" diff --git a/.python-version b/.python-version index 375f5ca..2419ad5 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.11.6 +3.11.9 diff --git a/Containerfile b/Containerfile index 9b6f16b..1edc988 100644 --- a/Containerfile +++ b/Containerfile @@ -1,27 +1,25 @@ # Base image for build ARG base_image_version=3.10.12 -FROM python:${base_image_version}-bullseye as builder +FROM python:${base_image_version}-slim-bullseye as builder # Switch workdir WORKDIR /opt/temply -# Arguments -ARG platform_arch -ARG app_version - # Copy files COPY . . +# Install build packages +RUN \ + apt-get update > /dev/null \ + && apt-get install -y --no-install-recommends \ + binutils="*" \ + && apt-get clean + # Install poetry -ENV \ - PATH="/opt/poetry/bin:${PATH}" \ - POETRY_HOME=/opt/poetry \ - POETRY_VIRTUALENVS_CREATE=false RUN \ - curl -sSL https://install.python-poetry.org | python3 - + pip3 install --no-cache-dir --upgrade pip poetry # Build RUN \ poetry install \ - && poetry run pyinstaller temply.spec \ - && mv /opt/temply/dist/temply /opt/temply/dist/temply-${app_version}-${platform_arch} + && poetry run pyinstaller temply.spec diff --git a/scripts/build.py b/scripts/build.py index fcfe897..ee3c34a 100644 --- a/scripts/build.py +++ b/scripts/build.py @@ -3,9 +3,9 @@ from pathlib import Path from time import time -from sh import git, podman +from sh import git, pyinstaller -from scripts.utils import Constants, container_backend, read_project_conf +from scripts.utils import Constants, container_backend, fatal, read_project_conf def run() -> None: @@ -20,29 +20,61 @@ def run() -> None: # Create dist dir local_dist_path = Path("dist") local_dist_path.mkdir(parents=True, exist_ok=False) - local_dist_path = local_dist_path.absolute().as_posix() + local_dist_path = local_dist_path.absolute() - cmd = container_backend() - for platform_arch in ["linux/amd64", "linux/arm64"]: - platform_arch_slug = platform_arch.replace("/", "-") - cmd( - "buildx", - "build", - "--platform", - platform_arch, - "-v", - f"{local_dist_path}:/opt/temply/dist", - "--build-arg", - f"base_image_version={python_version}", - "--build-arg", - f"platform_arch={platform_arch_slug}", - "--build-arg", - f"app_version={version}", - "-t", - f"{Constants.REGISTRY_URL}/temply:{image_id}", - "-f", - "Containerfile", - ".", - _out=sys.stdout, - _err=sys.stderr, - ) + system = platform.system().lower() + if system == "darwin": + pyinstaller("temply.spec", _out=sys.stdout, _err=sys.stderr) + arch = platform.machine() + arch = "amd64" if arch == "x86_64" else arch + Path("./dist/temply").replace(Path(f"./dist/temply-{version}-{system}-{arch}")) + elif system == "linux": + # Use cross-build to build both amd64 and arm64 versions. + cmd, env = container_backend() + for arch in ["amd64", "arm64"]: + platform_arch = f"linux/{arch}" + cmd( + "buildx", + "build", + "--load", + "--platform", + platform_arch, + "--build-arg", + f"base_image_version={python_version}", + "-t", + f"{Constants.REGISTRY_URL}/temply:{image_id}", + "-f", + "Containerfile", + ".", + _out=sys.stdout, + _err=sys.stderr, + _env=env, + ) + container_id = cmd( + "run", + "-d", + "--platform", + platform_arch, + "--entrypoint=cat", + f"{Constants.REGISTRY_URL}/temply:{image_id}", + _err=sys.stderr, + _env=env, + ).strip() + cmd( + "cp", + f"{container_id}:/opt/temply/dist/temply", + (local_dist_path / f"temply-{version}-linux-{arch}").as_posix(), + _out=sys.stdout, + _err=sys.stderr, + _env=env, + ) + cmd( + "rm", + "-f", + container_id, + _out=sys.stdout, + _err=sys.stderr, + _env=env, + ) + else: + fatal(f"Unsupported system: {system}") diff --git a/scripts/utils.py b/scripts/utils.py index 462cb3f..bcdc4ae 100644 --- a/scripts/utils.py +++ b/scripts/utils.py @@ -54,7 +54,7 @@ def detect_poetry() -> Command: fatal("`poetry` isn't detected") -def container_backend() -> Command: +def container_backend() -> tuple[Command, dict[str, str]]: """ Try to detect a container backend. Either podman or docker. @@ -65,13 +65,16 @@ def container_backend() -> Command: CommandNotFound: if a suitable backend can't be found. """ cmd = None + env = os.environ.copy() for backend in ["docker", "podman"]: try: cmd = Command(backend) - continue except CommandNotFound: - pass + continue + if "podman" == backend: + env["BUILDAH_FORMAT"] = "docker" + break if not cmd: raise CommandNotFound("Unable to find a suitable backend: docker or podman") - return cmd + return cmd, env