Skip to content

ENH: Support PyPI trusted publishing #5

ENH: Support PyPI trusted publishing

ENH: Support PyPI trusted publishing #5

name: 'Build, Test, Package ITK Remote Module'
on:
workflow_call:
inputs:
cmake-options:
description: 'CMake configuration parameters for the module under test'
required: false
type: string
default: ""
itk-wheel-tag:
# See https://github.com/InsightSoftwareConsortium/ITKPythonBuilds/releases
description: 'Github release version tag for the ITKPythonBuilds build archive to use'
required: false
type: string
default: 'v5.3.0'
itk-python-package-tag:
# See https://github.com/InsightSoftwareConsortium/ITKPythonPackage
description: 'Git tag or commit hash for ITKPythonPackage build scripts to use'
required: false
type: string
default: 'dc6a18600233ac69a8f42b7489e4edf6a5d8883a'
itk-python-package-org:
description: 'Github organization name for fetching ITKPythonPackage build scripts'
required: false
type: string
default: 'InsightSoftwareConsortium'
itk-module-deps:
description: 'Colon-delimited list of ITK remote module dependencies to build. Format as org/module_name@tag:...'
# example: InsightSoftwareConsortium/ITKMeshToPolyData@3ad8f08:InsightSoftwareConsortium/ITKBSplineGradient@0.3.0
required: false
type: string
python3-minor-versions:
description: 'JSON-formatted array of Python 3.x minor version wheel targets'
required: false
type: string
default: '["7","8","9","10","11"]'
manylinux-platforms:
description: 'JSON-formatted array of "<manylinux-image>-<arch>" specializations'
required: false
type: string
default: '["_2_28-x64","2014-x64","_2_28-aarch64"]'
test-notebooks:
description: 'Option to test Jupyter Notebooks in examples/ directory with applied changes'
required: false
type: boolean
default: false
secrets:
pypi_password:
required: false # Packages will not be uploaded to PyPI if not set
jobs:
build-linux-py:
runs-on: ubuntu-22.04
strategy:
max-parallel: 2
matrix:
python3-minor-version: ${{ fromJSON(inputs.python3-minor-versions) }}
manylinux-platform: ${{ fromJSON(inputs.manylinux-platforms) }}
steps:
- uses: actions/checkout@v3
- name: 'Free up disk space'
run: |
# Workaround for https://github.com/actions/virtual-environments/issues/709
df -h
sudo apt-get clean
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
df -h
- name: 'Fetch build script'
run: |
IPP_DOWNLOAD_GIT_TAG=${{ inputs.itk-python-package-tag }}
IPP_DOWNLOAD_ORG=${{ inputs.itk-python-package-org }}
curl -L https://raw.githubusercontent.com/${IPP_DOWNLOAD_ORG:=InsightSoftwareConsortium}/ITKPythonPackage/${IPP_DOWNLOAD_GIT_TAG:=master}/scripts/dockcross-manylinux-download-cache-and-build-module-wheels.sh -O
chmod u+x dockcross-manylinux-download-cache-and-build-module-wheels.sh
- name: 'Build 🐍 Python 📦 package'
shell: bash
run: |
rm -rf dist
export ITK_PACKAGE_VERSION=${{ inputs.itk-wheel-tag }}
export ITKPYTHONPACKAGE_TAG=${{ inputs.itk-python-package-tag }}
export ITKPYTHONPACKAGE_ORG=${{ inputs.itk-python-package-org }}
export ITK_MODULE_PREQ=${{ inputs.itk-module-deps }}
if [ -z ${{ inputs.cmake-options }} ]; then
CMAKE_OPTIONS=""
else
CMAKE_OPTIONS="--cmake_options ${{ inputs.cmake-options }}"
fi
MANYLINUX_PLATFORM=${{ matrix.manylinux-platform }}
echo "Manylinux platform ${MANYLINUX_PLATFORM}"
rm -rf ITKPythonPackage
export MANYLINUX_VERSION=`(echo ${MANYLINUX_PLATFORM} | cut -d '-' -f 1)`
export TARGET_ARCH=`(echo ${MANYLINUX_PLATFORM} | cut -d '-' -f 2)`
echo "Building for manylinux specialization ${MANYLINUX_VERSION} and target architecture ${TARGET_ARCH}"
./dockcross-manylinux-download-cache-and-build-module-wheels.sh cp3${{ matrix.python3-minor-version }} $CMAKE_OPTIONS
- name: Set up Python 3.10 for Validation
uses: actions/setup-python@v4
with:
python-version: "3.10"
- name: Validate build output
shell: bash
run: |
python -m pip install twine
ls dist/
MANYLINUX_PLATFORM=${{ matrix.manylinux-platform }}
MANYLINUX_VERSION=`(echo ${MANYLINUX_PLATFORM} | cut -d '-' -f 1)`
TARGET_ARCH_NAME=`(echo ${MANYLINUX_PLATFORM} | cut -d '-' -f 2)`
if [[ ${TARGET_ARCH_NAME} == "x64" ]]; then
TARGET_ARCH_NAME="x86_64" # Match auditwheel naming convention
fi
WHEEL_PATTERN="dist/itk_*cp3${{ matrix.python3-minor-version }}*manylinux${MANYLINUX_VERSION}*${TARGET_ARCH_NAME}.whl"
echo "Searching for wheels matching pattern ${WHEEL_PATTERN}"
python -m twine check ${WHEEL_PATTERN}
- name: Publish Python package as GitHub Artifact
uses: actions/upload-artifact@v3
with:
name: LinuxWheel3${{ matrix.python3-minor-version }}
path: dist/*.whl
build-macos-py:
runs-on: macos-12
strategy:
max-parallel: 2
matrix:
python3-minor-version: ${{ fromJSON(inputs.python3-minor-versions) }}
steps:
- uses: actions/checkout@v3
- name: 'Specific XCode version'
run: |
sudo xcode-select -s "/Applications/Xcode_13.2.1.app"
- name: Get specific version of CMake, Ninja
uses: lukka/get-cmake@v3.24.2
- name: 'Fetch build script'
run: |
IPP_DOWNLOAD_GIT_TAG=${{ inputs.itk-python-package-tag }}
IPP_DOWNLOAD_ORG=${{ inputs.itk-python-package-org }}
curl -L https://raw.githubusercontent.com/${IPP_DOWNLOAD_ORG:=InsightSoftwareConsortium}/ITKPythonPackage/${IPP_DOWNLOAD_GIT_TAG:=master}/scripts/macpython-download-cache-and-build-module-wheels.sh -O
chmod u+x macpython-download-cache-and-build-module-wheels.sh
- name: 'Build 🐍 Python 📦 package'
shell: bash
run: |
rm -rf dist
export ITK_PACKAGE_VERSION=${{ inputs.itk-wheel-tag }}
export ITKPYTHONPACKAGE_TAG=${{ inputs.itk-python-package-tag }}
export ITKPYTHONPACKAGE_ORG=${{ inputs.itk-python-package-org }}
export ITK_MODULE_PREQ=${{ inputs.itk-module-deps }}
export MACOSX_DEPLOYMENT_TARGET=10.9
if [ -z ${{ inputs.cmake-options }} ]; then
CMAKE_OPTIONS=""
else
CMAKE_OPTIONS="--cmake_options ${{ inputs.cmake-options }}"
fi
./macpython-download-cache-and-build-module-wheels.sh $CMAKE_OPTIONS "3.${{ matrix.python3-minor-version }}"
- name: Set up Python 3.10 for Validation
uses: actions/setup-python@v4
with:
python-version: "3.10"
- name: Validate build output
shell: bash
run: |
python -m pip install twine
ls dist/
WHEEL_PATTERN="dist/itk_*macosx*.whl"
EXPECTED_WHEEL_COUNT=1
WHEEL_COUNT=`(ls ${WHEEL_PATTERN} | wc -l)`
if (( ${WHEEL_COUNT} != ${EXPECTED_WHEEL_COUNT} )); then
echo "Expected ${EXPECTED_WHEEL_COUNT} wheels but found ${WHEEL_COUNT}"
exit 1
fi
python -m twine check ${WHEEL_PATTERN}
- name: Publish Python package as GitHub Artifact
uses: actions/upload-artifact@v3
with:
name: MacOSWheel3${{ matrix.python3-minor-version }}
path: dist/*.whl
build-windows-python-packages:
runs-on: windows-2022
strategy:
max-parallel: 2
matrix:
python3-minor-version: ${{ fromJSON(inputs.python3-minor-versions) }}
steps:
- name: Get specific version of CMake, Ninja
uses: lukka/get-cmake@v3.24.2
- uses: actions/checkout@v3
with:
path: "im"
- name: 'Reduce source path length'
shell: bash
run: |
# Move checked-out source to a shorter path to avoid Windows path length issues
mv im ../../
- name: 'Fetch build script'
shell: pwsh
run: |
cd ../../im
$ITKPYTHONPACKAGE_TAG = "${{ inputs.itk-python-package-tag }}"
$ITKPYTHONPACKAGE_ORG = "${{ inputs.itk-python-package-org }}"
$SCRIPT_UPSTREAM = "https://raw.githubusercontent.com/$ITKPYTHONPACKAGE_ORG/ITKPythonPackage/$ITKPYTHONPACKAGE_TAG/scripts/windows-download-cache-and-build-module-wheels.ps1"
echo "Fetching $SCRIPT_UPSTREAM"
(new-object net.webclient).DownloadString($SCRIPT_UPSTREAM) > windows-download-cache-and-build-module-wheels.ps1
- name: 'Build 🐍 Python 📦 package'
shell: pwsh
run: |
if (Test-Path dist) { rm dist -r -fo }
cd ../../im
& "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64 -SkipAutomaticLocation
$env:CC="cl.exe"
$env:CXX="cl.exe"
$env:ITK_PACKAGE_VERSION = "${{ inputs.itk-wheel-tag }}"
$env:ITKPYTHONPACKAGE_TAG = "${{ inputs.itk-python-package-tag }}"
$env:ITKPYTHONPACKAGE_ORG = "${{ inputs.itk-python-package-org }}"
$env:ITK_MODULE_PREQ = "${{ inputs.itk-module-deps }}"
./windows-download-cache-and-build-module-wheels.ps1 "${{ matrix.python3-minor-version }}" -cmake_options "${{ inputs.cmake-options }}"
mkdir -p '${{ github.workspace }}\dist'
cp 'dist\*.whl' '${{ github.workspace }}\dist'
- name: Set up Python 3.10 for Validation
uses: actions/setup-python@v4
with:
python-version: "3.10"
- name: Validate build output
shell: pwsh
run: |
python -m pip install twine
ls dist/
$WHEEL_PATTERN = "dist/itk_*cp3${{ matrix.python3-minor-version }}*win*.whl"
echo "Searching for wheels matching pattern ${WHEEL_PATTERN}"
python -m twine check ${WHEEL_PATTERN}
- name: Publish Python package as GitHub Artifact
uses: actions/upload-artifact@v3
with:
name: WindowsWheel3${{ matrix.python3-minor-version }}
path: dist/*.whl
test-linux-notebooks:
if: inputs.test-notebooks
needs:
- build-linux-py
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.${{ fromJSON(inputs.python3-minor-versions)[0] }}"
- name: Install build dependencies
shell: bash
run: |
if [[ -f requirements.txt ]]; then
python -m pip install -r requirements.txt
elif [[ -f ./binder/requirements.txt ]]; then
python -m pip install -r ./binder/requirements.txt
fi
python -m pip install pytest nbmake
- name: Download Python Package Artifacts
uses: actions/download-artifact@v3
with:
name: LinuxWheel3${{ fromJSON(inputs.python3-minor-versions)[0] }}
- name: Install Python Package Artifact
run: |
ls .
wheel_name_full=`(find . -name "*cp3${{ fromJSON(inputs.python3-minor-versions)[0] }}*-manylinux_2_28_x86_64.whl")`
echo "wheel_name_full ${wheel_name_full}"
# extract wheel name which may be an abbreviation of the module name
# ex. ./itk_splitcomponents-cp310..._64.whl -> itk-splitcomponents
wheel_name_prefix=`(echo ${wheel_name_full} | cut -d'-' -f1 | cut -d'/' -f2 | sed -e 's/_/-/g')`
echo "wheel_name_prefix ${wheel_name_prefix}"
python -m pip uninstall ${wheel_name_prefix} -y
python -m pip install ${wheel_name_full}
- name: Test notebooks
run: |
pytest --nbmake --nbmake-timeout=30000 examples/*.ipynb
check-secret:
runs-on: ubuntu-latest
outputs:
pypi-password-exists: ${{ steps.pypi-password-check.outputs.defined }}
steps:
- name: Check for Secret availability
id: pypi-password-check
# perform secret check & put boolean result as an output
shell: bash
run: |
if [ "${{ secrets.PYPI_PASSWORD }}" != '' ]; then
echo "defined=true" >> $GITHUB_OUTPUT;
else
echo "defined=false" >> $GITHUB_OUTPUT;
fi
publish-python-packages-to-pypi:
needs:
- check-secret
- build-linux-py
- build-macos-py
- build-windows-python-packages
runs-on: ubuntu-22.04
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
steps:
- name: Download Python Packages
uses: actions/download-artifact@v3
- name: Prepare packages for upload
run: |
ls -R
for d in */; do
mv ${d}/*.whl .
done
mkdir dist
mv *.whl dist/
ls dist
- name: Publish 🐍 Python 📦 package to PyPI
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && needs.check-secret.outputs.pypi-password-exists == 'true'
uses: pypa/gh-action-pypi-publish@v1.8.10
with:
skip_existing: true
user: __token__
password: ${{ secrets.pypi_password }}
- name: Publish 🐍 Python 📦 package to PyPI
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && needs.check-secret.outputs.pypi-password-exists != 'true'