mirror of
https://github.com/exo-explore/exo.git
synced 2026-02-15 08:34:31 -05:00
The existing CI typecheck job used `uv run basedpyright` which depends on a non-hermetic uv sync step. This replaces it with a fully hermetic typecheck as a Nix flake check using the uv2nix virtual environment. Added a `typecheckVenv` with dev dependencies, a `linuxOverlay` to ignore native shared library deps (NVIDIA, torch, triton, mlx) that aren't needed at type-check time, and `passthru` preservation plus `.pyi` stub copying on the `exo-pyo3-bindings` overlay so basedpyright can resolve the Rust bindings types. Also guarded the `mlx` Nix build override to macOS only since it requires Metal. Removed the old non-hermetic `typecheck` CI job since `nix flake check` now covers it. The hermetic check ensures type checking uses exactly the locked dependency versions and catches type errors without requiring a working uv/pip environment. Test plan: - CI (`nix flake check` runs on x86_64-linux, aarch64-linux, aarch64-darwin) - Verified `nix build ".#checks.x86_64-linux.typecheck"` passes with 0 errors
163 lines
5.6 KiB
Nix
163 lines
5.6 KiB
Nix
{ inputs, ... }:
|
|
{
|
|
perSystem =
|
|
{ config, self', pkgs, lib, system, ... }:
|
|
let
|
|
# Load workspace from uv.lock
|
|
workspace = inputs.uv2nix.lib.workspace.loadWorkspace {
|
|
workspaceRoot = inputs.self;
|
|
};
|
|
|
|
# Create overlay from workspace
|
|
# Use wheels from PyPI for most packages; we override mlx with our pure Nix Metal build
|
|
overlay = workspace.mkPyprojectOverlay { sourcePreference = "wheel"; };
|
|
|
|
# Override overlay to inject Nix-built components
|
|
exoOverlay = final: prev: {
|
|
# Replace workspace exo_pyo3_bindings with Nix-built wheel.
|
|
# Preserve passthru so mkVirtualEnv can resolve dependency groups.
|
|
# Copy .pyi stub + py.typed marker so basedpyright can find the types.
|
|
exo-pyo3-bindings = pkgs.stdenv.mkDerivation {
|
|
pname = "exo-pyo3-bindings";
|
|
version = "0.1.0";
|
|
src = self'.packages.exo_pyo3_bindings;
|
|
# Install from pre-built wheel
|
|
nativeBuildInputs = [ final.pyprojectWheelHook ];
|
|
dontStrip = true;
|
|
passthru = prev.exo-pyo3-bindings.passthru or { };
|
|
postInstall = ''
|
|
local siteDir=$out/${final.python.sitePackages}/exo_pyo3_bindings
|
|
cp ${inputs.self}/rust/exo_pyo3_bindings/exo_pyo3_bindings.pyi $siteDir/
|
|
touch $siteDir/py.typed
|
|
'';
|
|
};
|
|
};
|
|
|
|
python = pkgs.python313;
|
|
|
|
# Overlay to provide build systems and custom packages
|
|
buildSystemsOverlay = final: prev: {
|
|
# mlx-lm is a git dependency that needs setuptools
|
|
mlx-lm = prev.mlx-lm.overrideAttrs (old: {
|
|
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [
|
|
final.setuptools
|
|
];
|
|
});
|
|
} // lib.optionalAttrs pkgs.stdenv.hostPlatform.isDarwin {
|
|
# Use our pure Nix-built MLX with Metal support (macOS only)
|
|
mlx = self'.packages.mlx;
|
|
};
|
|
|
|
# Additional overlay for Linux-specific fixes (type checking env).
|
|
# Native wheels have shared lib dependencies we don't need at type-check time.
|
|
linuxOverlay = final: prev:
|
|
let
|
|
ignoreMissing = drv: drv.overrideAttrs { autoPatchelfIgnoreMissingDeps = [ "*" ]; };
|
|
nvidiaPackages = lib.filterAttrs (name: _: lib.hasPrefix "nvidia-" name) prev;
|
|
in
|
|
lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux (
|
|
(lib.mapAttrs (_: ignoreMissing) nvidiaPackages) // {
|
|
mlx = ignoreMissing prev.mlx;
|
|
torch = ignoreMissing prev.torch;
|
|
triton = ignoreMissing prev.triton;
|
|
}
|
|
);
|
|
|
|
pythonSet = (pkgs.callPackage inputs.pyproject-nix.build.packages {
|
|
inherit python;
|
|
}).overrideScope (
|
|
lib.composeManyExtensions [
|
|
inputs.pyproject-build-systems.overlays.default
|
|
overlay
|
|
exoOverlay
|
|
buildSystemsOverlay
|
|
linuxOverlay
|
|
]
|
|
);
|
|
exoVenv = pythonSet.mkVirtualEnv "exo-env" workspace.deps.default;
|
|
|
|
# Virtual environment with dev dependencies for testing
|
|
testVenv = pythonSet.mkVirtualEnv "exo-test-env" (
|
|
workspace.deps.default // {
|
|
exo = [ "dev" ]; # Include pytest, pytest-asyncio, pytest-env
|
|
}
|
|
);
|
|
|
|
mkPythonScript = name: path: pkgs.writeShellApplication {
|
|
inherit name;
|
|
runtimeInputs = [ exoVenv ];
|
|
runtimeEnv = {
|
|
EXO_DASHBOARD_DIR = self'.packages.dashboard;
|
|
EXO_RESOURCES_DIR = inputs.self + /resources;
|
|
};
|
|
text = ''exec python ${path} "$@"'';
|
|
};
|
|
|
|
benchVenv = pythonSet.mkVirtualEnv "exo-bench-env" {
|
|
exo-bench = [ ];
|
|
};
|
|
|
|
mkBenchScript = name: path: pkgs.writeShellApplication {
|
|
inherit name;
|
|
runtimeInputs = [ benchVenv ];
|
|
text = ''exec python ${path} "$@"'';
|
|
};
|
|
|
|
mkSimplePythonScript = name: path: pkgs.writeShellApplication {
|
|
inherit name;
|
|
runtimeInputs = [ pkgs.python313 ];
|
|
text = ''exec python ${path} "$@"'';
|
|
};
|
|
|
|
exoPackage = pkgs.runCommand "exo"
|
|
{
|
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
|
}
|
|
''
|
|
mkdir -p $out/bin
|
|
|
|
# Create wrapper script
|
|
makeWrapper ${exoVenv}/bin/exo $out/bin/exo \
|
|
--set EXO_DASHBOARD_DIR ${self'.packages.dashboard} \
|
|
--set EXO_RESOURCES_DIR ${inputs.self + /resources} \
|
|
${lib.optionalString pkgs.stdenv.hostPlatform.isDarwin "--prefix PATH : ${pkgs.macmon}/bin"}
|
|
'';
|
|
in
|
|
{
|
|
# Python package only available on macOS (requires MLX/Metal)
|
|
packages = lib.optionalAttrs pkgs.stdenv.hostPlatform.isDarwin
|
|
{
|
|
exo = exoPackage;
|
|
# Test environment for running pytest outside of Nix sandbox (needs GPU access)
|
|
exo-test-env = testVenv;
|
|
} // {
|
|
exo-bench = mkBenchScript "exo-bench" (inputs.self + /bench/exo_bench.py);
|
|
exo-get-all-models-on-cluster = mkSimplePythonScript "exo-get-all-models-on-cluster" (inputs.self + /tests/get_all_models_on_cluster.py);
|
|
};
|
|
|
|
checks = {
|
|
# Ruff linting (works on all platforms)
|
|
lint = pkgs.runCommand "ruff-lint" { } ''
|
|
export RUFF_CACHE_DIR="$TMPDIR/ruff-cache"
|
|
${pkgs.ruff}/bin/ruff check ${inputs.self}
|
|
touch $out
|
|
'';
|
|
|
|
# Hermetic basedpyright type checking
|
|
typecheck = pkgs.runCommand "typecheck"
|
|
{
|
|
nativeBuildInputs = [
|
|
testVenv
|
|
pkgs.basedpyright
|
|
];
|
|
}
|
|
''
|
|
cd ${inputs.self}
|
|
export HOME=$TMPDIR
|
|
basedpyright --pythonpath ${testVenv}/bin/python
|
|
touch $out
|
|
'';
|
|
};
|
|
};
|
|
}
|