Add a script to check for breaking C ABI changes #2
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: C ABI Compatibility Check | ||
| on: | ||
| workflow_dispatch: # Allow manual trigger for bootstrap | ||
| workflow_call: | ||
| pull_request: | ||
| paths: | ||
| - 'c/include/**' | ||
| - 'ci/check_c_abi.py' | ||
| - '.github/workflows/check-c-abi.yaml' | ||
| push: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - 'c/include/**' | ||
| - 'ci/check_c_abi.py' | ||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
| jobs: | ||
| # Extract and store baseline ABI from main branch | ||
| update-baseline: | ||
| if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main') | ||
| runs-on: ubuntu-latest | ||
| concurrency: | ||
| group: abi-baseline-update | ||
| cancel-in-progress: false # Queue updates, don't skip | ||
| steps: | ||
| - name: Checkout main branch | ||
| uses: actions/checkout@v4 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
| - name: Install system dependencies | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y \ | ||
| libclang-dev \ | ||
| clang \ | ||
| cmake \ | ||
| ninja-build | ||
| - name: Install Python dependencies | ||
| run: | | ||
| pip install --upgrade pip | ||
| pip install msgspec libclang termcolor | ||
| - name: Build C++ project to get dependencies (dlpack) | ||
| run: | | ||
| mkdir -p cpp/build | ||
| cd cpp/build | ||
| cmake .. \ | ||
| -GNinja \ | ||
| -DCMAKE_BUILD_TYPE=Debug \ | ||
| -DBUILD_TESTS=OFF \ | ||
| -DBUILD_EXAMPLES=OFF | ||
| echo "Build directory created and dependencies fetched" | ||
| - name: Extract ABI from main branch | ||
| run: | | ||
| mkdir -p baseline | ||
| python ci/check_c_abi.py extract \ | ||
| --header_path c/include \ | ||
| --include_file cuvs/core/all.h \ | ||
| --output_file baseline/c_abi.json.gz | ||
| echo "ABI extracted from main branch (commit: ${{ github.sha }})" | ||
| - name: Store commit-specific baseline | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: c-abi-baseline-${{ github.sha }} | ||
| path: baseline/c_abi.json.gz | ||
| retention-days: 90 # Keep for 3 months | ||
| - name: Store main baseline (latest, never expires) | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: c-abi-baseline-main | ||
| path: baseline/c_abi.json.gz | ||
| retention-days: 0 # Never expire | ||
| # Check PRs for breaking ABI changes | ||
| check-pr: | ||
| if: github.event_name == 'pull_request' || github.event_name == 'workflow_call' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout PR branch | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
| - name: Install system dependencies | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y \ | ||
| libclang-dev \ | ||
| clang \ | ||
| cmake \ | ||
| ninja-build | ||
| - name: Install Python dependencies | ||
| run: | | ||
| pip install --upgrade pip | ||
| pip install msgspec libclang termcolor | ||
| - name: Build C++ project to get dependencies (dlpack) | ||
| run: | | ||
| mkdir -p cpp/build | ||
| cd cpp/build | ||
| cmake .. \ | ||
| -GNinja \ | ||
| -DCMAKE_BUILD_TYPE=Debug \ | ||
| -DBUILD_TESTS=OFF \ | ||
| -DBUILD_EXAMPLES=OFF | ||
| echo "Build directory created and dependencies fetched" | ||
| - name: Find merge base commit | ||
| id: merge-base | ||
| run: | | ||
| git fetch origin main | ||
| MERGE_BASE=$(git merge-base HEAD origin/main) | ||
| echo "merge_base_sha=${MERGE_BASE}" >> $GITHUB_OUTPUT | ||
| echo "Merge base commit: ${MERGE_BASE}" | ||
| - name: Try to download baseline for merge-base commit (most accurate) | ||
| id: download-merge-base | ||
| continue-on-error: true | ||
| uses: dawidd6/action-download-artifact@v3 | ||
| with: | ||
| name: c-abi-baseline-${{ steps.merge-base.outputs.merge_base_sha }} | ||
| workflow: check-c-abi.yaml | ||
| commit: ${{ steps.merge-base.outputs.merge_base_sha }} | ||
| path: baseline/ | ||
| - name: Try to download latest main baseline (fallback 1) | ||
| id: download-main | ||
| if: steps.download-merge-base.outcome == 'failure' | ||
| continue-on-error: true | ||
| uses: dawidd6/action-download-artifact@v3 | ||
| with: | ||
| name: c-abi-baseline-main | ||
| workflow: check-c-abi.yaml | ||
| branch: main | ||
| path: baseline/ | ||
| - name: Extract baseline ABI from main branch (fallback 2) | ||
| if: steps.download-merge-base.outcome == 'failure' && steps.download-main.outcome == 'failure' | ||
| run: | | ||
| echo "⚠️ No baseline artifacts found, extracting from main branch..." | ||
| echo "This is slower but ensures we always have a baseline for comparison." | ||
| git worktree add ../cuvs-main main | ||
| mkdir -p ../cuvs-main/cpp/build | ||
| cd ../cuvs-main/cpp/build | ||
| cmake .. \ | ||
| -GNinja \ | ||
| -DCMAKE_BUILD_TYPE=Debug \ | ||
| -DBUILD_TESTS=OFF \ | ||
| -DBUILD_EXAMPLES=OFF | ||
| cd ../../.. | ||
| mkdir -p baseline | ||
| python ci/check_c_abi.py extract \ | ||
| --header_path ../cuvs-main/c/include \ | ||
| --include_file cuvs/core/all.h \ | ||
| --output_file baseline/c_abi.json.gz | ||
| echo "✓ Baseline ABI extracted from main branch" | ||
| - name: Report baseline source | ||
| run: | | ||
| if [ "${{ steps.download-merge-base.outcome }}" == "success" ]; then | ||
| echo "✓ Using baseline from merge-base commit: ${{ steps.merge-base.outputs.merge_base_sha }}" | ||
| elif [ "${{ steps.download-main.outcome }}" == "success" ]; then | ||
| echo "✓ Using latest main baseline (merge-base baseline not yet available)" | ||
| else | ||
| echo "✓ Using freshly extracted baseline from main branch" | ||
| fi | ||
| - name: Analyze current branch for ABI breaking changes | ||
| run: | | ||
| python ci/check_c_abi.py analyze \ | ||
| --abi_file baseline/c_abi.json.gz \ | ||
| --header_path c/include \ | ||
| --include_file cuvs/core/all.h | ||
| - name: Comment on PR with results | ||
| if: failure() && github.event_name == 'pull_request' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| github.rest.issues.createComment({ | ||
| issue_number: context.issue.number, | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| body: `## ⚠️ C ABI Breaking Changes Detected | ||
| This PR introduces breaking changes to the C ABI. Please review the changes carefully. | ||
| Breaking ABI changes are only allowed in major releases. If this is intentional for a major release, | ||
| the baseline ABI will need to be updated after merge. | ||
| See the job logs for details on what specific changes were detected. | ||
| ### What are breaking ABI changes? | ||
| Breaking ABI changes include: | ||
| - Removing functions from the public API | ||
| - Changing function signatures (parameters or return types) | ||
| - Removing struct members or changing their types | ||
| - Removing or changing enum values | ||
| ### Next steps | ||
| 1. Review the changes flagged in the CI logs | ||
| 2. If these changes are unintentional, update your PR to maintain ABI compatibility | ||
| 3. If these changes are required, ensure: | ||
| - This is part of a major version release | ||
| - The changes are documented in the changelog | ||
| - Migration guide is provided for users | ||
| For more information, see the [C ABI documentation](../docs/source/c_developer_guide.md). | ||
| ` | ||
| }); | ||