style: apply ruff formatting fixes #31
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: Build, Test and Publish LibAMM | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - main-dev | |
| paths: | |
| - 'src/**' | |
| - 'include/**' | |
| - 'test/**' | |
| - 'CMakeLists.txt' | |
| - 'setup.py' | |
| - '.github/workflows/build-and-publish.yml' | |
| pull_request: | |
| branches: | |
| - main | |
| - main-dev | |
| workflow_dispatch: | |
| inputs: | |
| repository: | |
| description: 'Target PyPI repository' | |
| required: false | |
| default: 'testpypi' | |
| type: choice | |
| options: | |
| - testpypi | |
| - pypi | |
| force_publish: | |
| description: 'Force publish even if no version change' | |
| required: false | |
| default: false | |
| type: boolean | |
| env: | |
| TORCH_VERSION: '2.5.0' | |
| PACKAGE_NAME: 'isage-libamm' | |
| jobs: | |
| # ============================================================================ | |
| # Job 1: Check version and changes | |
| # ============================================================================ | |
| check-version: | |
| name: Check version and changes | |
| runs-on: [self-hosted, linux, x64] | |
| outputs: | |
| version: ${{ steps.get_version.outputs.version }} | |
| has_version_change: ${{ steps.check_version.outputs.has_change }} | |
| should_publish: ${{ steps.decide.outputs.should_publish }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Get current version | |
| id: get_version | |
| run: | | |
| VERSION=$(python -c " | |
| import re | |
| with open('setup.py', 'r') as f: | |
| content = f.read() | |
| match = re.search(r\"version='([^']+)'\", content) | |
| if match: | |
| print(match.group(1)) | |
| else: | |
| print('0.1.0') | |
| ") | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Current version: $VERSION" | |
| - name: Check if version changed | |
| id: check_version | |
| run: | | |
| LATEST_TAG=$(git tag -l "v*" --sort=-version:refname | head -n 1 || echo "") | |
| if [ -z "$LATEST_TAG" ]; then | |
| echo "No previous tag found, treating as new release" | |
| echo "has_change=true" >> $GITHUB_OUTPUT | |
| else | |
| LATEST_VERSION=$(echo "$LATEST_TAG" | sed 's/^v//') | |
| CURRENT_VERSION="${{ steps.get_version.outputs.version }}" | |
| echo "Latest tag: $LATEST_TAG (version: $LATEST_VERSION)" | |
| echo "Current version: $CURRENT_VERSION" | |
| if [ "$LATEST_VERSION" != "$CURRENT_VERSION" ]; then | |
| echo "Version has changed: $LATEST_VERSION -> $CURRENT_VERSION" | |
| echo "has_change=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Version unchanged: $CURRENT_VERSION" | |
| echo "has_change=false" >> $GITHUB_OUTPUT | |
| fi | |
| fi | |
| - name: Decide whether to publish | |
| id: decide | |
| run: | | |
| SHOULD_PUBLISH="false" | |
| # 发布条件: | |
| # 1. push 到 main 分支(自动版本提升后会触发) | |
| # 2. 或者手动触发且 force_publish=true | |
| if [ "${{ github.ref }}" = "refs/heads/main" ] && [ "${{ github.event_name }}" = "push" ]; then | |
| SHOULD_PUBLISH="true" | |
| echo "✅ Publishing: Push to main branch" | |
| elif [ "${{ github.event.inputs.force_publish }}" = "true" ]; then | |
| SHOULD_PUBLISH="true" | |
| echo "✅ Publishing: Force publish enabled" | |
| else | |
| echo "❌ Not publishing: ref=${{ github.ref }}, event=${{ github.event_name }}" | |
| fi | |
| echo "should_publish=$SHOULD_PUBLISH" >> $GITHUB_OUTPUT | |
| echo "Decision: should_publish=$SHOULD_PUBLISH" | |
| # ============================================================================ | |
| # Job 2: Build and test C++ library | |
| # ============================================================================ | |
| cpp-build-test: | |
| name: Build and test C++ (Ubuntu) | |
| runs-on: [self-hosted, linux, x64] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc g++ cmake python3 python3-pip | |
| - name: Install PyTorch | |
| run: | | |
| pip3 install --no-cache-dir --default-timeout=100 --retries 5 torch==${{ env.TORCH_VERSION }} --index-url https://download.pytorch.org/whl/cpu | |
| - name: Configure CMake | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_PREFIX_PATH=$(python3 -c "import torch; print(torch.utils.cmake_prefix_path)") \ | |
| -DLIBAMM_SKIP_DATASET_COPY=ON \ | |
| -DENABLE_PYBIND=ON \ | |
| -DENABLE_HDF5=ON \ | |
| -DENABLE_UNIT_TESTS=ON | |
| - name: Build C++ library | |
| run: | | |
| cmake --build build --config Release -j$(nproc) | |
| - name: Run C++ tests | |
| working-directory: build/test | |
| run: | | |
| echo "=== Running C++ Tests ===" | |
| ./cpp_test "--success" || exit 1 | |
| ./sketch_test "--success" || exit 1 | |
| ./crs_test "--success" || exit 1 | |
| ./weighted_cr_test "--success" || exit 1 | |
| ./block_partition_test "--success" || exit 1 | |
| ./streaming_test "--success" || exit 1 | |
| echo "✅ All C++ tests passed!" | |
| # ============================================================================ | |
| # Job 3: Build Python wheels (multi-version, self-hosted) | |
| # ============================================================================ | |
| build-wheels: | |
| name: Build wheel (Python ${{ matrix.python-version }}) | |
| needs: [check-version, cpp-build-test] | |
| # Run on: PR (for testing) OR when should_publish is true (for publishing) | |
| if: | | |
| github.event_name == 'pull_request' || | |
| needs.check-version.outputs.should_publish == 'true' | |
| runs-on: [self-hosted, Linux, X64] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ['3.9', '3.10', '3.11', '3.12'] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Check system resources | |
| run: | | |
| echo "=== System Resources ===" | |
| free -h | |
| echo "=== Available memory ===" | |
| AVAILABLE_MEM=$(free -g | awk '/^Mem:/{print $7}') | |
| echo "Available: ${AVAILABLE_MEM}GB" | |
| if [ "$AVAILABLE_MEM" -lt 12 ]; then | |
| echo "⚠️ Warning: Only ${AVAILABLE_MEM}GB available" | |
| echo "LibAMM compilation requires at least 12GB free memory" | |
| fi | |
| - name: Install build dependencies | |
| run: | | |
| python -m pip install --upgrade pip setuptools wheel | |
| pip install build pybind11 numpy | |
| pip install torch==${{ env.TORCH_VERSION }} --index-url https://download.pytorch.org/whl/cpu | |
| - name: Update package name in setup.py | |
| run: | | |
| python -c " | |
| import re | |
| with open('setup.py', 'r') as f: | |
| content = f.read() | |
| # Update package name to isage-libamm | |
| content = re.sub(r\"name='PyAMM'\", \"name='${{ env.PACKAGE_NAME }}'\", content) | |
| with open('setup.py', 'w') as f: | |
| f.write(content) | |
| print('✅ Updated package name to ${{ env.PACKAGE_NAME }}') | |
| " | |
| - name: Build Python wheel | |
| env: | |
| CMAKE_BUILD_PARALLEL_LEVEL: '2' # Limit parallelism to avoid OOM | |
| run: | | |
| echo "=== Building Python wheel ===" | |
| echo "Version: ${{ needs.check-version.outputs.version }}" | |
| python -m build --wheel --no-isolation | |
| echo "=== Generated wheel ===" | |
| ls -lh dist/ | |
| - name: Test wheel installation and import | |
| run: | | |
| echo "=== Testing wheel installation ===" | |
| # Create test venv | |
| python -m venv test_env | |
| source test_env/bin/activate | |
| # Install dependencies | |
| pip install torch==${{ env.TORCH_VERSION }} --index-url https://download.pytorch.org/whl/cpu | |
| pip install numpy | |
| # Install the wheel | |
| pip install dist/*.whl | |
| # Test import | |
| python -c " | |
| import PyAMM | |
| print('✅ LibAMM (PyAMM module) successfully imported!') | |
| print('✅ Wheel is functional for Python ${{ matrix.python-version }}') | |
| " | |
| deactivate | |
| rm -rf test_env | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheel-py${{ matrix.python-version }} | |
| path: dist/*.whl | |
| retention-days: 30 | |
| # ============================================================================ | |
| # Job 4: Publish to PyPI | |
| # ============================================================================ | |
| publish-to-pypi: | |
| name: Publish to PyPI | |
| needs: [check-version, build-wheels] | |
| runs-on: [self-hosted, linux, x64] | |
| if: needs.check-version.outputs.should_publish == 'true' | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }} | |
| - name: Download all wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wheel-* | |
| path: dist/ | |
| merge-multiple: true | |
| - name: List wheels | |
| run: | | |
| echo "=== Wheels to publish ===" | |
| ls -lh dist/ | |
| echo "" | |
| echo "Total: $(ls -1 dist/*.whl | wc -l) wheels" | |
| - name: Determine target repository | |
| id: repo | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| REPOSITORY="${{ github.event.inputs.repository }}" | |
| elif [ "${{ github.ref_name }}" = "main" ]; then | |
| REPOSITORY="pypi" | |
| else | |
| REPOSITORY="testpypi" | |
| fi | |
| echo "repository=$REPOSITORY" >> $GITHUB_OUTPUT | |
| echo "📦 Publishing to: $REPOSITORY" | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| password: ${{ steps.repo.outputs.repository == 'pypi' && secrets.PYPI_API_TOKEN || secrets.TEST_PYPI_API_TOKEN }} | |
| repository-url: ${{ steps.repo.outputs.repository == 'testpypi' && 'https://test.pypi.org/legacy/' || '' }} | |
| packages-dir: dist/ | |
| verbose: true | |
| - name: Create Git tag | |
| if: steps.repo.outputs.repository == 'pypi' | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| TAG_NAME="v${{ needs.check-version.outputs.version }}" | |
| if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then | |
| echo "Tag $TAG_NAME already exists, skipping" | |
| else | |
| git tag -a "$TAG_NAME" -m "Release ${{ needs.check-version.outputs.version }}" | |
| git push origin "$TAG_NAME" | |
| echo "✅ Created and pushed tag: $TAG_NAME" | |
| fi | |
| - name: Create GitHub Release | |
| if: steps.repo.outputs.repository == 'pypi' | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: v${{ needs.check-version.outputs.version }} | |
| name: LibAMM v${{ needs.check-version.outputs.version }} | |
| body: | | |
| ## LibAMM v${{ needs.check-version.outputs.version }} | |
| High-performance Approximate Matrix Multiplication library with NumPy interface. | |
| ### 🚀 Installation | |
| \`\`\`bash | |
| pip install ${{ env.PACKAGE_NAME }}==${{ needs.check-version.outputs.version }} | |
| \`\`\` | |
| ### 📦 Features | |
| - Approximate matrix multiplication algorithms | |
| - NumPy-based Python interface | |
| - PyTorch backend for high performance | |
| - Comprehensive C++ test suite | |
| - Support for Python 3.9-3.12 | |
| ### 🔗 Links | |
| - [PyPI Package](https://pypi.org/project/${{ env.PACKAGE_NAME }}/${{ needs.check-version.outputs.version }}/) | |
| - [Documentation](https://github.com/intellistream/LibAMM) | |
| **Branch**: `${{ github.ref_name }}` | |
| **Commit**: `${{ github.sha }}` | |
| files: dist/*.whl | |
| draft: false | |
| prerelease: ${{ github.ref_name != 'main' }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # ============================================================================ | |
| # Job 5: Summary | |
| # ============================================================================ | |
| summary: | |
| name: Build Summary | |
| needs: [check-version, cpp-build-test, build-wheels, publish-to-pypi] | |
| runs-on: [self-hosted, linux, x64] | |
| if: always() | |
| steps: | |
| - name: Generate summary | |
| run: | | |
| echo "## 🔧 LibAMM Build & Publish Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Event**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Branch**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Version**: ${{ needs.check-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Should Publish**: ${{ needs.check-version.outputs.should_publish }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Job Status" >> $GITHUB_STEP_SUMMARY | |
| echo "- C++ Build & Test: ${{ needs.cpp-build-test.result }}" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ needs.check-version.outputs.should_publish }}" = "true" ]; then | |
| echo "- Python Wheels: ${{ needs.build-wheels.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- PyPI Publish: ${{ needs.publish-to-pypi.result }}" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ needs.publish-to-pypi.result }}" = "success" ]; then | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### ✅ Published Successfully" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Install with:" >> $GITHUB_STEP_SUMMARY | |
| echo '```bash' >> $GITHUB_STEP_SUMMARY | |
| echo "pip install ${{ env.PACKAGE_NAME }}==${{ needs.check-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| fi | |
| else | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### ℹ️ No Publish Needed" >> $GITHUB_STEP_SUMMARY | |
| echo "Version has not changed or conditions not met for publishing." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Check for failures | |
| if [ "${{ needs.cpp-build-test.result }}" = "failure" ] || \ | |
| [ "${{ needs.build-wheels.result }}" = "failure" ] || \ | |
| [ "${{ needs.publish-to-pypi.result }}" = "failure" ]; then | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### ❌ Build or Publish Failed" >> $GITHUB_STEP_SUMMARY | |
| exit 1 | |
| fi |