refactor: streamline example testing workflow and enhance input flexiโฆ #126
Workflow file for this run
This file contains hidden or 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: Test Python Examples | |
| on: | |
| # Run on push to bindings/python/ directory | |
| push: | |
| paths: | |
| - 'bindings/python/**' | |
| - '.github/workflows/test-python-examples.yml' | |
| # Run on pull request affecting bindings/python/ | |
| pull_request: | |
| paths: | |
| - 'bindings/python/**' | |
| - '.github/workflows/test-python-examples.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: | |
| examples: | |
| description: "Glob pattern(s) for examples to run (space-separated, relative to bindings/python/examples)." | |
| required: false | |
| type: string | |
| default: "01_*.py" | |
| # Allow manual trigger | |
| workflow_dispatch: | |
| inputs: | |
| examples: | |
| description: "Glob pattern(s) for examples to run (space-separated, relative to bindings/python/examples)." | |
| required: false | |
| default: "01_*.py" | |
| env: | |
| EXAMPLES: ${{ inputs.examples || '01_*.py' }} | |
| permissions: | |
| contents: read | |
| jobs: | |
| # First job: Download ArcadeDB JARs (platform-agnostic) | |
| download-jars: | |
| name: Download ArcadeDB JARs | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.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 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 | |
| echo "โ Downloaded $(ls -1 src/arcadedb_embedded/jars/*.jar | wc -l) JARs" | |
| - 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: arcadedb-jars-examples | |
| path: bindings/python/src/arcadedb_embedded/jars/*.jar | |
| retention-days: 1 | |
| # Second job: Test examples on each platform | |
| test-examples: | |
| name: Test Python Examples (${{ matrix.platform }}, Python ${{ matrix.python-version }}) | |
| runs-on: ${{ matrix.runs-on }} | |
| needs: download-jars | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] | |
| # Temporarily limit to three platforms (skip macOS x86_64, all Windows) | |
| # platform: ['linux/amd64', 'linux/arm64', 'darwin/amd64', 'darwin/arm64', 'windows/amd64', 'windows/arm64'] | |
| platform: ['linux/amd64', 'linux/arm64', 'darwin/arm64'] | |
| 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: darwin/amd64 | |
| # runs-on: macos-15-intel | |
| # - platform: windows/amd64 | |
| # runs-on: windows-2025 | |
| # - platform: windows/arm64 | |
| # runs-on: windows-11-arm | |
| # macOS x86_64 and all Windows temporarily disabled | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | |
| - name: Download JARs artifact | |
| uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 | |
| with: | |
| name: arcadedb-jars-examples | |
| 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@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '25' | |
| - name: Set up Docker Buildx (Linux only) | |
| if: matrix.platform == 'linux/amd64' || matrix.platform == 'linux/arm64' | |
| uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 | |
| - name: Set up Python | |
| uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Setup UV package manager | |
| shell: bash | |
| run: | | |
| curl -LsSf https://astral.sh/uv/install.sh | sh | |
| uv --version | |
| - name: Install Python build dependencies | |
| shell: bash | |
| run: | | |
| uv pip install --system build wheel setuptools | |
| # Windows currently disabled, 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 arcadedb-embedded (${{ matrix.platform }}) | |
| shell: bash | |
| run: | | |
| cd bindings/python | |
| echo "๐จ Building arcadedb-embedded for ${{ matrix.platform }} with Python ${{ matrix.python-version }}..." | |
| ./build.sh ${{ matrix.platform }} ${{ matrix.python-version }} | |
| # Note: Java is NOT required - arcadedb-embedded has bundled JRE! | |
| - name: Install ArcadeDB Python bindings | |
| shell: bash | |
| run: | | |
| cd bindings/python | |
| uv pip install --system dist/*embed*.whl | |
| - name: Install example dependencies | |
| shell: bash | |
| run: | | |
| # Install dependencies needed by examples | |
| # PyTorch is required by sentence-transformers, even on macOS | |
| # For Python 3.13+, wheels might only be on PyTorch official index | |
| if [[ "$OSTYPE" == "darwin"* ]]; then | |
| # macOS: Install CPU-only PyTorch (supports MPS acceleration) | |
| uv pip install --system torch --index-url https://download.pytorch.org/whl/cpu | |
| uv pip install --system "numpy<2.0" requests sentence-transformers | |
| else | |
| # Linux & Windows: Install CPU-only PyTorch to save space (avoid CUDA) | |
| uv pip install --system torch --index-url https://download.pytorch.org/whl/cpu | |
| uv pip install --system numpy requests sentence-transformers | |
| fi | |
| - name: Download datasets | |
| shell: bash | |
| run: | | |
| uv pip install --system tqdm py7zr lxml | |
| cd bindings/python/examples | |
| echo "๐ฅ Downloading MovieLens Small dataset..." | |
| python3 download_data.py movielens-small | |
| echo "๐ฅ Downloading Stack Overflow Small dataset..." | |
| python3 download_data.py stackoverflow-small | |
| - name: Install timeout command (macOS only) | |
| if: matrix.platform == 'darwin/amd64' || matrix.platform == 'darwin/arm64' | |
| shell: bash | |
| run: | | |
| # macOS doesn't have timeout command by default, use coreutils | |
| brew install coreutils | |
| # Windows currently disabled, no env override needed | |
| # - 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 | |
| - name: Run all examples | |
| id: run_examples | |
| shell: bash | |
| env: | |
| # Increase JVM heap for large CSV imports (example 04) | |
| ARCADEDB_JVM_ARGS: "-Xmx8g -Xms8g" | |
| run: | | |
| cd bindings/python/examples | |
| echo "๐ Running Python Examples..." | |
| echo "" | |
| # Initialize counters | |
| total=0 | |
| passed=0 | |
| failed=0 | |
| skipped=0 | |
| # Create results file | |
| results_file="example-results.txt" | |
| > $results_file | |
| # Select example files by pattern(s) in EXAMPLES (space-separated globs) | |
| examples=$(ls $EXAMPLES 2>/dev/null | sort) | |
| if [ -z "$examples" ]; then | |
| echo "โ No example files found!" | |
| exit 1 | |
| fi | |
| # Detect timeout command (macOS uses gtimeout, Linux uses timeout) | |
| if command -v gtimeout &> /dev/null; then | |
| TIMEOUT_CMD="gtimeout" | |
| else | |
| TIMEOUT_CMD="timeout" | |
| fi | |
| # Run each example | |
| for example in $examples; do | |
| total=$((total + 1)) | |
| # Set example-specific parameters and timeout | |
| case "$example" in | |
| "04_csv_import_documents.py") | |
| example_args="--dataset movielens-small --export" | |
| example_name="$example (movielens-small dataset with export)" | |
| timeout_duration=900 # 15 minutes | |
| ;; | |
| "05_csv_import_graph.py") | |
| example_args="--dataset movielens-small --method java --import-jsonl ./exports/movielens_small_db.jsonl.tgz --export" | |
| example_name="$example (movielens-small dataset, embedded java method, import/export)" | |
| timeout_duration=900 # 15 minutes | |
| ;; | |
| "06_vector_search_recommendations.py") | |
| example_args="--import-jsonl ./exports/movielens_graph_small_db.jsonl.tgz" | |
| example_name="$example (vector search, import from JSONL)" | |
| timeout_duration=900 # 15 minutes | |
| ;; | |
| "07_stackoverflow_multimodel.py") | |
| example_args="--dataset stackoverflow-small" | |
| example_name="$example (stackoverflow-small dataset)" | |
| timeout_duration=1800 # 30 minutes | |
| ;; | |
| *) | |
| example_args="" | |
| example_name="$example" | |
| timeout_duration=900 # 15 minutes default | |
| ;; | |
| esac | |
| log_file="${example%.py}.log" | |
| echo "----------------------------------------" | |
| echo "๐ Running: $example_name" | |
| echo "----------------------------------------" | |
| # Run the example with appropriate parameters | |
| if $TIMEOUT_CMD $timeout_duration python "$example" $example_args > "$log_file" 2>&1; then | |
| echo "โ PASSED: $example_name" | tee -a $results_file | |
| passed=$((passed + 1)) | |
| else | |
| exit_code=$? | |
| if [ $exit_code -eq 124 ]; then | |
| echo "โฑ๏ธ TIMEOUT: $example_name (exceeded $((timeout_duration/60)) minutes)" | tee -a $results_file | |
| failed=$((failed + 1)) | |
| else | |
| echo "โ FAILED: $example_name (exit code: $exit_code)" | tee -a $results_file | |
| failed=$((failed + 1)) | |
| fi | |
| # Show last 20 lines of error log | |
| echo "Last 20 lines of output:" | |
| tail -n 20 "$log_file" | |
| fi | |
| echo "" | |
| done | |
| # Print summary | |
| echo "========================================" | |
| echo "๐ EXAMPLE TEST SUMMARY" | |
| echo "========================================" | |
| echo "Total: $total" | |
| echo "Passed: $passed โ " | |
| echo "Failed: $failed โ" | |
| echo "Skipped: $skipped โญ๏ธ" | |
| echo "========================================" | |
| echo "" | |
| echo "Examples pattern(s): $EXAMPLES" | |
| # Output to GitHub Actions | |
| echo "total=$total" >> $GITHUB_OUTPUT | |
| echo "passed=$passed" >> $GITHUB_OUTPUT | |
| echo "failed=$failed" >> $GITHUB_OUTPUT | |
| echo "skipped=$skipped" >> $GITHUB_OUTPUT | |
| # Show detailed results | |
| echo "Detailed Results:" | |
| cat $results_file | |
| # Exit with error if any failed | |
| if [ $failed -gt 0 ]; then | |
| echo "โ Some examples failed!" | |
| exit 1 | |
| else | |
| echo "โ All examples passed!" | |
| fi | |
| # Save list of examples for summary | |
| echo "$examples" > examples-ran.txt | |
| - name: Generate test summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| cd bindings/python/examples | |
| echo "## ๐ฎ Python Examples Test Results (${{ matrix.platform }})" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| total="${{ steps.run_examples.outputs.total || '0' }}" | |
| passed="${{ steps.run_examples.outputs.passed || '0' }}" | |
| failed="${{ steps.run_examples.outputs.failed || '0' }}" | |
| if [ "${{ steps.run_examples.outcome }}" = "success" ]; then | |
| echo "โ **Status**: ALL EXAMPLES PASSED ($passed/$total)" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "โ **Status**: SOME EXAMPLES FAILED ($passed/$total passed)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Metric | Count |" >> $GITHUB_STEP_SUMMARY | |
| echo "|--------|------:|" >> $GITHUB_STEP_SUMMARY | |
| echo "| ๐ Total | $total |" >> $GITHUB_STEP_SUMMARY | |
| echo "| โ Passed | $passed |" >> $GITHUB_STEP_SUMMARY | |
| echo "| โ Failed | $failed |" >> $GITHUB_STEP_SUMMARY | |
| echo "| โญ๏ธ Skipped | ${{ steps.run_examples.outputs.skipped || '0' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Add detailed results if available | |
| if [ -f example-results.txt ]; then | |
| echo "### Detailed Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| cat example-results.txt >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Examples Tested" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Pattern(s)**: $EXAMPLES" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -f examples-ran.txt ]; then | |
| while IFS= read -r example; do | |
| [ -n "$example" ] && echo "- **$example**" >> $GITHUB_STEP_SUMMARY | |
| done < examples-ran.txt | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| - name: Upload example logs | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: example-logs-${{ 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/examples/*.log | |
| bindings/python/examples/example-results.txt | |
| retention-days: 7 | |
| - name: Upload example databases | |
| if: failure() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: example-databases-${{ 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/examples/my_test_databases/ | |
| retention-days: 3 | |
| # Summary job that checks all platforms | |
| test-examples-summary: | |
| name: Examples Test Summary | |
| needs: test-examples | |
| runs-on: ubuntu-latest | |
| if: always() | |
| steps: | |
| - name: Check test results | |
| shell: bash | |
| run: | | |
| echo "## ๐ฏ Overall Examples Test Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ needs.test-examples.result }}" = "success" ]; then | |
| echo "โ **All platforms passed example testing!**" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "All examples ran successfully across all enabled platforms." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Platforms tested**: linux/amd64, linux/arm64, darwin/arm64" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "โ **Some platforms failed example testing**" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Please check the individual platform jobs for details." >> $GITHUB_STEP_SUMMARY | |
| exit 1 | |
| fi |