Build wheels (python-tags=["cp313t"], platform-tag=macosx_arm64, unoptimized=false, include-debug-info-for-macos=false, run_tests=true, use-server-rc=false, server-tag=latest) #1104
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: 'Build wheels' | |
run-name: 'Build wheels (python-tags=${{ inputs.python-tags }}, platform-tag=${{ inputs.platform-tag }}, unoptimized=${{ inputs.unoptimized }}, include-debug-info-for-macos=${{ inputs.include-debug-info-for-macos }}, run_tests=${{ inputs.run_tests }}, use-server-rc=${{ inputs.use-server-rc }}, server-tag=${{ inputs.server-tag }})' | |
# Build wheels on all (or select) Python versions supported by the Python client for a specific platform | |
on: | |
workflow_dispatch: | |
inputs: | |
# These are the usual cases for building wheels: | |
# | |
# 1. One wheel for *one* supported Python version. This is for running specialized tests that only need one Python version | |
# like valgrind or a failing QE test. And usually, we only need one wheel for debugging purposes. | |
# 2. Wheels for *all* supported Python versions for *one* supported platform. This is useful for testing workflow changes for a | |
# single OS or CPU architecture (e.g testing that changes to debugging modes work on all Python versions) | |
# 3. Wheels for *all* supported Python versions and *all* supported platforms. This is for building wheels for different | |
# CI/CD stages (e.g dev, stage, or master). We can also test debugging modes for all platforms that support them | |
# | |
# We're able to combine case 1 and 2 into one workflow by creating an input that takes in a JSON list of strings (Python tags) | |
# to build wheels for. Actual list inputs aren't supported yet, so it's actually a JSON list encoded as a string. | |
# | |
# However, it's harder to combine this workflow (case 1 + 2) with case 3, because matrix outputs don't exist yet | |
# in Github Actions. So all jobs in the cibuildwheel job would have to pass for a self hosted job to run. | |
# We want each platform to be tested independently of each other, | |
# so there is a wrapper workflow that has a list of platforms to test and reuses this workflow for each platform. | |
# If one platform fails, it will not affect the building and testing of another platform (we disable fail fast mode) | |
python-tags: | |
type: string | |
description: Valid JSON list of Python tags to build the client for | |
required: false | |
default: '["cp38", "cp39", "cp310", "cp311", "cp312", "cp313", "cp313t"]' | |
platform-tag: | |
description: Platform to build the client for. | |
type: choice | |
required: true | |
options: | |
- manylinux_x86_64 | |
- manylinux_aarch64 | |
- macosx_x86_64 | |
- macosx_arm64 | |
- win_amd64 | |
# Makes debugging via gh cli easier. | |
default: manylinux_x86_64 | |
unoptimized: | |
description: 'macOS or Linux: Apply -O0 flag?' | |
# Windows supports a different flag to disable optimizations, but we haven't added support for it yet | |
type: boolean | |
required: false | |
default: false | |
include-debug-info-for-macos: | |
description: 'macOS: Build wheels for debugging?' | |
type: boolean | |
required: false | |
default: false | |
run_tests: | |
description: 'Run Aerospike server and run tests using built wheels?' | |
type: boolean | |
required: false | |
default: false | |
use-server-rc: | |
type: boolean | |
required: true | |
default: false | |
description: 'Test against server release candidate?' | |
server-tag: | |
required: true | |
default: 'latest' | |
description: 'Server docker image tag' | |
workflow_call: | |
inputs: | |
# See workflow call hack in update-version.yml | |
is_workflow_call: | |
type: boolean | |
default: true | |
required: false | |
python-tags: | |
type: string | |
required: false | |
default: '["cp38", "cp39", "cp310", "cp311", "cp312", "cp313", "cp313t"]' | |
platform-tag: | |
type: string | |
required: true | |
# Only used in workflow_call event | |
sha-to-build-and-test: | |
type: string | |
required: true | |
unoptimized: | |
type: boolean | |
required: false | |
default: false | |
include-debug-info-for-macos: | |
type: boolean | |
required: false | |
default: false | |
run_tests: | |
type: boolean | |
required: false | |
default: false | |
use-server-rc: | |
required: false | |
type: boolean | |
default: false | |
description: 'Test against server release candidate?' | |
server-tag: | |
required: false | |
type: string | |
default: 'latest' | |
description: 'Server docker image tag' | |
secrets: | |
# Just make all the secrets required to make things simpler... | |
DOCKER_HUB_BOT_USERNAME: | |
required: true | |
DOCKER_HUB_BOT_PW: | |
required: true | |
MAC_M1_SELF_HOSTED_RUNNER_PW: | |
required: true | |
env: | |
COMMIT_SHA_TO_BUILD_AND_TEST: ${{ inputs.is_workflow_call == true && inputs.sha-to-build-and-test || github.sha }} | |
# Note that environment variables in Github are all strings | |
# Github mac m1 and windows runners don't support Docker / nested virtualization | |
# so we need to use self-hosted runners to test wheels for these platforms | |
RUN_INTEGRATION_TESTS_IN_CIBW: ${{ inputs.run_tests && (startsWith(inputs.platform-tag, 'manylinux') || inputs.platform-tag == 'macosx_x86_64') }} | |
jobs: | |
# Maps don't exist in Github Actions, so we have to store the map using a script and fetch it in a job | |
# This uses up more billing minutes (rounded up to 1 minute for each job run), | |
# but this should be ok based on the minutes usage data for the aerospike organization | |
get-runner-os: | |
outputs: | |
runner-os: ${{ steps.get-runner-os.outputs.runner_os }} | |
runs-on: ubuntu-22.04 | |
steps: | |
- id: get-runner-os | |
# Single source of truth for which runner OS to use for each platform tag | |
run: | | |
declare -A hashmap | |
hashmap[manylinux_x86_64]="ubuntu-22.04" | |
hashmap[manylinux_aarch64]="aerospike_arm_runners_2" | |
hashmap[macosx_x86_64]="macos-12-large" | |
hashmap[macosx_arm64]="macos-14" | |
hashmap[win_amd64]="windows-2022" | |
echo runner_os=${hashmap[${{ inputs.platform-tag }}]} >> $GITHUB_OUTPUT | |
# Bash >= 4 supports hashmaps | |
shell: bash | |
cibuildwheel: | |
needs: get-runner-os | |
strategy: | |
matrix: | |
python-tag: ${{ fromJSON(inputs.python-tags) }} | |
fail-fast: false | |
runs-on: ${{ needs.get-runner-os.outputs.runner-os }} | |
env: | |
BUILD_IDENTIFIER: "${{ matrix.python-tag }}-${{ inputs.platform-tag }}" | |
steps: | |
- name: Create status check message | |
run: echo STATUS_CHECK_MESSAGE="cibuildwheel (${{ env.BUILD_IDENTIFIER }})" >> $GITHUB_ENV | |
shell: bash | |
- name: Show job status for commit | |
uses: myrotvorets/[email protected] | |
if: ${{ github.event_name != 'push' && github.event_name != 'pull_request' }} | |
with: | |
sha: ${{ env.COMMIT_SHA_TO_BUILD_AND_TEST }} | |
context: ${{ env.STATUS_CHECK_MESSAGE }} | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
ref: ${{ env.COMMIT_SHA_TO_BUILD_AND_TEST }} | |
# We need the last tag before the ref, so we can relabel the version if needed | |
fetch-depth: 0 | |
- name: 'macOS arm64: Install experimental Python 3.8 macOS arm64 build' | |
# By default, cibuildwheel installs and uses Python 3.8 x86_64 to cross compile macOS arm64 wheels | |
# There is a bug that builds macOS x86_64 wheels instead, so we install this Python 3.8 native ARM build to ensure | |
# the wheel is compiled for macOS arm64 | |
# https://cibuildwheel.pypa.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64 | |
if: ${{ matrix.python-tag == 'cp38' && inputs.platform-tag == 'macosx_arm64' }} | |
run: | | |
curl -o /tmp/Python38.pkg https://www.python.org/ftp/python/3.8.10/python-3.8.10-macos11.pkg | |
sudo installer -pkg /tmp/Python38.pkg -target / | |
sh "/Applications/Python 3.8/Install Certificates.command" | |
- name: 'Windows: Add msbuild to PATH' | |
if: ${{ inputs.platform-tag == 'win_amd64' }} | |
uses: microsoft/[email protected] | |
- name: 'Windows: Install C client deps' | |
if: ${{ inputs.platform-tag == 'win_amd64' }} | |
run: nuget restore | |
working-directory: aerospike-client-c/vs | |
- name: 'macOS x86: Setup Docker using colima for testing' | |
if: ${{ env.RUN_INTEGRATION_TESTS_IN_CIBW == 'true' && inputs.platform-tag == 'macosx_x86_64' }} | |
uses: ./.github/actions/setup-docker-on-macos | |
- name: 'macOS x86: run Aerospike server in Docker container and connect via localhost' | |
if: ${{ env.RUN_INTEGRATION_TESTS_IN_CIBW == 'true' && inputs.platform-tag == 'macosx_x86_64' }} | |
uses: ./.github/actions/run-ee-server | |
with: | |
use-server-rc: ${{ inputs.use-server-rc }} | |
server-tag: ${{ inputs.server-tag }} | |
docker-hub-username: ${{ secrets.DOCKER_HUB_BOT_USERNAME }} | |
docker-hub-password: ${{ secrets.DOCKER_HUB_BOT_PW }} | |
# TODO: combine this composite action and the above into one | |
- name: "Linux: run Aerospike server in Docker container and configure config.conf to connect to the server container's Docker IP address" | |
if: ${{ env.RUN_INTEGRATION_TESTS_IN_CIBW == 'true' && startsWith(inputs.platform-tag, 'manylinux') }} | |
uses: ./.github/actions/run-ee-server-for-ext-container | |
with: | |
use-server-rc: ${{ inputs.use-server-rc }} | |
server-tag: ${{ inputs.server-tag }} | |
docker-hub-username: ${{ secrets.DOCKER_HUB_BOT_USERNAME }} | |
docker-hub-password: ${{ secrets.DOCKER_HUB_BOT_PW }} | |
- name: If not running tests against server, only run basic import test | |
if: ${{ env.RUN_INTEGRATION_TESTS_IN_CIBW == 'false' }} | |
# Use double quotes otherwise Windows will throw this error in cibuildwheel | |
# 'import | |
# ^ | |
# SyntaxError: EOL while scanning string literal | |
run: echo "TEST_COMMAND=python -c \"import aerospike\"" >> $GITHUB_ENV | |
shell: bash | |
- name: Otherwise, enable integration tests | |
if: ${{ env.RUN_INTEGRATION_TESTS_IN_CIBW == 'true' }} | |
run: echo "TEST_COMMAND=cd {project}/test/ && pip install -r requirements.txt && python -m pytest new_tests/" >> $GITHUB_ENV | |
shell: bash | |
- name: Set unoptimize flag | |
if: ${{ inputs.unoptimized && (startsWith(inputs.platform-tag, 'manylinux') || startsWith(inputs.platform-tag, 'macosx')) }} | |
run: echo "UNOPTIMIZED=1" >> $GITHUB_ENV | |
- name: Set include dsym flag | |
if: ${{ inputs.include-debug-info-for-macos && startsWith(inputs.platform-tag, 'macosx') }} | |
run: echo "INCLUDE_DSYM=1" >> $GITHUB_ENV | |
- name: Build wheel | |
uses: pypa/[email protected] | |
env: | |
CIBW_ENVIRONMENT_PASS_LINUX: ${{ inputs.unoptimized && 'UNOPTIMIZED' || '' }} | |
CIBW_ENVIRONMENT_MACOS: SSL_LIB_PATH="$(brew --prefix [email protected])/lib/" CPATH="$(brew --prefix [email protected])/include/" STATIC_SSL=1 | |
CIBW_BUILD: ${{ env.BUILD_IDENTIFIER }} | |
CIBW_BUILD_FRONTEND: build | |
CIBW_BEFORE_ALL_LINUX: > | |
yum install openssl-devel -y && | |
yum install python-devel -y && | |
yum install python-setuptools -y | |
# delvewheel is not enabled by default but we do need to repair the wheel | |
CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" | |
CIBW_FREE_THREADED_SUPPORT: ${{ matrix.python-tag == 'cp313t' && '1' || '0' }} | |
CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair --add-path ./aerospike-client-c/vs/x64/Release -w {dest_dir} {wheel}" | |
CIBW_TEST_COMMAND: ${{ env.TEST_COMMAND }} | |
- name: Upload wheels to GitHub | |
uses: actions/upload-artifact@v4 | |
if: ${{ !cancelled() }} | |
with: | |
path: ./wheelhouse/*.whl | |
name: ${{ env.BUILD_IDENTIFIER }}.build | |
- name: Set final commit status | |
uses: myrotvorets/[email protected] | |
if: ${{ always() && github.event_name != 'push' && github.event_name != 'pull_request' }} | |
with: | |
sha: ${{ env.COMMIT_SHA_TO_BUILD_AND_TEST }} | |
status: ${{ job.status }} | |
context: ${{ env.STATUS_CHECK_MESSAGE }} | |
test-self-hosted: | |
needs: cibuildwheel | |
# There's a top-level env variable for this but we can't use it here, unfortunately | |
if: ${{ inputs.run_tests && (inputs.platform-tag == 'macosx_arm64' || inputs.platform-tag == 'win_amd64') }} | |
strategy: | |
fail-fast: false | |
matrix: | |
python-tag: ${{ fromJSON(inputs.python-tags) }} | |
runs-on: ${{ inputs.platform-tag == 'macosx_arm64' && fromJSON('["self-hosted", "macOS", "ARM64"]') || fromJSON('["self-hosted", "Windows", "X64"]') }} | |
env: | |
BUILD_IDENTIFIER: "${{ matrix.python-tag }}-${{ inputs.platform-tag }}" | |
steps: | |
- name: Create status check message | |
run: echo STATUS_CHECK_MESSAGE="Test on self hosted (${{ env.BUILD_IDENTIFIER }})" >> $GITHUB_ENV | |
shell: bash | |
- name: Show job status for commit | |
uses: myrotvorets/[email protected] | |
if: ${{ github.event_name != 'push' && github.event_name != 'pull_request' }} | |
with: | |
sha: ${{ env.COMMIT_SHA_TO_BUILD_AND_TEST }} | |
context: ${{ env.STATUS_CHECK_MESSAGE }} | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ env.COMMIT_SHA_TO_BUILD_AND_TEST }} | |
sparse-checkout-cone-mode: false | |
sparse-checkout: | | |
!pyproject.toml | |
test/** | |
.github/** | |
# Need to be able to save Docker Hub credentials to keychain | |
- if: ${{ inputs.platform-tag == 'macosx_arm64' && inputs.use-server-rc }} | |
run: security unlock-keychain -p ${{ secrets.MAC_M1_SELF_HOSTED_RUNNER_PW }} | |
- uses: ./.github/actions/run-ee-server | |
with: | |
use-server-rc: ${{ inputs.use-server-rc }} | |
server-tag: ${{ inputs.server-tag }} | |
docker-hub-username: ${{ secrets.DOCKER_HUB_BOT_USERNAME }} | |
docker-hub-password: ${{ secrets.DOCKER_HUB_BOT_PW }} | |
- name: Download wheel | |
uses: actions/download-artifact@v4 | |
with: | |
name: ${{ env.BUILD_IDENTIFIER }}.build | |
- name: Convert Python tag to Python version | |
if: ${{ matrix.python-tag != 'cp313t' }} | |
# Don't use sed because we want this command to work on both mac and windows | |
# The command used in GNU sed is different than in macOS sed | |
run: | | |
PYTHON_TAG=${{ matrix.python-tag }} | |
PYTHON_VERSION="${PYTHON_TAG/cp/}" | |
echo PYTHON_VERSION="${PYTHON_VERSION/3/3.}" >> $GITHUB_ENV | |
shell: bash | |
- uses: actions/setup-python@v5 | |
if: ${{ matrix.python-tag != 'cp313t' }} | |
with: | |
python-version: ${{ env.PYTHON_VERSION }} | |
- if: ${{ matrix.python-tag == 'cp313t' }} | |
uses: astral-sh/setup-uv@v3 | |
with: | |
version: "0.4.x" | |
- if: ${{ matrix.python-tag == 'cp313t' }} | |
# Will automatically install Python 3.13t if not present | |
run: uv venv --python 3.13t | |
shell: bash | |
# TODO: check if uv is installed, instead | |
- run: echo PIP_COMMAND="${{ matrix.python-tag == 'cp313t' && 'uv' || '' }} pip" >> $GITHUB_ENV | |
shell: bash | |
- name: Install wheel | |
run: ${{ env.PIP_COMMAND }} install aerospike --force-reinstall --no-index --find-links=./ | |
shell: bash | |
- name: Connect to Docker container on remote machine with Docker daemon | |
if: ${{ inputs.platform-tag == 'win_amd64' }} | |
# DOCKER_HOST contains the IP address of the remote machine | |
run: | | |
$env:DOCKER_HOST_IP = $env:DOCKER_HOST | foreach {$_.replace("tcp://","")} | foreach {$_.replace(":2375", "")} | |
crudini --set config.conf enterprise-edition hosts ${env:DOCKER_HOST_IP}:3000 | |
working-directory: test | |
- run: ${{ env.PIP_COMMAND }} install pytest -c requirements.txt | |
working-directory: test | |
shell: bash | |
- run: echo PYTHON_M_COMMAND=${{ matrix.python-tag == 'cp313t' && 'uv run --module' || 'python3 -m' }} >> $GITHUB_ENV | |
shell: bash | |
- run: ${{ env.PYTHON_M_COMMAND }} pytest -s new_tests/ | |
working-directory: test | |
shell: bash | |
- name: Show job status for commit | |
if: ${{ always() && github.event_name != 'push' && github.event_name != 'pull_request' }} | |
uses: myrotvorets/[email protected] | |
with: | |
sha: ${{ env.COMMIT_SHA_TO_BUILD_AND_TEST }} | |
status: ${{ job.status }} | |
context: ${{ env.STATUS_CHECK_MESSAGE }} |