Assert integration test assets are configured #45
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: Build and Release | |
| on: | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| jobs: | |
| dotnet-build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: 9.0.x | |
| - name: Restore dependencies | |
| run: dotnet restore | |
| - name: Build managed projects | |
| run: dotnet build --configuration Release --no-restore | |
| native-linux: | |
| needs: dotnet-build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install build dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential cmake libopenblas-dev liblapack-dev liblapacke-dev | |
| - name: Configure native build | |
| run: cmake -S native -B native/build/linux -DCMAKE_BUILD_TYPE=Release | |
| - name: Build native library | |
| run: cmake --build native/build/linux --target mlxsharp --config Release | |
| - name: Package native artifact | |
| run: | | |
| mkdir -p artifacts/native/linux-x64 | |
| cp native/build/linux/libmlxsharp.so artifacts/native/linux-x64/ | |
| - name: Upload native artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: native-linux-x64 | |
| path: artifacts/native/linux-x64/libmlxsharp.so | |
| native-macos: | |
| needs: dotnet-build | |
| runs-on: macos-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install CMake | |
| run: brew install cmake | |
| - name: Configure native build | |
| run: cmake -S native -B native/build/macos -DCMAKE_BUILD_TYPE=Release | |
| - name: Build native library | |
| run: cmake --build native/build/macos --target mlxsharp --config Release | |
| - name: Package native artifact | |
| run: | | |
| mkdir -p artifacts/native/osx-arm64 | |
| cp native/build/macos/libmlxsharp.dylib artifacts/native/osx-arm64/ | |
| cp native/build/macos/extern/mlx/mlx/backend/metal/kernels/mlx.metallib artifacts/native/osx-arm64/ | |
| - name: Upload native artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: native-osx-arm64 | |
| path: artifacts/native/osx-arm64 | |
| package-test: | |
| needs: | |
| - native-linux | |
| - native-macos | |
| runs-on: macos-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: 9.0.x | |
| - name: Show .NET info | |
| run: dotnet --info | |
| - name: Restore dependencies | |
| run: dotnet restore | |
| - name: Build C# projects (initial validation) | |
| run: dotnet build --configuration Release --no-restore | |
| - name: Setup Python for HuggingFace | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Python dependencies | |
| run: python -m pip install huggingface_hub mlx-lm | |
| - name: Download test model from HuggingFace | |
| run: | | |
| mkdir -p models | |
| huggingface-cli download mlx-community/Qwen1.5-0.5B-Chat-4bit --local-dir models/Qwen1.5-0.5B-Chat-4bit | |
| echo "Model files:" | |
| ls -la models/Qwen1.5-0.5B-Chat-4bit/ | |
| - name: Download macOS native library | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: native-osx-arm64 | |
| path: artifacts/native/osx-arm64 | |
| - name: Download Linux native library | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: native-linux-x64 | |
| path: artifacts/native/linux-x64 | |
| - name: Stage native libraries in project | |
| run: | | |
| mkdir -p src/MLXSharp/runtimes/osx-arm64/native | |
| cp artifacts/native/osx-arm64/libmlxsharp.dylib src/MLXSharp/runtimes/osx-arm64/native/ | |
| cp artifacts/native/osx-arm64/mlx.metallib src/MLXSharp/runtimes/osx-arm64/native/ | |
| mkdir -p src/MLXSharp/runtimes/linux-x64/native | |
| cp artifacts/native/linux-x64/libmlxsharp.so src/MLXSharp/runtimes/linux-x64/native/ | |
| - name: Build C# projects with native libraries | |
| run: dotnet build --configuration Release --no-restore | |
| - name: Copy native library to test output | |
| run: | | |
| TEST_OUTPUT="src/MLXSharp.Tests/bin/Release/net9.0" | |
| mkdir -p "$TEST_OUTPUT/runtimes/osx-arm64/native" | |
| cp src/MLXSharp/runtimes/osx-arm64/native/libmlxsharp.dylib "$TEST_OUTPUT/runtimes/osx-arm64/native/" | |
| cp src/MLXSharp/runtimes/osx-arm64/native/mlx.metallib "$TEST_OUTPUT/runtimes/osx-arm64/native/" | |
| ls -la "$TEST_OUTPUT/runtimes/osx-arm64/native/" | |
| - name: Run tests | |
| run: | | |
| dotnet test \ | |
| --configuration Release \ | |
| --no-build \ | |
| --logger "trx;LogFileName=TestResults.trx" \ | |
| --logger "console;verbosity=detailed" \ | |
| --results-directory artifacts/test-results | |
| env: | |
| MLXSHARP_MODEL_PATH: ${{ github.workspace }}/models/Qwen1.5-0.5B-Chat-4bit | |
| - name: Prepare artifact folders | |
| run: | | |
| mkdir -p artifacts/test-results | |
| mkdir -p artifacts/packages | |
| mkdir -p artifacts/native | |
| cp artifacts/native/osx-arm64/libmlxsharp.dylib artifacts/native/ | |
| cp artifacts/native/osx-arm64/mlx.metallib artifacts/native/ | |
| cp artifacts/native/linux-x64/libmlxsharp.so artifacts/native/ | |
| - name: Pack MLXSharp library | |
| run: dotnet pack src/MLXSharp/MLXSharp.csproj --configuration Release --output artifacts/packages -p:MLXSharpMacNativeBinary=$GITHUB_WORKSPACE/artifacts/native/osx-arm64/libmlxsharp.dylib -p:MLXSharpMacMetallibBinary=$GITHUB_WORKSPACE/artifacts/native/osx-arm64/mlx.metallib -p:MLXSharpLinuxNativeBinary=$GITHUB_WORKSPACE/artifacts/native/linux-x64/libmlxsharp.so | |
| - name: Pack MLXSharp.SemanticKernel library | |
| run: dotnet pack src/MLXSharp.SemanticKernel/MLXSharp.SemanticKernel.csproj --configuration Release --output artifacts/packages -p:MLXSharpMacNativeBinary=$GITHUB_WORKSPACE/artifacts/native/osx-arm64/libmlxsharp.dylib -p:MLXSharpMacMetallibBinary=$GITHUB_WORKSPACE/artifacts/native/osx-arm64/mlx.metallib -p:MLXSharpLinuxNativeBinary=$GITHUB_WORKSPACE/artifacts/native/linux-x64/libmlxsharp.so -p:MLXSharpSkipLinuxNativeValidation=true | |
| - name: Verify package contains native libraries | |
| run: | | |
| echo "Checking package contents..." | |
| shopt -s nullglob | |
| packages=(artifacts/packages/*.nupkg) | |
| if [ ${#packages[@]} -eq 0 ]; then | |
| echo "✗ ERROR: No packages were produced" | |
| exit 1 | |
| fi | |
| missing=0 | |
| for package in "${packages[@]}"; do | |
| echo "Inspecting ${package}" | |
| filename=$(basename "${package}") | |
| case "${filename}" in | |
| MLXSharp.SemanticKernel.*.nupkg) | |
| echo " ↷ Skipping native check for ${filename}" | |
| ;; | |
| MLXSharp.*.nupkg) | |
| package_missing=0 | |
| if unzip -l "${package}" | grep -q "runtimes/osx-arm64/native/libmlxsharp.dylib"; then | |
| echo " ✓ macOS library present" | |
| else | |
| echo " ✗ macOS library missing" | |
| package_missing=1 | |
| fi | |
| if unzip -l "${package}" | grep -q "runtimes/osx-arm64/native/mlx.metallib"; then | |
| echo " ✓ macOS metallib present" | |
| else | |
| echo " ✗ macOS metallib missing" | |
| package_missing=1 | |
| fi | |
| if unzip -l "${package}" | grep -q "runtimes/linux-x64/native/libmlxsharp.so"; then | |
| echo " ✓ Linux library present" | |
| else | |
| echo " ✗ Linux library missing" | |
| package_missing=1 | |
| fi | |
| if [ ${package_missing} -ne 0 ]; then | |
| unzip -l "${package}" | |
| missing=1 | |
| fi | |
| ;; | |
| *) | |
| echo " ↷ Skipping native check for ${filename}" | |
| ;; | |
| esac | |
| done | |
| if [ $missing -ne 0 ]; then | |
| exit 1 | |
| fi | |
| - name: Upload native artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: native-libs | |
| path: artifacts/native | |
| - name: Upload packages artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: packages | |
| path: artifacts/packages | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-results | |
| path: artifacts/test-results | |
| - name: Publish test results summary | |
| if: always() | |
| run: | | |
| python - <<'PY' | |
| import os | |
| import xml.etree.ElementTree as ET | |
| trx_path = "artifacts/test-results/TestResults.trx" | |
| summary_path = os.environ.get("GITHUB_STEP_SUMMARY") | |
| if not os.path.exists(trx_path) or not summary_path: | |
| print("No test results found to summarize.") | |
| raise SystemExit(0) | |
| ns = {"trx": "http://microsoft.com/schemas/VisualStudio/TeamTest/2010"} | |
| root = ET.parse(trx_path).getroot() | |
| counters = root.find(".//trx:ResultSummary/trx:Counters", ns) | |
| with open(summary_path, "a", encoding="utf-8") as summary: | |
| summary.write("### Test Results\n\n") | |
| if counters is not None: | |
| metrics = { | |
| "Total": counters.attrib.get("total", "0"), | |
| "Executed": counters.attrib.get("executed", "0"), | |
| "Passed": counters.attrib.get("passed", "0"), | |
| "Failed": counters.attrib.get("failed", "0"), | |
| "Errors": counters.attrib.get("error", "0"), | |
| "Timeouts": counters.attrib.get("timeout", "0"), | |
| "Aborted": counters.attrib.get("aborted", "0"), | |
| "Inconclusive": counters.attrib.get("inconclusive", "0"), | |
| "Skipped": counters.attrib.get("notExecuted", "0") | |
| } | |
| for label, value in metrics.items(): | |
| if value and value != "0": | |
| summary.write(f"- {label}: {value}\n") | |
| else: | |
| summary.write("- No counters were present in the TRX log.\n") | |
| failed_results = root.findall(".//trx:UnitTestResult[@outcome!='Passed']", ns) | |
| if failed_results: | |
| summary.write("\nFailed Tests:\n") | |
| for result in failed_results: | |
| test_name = result.attrib.get("testName", "(unknown test)") | |
| outcome = result.attrib.get("outcome", "Unknown") | |
| summary.write(f"- {test_name} ({outcome})\n") | |
| summary.write("\n") | |
| PY | |
| release: | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| needs: package-test | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| submodules: recursive | |
| # - name: Determine MLX version | |
| # id: mlx_version | |
| # run: | | |
| # git submodule update --init --recursive | |
| # git -C extern/mlx fetch --tags | |
| # | |
| # if MLX_TAG=$(git -C extern/mlx describe --tags --abbrev=0 2>/dev/null); then | |
| # echo "Detected MLX tag: ${MLX_TAG}" | |
| # else | |
| # echo "::warning::Unable to determine MLX tag; falling back to commit hash" | |
| # MLX_TAG="unknown" | |
| # fi | |
| # | |
| # MLX_COMMIT=$(git -C extern/mlx rev-parse --short HEAD) | |
| # | |
| # echo "tag=${MLX_TAG}" >> "$GITHUB_OUTPUT" | |
| # echo "commit=${MLX_COMMIT}" >> "$GITHUB_OUTPUT" | |
| # | |
| # - name: Extract version from Directory.Build.props | |
| # id: version | |
| # run: | | |
| # VERSION=$(grep -oP '<Version>\K[^<]+' Directory.Build.props) | |
| # echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| # echo "Extracted version: $VERSION" | |
| # | |
| # - name: Check if tag exists | |
| # id: check_tag | |
| # run: | | |
| # if git ls-remote --tags origin | grep -q "refs/tags/v${{ steps.version.outputs.version }}"; then | |
| # echo "exists=true" >> $GITHUB_OUTPUT | |
| # echo "Tag v${{ steps.version.outputs.version }} already exists" | |
| # else | |
| # echo "exists=false" >> $GITHUB_OUTPUT | |
| # echo "Tag v${{ steps.version.outputs.version }} does not exist" | |
| # fi | |
| # | |
| # - name: Download package artifacts | |
| # if: steps.check_tag.outputs.exists == 'false' | |
| # uses: actions/download-artifact@v4 | |
| # with: | |
| # name: packages | |
| # path: artifacts/packages | |
| # | |
| # - name: Publish to NuGet | |
| # if: steps.check_tag.outputs.exists == 'false' | |
| # run: | | |
| # for package in artifacts/packages/*.nupkg; do | |
| # echo "Publishing $package..." | |
| # dotnet nuget push "$package" \ | |
| # --api-key ${{ secrets.NUGET_API_KEY }} \ | |
| # --source https://api.nuget.org/v3/index.json \ | |
| # --skip-duplicate || true | |
| # done | |
| # | |
| # - name: Create Git tag | |
| # if: steps.check_tag.outputs.exists == 'false' | |
| # run: | | |
| # git config user.name "github-actions[bot]" | |
| # git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # git tag -a "v${{ steps.version.outputs.version }}" -m "Release v${{ steps.version.outputs.version }}" | |
| # git push origin "v${{ steps.version.outputs.version }}" | |
| # | |
| # - name: Generate release notes | |
| # if: steps.check_tag.outputs.exists == 'false' | |
| # id: release_notes | |
| # env: | |
| # RELEASE_VERSION: ${{ steps.version.outputs.version }} | |
| # MLX_TAG: ${{ steps.mlx_version.outputs.tag }} | |
| # MLX_COMMIT: ${{ steps.mlx_version.outputs.commit }} | |
| # run: | | |
| # PREVIOUS_TAG=$(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1) 2>/dev/null || echo "") | |
| # if [ -z "$PREVIOUS_TAG" ]; then | |
| # COMMITS=$(git log --pretty=format:"- %s (%h)" --reverse) | |
| # else | |
| # COMMITS=$(git log ${PREVIOUS_TAG}..HEAD --pretty=format:"- %s (%h)" --reverse) | |
| # fi | |
| # | |
| # echo "## What's Changed" > release_notes.md | |
| # echo "" >> release_notes.md | |
| # echo "$COMMITS" >> release_notes.md | |
| # echo "" >> release_notes.md | |
| # echo "## Upstream MLX" >> release_notes.md | |
| # echo "- Version: ${MLX_TAG}" >> release_notes.md | |
| # echo "- Commit: ${MLX_COMMIT}" >> release_notes.md | |
| # echo "" >> release_notes.md | |
| # echo "## NuGet Packages" >> release_notes.md | |
| # echo "- [MLXSharp v${RELEASE_VERSION}](https://www.nuget.org/packages/MLXSharp/${RELEASE_VERSION})" >> release_notes.md | |
| # echo "- [MLXSharp.SemanticKernel v${RELEASE_VERSION}](https://www.nuget.org/packages/MLXSharp.SemanticKernel/${RELEASE_VERSION})" >> release_notes.md | |
| # | |
| # cat release_notes.md | |
| # | |
| # - name: Create GitHub Release | |
| # if: steps.check_tag.outputs.exists == 'false' | |
| # uses: softprops/action-gh-release@v1 | |
| # with: | |
| # tag_name: v${{ steps.version.outputs.version }} | |
| # name: Release v${{ steps.version.outputs.version }} | |
| # body_path: release_notes.md | |
| # files: artifacts/packages/* | |
| # env: | |
| # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |