Skip to content

Commit 0dcd7ac

Browse files
committed
Add CI workflows for building and testing Linux wheels on x86_64 and aarch64 architectures
1 parent 97504d4 commit 0dcd7ac

File tree

4 files changed

+168
-58
lines changed

4 files changed

+168
-58
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
name: Build Linux aarch64 Wheels
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
build-linux-aarch64:
8+
name: Build Linux aarch64 Wheels
9+
# Use native ARM64 runners for much faster builds (vs QEMU emulation)
10+
runs-on: ubuntu-latest-arm64
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
with:
15+
submodules: recursive
16+
17+
# Setup Go for BoringSSL build
18+
- name: Setup Go
19+
uses: actions/setup-go@v5
20+
with:
21+
go-version: '1.21'
22+
cache: true
23+
24+
# Build wheels with cibuildwheel (handles manylinux containers)
25+
# Vendor dependencies are built inside the manylinux container and cached across Python versions
26+
# Note: GitHub Actions cache doesn't work here since vendor dir is inside the container
27+
- name: Build wheels
28+
uses: pypa/cibuildwheel@v3.3
29+
env:
30+
# Build for all Python versions on aarch64
31+
CIBW_BUILD: cp38-manylinux_aarch64 cp39-manylinux_aarch64 cp310-manylinux_aarch64 cp311-manylinux_aarch64 cp312-manylinux_aarch64 cp313-manylinux_aarch64 cp314-manylinux_aarch64
32+
# Vendor build happens inside manylinux container via before-build (from pyproject.toml)
33+
# The script will detect cached libraries and skip rebuilding across Python versions
34+
CIBW_ARCHS_LINUX: aarch64
35+
36+
# Setup Python versions for testing
37+
- name: Setup Python versions
38+
uses: actions/setup-python@v5
39+
with:
40+
python-version: |
41+
3.8
42+
3.9
43+
3.10
44+
3.11
45+
3.12
46+
3.13
47+
3.14
48+
allow-prereleases: true
49+
50+
# Test the built wheels on native ARM64
51+
- name: Test wheels
52+
run: |
53+
# Test each wheel that was built
54+
for wheel in ./wheelhouse/*.whl; do
55+
echo "========================================"
56+
echo "Testing wheel: $(basename "$wheel")"
57+
echo "========================================"
58+
59+
# Extract Python version from wheel filename (e.g., cp39, cp310)
60+
python_tag=$(basename "$wheel" | grep -oE 'cp[0-9]+' | head -1)
61+
python_version="${python_tag:2:1}.${python_tag:3}"
62+
63+
echo "Setting up Python $python_version..."
64+
65+
# Try to find the matching Python version
66+
python_cmd=""
67+
for cmd in "python${python_version}" "python3.${python_version#*.}" "python3"; do
68+
if command -v "$cmd" &> /dev/null; then
69+
# Verify this is the right version
70+
version_check=$($cmd --version 2>&1 | grep -oE '[0-9]+\.[0-9]+' | head -1)
71+
if [ "$version_check" = "$python_version" ]; then
72+
python_cmd="$cmd"
73+
break
74+
fi
75+
fi
76+
done
77+
78+
# Fall back to python3 if we couldn't find exact match
79+
if [ -z "$python_cmd" ]; then
80+
echo "WARNING: Python $python_version not found, using python3"
81+
python_cmd="python3"
82+
fi
83+
84+
echo "Using Python: $($python_cmd --version)"
85+
86+
# Install the wheel in a fresh environment
87+
"$python_cmd" -m pip install --force-reinstall "$wheel"
88+
89+
# Run the test script
90+
echo ""
91+
echo "Running tests..."
92+
"$python_cmd" scripts/test_local_build.py
93+
94+
# Check exit code
95+
if [ $? -eq 0 ]; then
96+
echo ""
97+
echo "[OK] Wheel test PASSED: $(basename "$wheel")"
98+
else
99+
echo ""
100+
echo "[FAIL] Wheel test FAILED: $(basename "$wheel")"
101+
exit 1
102+
fi
103+
104+
echo ""
105+
done
106+
107+
echo "========================================"
108+
echo "All wheel tests PASSED"
109+
echo "========================================"
110+
111+
# Upload wheels as artifacts
112+
- name: Upload wheels
113+
uses: actions/upload-artifact@v4
114+
with:
115+
name: wheels-linux-aarch64
116+
path: ./wheelhouse/*.whl
117+
if-no-files-found: error
118+
retention-days: 5
119+
# Retry on transient failures
120+
continue-on-error: false
Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
name: Build Linux Wheels
1+
name: Build Linux x86_64 Wheels
22

33
on:
44
workflow_call:
55

66
jobs:
7-
build-linux:
8-
name: Build Linux Wheels (${{ matrix.arch }})
7+
build-linux-x86_64:
8+
name: Build Linux x86_64 Wheels
99
runs-on: ubuntu-latest
10-
strategy:
11-
fail-fast: false
12-
matrix:
13-
arch: [x86_64, aarch64]
1410

1511
steps:
1612
- uses: actions/checkout@v4
@@ -24,46 +20,20 @@ jobs:
2420
go-version: '1.21'
2521
cache: true
2622

27-
# Setup QEMU for ARM64 emulation (only needed for aarch64 builds on x86_64 runners)
28-
- name: Set up QEMU
29-
if: matrix.arch == 'aarch64'
30-
uses: docker/setup-qemu-action@v3
31-
with:
32-
platforms: arm64
33-
34-
# Cache vendor dependencies to speed up builds (separate cache per architecture)
35-
- name: Restore vendor cache
36-
id: cache-vendor
37-
uses: actions/cache/restore@v4
38-
with:
39-
path: vendor
40-
key: vendor-linux-${{ matrix.arch }}-${{ hashFiles('scripts/linux/setup_vendors.sh') }}-v11
41-
restore-keys: |
42-
vendor-linux-${{ matrix.arch }}-${{ hashFiles('scripts/linux/setup_vendors.sh') }}-v11
43-
4423
# Build wheels with cibuildwheel (handles manylinux containers)
45-
# The vendor directory is mounted into the container and shared across Python versions
46-
# The setup script will skip rebuilding if libraries already exist
24+
# Vendor dependencies are built inside the manylinux container and cached across Python versions
25+
# Note: GitHub Actions cache doesn't work here since vendor dir is inside the container
4726
- name: Build wheels
4827
uses: pypa/cibuildwheel@v3.3
4928
env:
50-
# Build for all Python versions on the specific architecture
51-
CIBW_BUILD: cp38-manylinux_${{ matrix.arch }} cp39-manylinux_${{ matrix.arch }} cp310-manylinux_${{ matrix.arch }} cp311-manylinux_${{ matrix.arch }} cp312-manylinux_${{ matrix.arch }} cp313-manylinux_${{ matrix.arch }} cp314-manylinux_${{ matrix.arch }}
29+
# Build for all Python versions on x86_64
30+
CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp313-manylinux_x86_64 cp314-manylinux_x86_64
5231
# Vendor build happens inside manylinux container via before-build (from pyproject.toml)
53-
# The script will detect cached libraries and skip rebuilding
54-
CIBW_ARCHS_LINUX: ${{ matrix.arch }}
55-
56-
# Save vendor cache after build
57-
- name: Save vendor cache
58-
if: steps.cache-vendor.outputs.cache-hit != 'true'
59-
uses: actions/cache/save@v4
60-
with:
61-
path: vendor
62-
key: vendor-linux-${{ matrix.arch }}-${{ hashFiles('scripts/linux/setup_vendors.sh') }}-v11
32+
# The script will detect cached libraries and skip rebuilding across Python versions
33+
CIBW_ARCHS_LINUX: x86_64
6334

64-
# Setup Python versions for testing (only for x86_64)
35+
# Setup Python versions for testing
6536
- name: Setup Python versions
66-
if: matrix.arch == 'x86_64'
6737
uses: actions/setup-python@v5
6838
with:
6939
python-version: |
@@ -76,9 +46,8 @@ jobs:
7646
3.14
7747
allow-prereleases: true
7848

79-
# Test the built wheels (only for x86_64 - aarch64 wheels cannot be tested on x86_64 runners)
49+
# Test the built wheels
8050
- name: Test wheels
81-
if: matrix.arch == 'x86_64'
8251
run: |
8352
# Test each wheel that was built
8453
for wheel in ./wheelhouse/*.whl; do
@@ -138,11 +107,11 @@ jobs:
138107
echo "All wheel tests PASSED"
139108
echo "========================================"
140109
141-
# Upload wheels as artifacts (separate artifact per architecture)
110+
# Upload wheels as artifacts
142111
- name: Upload wheels
143112
uses: actions/upload-artifact@v4
144113
with:
145-
name: wheels-linux-${{ matrix.arch }}
114+
name: wheels-linux-x86_64
146115
path: ./wheelhouse/*.whl
147116
if-no-files-found: error
148117
retention-days: 5

.github/workflows/ci.yml

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,15 @@ jobs:
3434
# ============================================================
3535
# Build Test Jobs
3636
# ============================================================
37-
build-test-linux:
38-
name: Build Test (Linux)
37+
build-test-linux-x86_64:
38+
name: Build Test (Linux x86_64)
3939
needs: load-config
40-
uses: ./.github/workflows/_build_linux.yml
40+
uses: ./.github/workflows/_build_linux_x86_64.yml
41+
42+
build-test-linux-aarch64:
43+
name: Build Test (Linux aarch64)
44+
needs: load-config
45+
uses: ./.github/workflows/_build_linux_aarch64.yml
4146

4247
build-test-macos:
4348
name: Build Test (macOS)
@@ -54,7 +59,7 @@ jobs:
5459
# ============================================================
5560
summary:
5661
name: CI Summary
57-
needs: [load-config, test, build-test-linux, build-test-macos, build-test-windows]
62+
needs: [load-config, test, build-test-linux-x86_64, build-test-linux-aarch64, build-test-macos, build-test-windows]
5863
if: always()
5964
runs-on: ubuntu-latest
6065

@@ -82,13 +87,23 @@ jobs:
8287
echo ""
8388
echo "Build Tests:"
8489
85-
# Linux
86-
if [ "${{ needs.build-test-linux.result }}" == "success" ]; then
87-
echo " ✅ Linux: PASSED"
88-
elif [ "${{ needs.build-test-linux.result }}" == "skipped" ]; then
89-
echo " ⊘ Linux: SKIPPED"
90+
# Linux x86_64
91+
if [ "${{ needs.build-test-linux-x86_64.result }}" == "success" ]; then
92+
echo " ✅ Linux x86_64: PASSED"
93+
elif [ "${{ needs.build-test-linux-x86_64.result }}" == "skipped" ]; then
94+
echo " ⊘ Linux x86_64: SKIPPED"
95+
else
96+
echo " ❌ Linux x86_64: FAILED"
97+
EXIT_CODE=1
98+
fi
99+
100+
# Linux aarch64
101+
if [ "${{ needs.build-test-linux-aarch64.result }}" == "success" ]; then
102+
echo " ✅ Linux aarch64: PASSED"
103+
elif [ "${{ needs.build-test-linux-aarch64.result }}" == "skipped" ]; then
104+
echo " ⊘ Linux aarch64: SKIPPED"
90105
else
91-
echo " ❌ Linux: FAILED"
106+
echo " ❌ Linux aarch64: FAILED"
92107
EXIT_CODE=1
93108
fi
94109

.github/workflows/release.yml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,17 @@ jobs:
4242
if: always() && (needs.test.result == 'success' || needs.test.result == 'skipped')
4343
uses: ./.github/workflows/_build_windows.yml
4444

45-
build-linux:
46-
name: Build Linux Wheels
45+
build-linux-x86_64:
46+
name: Build Linux x86_64 Wheels
4747
needs: [config, test]
4848
if: always() && (needs.test.result == 'success' || needs.test.result == 'skipped')
49-
uses: ./.github/workflows/_build_linux.yml
49+
uses: ./.github/workflows/_build_linux_x86_64.yml
50+
51+
build-linux-aarch64:
52+
name: Build Linux aarch64 Wheels
53+
needs: [config, test]
54+
if: always() && (needs.test.result == 'success' || needs.test.result == 'skipped')
55+
uses: ./.github/workflows/_build_linux_aarch64.yml
5056

5157
build-macos:
5258
name: Build macOS Wheels
@@ -56,7 +62,7 @@ jobs:
5662

5763
publish:
5864
name: Publish Release
59-
needs: [build-windows, build-linux, build-macos]
65+
needs: [build-windows, build-linux-x86_64, build-linux-aarch64, build-macos]
6066
if: always() && startsWith(github.ref, 'refs/tags/v')
6167
runs-on: ubuntu-latest
6268
environment:

0 commit comments

Comments
 (0)