Skip to content

fix: arcadedb-embedded PyPI wheel will not install on manylinux_2_34_… #475

fix: arcadedb-embedded PyPI wheel will not install on manylinux_2_34_…

fix: arcadedb-embedded PyPI wheel will not install on manylinux_2_34_… #475

name: Test Python Bindings
on:
# Run on push to bindings/python/ directory
push:
paths:
- 'bindings/python/**'
- '**/*.java'
- '.github/workflows/test-python-bindings.yml'
# Run on pull request affecting bindings/python/
pull_request:
paths:
- 'bindings/python/**'
- '**/*.java'
- '.github/workflows/test-python-bindings.yml'
# Run after release workflow completes
workflow_run:
workflows: ["Release"]
types: [completed]
# Allow being called by other workflows (e.g., release workflow)
workflow_call:
inputs:
build-version:
description: "Override package version (PEP 440) for build.sh"
required: false
type: string
# Allow manual trigger
workflow_dispatch:
permissions:
contents: read
jobs:
# First job: Download ArcadeDB JARs (platform-agnostic)
download-jars:
name: Download ArcadeDB JARs
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Download JARs from ArcadeDB Docker image
shell: bash
run: |
cd bindings/python
# Detect ArcadeDB version from pom.xml (Docker format: X.Y.Z-SNAPSHOT)
ARCADEDB_TAG=$(python3 scripts/extract_version.py --format=docker)
echo "📌 ArcadeDB version: $ARCADEDB_TAG"
# Download JARs from official Docker image
echo "📦 Downloading JARs from arcadedata/arcadedb:$ARCADEDB_TAG..."
# Create output directory
mkdir -p src/arcadedb_embedded/jars
# Create a temporary container and copy JARs from it
CONTAINER_ID=$(docker create arcadedata/arcadedb:$ARCADEDB_TAG)
docker cp $CONTAINER_ID:/home/arcadedb/lib/. src/arcadedb_embedded/jars/
docker rm $CONTAINER_ID
# Verify JARs were downloaded
ls -lh src/arcadedb_embedded/jars/
JAR_COUNT=$(ls -1 src/arcadedb_embedded/jars/*.jar 2>/dev/null | wc -l)
echo "✅ Downloaded $JAR_COUNT JAR files"
- name: Remove excluded JARs
shell: bash
run: |
cd bindings/python
JARS_DIR="src/arcadedb_embedded/jars"
EXCLUSIONS_FILE="jar_exclusions.txt"
if [[ -f "$EXCLUSIONS_FILE" ]]; then
echo "🗑️ Removing excluded JARs from jar_exclusions.txt..."
EXCLUSION_COUNT=0
while IFS= read -r pattern || [[ -n "$pattern" ]]; do
# Skip empty lines and comments
if [[ -n "$pattern" ]] && [[ ! "$pattern" =~ ^# ]]; then
echo " Processing pattern: $pattern"
# Remove matching JARs
for jar in "$JARS_DIR"/$pattern; do
if [[ -f "$jar" ]]; then
rm -f "$jar"
echo " - Removed: $(basename "$jar")"
EXCLUSION_COUNT=$((EXCLUSION_COUNT + 1))
fi
done
fi
done < "$EXCLUSIONS_FILE"
JAR_COUNT_AFTER=$(ls -1 "$JARS_DIR"/*.jar 2>/dev/null | wc -l)
echo "✅ Removed $EXCLUSION_COUNT JAR(s), $JAR_COUNT_AFTER remaining"
else
echo "⚠️ No jar_exclusions.txt found, skipping exclusions"
fi
- name: Upload filtered JARs as artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: arcadedb-jars
path: bindings/python/src/arcadedb_embedded/jars/*.jar
retention-days: 1
# Second job: Build and test on each platform
test:
name: Test arcadedb-embedded (${{ matrix.platform }}, Python ${{ matrix.python-version }})
runs-on: ${{ matrix.runs-on }}
needs: download-jars
env:
BUILD_VERSION: ${{ inputs.build-version }}
strategy:
# Don't cancel other matrix jobs if one fails
fail-fast: false
matrix:
python-version:
- '3.10'
- '3.11'
- '3.12'
- '3.13'
- '3.14'
# Temporarily limit to four platforms (skip macOS x86_64, Windows ARM64)
# platform: ['linux/amd64', 'linux/arm64', 'darwin/amd64', 'darwin/arm64', 'windows/amd64', 'windows/arm64']
platform: ['linux/amd64', 'linux/arm64', 'darwin/arm64', 'windows/amd64']
include:
- platform: linux/amd64
runs-on: ubuntu-24.04
- platform: linux/arm64
runs-on: ubuntu-24.04-arm
- platform: darwin/arm64
runs-on: macos-15
- platform: windows/amd64
runs-on: windows-2025
# - platform: darwin/amd64
# runs-on: macos-15-intel
# - platform: windows/arm64
# runs-on: windows-11-arm
# macOS x86_64 and Windows ARM64 temporarily disabled
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
- name: Setup UV package manager
shell: bash
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
export PATH="$HOME/.local/bin:$PATH"
uv --version
- name: Download ArcadeDB JARs artifact
# Skip for Linux: Docker build downloads JARs from ArcadeDB image directly
# Only needed for native builds (macOS/Windows)
if: matrix.platform != 'linux/amd64' && matrix.platform != 'linux/arm64'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: arcadedb-jars
path: bindings/python/src/arcadedb_embedded/jars
- name: Set up Java (for native builds on macOS/Windows)
if: matrix.platform != 'linux/amd64' && matrix.platform != 'linux/arm64'
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'corretto'
java-version: '25'
- name: Set up Docker Buildx (Linux only)
if: matrix.platform == 'linux/amd64' || matrix.platform == 'linux/arm64'
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Install Python build dependencies
shell: bash
run: |
uv pip install --system build wheel setuptools
- name: Set UTF-8 encoding (Windows only)
if: matrix.platform == 'windows/amd64' || matrix.platform == 'windows/arm64'
shell: bash
run: |
echo "PYTHONIOENCODING=utf-8" >> $GITHUB_ENV
echo "PYTHONUTF8=1" >> $GITHUB_ENV
# Windows currently enabled, no symlink needed
# - name: Create python3 symlink (Windows only)
# if: matrix.platform == 'windows/amd64' || matrix.platform == 'windows/arm64'
# shell: bash
# run: |
# PYTHON_DIR=$(dirname "$(which python)")
# ln -s "$PYTHON_DIR/python.exe" "$PYTHON_DIR/python3.exe" || true
# python3 --version
- name: Build and test arcadedb-embedded (${{ matrix.platform }})
shell: bash
run: |
cd bindings/python
echo "🔨 Building arcadedb-embedded for ${{ matrix.platform }} with Python ${{ matrix.python-version }}..."
./scripts/build.sh ${{ matrix.platform }} ${{ matrix.python-version }}
- name: Extract wheel for additional testing
shell: bash
run: |
cd bindings/python
mkdir -p test-install
cp dist/*.whl test-install/
- name: Set up Python for host testing
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
# Note: Java is NOT required for arcadedb-embedded (JRE is bundled)
# This package works without any external Java installation!
- name: Install wheel and test dependencies
shell: bash
run: |
cd bindings/python
uv pip install --system test-install/*.whl pytest pytest-cov requests numpy
- name: Run pytest on host
id: pytest
shell: bash
run: |
cd bindings/python
echo "🧪 Testing arcadedb-embedded (with bundled JRE)..."
# Run pytest with JUnit XML output for structured results
set +e # Don't exit on test failures
pytest tests/ -v --tb=short --color=yes --junitxml=test-results.xml
PYTEST_EXIT_CODE=$?
set -e
# Parse JUnit XML for test counts (cross-platform XML parsing)
if [ -f "test-results.xml" ]; then
# Extract counts from testsuite element
TOTAL=$(grep -oE 'tests="[0-9]+"' test-results.xml | grep -oE '[0-9]+' | head -1)
FAILED=$(grep -oE 'failures="[0-9]+"' test-results.xml | grep -oE '[0-9]+' | head -1)
SKIPPED=$(grep -oE 'skipped="[0-9]+"' test-results.xml | grep -oE '[0-9]+' | head -1)
ERRORS=$(grep -oE 'errors="[0-9]+"' test-results.xml | grep -oE '[0-9]+' | head -1)
# Default to 0 if not found
TOTAL=${TOTAL:-0}
FAILED=${FAILED:-0}
SKIPPED=${SKIPPED:-0}
ERRORS=${ERRORS:-0}
# Calculate passed tests
PASSED=$((TOTAL - FAILED - SKIPPED - ERRORS))
echo "passed=$PASSED" >> $GITHUB_OUTPUT
echo "skipped=$SKIPPED" >> $GITHUB_OUTPUT
echo "failed=$FAILED" >> $GITHUB_OUTPUT
echo "✅ Test results: $PASSED passed, $SKIPPED skipped, $FAILED failed"
# Force failure if there are failures or errors
if [ "$FAILED" -gt 0 ] || [ "$ERRORS" -gt 0 ]; then
echo "❌ Tests failed (failures=$FAILED, errors=$ERRORS)"
exit 1
fi
else
echo "⚠️ test-results.xml not found"
exit 1
fi
# Exit with pytest's exit code
exit $PYTEST_EXIT_CODE
- name: Generate test summary
if: always()
shell: python
run: |
import os
import zipfile
import glob
from pathlib import Path
summary_file = os.environ.get('GITHUB_STEP_SUMMARY', '/dev/null')
with open(summary_file, 'a', encoding='utf-8') as f:
f.write("## 🧪 Test Results: arcadedb-embedded (${{ matrix.platform }})\n")
f.write("\n")
# Check test status
if "${{ steps.pytest.outcome }}" == "success":
f.write("✅ **Status**: PASSED\n")
else:
f.write("❌ **Status**: FAILED\n")
f.write("\n")
f.write("| Metric | Count |\n")
f.write("|--------|-------|\n")
# Show test results
f.write("| ✅ Passed | ${{ steps.pytest.outputs.passed || 'N/A' }} |\n")
f.write("| ⏭️ Skipped | ${{ steps.pytest.outputs.skipped || 'N/A' }} |\n")
f.write("| ❌ Failed | ${{ steps.pytest.outputs.failed || 'N/A' }} |\n")
f.write("\n")
f.write("### 📦 Package Info:\n")
f.write("\n")
# Get wheel size and analyze contents
wheel_files = glob.glob("bindings/python/dist/*.whl")
if wheel_files:
wheel_file = wheel_files[0]
# Get wheel size
wheel_size_bytes = os.path.getsize(wheel_file)
wheel_size_mb = wheel_size_bytes / 1024 / 1024
# Analyze wheel contents
jre_size_mb = "N/A"
jar_size_mb = "N/A"
installed_size_mb = "N/A"
try:
# Extract and analyze (cross-platform)
import tempfile
import shutil
with tempfile.TemporaryDirectory() as temp_dir:
with zipfile.ZipFile(wheel_file, 'r') as whl:
whl.extractall(temp_dir)
# Calculate JRE size
jre_dir = None
for root, dirs, files in os.walk(temp_dir):
if 'jre' in dirs:
jre_dir = os.path.join(root, 'jre')
break
if jre_dir and os.path.isdir(jre_dir):
jre_size_kb = 0
for root, dirs, files in os.walk(jre_dir):
for file in files:
jre_size_kb += os.path.getsize(os.path.join(root, file))
jre_size_mb = jre_size_kb / 1024 / 1024
# Calculate JAR size
jar_files = []
for root, dirs, files in os.walk(temp_dir):
for file in files:
if file.endswith('.jar'):
jar_files.append(os.path.join(root, file))
if jar_files:
jar_size_kb = sum(os.path.getsize(f) for f in jar_files) / 1024
jar_size_mb = jar_size_kb / 1024
# Calculate total size
total_size_kb = 0
for root, dirs, files in os.walk(temp_dir):
for file in files:
total_size_kb += os.path.getsize(os.path.join(root, file))
installed_size_mb = total_size_kb / 1024 / 1024
except Exception as e:
print(f"Warning: Could not analyze wheel contents: {e}")
f.write(f"- **Wheel Size**: {wheel_size_mb:.1f}M (compressed)\n")
if isinstance(jre_size_mb, str):
f.write(f"- **JRE Size**: {jre_size_mb} (uncompressed)\n")
else:
f.write(f"- **JRE Size**: {jre_size_mb:.1f}M (uncompressed)\n")
if isinstance(jar_size_mb, str):
f.write(f"- **JARs Size**: {jar_size_mb} (uncompressed)\n")
else:
f.write(f"- **JARs Size**: {jar_size_mb:.1f}M (uncompressed)\n")
if isinstance(installed_size_mb, str):
f.write(f"- **Installed Size**: {installed_size_mb} (total uncompressed)\n")
else:
f.write(f"- **Installed Size**: ~{installed_size_mb:.0f}M (total uncompressed)\n")
f.write("- **Platform**: ${{ matrix.platform }}\n")
f.write("- **Features**: All ArcadeDB features (some JARs excluded, see jar_exclusions.txt)\n")
- name: Upload test results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-results-${{ matrix.platform == 'linux/amd64' && 'linux-amd64' || matrix.platform == 'linux/arm64' && 'linux-arm64' || matrix.platform == 'darwin/amd64' && 'darwin-amd64' || matrix.platform == 'darwin/arm64' && 'darwin-arm64' || matrix.platform == 'windows/amd64' && 'windows-amd64' || 'windows-arm64' }}-py${{ matrix.python-version }}
path: |
bindings/python/pytest-output.txt
bindings/python/.coverage
retention-days: 7
- name: Upload wheel artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: wheel-${{ matrix.platform == 'linux/amd64' && 'linux-amd64' || matrix.platform == 'linux/arm64' && 'linux-arm64' || matrix.platform == 'darwin/amd64' && 'darwin-amd64' || matrix.platform == 'darwin/arm64' && 'darwin-arm64' || matrix.platform == 'windows/amd64' && 'windows-amd64' || 'windows-arm64' }}-py${{ matrix.python-version }}
path: bindings/python/dist/*.whl
retention-days: 7
- name: Upload wheel artifact for release
if: matrix.python-version == '3.12'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: wheel-${{ matrix.platform == 'linux/amd64' && 'linux-amd64' || matrix.platform == 'linux/arm64' && 'linux-arm64' || matrix.platform == 'darwin/amd64' && 'darwin-amd64' || matrix.platform == 'darwin/arm64' && 'darwin-arm64' || matrix.platform == 'windows/amd64' && 'windows-amd64' || 'windows-arm64' }}-test
path: bindings/python/dist/*.whl
retention-days: 7
# Summary job that checks all platforms
test-summary:
name: Test Summary
needs: test
runs-on: ubuntu-24.04
if: always()
steps:
- name: Check test results
run: |
echo "## 🎯 Overall Test Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.test.result }}" = "success" ]; then
echo "✅ **All platforms passed testing!**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The arcadedb-embedded package has been successfully built and tested." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Package**: arcadedb-embedded" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "ℹ️ **Note**: Some platform/Python combinations are excluded from testing:" >> $GITHUB_STEP_SUMMARY
echo "- Windows ARM64 (no GitHub-hosted runners available)" >> $GITHUB_STEP_SUMMARY
echo "- macOS x86_64 (temporarily disabled)" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Some platforms failed testing**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Please check the individual test jobs for details." >> $GITHUB_STEP_SUMMARY
exit 1
fi