Skip to content

Commit e47378c

Browse files
committed
Download native MLXSharp release assets in CI
1 parent 7d019e0 commit e47378c

File tree

5 files changed

+79
-205
lines changed

5 files changed

+79
-205
lines changed

.github/workflows/ci.yml

Lines changed: 75 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,71 +25,103 @@ jobs:
2525
- name: Build managed projects
2626
run: dotnet build --configuration Release --no-restore
2727

28-
native-linux:
28+
native-assets:
2929
needs: dotnet-build
3030
runs-on: ubuntu-latest
31+
env:
32+
NATIVE_RELEASE_REPO: ${{ vars.MLXSHARP_NATIVE_REPO || 'ManagedCode/MLXSharp' }}
33+
NATIVE_RELEASE_TAG: ${{ vars.MLXSHARP_NATIVE_TAG || '' }}
3134
steps:
32-
- name: Checkout repository
33-
uses: actions/checkout@v4
34-
with:
35-
submodules: recursive
36-
37-
- name: Install build dependencies
35+
- name: Install tooling
3836
run: |
3937
sudo apt-get update
40-
sudo apt-get install -y build-essential cmake libopenblas-dev liblapack-dev liblapacke-dev
38+
sudo apt-get install -y jq unzip
4139
42-
- name: Configure native build
43-
run: cmake -S native -B native/build/linux -DCMAKE_BUILD_TYPE=Release
40+
- name: Download official native binaries
41+
env:
42+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43+
run: |
44+
set -euo pipefail
4445
45-
- name: Build native library
46-
run: cmake --build native/build/linux --target mlxsharp --config Release
46+
repo="${NATIVE_RELEASE_REPO}"
47+
if [ -z "$repo" ]; then
48+
echo "::error::NATIVE_RELEASE_REPO must be provided" >&2
49+
exit 1
50+
fi
4751
48-
- name: Package native artifact
49-
run: |
50-
mkdir -p artifacts/native/linux-x64
51-
cp native/build/linux/libmlxsharp.so artifacts/native/linux-x64/
52+
if [ -n "${NATIVE_RELEASE_TAG}" ]; then
53+
release_api="https://api.github.com/repos/${repo}/releases/tags/${NATIVE_RELEASE_TAG}"
54+
else
55+
release_api="https://api.github.com/repos/${repo}/releases/latest"
56+
fi
5257
53-
- name: Upload native artifact
54-
uses: actions/upload-artifact@v4
55-
with:
56-
name: native-linux-x64
57-
path: artifacts/native/linux-x64/libmlxsharp.so
58+
echo "Fetching release metadata from ${release_api}"
59+
response=$(curl -fsSL -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${GITHUB_TOKEN}" "$release_api")
60+
tag=$(echo "$response" | jq -r '.tag_name // ""')
5861
59-
native-macos:
60-
needs: dotnet-build
61-
runs-on: macos-latest
62-
steps:
63-
- name: Checkout repository
64-
uses: actions/checkout@v4
65-
with:
66-
submodules: recursive
62+
if [ -z "$tag" ]; then
63+
echo "::error::Unable to resolve release tag from ${release_api}" >&2
64+
exit 1
65+
fi
6766
68-
- name: Install CMake
69-
run: brew install cmake
67+
echo "Using native release ${repo}@${tag}"
7068
71-
- name: Configure native build
72-
run: cmake -S native -B native/build/macos -DCMAKE_BUILD_TYPE=Release
69+
nupkg_asset=$(echo "$response" | jq -r '.assets[] | select(.name | startswith("ManagedCode.MLXSharp.")) | select(.name | endswith(".nupkg")) | .name' | head -n1)
70+
if [ -z "$nupkg_asset" ] || [ "$nupkg_asset" = "null" ]; then
71+
echo "::error::No ManagedCode.MLXSharp.*.nupkg asset found in release ${tag}" >&2
72+
exit 1
73+
fi
7374
74-
- name: Build native library
75-
run: cmake --build native/build/macos --target mlxsharp --config Release
75+
asset_url=$(echo "$response" | jq -r --arg name "$nupkg_asset" '.assets[] | select(.name == $name) | .url')
76+
if [ -z "$asset_url" ] || [ "$asset_url" = "null" ]; then
77+
echo "::error::Failed to resolve download URL for ${nupkg_asset}" >&2
78+
exit 1
79+
fi
7680
77-
- name: Package native artifact
78-
run: |
79-
mkdir -p artifacts/native/osx-arm64
80-
cp native/build/macos/libmlxsharp.dylib artifacts/native/osx-arm64/
81-
cp native/build/macos/extern/mlx/mlx/backend/metal/kernels/mlx.metallib artifacts/native/osx-arm64/
81+
mkdir -p work artifacts/native/osx-arm64 artifacts/native/linux-x64
8282
83-
- name: Upload native artifact
83+
echo "Downloading ${nupkg_asset}"
84+
curl -fsSL -H "Accept: application/octet-stream" -H "Authorization: Bearer ${GITHUB_TOKEN}" "$asset_url" -o work/native.nupkg
85+
86+
echo "Extracting native runtimes"
87+
unzip -q work/native.nupkg 'runtimes/osx-arm64/native/*' -d work/extract
88+
unzip -q work/native.nupkg 'runtimes/linux-x64/native/*' -d work/extract
89+
90+
shopt -s nullglob
91+
mac_files=(work/extract/runtimes/osx-arm64/native/*)
92+
linux_files=(work/extract/runtimes/linux-x64/native/*)
93+
94+
if [ ${#mac_files[@]} -eq 0 ]; then
95+
echo "::error::macOS native assets are missing from ${nupkg_asset}" >&2
96+
exit 1
97+
fi
98+
99+
if [ ${#linux_files[@]} -eq 0 ]; then
100+
echo "::error::Linux native assets are missing from ${nupkg_asset}" >&2
101+
exit 1
102+
fi
103+
104+
cp work/extract/runtimes/osx-arm64/native/* artifacts/native/osx-arm64/
105+
cp work/extract/runtimes/linux-x64/native/* artifacts/native/linux-x64/
106+
107+
echo "Staged native artifacts:"
108+
ls -R artifacts/native
109+
110+
- name: Upload macOS native artifact
84111
uses: actions/upload-artifact@v4
85112
with:
86113
name: native-osx-arm64
87114
path: artifacts/native/osx-arm64
88115

116+
- name: Upload Linux native artifact
117+
uses: actions/upload-artifact@v4
118+
with:
119+
name: native-linux-x64
120+
path: artifacts/native/linux-x64
121+
89122
package-test:
90123
needs:
91-
- native-linux
92-
- native-macos
124+
- native-assets
93125
runs-on: macos-latest
94126
steps:
95127
- name: Checkout repository

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ The CMake project vendored from MLX builds MLX and the shim in one go. macOS bui
101101

102102
## CI overview
103103
1. `dotnet-build` (Ubuntu): restores the solution and compiles managed projects.
104-
2. `native-linux` / `native-macos`: compile `libmlxsharp.so` and `libmlxsharp.dylib` in parallel.
105-
3. `package-test` (macOS): downloads both native artifacts, stages them into `src/MLXSharp/runtimes/{rid}/native`, rebuilds, runs the integration tests, and produces NuGet packages.
104+
2. `native-assets` (Ubuntu): downloads the signed native binaries published with the latest MLXSharp release and uploads them as workflow artifacts.
105+
3. `package-test` (macOS): pulls down the staged native artifacts, copies them into `src/MLXSharp/runtimes/{rid}/native`, rebuilds, runs the integration tests, and produces NuGet packages.
106106

107107
## Testing
108108
The managed integration tests still piggy-back on `mlx_lm` until the native runner is feature-complete. Bring your own HuggingFace bundle (any MLX-compatible repo) and point `MLXSHARP_MODEL_PATH` to it before running:
@@ -117,7 +117,7 @@ dotnet test
117117

118118
`MLXSHARP_HF_MODEL_ID` is picked up by the Python smoke test; omit it to fall back to `mlx-community/Qwen1.5-0.5B-Chat-4bit`.
119119

120-
When running locally you can place prebuilt binaries under `libs/native-osx-arm64` (and/or `libs/native-libs`) and a corresponding model bundle under `model/`. The test harness auto-discovers these folders and configures `MLXSHARP_LIBRARY`, `MLXSHARP_MODEL_PATH`, and `MLXSHARP_TOKENIZER_PATH` so you can iterate completely offline. If no native binary is present the tests now download the latest signed `ManagedCode.MLXSharp` NuGet package and stage its `runtimes/{rid}/native/libmlxsharp.{dylib|so}` assets under `libs/native-libs/` automatically.
120+
When running locally you can place prebuilt binaries under `libs/native-osx-arm64` (and/or `libs/native-libs`) and a corresponding model bundle under `model/`. The test harness auto-discovers these folders and configures `MLXSHARP_LIBRARY`, `MLXSHARP_MODEL_PATH`, and `MLXSHARP_TOKENIZER_PATH` so you can iterate completely offline.
121121

122122
The integration suite invokes `python -m mlx_lm.generate` with deterministic settings (temperature `0`, seed `42`) and asserts that the generated response for prompts like “Скільки буде 2+2?” contains the correct answer. Test output includes the raw generation transcript so you can verify the model behaviour directly from the CI logs.
123123

src/MLXSharp.Tests/ModelIntegrationTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ private static void EnsureAssets()
6666
Assert.True(System.IO.Directory.Exists(modelPath), $"Native model bundle not found at '{modelPath}'.");
6767

6868
var library = Environment.GetEnvironmentVariable("MLXSHARP_LIBRARY");
69-
Assert.False(string.IsNullOrWhiteSpace(library), "Native libmlxsharp library is not configured. Set MLXSHARP_LIBRARY to the compiled native library or rely on the official ManagedCode.MLXSharp package that the test harness can download automatically.");
69+
Assert.False(string.IsNullOrWhiteSpace(library), "Native libmlxsharp library is not configured. Set MLXSHARP_LIBRARY to the staged native library that ships with the official MLXSharp release.");
7070
Assert.True(System.IO.File.Exists(library), $"Native libmlxsharp library not found at '{library}'.");
7171
}
7272
}

src/MLXSharp.Tests/NativeBinaryManager.cs

Lines changed: 0 additions & 147 deletions
This file was deleted.

src/MLXSharp.Tests/TestEnvironment.cs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,6 @@ public static void EnsureInitialized()
2525

2626
private static void ConfigureNativeLibrary(string repoRoot)
2727
{
28-
if (NativeBinaryManager.TryEnsureNativeLibrary(repoRoot, out var officialLibrary, out var downloadError) && officialLibrary is not null)
29-
{
30-
ApplyNativeLibrary(officialLibrary);
31-
return;
32-
}
33-
34-
if (!string.IsNullOrWhiteSpace(downloadError))
35-
{
36-
Console.Error.WriteLine($"Failed to download official MLXSharp native library: {downloadError}");
37-
}
38-
3928
var existing = Environment.GetEnvironmentVariable("MLXSHARP_LIBRARY");
4029
if (!string.IsNullOrWhiteSpace(existing) && File.Exists(existing))
4130
{

0 commit comments

Comments
 (0)