diff --git a/.github/workflows/build-mjml-python.yml b/.github/workflows/build-mjml-python.yml new file mode 100644 index 00000000000..e68aa1e4118 --- /dev/null +++ b/.github/workflows/build-mjml-python.yml @@ -0,0 +1,127 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on the `linux` job of +# https://github.com/mgd020/mjml-python/blob/v1.4.1/.github/workflows/_build-wheels.yml +name: Build mjml-python wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/mjml-python.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-mjml-python.yml' + - 'docs/packages/mjml-python.yaml' + - 'patches/mjml-python/**' + push: + branches: [main] + paths: + - '.github/workflows/build-mjml-python.yml' + - 'docs/packages/mjml-python.yaml' + - 'patches/mjml-python/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: mjml-python + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build mjml-python ${{ matrix.version }} ${{ matrix.tag }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + include: + # Cargo.toml hardcodes pyo3's abi3-py38 feature unconditionally; + # the riscv64 manylinux image's oldest interpreter is cp39 (no + # cp38), so patches/ bumps it to abi3-py39 -- gotcha 96. Built on + # cp39 and re-tested on the newer ones via find_compatible_wheel. + - tag: cp39-abi3 + build: >- + cp39-manylinux_riscv64 cp310-manylinux_riscv64 + cp311-manylinux_riscv64 cp312-manylinux_riscv64 + cp313-manylinux_riscv64 cp314-manylinux_riscv64 + - tag: cp314t + build: cp314t-manylinux_riscv64 + + env: + MJML_PYTHON_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout mjml-python v${{ env.MJML_PYTHON_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: mgd020/mjml-python + ref: v${{ env.MJML_PYTHON_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Patch mjml-python source + run: git apply python-wheels/patches/mjml-python/${{ env.MJML_PYTHON_VERSION }}/00*.patch + + - name: Build wheels + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + output-dir: wheelhouse/ + env: + # musllinux is dropped: rustup.rs ships no riscv64 musl toolchain. + CIBW_BUILD: ${{ matrix.build }} + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + # mjml-python ships no [tool.cibuildwheel], so the Rust toolchain + # its maturin backend needs is installed in-container here. + CIBW_BEFORE_ALL_LINUX: >- + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + CIBW_ENVIRONMENT_LINUX: PATH="$PATH:$HOME/.cargo/bin" + CIBW_TEST_SOURCES: tests + CIBW_TEST_COMMAND: >- + python -c "import mjml.mjml as m; assert m.__file__.endswith('.so'), m.__file__" && + python -m unittest -v + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: mjml-python-${{ env.MJML_PYTHON_VERSION }}-${{ matrix.tag }}-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish mjml-python ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + artifact-pattern: mjml-python-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/mjml-python.yaml b/docs/packages/mjml-python.yaml new file mode 100644 index 00000000000..67999ab596b --- /dev/null +++ b/docs/packages/mjml-python.yaml @@ -0,0 +1,5 @@ +package-name: mjml-python +source-code: https://github.com/mgd020/mjml-python +license: MIT +versions: +- version: 1.4.1 diff --git a/patches/mjml-python/1.4.1/0001-Cargo-bump-abi3-floor-to-py39.patch b/patches/mjml-python/1.4.1/0001-Cargo-bump-abi3-floor-to-py39.patch new file mode 100644 index 00000000000..168a3d29a15 --- /dev/null +++ b/patches/mjml-python/1.4.1/0001-Cargo-bump-abi3-floor-to-py39.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Fri, 25 Sep 2026 00:00:00 +0000 +Subject: [PATCH] Cargo: bump abi3 floor to py39 + +Upstream-Status: Inappropriate [manylinux_2_39_riscv64 ships no cp38 interpreter] + +The riscv64 manylinux image's oldest available CPython is cp39 (no cp38), +so a wheel built here and tagged cp38-abi3 (this crate's abi3-py38 +feature) would never actually be compiled or tested against its own +declared floor -- gotcha 96. Bump the pyo3 abi3 feature to abi3-py39 to +make the riscv64 wheel's tag match the oldest interpreter this repo can +build and test it on; every other platform's wheel is unaffected. + +Signed-off-by: Ludovic Henry +--- + Cargo.toml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Cargo.toml b/Cargo.toml +index c3f55b0..26e8f79 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -10,4 +10,4 @@ crate-type = ["cdylib"] + + [dependencies] + mrml = "5.0.0" +-pyo3 = { version = "~0.29.0", features = ["abi3-py38", "extension-module"] } ++pyo3 = { version = "~0.29.0", features = ["abi3-py39", "extension-module"] }