Skip to content

Build Python Package #433

Build Python Package

Build Python Package #433

Workflow file for this run

name: Build Python Package
on:
workflow_dispatch:
inputs:
incoming_ref:
description: >
The ref from Cantera/cantera to be built. Can be a tag, commit hash,
or branch name.
required: true
default: "main"
upload:
description: Attempt to upload to PyPI
required: true
default: "false"
concurrency:
group: ${{ github.ref }}-${{ github.event.inputs.incoming_ref}}
cancel-in-progress: true
env:
ACTION_URL: "https://github.com/Cantera/pypi-packages/actions/runs/${{ github.run_id }}"
jobs:
dump:
name: Dump the input parameters for the workflow
runs-on: ubuntu-22.04
steps:
- name: Dump Event Payload
run: jq . "$GITHUB_EVENT_PATH"
- name: Echo the input variables
run: |
echo "${{ github.event.inputs.incoming_ref }}"
echo "${{ github.event.inputs.upload }}"
post-pending-status:
name: Post a pending workflow status to Cantera/cantera
runs-on: ubuntu-22.04
env:
GITHUB_TOKEN: ${{ secrets.CANTERA_REPO_STATUS }}
outputs:
incoming-sha: ${{ steps.get-incoming-sha.outputs.incoming-sha }}
tag-ref: ${{ steps.munge-incoming-ref.outputs.tag-ref }}
steps:
- name: Munge the incoming ref
id: munge-incoming-ref
run: |
import os
import re
from pathlib import Path
INCOMING_REF = "${{ github.event.inputs.incoming_ref }}"
INCOMING_SHA = ""
if INCOMING_REF.startswith("refs/"):
INCOMING_REF = INCOMING_REF.replace("refs/", "")
elif re.match(r"^v\d\.\d\.\d.*$", INCOMING_REF) is not None:
INCOMING_REF = f"tags/{INCOMING_REF}"
elif re.match(r"^[a-f0-9]{6,40}", INCOMING_REF) is not None:
INCOMING_SHA = INCOMING_REF
else:
INCOMING_REF = f"heads/{INCOMING_REF}"
TAG_REF = "false"
if INCOMING_REF.startswith("tags"):
TAG_REF = "true"
Path(os.environ["GITHUB_ENV"]).write_text(
f"INCOMING_REF={INCOMING_REF}\n"
f"TAG_REF={TAG_REF}\n"
f"INCOMING_SHA={INCOMING_SHA}"
)
Path(os.environ["GITHUB_OUTPUT"]).write_text(
f"tag-ref={TAG_REF}"
)
shell: python
- name: Get the SHA associated with the incoming ref
id: get-incoming-sha
run: |
if [[ "${INCOMING_SHA}" == "" ]]; then
INCOMING_SHA=$(gh api repos/cantera/cantera/git/matching-refs/${INCOMING_REF} \
-H "Accept: application/vnd.github.v3+json" --jq ".[0].object.sha")
echo "INCOMING_SHA=${INCOMING_SHA}" >> $GITHUB_ENV
fi
# This needs to be in this step to be output to other jobs.
echo "incoming-sha=${INCOMING_SHA}" >> $GITHUB_OUTPUT
- name: Post the status to the upstream commit
id: set-the-status
if: env.TAG_REF == 'false'
run: |
gh api repos/cantera/cantera/statuses/${INCOMING_SHA} \
-H "Accept: application/vnd.github.v3+json" \
--field state='pending' \
--field target_url=$ACTION_URL \
--field context='PyPI Package Build' \
--field description="Pending build" \
--silent
sdist:
name: Build the sdist
runs-on: ubuntu-22.04
needs:
- "post-pending-status"
outputs:
job-status: ${{ job.status }}
steps:
- uses: actions/checkout@v4
name: Checkout the repository
with:
repository: "Cantera/cantera"
submodules: recursive
ref: ${{ github.event.inputs.incoming_ref }}
- name: Set Up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: python3 -m pip install -U pip scons build
- name: Build the sdist
run: |
python3 `which scons` sdist
- name: Archive the built sdist
uses: actions/upload-artifact@v4
with:
path: ./build/python_sdist/dist/*.tar.gz
name: cibw-sdist
if-no-files-found: error
# Copied from https://github.com/hynek/build-and-inspect-python-package/
- name: Show SDist contents hierarchically, including metadata.
shell: bash
run: |
mkdir -p /tmp/out/sdist
cp build/python_sdist/dist/*.tar.gz /tmp/
cd /tmp
tar xf *.tar.gz -C out/sdist
echo -e '\n<details><summary>SDist contents</summary>\n' >> $GITHUB_STEP_SUMMARY
(cd /tmp/out/sdist && tree -Da --timefmt="%Y-%m-%dT%H:%M:%SZ" * | sed 's/^/ /' | tee -a $GITHUB_STEP_SUMMARY)
echo -e '\n</details>\n' >> $GITHUB_STEP_SUMMARY
echo ----- Metadata Follows -----
echo -e '\n<details><summary>Metadata</summary>\n' >> $GITHUB_STEP_SUMMARY
cat out/sdist/*/PKG-INFO | sed 's/^/ /' | tee -a $GITHUB_STEP_SUMMARY
echo -e '\n</details>\n' >> $GITHUB_STEP_SUMMARY
echo ----- End of Metadata -----
linux-wheel:
name: Build Linux_${{ matrix.arch }} for py${{ matrix.python || '-all' }}
runs-on: ${{ matrix.os }}
needs: ["sdist", "post-pending-status"]
outputs:
job-status: ${{ job.status }}
strategy:
matrix:
# Wheel builds are fast except for aarch64, so split that into multiple jobs,
# one for each Python version
os: [ubuntu-latest]
arch: [aarch64]
python: ["3.8", "3.9", "3.10", "3.11", "3.12"]
include:
- os: ubuntu-latest
arch: x86_64
fail-fast: false
steps:
- name: Download pre-built sdist
uses: actions/download-artifact@v4
with:
name: cibw-sdist
- name: Extract the sdist tarball
run: tar -xvf *.tar.gz --strip-components=1
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: all
if: matrix.arch != 'x86_64'
- name: Set some vars
run: |
PYTHON="${{ matrix.python }}"
if [[ $PYTHON == "" ]]; then PYTHON="*"; fi
CIBW_BUILD="cp${PYTHON//./}-*_${{ matrix.arch }}"
echo "CIBW_BUILD=${CIBW_BUILD}" | tee -a $GITHUB_ENV
if [[ "${{ matrix.arch }}" == "aarch64" ]]; then CIBW_CONTAINER_ENGINE="docker; create_args: --platform linux/arm64"; else CIBW_CONTAINER_ENGINE="docker"; fi
echo "CIBW_CONTAINER_ENGINE=${CIBW_CONTAINER_ENGINE}" | tee -a $GITHUB_ENV
- name: Build wheels
uses: pypa/cibuildwheel@v2.19.2
env:
CIBW_ENVIRONMENT: CT_SKIP_SLOW=1
CIBW_BUILD: ${{ env.CIBW_BUILD }}
CIBW_ARCHS: ${{ matrix.arch }}
CIBW_CONTAINER_ENGINE: ${{ env.CIBW_CONTAINER_ENGINE }}
# cibuildwheel on Linux uses a Docker container to run the build, so
# runner.temp is not available. cibuildwheel also uses the /tmp folder, so
# we should be pretty safe to also use that.
CIBW_BEFORE_TEST: |
curl -sL "https://github.com/cantera/cantera/archive/${{ needs.post-pending-status.outputs.incoming-sha }}.tar.gz" -o /tmp/cantera.tar.gz \
&& tar -xzf /tmp/cantera.tar.gz --strip-components=1 -C /tmp "cantera-${{ needs.post-pending-status.outputs.incoming-sha }}/test"
- name: Archive the built wheels
uses: actions/upload-artifact@v4
with:
path: ./wheelhouse/*.whl
name: cibw-wheels-${{ runner.os }}-${{ strategy.job-index }}
windows-wheel:
name: Build Windows Wheels
runs-on: windows-2022
needs: ["sdist", "post-pending-status"]
outputs:
job-status: ${{ job.status }}
fail-fast: false
strategy:
matrix:
include:
- os: windows-2022
arch: AMD64
boost-toolset: msvc
boost-platform-version: 2022
boost-version: "1.85.0"
steps:
- uses: actions/checkout@v4
name: Checkout the repository
- name: Download pre-built sdist
uses: actions/download-artifact@v4
with:
name: cibw-sdist
- name: Extract the sdist tarball
run: tar -xvf *.tar.gz --strip-components=1
shell: bash
- name: Install boost
uses: MarkusJx/install-boost@v2.4.5
id: install-boost
with:
# REQUIRED: Specify the required boost version
# A list of supported versions can be found here:
# https://github.com/MarkusJx/prebuilt-boost/blob/main/versions-manifest.json
boost_version: ${{ matrix.boost-version }}
# OPTIONAL: Specify a custon install location
boost_install_dir: ${{ runner.temp }}
toolset: ${{ matrix.boost-toolset }}
platform_version: ${{ matrix.boost-platform-version }}
# Cache HDF5
- name: Cache built HDF5
uses: actions/cache@v4
with:
key: ${{ matrix.os }}-${{ matrix.arch }}-0
path: ${{ runner.temp }}/cache/hdf5
# Windows HDF5
- uses: nuget/setup-nuget@v2
if: runner.os == 'Windows'
- uses: actions/setup-python@v5
with:
python-version: 3.12
if: runner.os == 'Windows'
- run: bash ./cibw_before_all_windows.sh "${{ runner.temp }}"
if: runner.os == 'Windows'
- name: Set some environment variables
run: |
$BOOST_ROOT = "${{ steps.install-boost.outputs.BOOST_ROOT }}" -replace "\\", "/"
echo "BOOST_ROOT=$BOOST_ROOT" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
$CANTERA_TEST_DIR = "${{ runner.temp }}" -replace "\\", "/"
echo "CANTERA_TEST_DIR=$CANTERA_TEST_DIR" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
shell: pwsh
if: runner.os == 'Windows'
- name: Build wheels
uses: pypa/cibuildwheel@v2.19.2
env:
CIBW_ENVIRONMENT: Boost_INCLUDE_DIRS="${{ env.BOOST_ROOT }}/include" CT_SKIP_SLOW=1
CIBW_ARCHS: "AMD64"
CIBW_BEFORE_TEST: |
curl -sL "https://github.com/cantera/cantera/archive/${{ needs.post-pending-status.outputs.incoming-sha }}.tar.gz" -o ${{ env.CANTERA_TEST_DIR }}/cantera.tar.gz && \
tar -xzf ${{ env.CANTERA_TEST_DIR }}/cantera.tar.gz --strip-components=1 -C ${{ env.CANTERA_TEST_DIR }} "cantera-${{ needs.post-pending-status.outputs.incoming-sha }}/test"
- name: Archive the built wheels
uses: actions/upload-artifact@v4
with:
path: ./wheelhouse/*.whl
name: cibw-wheels-${{ runner.os }}-${{ strategy.job-index }}
macos-wheel:
name: Build ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
needs: ["sdist", "post-pending-status"]
outputs:
job-status: ${{ job.status }}
strategy:
matrix:
include:
- os: macos-14
arch: aarch64
boost-toolset: clang
# Since we only use the headers, we can use the platform version for this
# macos version
boost-platform-version: "14"
boost-version: "1.85.0"
- os: macos-13
arch: x86
boost-toolset: clang
# Since we only use the headers, we can use the platform version for this
# macos version
boost-platform-version: "13"
boost-version: "1.85.0"
fail-fast: false
steps:
- uses: actions/checkout@v4
name: Checkout the repository
- name: Download pre-built sdist
uses: actions/download-artifact@v4
with:
name: cibw-sdist
- name: Extract the sdist tarball
run: tar -xvf *.tar.gz --strip-components=1
# Cache HDF5
- name: Cache built HDF5
uses: actions/cache@v4
with:
key: ${{ matrix.os }}-${{ matrix.arch }}-0
path: ${{ runner.temp }}/cache/hdf5
# macOS HDF5
- run: bash ./cibw_before_all_macos.sh "${{ runner.temp }}"
if: runner.os == 'macOS'
# Hack for 3.8 bug (https://github.com/pypa/cibuildwheel/pull/1871#issuecomment-2161613619)
- uses: actions/setup-python@v5
with:
python-version: 3.8
if: runner.os == 'macOS' && matrix.arch == 'aarch64'
- name: Install boost
uses: MarkusJx/install-boost@v2.4.5
id: install-boost
with:
# REQUIRED: Specify the required boost version
# A list of supported versions can be found here:
# https://github.com/MarkusJx/prebuilt-boost/blob/main/versions-manifest.json
boost_version: ${{ matrix.boost-version }}
# OPTIONAL: Specify a custon install location
boost_install_dir: ${{ runner.temp }}
toolset: ${{ matrix.boost-toolset }}
platform_version: ${{ matrix.boost-platform-version }}
arch: ${{ matrix.arch }}
- name: Build wheels
uses: pypa/cibuildwheel@v2.19.2
env:
CIBW_ENVIRONMENT: MACOSX_DEPLOYMENT_TARGET="${{ env.MACOSX_DEPLOYMENT_TARGET }}" Boost_ROOT="${{ steps.install-boost.outputs.BOOST_ROOT }}" HDF5_ROOT=${{ env.HDF5_DIR }} CT_SKIP_SLOW=1
CIBW_BUILD: cp*-*
CIBW_TEST_COMMAND: pytest -vv --durations=100 ${{ runner.temp }}/test/python
CIBW_BEFORE_TEST: |
curl -sL "https://github.com/cantera/cantera/archive/${{ needs.post-pending-status.outputs.incoming-sha }}.tar.gz" -o ${{ runner.temp }}/cantera.tar.gz &&
tar -xzf ${{ runner.temp }}/cantera.tar.gz --strip-components=1 -C ${{ runner.temp }} "cantera-${{ needs.post-pending-status.outputs.incoming-sha }}/test"
- name: Archive the built wheels
uses: actions/upload-artifact@v4
with:
path: ./wheelhouse/*.whl
name: cibw-wheels-${{ runner.os }}-${{ strategy.job-index }}
publish-files-to-pypi:
name: Publish distribution files to PyPI
runs-on: ubuntu-22.04
outputs:
job-status: ${{ job.status }}
needs:
- "sdist"
- "linux-wheel"
- "windows-wheel"
- "macos-wheel"
if: github.event.inputs.upload == 'true'
permissions:
id-token: write
environment: pypi
steps:
- name: Download pre-built wheels
uses: actions/download-artifact@v4
with:
path: dist
pattern: cibw-*
merge-multiple: true
- name: pypi-publish
uses: pypa/gh-action-pypi-publish@release/v1
send_status_to_cantera:
name: Send jobs status to Cantera/cantera
runs-on: ubuntu-22.04
needs:
- "post-pending-status"
- "sdist"
- "linux-wheel"
- "windows-wheel"
- "macos-wheel"
- "publish-files-to-pypi"
if: always()
steps:
- name: Collect statuses
run: |
from collections import Counter
import os
statuses = {
"sdist": "${{needs.sdist.outputs.job-status}}",
"linux": "${{needs.linux-wheel.outputs.job-status}}",
"windows": "${{needs.windows-wheel.outputs.job-status}}",
"macos": "${{needs.macos-wheel.outputs.job-status}}",
"publish": "${{needs.publish-files-to-pypi.outputs.job-status}}",
}
# This is a deliberate comparison to the empty string.
if statuses["publish"] == "" and "${{ github.event.inputs.upload }}" == "false":
publish = statuses.pop("publish")
else:
publish = ""
if all(v == "success" for v in statuses.values()):
overall_status = "success"
elif any(v in ("cancelled", "") for v in statuses.values()):
overall_status = "error"
elif any(v == "failure" for v in statuses.values()):
overall_status = "failure"
status_counts = Counter(statuses.values())
status_counts.update([publish])
description = []
if overall_status in ("error", "failure"):
if status_counts.get("success") is not None:
description.append(f"{status_counts['success']} succeeded")
if status_counts.get("cancelled") is not None:
description.append(f"{status_counts['cancelled']} cancelled")
if status_counts.get("failure") is not None:
description.append(f"{status_counts['failure']} failed")
if status_counts.get("") is not None:
description.append(f"{status_counts['']} skipped")
description = ", ".join(description)
else:
description = "Successfully built Python wheels!"
with open(os.environ["GITHUB_ENV"], "a") as gh_env:
gh_env.write(f"OVERALL_STATUS={overall_status}\nDESCRIPTION={description}")
shell: python
- name: Post the status to the upstream commit
if: needs.post-pending-status.outputs.tag-ref == 'false'
run: |
INCOMING_SHA=${{ needs.post-pending-status.outputs.incoming-sha }}
gh api repos/cantera/cantera/statuses/${INCOMING_SHA} \
-H "Accept: application/vnd.github.v3+json" \
--field state="${OVERALL_STATUS}" \
--field target_url=$ACTION_URL \
--field context='PyPI Package Build' \
--field description="${DESCRIPTION}" \
--silent
env:
GITHUB_TOKEN: ${{ secrets.CANTERA_REPO_STATUS }}