diff --git a/.envrc b/.envrc index 894571b..3add915 100644 --- a/.envrc +++ b/.envrc @@ -1,3 +1,5 @@ -source_url "https://raw.githubusercontent.com/cachix/devenv/82c0147677e510b247d8b9165c54f73d32dfd899/direnvrc" "sha256-7u4iDd1nZpxL4tCzmPG0dQgC5V+/44Ba+tHkPob1v2k=" +if ! has nix_direnv_manual_version; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.6/direnvrc" "sha256-RYcUJaRS3fmx9EynmEn1o1Q4BAE_hNiBhqdwaFm6zSE=" +fi -use devenv +use flake diff --git a/devenv.lock b/devenv.lock deleted file mode 100644 index 4bb9366..0000000 --- a/devenv.lock +++ /dev/null @@ -1,173 +0,0 @@ -{ - "nodes": { - "devenv": { - "locked": { - "dir": "src/modules", - "lastModified": 1781800860, - "owner": "cachix", - "repo": "devenv", - "rev": "d59d872d80876d9eeb3e214d3b088bc4a14a9c4f", - "type": "github" - }, - "original": { - "dir": "src/modules", - "owner": "cachix", - "repo": "devenv", - "type": "github" - } - }, - "fenix": { - "inputs": { - "nixpkgs": "nixpkgs", - "rust-analyzer-src": "rust-analyzer-src" - }, - "locked": { - "lastModified": 1781779700, - "owner": "nix-community", - "repo": "fenix", - "rev": "ad30e585c7a2917325943c2b19511f5a249eff53", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "fenix", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1767039857, - "owner": "NixOS", - "repo": "flake-compat", - "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "flake-compat", - "type": "github" - } - }, - "git-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1781733627, - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "3bbec39bc90eadfa031e6f3b77272f3f60803e39", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1762808025, - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1781577229, - "owner": "nixos", - "repo": "nixpkgs", - "rev": "567a49d1913ce81ac6e9582e3553dd90a955875f", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1781607440, - "owner": "nixos", - "repo": "nixpkgs", - "rev": "3e41b24abd260e8f71dbe2f5737d24122f972158", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "devenv": "devenv", - "fenix": "fenix", - "git-hooks": "git-hooks", - "nixpkgs": "nixpkgs_2", - "pre-commit-hooks": [ - "git-hooks" - ], - "rust-overlay": "rust-overlay" - } - }, - "rust-analyzer-src": { - "flake": false, - "locked": { - "lastModified": 1781714865, - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "abb1301c3c14a40645bb2588b1cc858fe374b527", - "type": "github" - }, - "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", - "type": "github" - } - }, - "rust-overlay": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1781850613, - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "4baecb43a008cd004e5220a777e1724bd8d43e43", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/devenv.nix b/devenv.nix deleted file mode 100644 index f9d0cbc..0000000 --- a/devenv.nix +++ /dev/null @@ -1,88 +0,0 @@ -{ - pkgs, - lib, - config, - inputs, - ... -}: let - ortVersion = "1.23.2"; - _ortVersionCheck = - if pkgs.onnxruntime.version == ortVersion - then null - else throw "pkgs.onnxruntime.version (${pkgs.onnxruntime.version}) must match ortVersion in flake.nix (${ortVersion})"; -in { - devenv.warnOnNewVersion = false; - - cachix.enable = false; - - git-hooks.install.enable = true; - git-hooks.hooks = { - rustfmt.enable = true; - clippy = { - enable = true; - settings.allFeatures = true; - }; - }; - - # Use pinned Rust toolchain from languages.rust for git-hooks wrappers - # (git-hooks.nix defaults to nixpkgs's cargo/clippy/rustfmt, ignoring the pin) - git-hooks.tools.cargo = lib.mkDefault config.languages.rust.toolchain.cargo; - git-hooks.tools.clippy = lib.mkDefault config.languages.rust.toolchain.clippy; - git-hooks.tools.rustfmt = lib.mkDefault config.languages.rust.toolchain.rustfmt; - - packages = [ - pkgs.openssl - pkgs.nodejs - pkgs.watchman - pkgs.vscode-langservers-extracted - pkgs.cargo-xwin - pkgs.clang - pkgs.onnxruntime - pkgs.cargo-watch - pkgs.tailwindcss_4 - pkgs.python3 - pkgs.fontconfig - pkgs.fontconfig.dev - pkgs.libGL - pkgs.libGLU - pkgs.libclang - pkgs.wayland - pkgs.libxkbcommon - ]; - - languages.rust = { - enable = true; - channel = "stable"; - version = "1.91.1"; - components = ["rustc" "clippy" "rustfmt" "cargo" "rust-analyzer"]; - targets = ["x86_64-unknown-linux-gnu" "x86_64-pc-windows-msvc"]; - mold.enable = true; - }; - - env = { - # tikv-jemalloc-sys configure flags: -O0 + -Werror triggers glibc _FORTIFY_SOURCE warning - NIX_CFLAGS_COMPILE = "-Wno-error=cpp"; - LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; - LD_LIBRARY_PATH = "${pkgs.wayland}/lib:${pkgs.libxkbcommon}/lib:${pkgs.pipewire}/lib:${pkgs.libglvnd}/lib"; - ORT_DYLIB_PATH = "${pkgs.onnxruntime}/lib/libonnxruntime.so"; - S3_ENDPOINT = "http://127.0.0.1:19000"; - S3_BUCKET = "minne-tests"; - MINNE_TEST_S3_ENDPOINT = "http://127.0.0.1:19000"; - MINNE_TEST_S3_BUCKET = "minne-tests"; - }; - - services.minio = { - enable = true; - listenAddress = "127.0.0.1:19000"; - consoleAddress = "127.0.0.1:19001"; - buckets = ["minne-tests"]; - accessKey = "minioadmin"; - secretKey = "minioadmin"; - region = "us-east-1"; - }; - - processes = { - surreal_db.exec = "docker run --rm --pull always -p 8000:8000 --net=host --user $(id -u) -v $(pwd)/database:/database surrealdb/surrealdb:v2.6.5-dev start rocksdb:/database/database.db --user root_user --pass root_password"; - tailwind.exec = "tailwindcss --cwd html-router -i app.css -o assets/style.css --watch=always"; - }; -} diff --git a/devenv.yaml b/devenv.yaml deleted file mode 100644 index 016a29d..0000000 --- a/devenv.yaml +++ /dev/null @@ -1,14 +0,0 @@ -inputs: - fenix: - url: github:nix-community/fenix - nixpkgs: - url: github:nixos/nixpkgs/nixpkgs-unstable - rust-overlay: - url: github:oxalica/rust-overlay - inputs: - nixpkgs: - follows: nixpkgs -allowUnfree: true -nixpkgs: - permittedInsecurePackages: - - "minio-2025-10-15T17-29-55Z" diff --git a/flake.lock b/flake.lock index 49d28c6..3f9b26c 100644 --- a/flake.lock +++ b/flake.lock @@ -36,21 +36,80 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, + "flake-compat": { + "flake": false, "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1778716662, + "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781733627, + "narHash": "sha256-U3yTuGBnmXvXoQI3qkpfEDsn9RovQPAjN7ndRco+3u0=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "3bbec39bc90eadfa031e6f3b77272f3f60803e39", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", "type": "github" } }, @@ -70,11 +129,27 @@ "type": "github" } }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1777168982, + "narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, "root": { "inputs": { "crane": "crane", "fenix": "fenix", - "flake-utils": "flake-utils", + "flake-parts": "flake-parts", + "git-hooks": "git-hooks", "nixpkgs": "nixpkgs" } }, @@ -94,21 +169,6 @@ "repo": "rust-analyzer", "type": "github" } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index b7cf55a..09783c5 100644 --- a/flake.nix +++ b/flake.nix @@ -3,414 +3,44 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; + flake-parts.url = "github:hercules-ci/flake-parts"; crane.url = "github:ipetkov/crane"; fenix.url = "github:nix-community/fenix"; fenix.inputs.nixpkgs.follows = "nixpkgs"; + git-hooks.url = "github:cachix/git-hooks.nix"; + git-hooks.inputs.nixpkgs.follows = "nixpkgs"; }; - outputs = { - self, - nixpkgs, - flake-utils, - crane, - fenix, - }: let - inherit (nixpkgs.legacyPackages.x86_64-linux) lib; - ortVersion = "1.23.2"; - in - flake-utils.lib.eachDefaultSystem (system: let - pkgs = nixpkgs.legacyPackages.${system}; - lib = pkgs.lib; - craneLib = crane.mkLib pkgs; - libExt = - if pkgs.stdenv.isDarwin - then "dylib" - else "so"; - minneVersion = "1.0.5"; - mozjsRelease = "mozjs-sys-v140.10.1-0"; + outputs = + inputs@{ flake-parts, ... }: + let + ortVersion = "1.23.2"; + toolchainFile = ./rust-toolchain.toml; + rustVersion = (builtins.fromTOML (builtins.readFile toolchainFile)).toolchain.channel; + in + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ + ./nix/context.nix + ./nix/package.nix + ./nix/checks.nix + ./nix/dev-shell.nix + ]; - mozjsTarget = - { - "x86_64-linux" = "x86_64-unknown-linux-gnu"; - "aarch64-linux" = "aarch64-unknown-linux-gnu"; - "aarch64-darwin" = "aarch64-apple-darwin"; - "x86_64-darwin" = "x86_64-apple-darwin"; - } - .${system} - or (throw "mozjs prebuilt archive not configured for system ${system}"); + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; - mozjsHashes = { - "x86_64-unknown-linux-gnu" = "sha256-e5kW8HTg6Hrd3sGgU9bqFNTTf7wJCChFOwKE3xyYT4Q="; - "aarch64-unknown-linux-gnu" = "sha256-VXrcktvjSH+14tO9Kzx+n9f/9ZQGAzfEsniiT+xKT6Q="; - "aarch64-apple-darwin" = "sha256-T3y73nVic6R60keUpmVRFe110Eh7AcE/VwZQWXRU9A0="; - "x86_64-apple-darwin" = "sha256-4v6f6c1OwYdg1FKnFfdLEsrRdyghcxup4gF7ioTZzm4="; + _module.args = { + inherit ortVersion toolchainFile rustVersion; }; - # Pre-download mozjs binary archive for mozjs_sys (servo dep). - # When updating mozjs_sys version in Cargo.lock, update mozjsRelease + hashes. - mozjsArchive = pkgs.fetchurl { - url = "https://github.com/servo/mozjs/releases/download/${mozjsRelease}/libmozjs-${mozjsTarget}.tar.gz"; - hash = mozjsHashes.${mozjsTarget} or (throw "missing mozjs hash for ${mozjsTarget}"); - }; - - # Extra paths (common/db, html-router/templates, html-router/assets) are - # embedded at compile time via include_dir! / minijinja_embed. - commonArgs = { - version = minneVersion; - src = lib.cleanSourceWith { - src = ./.; - filter = path: type: - craneLib.filterCargoSources path type - || lib.any (x: lib.hasPrefix (toString x) (toString path)) [ - (toString ./Cargo.lock) - (toString ./common/db) - (toString ./html-router/templates) - (toString ./html-router/assets) - ]; + flake = { + lib = { + inherit ortVersion rustVersion; }; - strictDeps = true; - - buildInputs = [ - pkgs.openssl - pkgs.libglvnd - pkgs.onnxruntime - pkgs.fontconfig # .pc for yeslogic-fontconfig-sys (servo dep) - pkgs.libclang.lib # libclang.so for bindgen (servo dep) - ]; - - nativeBuildInputs = [ - pkgs.pkg-config - pkgs.rustfmt - pkgs.makeWrapper - pkgs.python3 # needed by servo's stylo crate build.rs - pkgs.llvmPackages.llvm # llvm-objdump for mozjs_sys (servo dep) - pkgs.rustPlatform.bindgenHook # configures bindgen (servo deps) - ]; - - # Provide pre-downloaded mozjs archive so it doesn't need network - MOZJS_ARCHIVE = "${mozjsArchive}"; - }; - - # Build *just* the cargo dependencies using a dummy source so that source - # code changes don't invalidate the cached dependency layer. - cargoArtifacts = craneLib.buildDepsOnly (commonArgs - // { - pname = "minne"; - cargoExtraArgs = "--workspace"; - doCheck = false; - }); - - minne-pkg = - if pkgs.onnxruntime.version == ortVersion - then - craneLib.buildPackage (commonArgs - // { - pname = "minne"; - version = minneVersion; - inherit cargoArtifacts; - doCheck = false; # checks are in separate derivations - doInstallCargoArtifacts = true; # for reuse by check derivations - - postInstall = - lib.optionalString pkgs.stdenv.isLinux '' - wrapProgram $out/bin/main \ - --prefix LD_LIBRARY_PATH : ${pkgs.libglvnd}/lib \ - --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} - for b in worker server; do - if [ -x "$out/bin/$b" ]; then - wrapProgram $out/bin/$b \ - --prefix LD_LIBRARY_PATH : ${pkgs.libglvnd}/lib \ - --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} - fi - done - '' - + lib.optionalString pkgs.stdenv.isDarwin '' - for b in main worker server; do - if [ -x "$out/bin/$b" ]; then - wrapProgram $out/bin/$b \ - --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} - fi - done - ''; - }) - else throw "pkgs.onnxruntime.version (${pkgs.onnxruntime.version}) must match ortVersion in flake.nix (${ortVersion})"; - - targetTriple = pkgs.stdenv.hostPlatform.config; - - releaseCommonArgs = { - inherit minneVersion targetTriple; - bzip2 = pkgs.bzip2.out; - brotli = pkgs.brotli.lib; - srcRoot = ./.; - }; - - minne-release = - if pkgs.stdenv.isLinux - then - pkgs.callPackage ./nix/minne-release.nix (releaseCommonArgs // { - platform = "linux"; - inherit minne-pkg; - }) - else if pkgs.stdenv.isDarwin - then - pkgs.callPackage ./nix/minne-release.nix (releaseCommonArgs // { - platform = "darwin"; - inherit minne-pkg; - }) - else null; - - windowsTarget = "x86_64-pc-windows-msvc"; - - windowsRustToolchain = - if system == "x86_64-linux" - then - let - fenixPkgs = fenix.packages.${system}; - in - fenixPkgs.combine [ - fenixPkgs.stable.defaultToolchain - fenixPkgs.targets.${windowsTarget}.stable.rust-std - ] - else null; - - windowsCraneLib = - if system == "x86_64-linux" - then craneLib.overrideToolchain (_: windowsRustToolchain) - else craneLib; - - mozjsArchiveWindows = pkgs.fetchurl { - url = "https://github.com/servo/mozjs/releases/download/${mozjsRelease}/libmozjs-${windowsTarget}.tar.gz"; - hash = "sha256-nEX55a4vZJGxlDMCea9TEee60HiNe/yQzXtUqMlaM3c="; - }; - - ortArchiveWindows = pkgs.fetchurl { - url = "https://github.com/microsoft/onnxruntime/releases/download/v${ortVersion}/onnxruntime-win-x64-${ortVersion}.zip"; - hash = "sha256-CzjfmvIYNOQec9YC2Q21ywbb0cphiUi48dZtYHrJ880="; - }; - - windowsCross = pkgs.callPackage ./nix/windows-cross.nix {}; - - inherit (windowsCross) clangClWrapper xwinCargoCache; - - # blake3's build.rs only enables MSVC asm when CC_x86_64_pc_windows_msvc is exactly - # "cl" or "cl.exe" (not a store path). Route through the clang-cl wrapper. - # cc-rs invokes ml64.exe for MSVC asm; llvm-ml64 is ml64-compatible. - msvcShim = pkgs.symlinkJoin { - name = "minne-msvc-shim"; - paths = [ - (pkgs.writeShellScriptBin "cl.exe" '' - exec ${clangClWrapper} "$@" - '') - (pkgs.writeShellScriptBin "ml64.exe" '' - exec ${pkgs.llvmPackages.llvm}/bin/llvm-ml64 "$@" - '') - ]; - }; - - # Offline MSVC env (nixpkgs cargo-xwin lacks `cache` and tries to download CRT in sandbox). - xwinSetup = pkgs.writeShellScript "minne-xwin-setup" '' - set -eo pipefail - - cache=${xwinCargoCache} - crt="$cache/xwin/crt" - sdk="$cache/xwin/sdk" - - export PATH="${msvcShim}/bin:${pkgs.llvmPackages.clang-unwrapped}/bin:${pkgs.llvmPackages.lld}/bin:${pkgs.llvmPackages.llvm}/bin:$PATH" - - # Host build scripts (webrender, etc.) run on Linux and need libstdc++ at runtime. - export LD_LIBRARY_PATH="${pkgs.stdenv.cc.cc.lib}/lib''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" - - export AR_x86_64_pc_windows_msvc=${pkgs.llvmPackages.llvm}/bin/llvm-lib - export BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_msvc="-I$crt/include -I$sdk/include/ucrt -I$sdk/include/um -I$sdk/include/shared -I$sdk/include/winrt" - export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=${pkgs.llvmPackages.lld}/bin/lld-link - export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS="-C linker-flavor=lld-link -Lnative=$crt/lib/x86_64 -Lnative=$sdk/lib/um/x86_64 -Lnative=$sdk/lib/ucrt/x86_64" - export CC_x86_64_pc_windows_msvc=cl.exe - export CXX_x86_64_pc_windows_msvc=cl.exe - export REAL_CLANG_CL=${pkgs.llvmPackages.clang-unwrapped}/bin/clang-cl - export REAL_LLD_LINK=${pkgs.llvmPackages.lld}/bin/lld-link - - _imsvc="--target=x86_64-pc-windows-msvc -Wno-unused-command-line-argument -fuse-ld=lld-link /imsvc $crt/include /imsvc $sdk/include/ucrt /imsvc $sdk/include/um /imsvc $sdk/include/shared /imsvc $sdk/include/winrt" - export CFLAGS_x86_64_pc_windows_msvc="$_imsvc" - export CXXFLAGS_x86_64_pc_windows_msvc="$_imsvc /EHsc" - export CL_FLAGS="--target=x86_64-pc-windows-msvc -Wno-unused-command-line-argument -fuse-ld=lld-link /imsvc $crt/include /imsvc $sdk/include/ucrt /imsvc $sdk/include/um /imsvc $sdk/include/shared /imsvc $sdk/include/winrt" - - export CMAKE_GENERATOR=Ninja - export CMAKE_SYSTEM_NAME=Windows - export CMAKE_TOOLCHAIN_FILE_x86_64_pc_windows_msvc="$cache/cmake/clang-cl/x86_64-pc-windows-msvc-toolchain.cmake" - - export LIB="$crt/lib/x86_64;$sdk/lib/um/x86_64;$sdk/lib/ucrt/x86_64" - export RCFLAGS="-I$crt/include -I$sdk/include/ucrt -I$sdk/include/um -I$sdk/include/shared -I$sdk/include/winrt" - export TARGET_AR=${pkgs.llvmPackages.llvm}/bin/llvm-lib - export TARGET_CC=${pkgs.llvmPackages.clang-unwrapped}/bin/clang-cl - export TARGET_CXX=${pkgs.llvmPackages.clang-unwrapped}/bin/clang-cl - export WINEDEBUG=-all - ''; - - windowsCommonArgs = - commonArgs - // { - MOZJS_ARCHIVE = "${mozjsArchiveWindows}"; - CARGO_BUILD_TARGET = windowsTarget; - doIncludeCrossToolchainEnv = false; - env.CARGO_PROFILE = "dist"; - buildInputs = [ - pkgs.openssl - pkgs.fontconfig - pkgs.libclang.lib - ]; - nativeBuildInputs = - commonArgs.nativeBuildInputs - ++ [ - pkgs.llvmPackages.llvm - pkgs.llvmPackages.clang-unwrapped - pkgs.llvmPackages.lld - pkgs.stdenv.cc.cc.lib # host build scripts (e.g. webrender) link libstdc++ - ]; - }; - - windowsCargoArtifacts = - if system == "x86_64-linux" - then - windowsCraneLib.buildDepsOnly (windowsCommonArgs - // { - pname = "minne"; - cargoExtraArgs = "--workspace"; - doCheck = false; - preBuild = "source ${xwinSetup}"; - }) - else null; - - minne-pkg-windows = - if system == "x86_64-linux" - then - windowsCraneLib.buildPackage (windowsCommonArgs - // { - pname = "minne-windows"; - version = minneVersion; - cargoArtifacts = windowsCargoArtifacts; - cargoExtraArgs = "--target ${windowsTarget} -p main --bin main --bin server --bin worker"; - doCheck = false; - doInstallCargoArtifacts = false; - preBuild = "source ${xwinSetup}"; - installPhaseCommand = '' - mkdir -p "$out/bin" - for b in main server worker; do - install -m 755 "target/${windowsTarget}/dist/$b.exe" "$out/bin/$b.exe" - done - ''; - }) - else null; - - minne-release-windows = - if system == "x86_64-linux" - then - pkgs.callPackage ./nix/minne-release.nix (releaseCommonArgs // { - platform = "windows"; - inherit minne-pkg-windows ortArchiveWindows; - targetTriple = windowsTarget; - }) - else null; - - dockerImage = pkgs.dockerTools.buildLayeredImage { - name = "minne"; - tag = minneVersion; - created = "now"; - - contents = [ - minne-pkg - pkgs.cacert - pkgs.bashInteractive - pkgs.libglvnd - pkgs.fontconfig.lib - pkgs.freetype - pkgs.stdenv.cc.cc.lib # libgomp (OpenMP) for ONNX Runtime - ]; - - maxLayers = 25; - - config = { - Cmd = ["${minne-pkg}/bin/main"]; - Env = [ - "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-certificates.crt" - "ORT_DYLIB_PATH=${pkgs.onnxruntime}/lib/libonnxruntime.${libExt}" - ]; - ExposedPorts = {"3000/tcp" = {};}; - User = "appuser"; - }; - }; - in { - packages = { - inherit minne-pkg dockerImage; - default = minne-pkg; - } - // lib.optionalAttrs (minne-release != null) { - minne-release = minne-release; - } - // lib.optionalAttrs (minne-release-windows != null) { - inherit xwinCargoCache; - minne-release-windows = minne-release-windows; - }; - - apps = { - main = { - type = "app"; - program = "${minne-pkg}/bin/main"; - meta.description = "Minne main server — API, web UI, and background worker"; - }; - worker = { - type = "app"; - program = "${minne-pkg}/bin/worker"; - meta.description = "Minne standalone background worker (ingestion, indexing, maintenance)"; - }; - server = { - type = "app"; - program = "${minne-pkg}/bin/server"; - meta.description = "Minne API-only server (no background worker)"; - }; - default = { - type = "app"; - program = "${minne-pkg}/bin/main"; - meta.description = "Minne main server — API, web UI, and background worker"; - }; - }; - - checks = { - ortVersion = pkgs.runCommand "ort-version-check" {} '' - if [ "${pkgs.onnxruntime.version}" != "${ortVersion}" ]; then - echo "pkgs.onnxruntime.version is ${pkgs.onnxruntime.version}, but flake pins ${ortVersion}" >&2 - echo "Update ortVersion in flake.nix or wait for nixpkgs to catch up." >&2 - exit 1 - fi - touch $out - ''; - - minne-clippy = craneLib.cargoClippy (commonArgs - // { - cargoArtifacts = minne-pkg; - pname = "minne"; - cargoClippyExtraArgs = "--all-targets -- --deny warnings"; - }); - - minne-test = craneLib.cargoTest (commonArgs - // { - cargoArtifacts = minne-pkg; - pname = "minne"; - buildInputs = commonArgs.buildInputs ++ [pkgs.cacert]; - SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-certificates.crt"; - cargoTestExtraArgs = "--lib --bins"; - }); - - minne-fmt = craneLib.cargoFmt { - pname = "minne-fmt"; - version = minneVersion; - src = craneLib.cleanCargoSource ./.; - }; - }; - }) - // { - lib = { - inherit ortVersion; }; }; } diff --git a/nix/checks.nix b/nix/checks.nix new file mode 100644 index 0000000..3e303af --- /dev/null +++ b/nix/checks.nix @@ -0,0 +1,82 @@ +# Flake checks: version gates, clippy, tests, formatting, VM smoke. +{ + ortVersion, + ... +}: +{ + perSystem = + { + pkgs, + lib, + minneCtx, + ... + }: + let + inherit (minneCtx) + craneLib + rustToolchain + commonArgs + minne-pkg + minneVersion + vmSmokeTest + src + ; + in + { + checks = { + ortVersion = pkgs.runCommand "ort-version-check" { } '' + if [ "${pkgs.onnxruntime.version}" != "${ortVersion}" ]; then + echo "pkgs.onnxruntime.version is ${pkgs.onnxruntime.version}, but flake pins ${ortVersion}" >&2 + echo "Update ortVersion in flake.nix or wait for nixpkgs to catch up." >&2 + exit 1 + fi + touch $out + ''; + + rustToolchain = + pkgs.runCommand "rust-toolchain-check" + { + nativeBuildInputs = [ rustToolchain.toolchain ]; + } + '' + expected="${rustToolchain.rustVersion}" + actual="$(${rustToolchain.toolchain}/bin/rustc --version | awk '{print $2}')" + if [ "$actual" != "$expected" ]; then + echo "rustc version mismatch: expected $expected, got $actual" >&2 + echo "Update rust-toolchain.toml and rebuild the fenix toolchain." >&2 + exit 1 + fi + touch $out + ''; + + minne-clippy = craneLib.cargoClippy ( + commonArgs + // { + cargoArtifacts = minne-pkg; + pname = "minne"; + cargoClippyExtraArgs = "--all-targets -- --deny warnings"; + } + ); + + minne-test = craneLib.cargoTest ( + commonArgs + // { + cargoArtifacts = minne-pkg; + pname = "minne"; + buildInputs = commonArgs.buildInputs ++ [ pkgs.cacert ]; + SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-certificates.crt"; + cargoTestExtraArgs = "--lib --bins"; + } + ); + + minne-fmt = craneLib.cargoFmt { + pname = "minne-fmt"; + version = minneVersion; + src = craneLib.cleanCargoSource src; + }; + } + // lib.optionalAttrs (vmSmokeTest != null) { + minne-vm-smoke = vmSmokeTest; + }; + }; +} diff --git a/nix/context.nix b/nix/context.nix new file mode 100644 index 0000000..cebcded --- /dev/null +++ b/nix/context.nix @@ -0,0 +1,33 @@ +# Evaluates shared build context once per system for downstream flake-parts modules. +{ + inputs, + ortVersion, + toolchainFile, + ... +}: +{ + perSystem = + { system, ... }: + let + pkgs = import inputs.nixpkgs { + inherit system; + config = { + allowUnfree = true; + permittedInsecurePackages = [ "minio-2025-10-15T17-29-55Z" ]; + }; + }; + in + { + _module.args.pkgs = pkgs; + _module.args.minneCtx = import ./minne-lib.nix { + inherit + inputs + pkgs + system + ortVersion + toolchainFile + ; + src = inputs.self; + }; + }; +} diff --git a/nix/dev-shell.nix b/nix/dev-shell.nix new file mode 100644 index 0000000..6440ff6 --- /dev/null +++ b/nix/dev-shell.nix @@ -0,0 +1,172 @@ +# Local development shell, git hooks, and process-compose runner. +{ inputs, ... }: +{ + perSystem = + { + pkgs, + lib, + system, + minneCtx, + ... + }: + let + inherit (minneCtx) rustToolchain surrealdbPkg devGraphicsLibs; + + ortDylib = + if pkgs.stdenv.isDarwin then + "${pkgs.onnxruntime}/lib/libonnxruntime.dylib" + else + "${pkgs.onnxruntime}/lib/libonnxruntime.so"; + + processComposeFile = pkgs.writeText "minne-process-compose.yaml" '' + version: "0.5" + + environment: + - MINIO_ROOT_USER=minioadmin + - MINIO_ROOT_PASSWORD=minioadmin + - MINIO_REGION=us-east-1 + + processes: + surreal_db: + command: | + mkdir -p database + exec ${surrealdbPkg}/bin/surreal start \ + --bind 127.0.0.1:8000 \ + --log info \ + --user root_user \ + --pass root_password \ + rocksdb:database/database.db + availability: + restart: on_failure + + tailwind: + command: ${pkgs.tailwindcss_4}/bin/tailwindcss --cwd html-router -i app.css -o assets/style.css --watch=always + availability: + restart: on_failure + + minio: + command: | + mkdir -p .data/minio + exec ${pkgs.minio}/bin/minio server .data/minio \ + --address 127.0.0.1:19000 \ + --console-address 127.0.0.1:19001 + availability: + restart: on_failure + + minio_setup: + command: | + for _ in $(seq 1 30); do + if ${pkgs.minio-client}/bin/mc alias set local http://127.0.0.1:19000 minioadmin minioadmin 2>/dev/null; then + ${pkgs.minio-client}/bin/mc mb local/minne-tests --ignore-existing + exit 0 + fi + sleep 1 + done + echo "minio did not become ready" >&2 + exit 1 + depends_on: + minio: + condition: process_started + ''; + + processComposeRunner = pkgs.writeShellScriptBin "minne-dev-up" '' + set -euo pipefail + root="''${MINNE_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}" + cd "$root" + exec ${pkgs.process-compose}/bin/process-compose up -f ${processComposeFile} "$@" + ''; + + moldFlags = if pkgs.stdenv.isLinux then "-C link-arg=-fuse-ld=mold" else ""; + + preCommitCheck = inputs.git-hooks.lib.${system}.run { + src = inputs.self; + hooks = { + rustfmt.enable = true; + clippy = { + enable = true; + settings.allFeatures = true; + }; + nixfmt.enable = true; + }; + tools = { + cargo = rustToolchain.toolchain; + clippy = rustToolchain.toolchain; + rustfmt = rustToolchain.toolchain; + nixfmt = pkgs.nixfmt; + }; + }; + + installGitHooks = '' + legacy="$(git rev-parse --git-path hooks/pre-commit.legacy 2>/dev/null || true)" + if [ -n "$legacy" ] && [ -f "$legacy" ]; then + rm -f "$legacy" + fi + # prek migration can leave core.hooksPath set; pre-commit refuses to install then. + hooks_path="$(git config --local --get core.hooksPath 2>/dev/null || true)" + if [ -n "$hooks_path" ]; then + git config --local --unset-all core.hooksPath + fi + ''; + + devPackages = [ + rustToolchain.toolchain + surrealdbPkg + processComposeRunner + pkgs.process-compose + pkgs.minio + pkgs.minio-client + pkgs.openssl + pkgs.nodejs + pkgs.watchman + pkgs.vscode-langservers-extracted + pkgs.cargo-xwin + pkgs.clang + pkgs.onnxruntime + pkgs.cargo-watch + pkgs.tailwindcss_4 + pkgs.python3 + pkgs.fontconfig + pkgs.fontconfig.dev + pkgs.libGL + pkgs.libGLU + pkgs.libclang + pkgs.mold + pkgs.nixfmt + ]; + + devEnv = { + NIX_CFLAGS_COMPILE = "-Wno-error=cpp"; + LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; + LD_LIBRARY_PATH = lib.makeLibraryPath devGraphicsLibs; + ORT_DYLIB_PATH = ortDylib; + S3_ENDPOINT = "http://127.0.0.1:19000"; + S3_BUCKET = "minne-tests"; + MINNE_TEST_S3_ENDPOINT = "http://127.0.0.1:19000"; + MINNE_TEST_S3_BUCKET = "minne-tests"; + RUSTFLAGS = moldFlags; + MINNE_ROOT = "${inputs.self}"; + }; + in + { + packages.process-compose-runner = processComposeRunner; + + apps.dev = { + type = "app"; + program = "${processComposeRunner}/bin/minne-dev-up"; + meta.description = "Start local dev services (SurrealDB, MinIO, Tailwind)"; + }; + + devShells.default = pkgs.mkShell { + packages = devPackages; + env = devEnv; + shellHook = '' + ${preCommitCheck.shellHook} + ${installGitHooks} + echo "Minne dev shell (fenix ${rustToolchain.rustVersion})" + echo " nix run .#dev # or: minne-dev-up" + echo " cargo test --workspace" + echo " nix flake check" + ''; + }; + }; +} diff --git a/nix/minne-lib.nix b/nix/minne-lib.nix new file mode 100644 index 0000000..0971fc3 --- /dev/null +++ b/nix/minne-lib.nix @@ -0,0 +1,373 @@ +# Shared build context for packages, checks, and the dev shell. +{ + inputs, + pkgs, + system, + src, + ortVersion, + toolchainFile, +}: +let + lib = pkgs.lib; + inherit (inputs) crane fenix; + + surrealdbPkg = + if system == "x86_64-linux" then pkgs.callPackage ./surrealdb-binary.nix { } else pkgs.surrealdb; + + rustToolchain = import ./rust-toolchain.nix { + inherit fenix system toolchainFile; + }; + + craneLib = rustToolchain.mkCraneLib pkgs (crane.mkLib pkgs); + + libExt = if pkgs.stdenv.isDarwin then "dylib" else "so"; + + linuxRuntimeLibs = with pkgs; [ + libglvnd + stdenv.cc.cc.lib + zlib + fontconfig.lib + freetype + openssl.out + onnxruntime + ]; + + devGraphicsLibs = with pkgs; [ + wayland + libxkbcommon + pipewire + libglvnd + ]; + + wrapLinuxBinary = libExt: '' + wrapProgram $out/bin/main \ + --prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath linuxRuntimeLibs} \ + --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} + for b in worker server; do + if [ -x "$out/bin/$b" ]; then + wrapProgram $out/bin/$b \ + --prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath linuxRuntimeLibs} \ + --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} + fi + done + ''; + + minneVersion = "1.0.5"; + mozjsRelease = "mozjs-sys-v140.10.1-0"; + + mozjsTarget = + { + "x86_64-linux" = "x86_64-unknown-linux-gnu"; + "aarch64-linux" = "aarch64-unknown-linux-gnu"; + "aarch64-darwin" = "aarch64-apple-darwin"; + "x86_64-darwin" = "x86_64-apple-darwin"; + } + .${system} or (throw "mozjs prebuilt archive not configured for system ${system}"); + + mozjsHashes = { + "x86_64-unknown-linux-gnu" = "sha256-e5kW8HTg6Hrd3sGgU9bqFNTTf7wJCChFOwKE3xyYT4Q="; + "aarch64-unknown-linux-gnu" = "sha256-VXrcktvjSH+14tO9Kzx+n9f/9ZQGAzfEsniiT+xKT6Q="; + "aarch64-apple-darwin" = "sha256-T3y73nVic6R60keUpmVRFe110Eh7AcE/VwZQWXRU9A0="; + "x86_64-apple-darwin" = "sha256-4v6f6c1OwYdg1FKnFfdLEsrRdyghcxup4gF7ioTZzm4="; + }; + + mozjsArchive = pkgs.fetchurl { + url = "https://github.com/servo/mozjs/releases/download/${mozjsRelease}/libmozjs-${mozjsTarget}.tar.gz"; + hash = mozjsHashes.${mozjsTarget} or (throw "missing mozjs hash for ${mozjsTarget}"); + }; + + commonArgs = { + version = minneVersion; + src = lib.cleanSourceWith { + inherit src; + filter = + path: type: + craneLib.filterCargoSources path type + || lib.any (x: lib.hasPrefix (toString x) (toString path)) [ + (toString src + "/Cargo.lock") + (toString src + "/common/db") + (toString src + "/html-router/templates") + (toString src + "/html-router/assets") + ]; + }; + strictDeps = true; + + buildInputs = [ + pkgs.openssl + pkgs.libglvnd + pkgs.onnxruntime + pkgs.fontconfig + pkgs.libclang.lib + ]; + + nativeBuildInputs = [ + pkgs.pkg-config + pkgs.rustfmt + pkgs.makeWrapper + pkgs.python3 + pkgs.llvmPackages.llvm + pkgs.rustPlatform.bindgenHook + pkgs.stdenv.cc.cc.lib + ]; + + MOZJS_ARCHIVE = "${mozjsArchive}"; + env.LD_LIBRARY_PATH = lib.makeLibraryPath linuxRuntimeLibs; + }; + + cargoArtifacts = craneLib.buildDepsOnly ( + commonArgs + // { + pname = "minne"; + cargoExtraArgs = "--workspace"; + doCheck = false; + } + ); + + minne-pkg = + if pkgs.onnxruntime.version == ortVersion then + craneLib.buildPackage ( + commonArgs + // { + pname = "minne"; + version = minneVersion; + inherit cargoArtifacts; + doCheck = false; + doInstallCargoArtifacts = true; + + postInstall = + lib.optionalString pkgs.stdenv.isLinux (wrapLinuxBinary libExt) + + lib.optionalString pkgs.stdenv.isDarwin '' + for b in main worker server; do + if [ -x "$out/bin/$b" ]; then + wrapProgram $out/bin/$b \ + --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} + fi + done + ''; + } + ) + else + throw "pkgs.onnxruntime.version (${pkgs.onnxruntime.version}) must match ortVersion (${ortVersion})"; + + targetTriple = pkgs.stdenv.hostPlatform.config; + + releaseCommonArgs = { + inherit minneVersion targetTriple; + bzip2 = pkgs.bzip2.out; + brotli = pkgs.brotli.lib; + srcRoot = src; + }; + + minne-release = + if pkgs.stdenv.isLinux then + pkgs.callPackage ./minne-release.nix ( + releaseCommonArgs + // { + platform = "linux"; + inherit minne-pkg; + } + ) + else if pkgs.stdenv.isDarwin then + pkgs.callPackage ./minne-release.nix ( + releaseCommonArgs + // { + platform = "darwin"; + inherit minne-pkg; + } + ) + else + null; + + windowsTarget = rustToolchain.windowsTarget; + + mozjsArchiveWindows = pkgs.fetchurl { + url = "https://github.com/servo/mozjs/releases/download/${mozjsRelease}/libmozjs-${windowsTarget}.tar.gz"; + hash = "sha256-nEX55a4vZJGxlDMCea9TEee60HiNe/yQzXtUqMlaM3c="; + }; + + ortArchiveWindows = pkgs.fetchurl { + url = "https://github.com/microsoft/onnxruntime/releases/download/v${ortVersion}/onnxruntime-win-x64-${ortVersion}.zip"; + hash = "sha256-CzjfmvIYNOQec9YC2Q21ywbb0cphiUi48dZtYHrJ880="; + }; + + windowsCross = pkgs.callPackage ./windows-cross.nix { }; + + inherit (windowsCross) clangClWrapper xwinCargoCache; + + msvcShim = pkgs.symlinkJoin { + name = "minne-msvc-shim"; + paths = [ + (pkgs.writeShellScriptBin "cl.exe" '' + exec ${clangClWrapper} "$@" + '') + (pkgs.writeShellScriptBin "ml64.exe" '' + exec ${pkgs.llvmPackages.llvm}/bin/llvm-ml64 "$@" + '') + ]; + }; + + xwinSetup = pkgs.writeShellScript "minne-xwin-setup" '' + set -eo pipefail + + cache=${xwinCargoCache} + crt="$cache/xwin/crt" + sdk="$cache/xwin/sdk" + + export PATH="${msvcShim}/bin:${pkgs.llvmPackages.clang-unwrapped}/bin:${pkgs.llvmPackages.lld}/bin:${pkgs.llvmPackages.llvm}/bin:$PATH" + export LD_LIBRARY_PATH="${pkgs.stdenv.cc.cc.lib}/lib''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + + export AR_x86_64_pc_windows_msvc=${pkgs.llvmPackages.llvm}/bin/llvm-lib + export BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_msvc="-I$crt/include -I$sdk/include/ucrt -I$sdk/include/um -I$sdk/include/shared -I$sdk/include/winrt" + export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=${pkgs.llvmPackages.lld}/bin/lld-link + export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS="-C linker-flavor=lld-link -Lnative=$crt/lib/x86_64 -Lnative=$sdk/lib/um/x86_64 -Lnative=$sdk/lib/ucrt/x86_64" + export CC_x86_64_pc_windows_msvc=cl.exe + export CXX_x86_64_pc_windows_msvc=cl.exe + export REAL_CLANG_CL=${pkgs.llvmPackages.clang-unwrapped}/bin/clang-cl + export REAL_LLD_LINK=${pkgs.llvmPackages.lld}/bin/lld-link + + _imsvc="--target=x86_64-pc-windows-msvc -Wno-unused-command-line-argument -fuse-ld=lld-link /imsvc $crt/include /imsvc $sdk/include/ucrt /imsvc $sdk/include/um /imsvc $sdk/include/shared /imsvc $sdk/include/winrt" + export CFLAGS_x86_64_pc_windows_msvc="$_imsvc" + export CXXFLAGS_x86_64_pc_windows_msvc="$_imsvc /EHsc" + export CL_FLAGS="--target=x86_64-pc-windows-msvc -Wno-unused-command-line-argument -fuse-ld=lld-link /imsvc $crt/include /imsvc $sdk/include/ucrt /imsvc $sdk/include/um /imsvc $sdk/include/shared /imsvc $sdk/include/winrt" + + export CMAKE_GENERATOR=Ninja + export CMAKE_SYSTEM_NAME=Windows + export CMAKE_TOOLCHAIN_FILE_x86_64_pc_windows_msvc="$cache/cmake/clang-cl/x86_64-pc-windows-msvc-toolchain.cmake" + + export LIB="$crt/lib/x86_64;$sdk/lib/um/x86_64;$sdk/lib/ucrt/x86_64" + export RCFLAGS="-I$crt/include -I$sdk/include/ucrt -I$sdk/include/um -I$sdk/include/shared -I$sdk/include/winrt" + export TARGET_AR=${pkgs.llvmPackages.llvm}/bin/llvm-lib + export TARGET_CC=${pkgs.llvmPackages.clang-unwrapped}/bin/clang-cl + export TARGET_CXX=${pkgs.llvmPackages.clang-unwrapped}/bin/clang-cl + export WINEDEBUG=-all + ''; + + windowsCommonArgs = commonArgs // { + MOZJS_ARCHIVE = "${mozjsArchiveWindows}"; + CARGO_BUILD_TARGET = windowsTarget; + doIncludeCrossToolchainEnv = false; + env.CARGO_PROFILE = "dist"; + buildInputs = [ + pkgs.openssl + pkgs.fontconfig + pkgs.libclang.lib + ]; + nativeBuildInputs = commonArgs.nativeBuildInputs ++ [ + pkgs.llvmPackages.llvm + pkgs.llvmPackages.clang-unwrapped + pkgs.llvmPackages.lld + pkgs.stdenv.cc.cc.lib + ]; + }; + + windowsCargoArtifacts = + if system == "x86_64-linux" then + craneLib.buildDepsOnly ( + windowsCommonArgs + // { + pname = "minne"; + cargoExtraArgs = "--workspace"; + doCheck = false; + preBuild = "source ${xwinSetup}"; + } + ) + else + null; + + minne-pkg-windows = + if system == "x86_64-linux" then + craneLib.buildPackage ( + windowsCommonArgs + // { + pname = "minne-windows"; + version = minneVersion; + cargoArtifacts = windowsCargoArtifacts; + cargoExtraArgs = "--target ${windowsTarget} -p main --bin main --bin server --bin worker"; + doCheck = false; + doInstallCargoArtifacts = false; + preBuild = "source ${xwinSetup}"; + installPhaseCommand = '' + mkdir -p "$out/bin" + for b in main server worker; do + install -m 755 "target/${windowsTarget}/dist/$b.exe" "$out/bin/$b.exe" + done + ''; + } + ) + else + null; + + minne-release-windows = + if system == "x86_64-linux" then + pkgs.callPackage ./minne-release.nix ( + releaseCommonArgs + // { + platform = "windows"; + inherit minne-pkg-windows ortArchiveWindows; + targetTriple = windowsTarget; + } + ) + else + null; + + dockerImage = pkgs.dockerTools.buildLayeredImage { + name = "minne"; + tag = minneVersion; + created = "now"; + + contents = [ + minne-pkg + pkgs.cacert + pkgs.bashInteractive + pkgs.libglvnd + pkgs.fontconfig.lib + pkgs.freetype + pkgs.stdenv.cc.cc.lib + ]; + + maxLayers = 25; + + config = { + Cmd = [ "${minne-pkg}/bin/main" ]; + Env = [ + "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-certificates.crt" + "ORT_DYLIB_PATH=${pkgs.onnxruntime}/lib/libonnxruntime.${libExt}" + ]; + ExposedPorts = { + "3000/tcp" = { }; + }; + User = "appuser"; + }; + }; + + vmSmokeTest = + if system == "x86_64-linux" then + pkgs.callPackage ./vm-smoke-test.nix { + inherit minne-pkg; + surrealdb = surrealdbPkg; + } + else + null; +in +{ + inherit + src + ortVersion + lib + libExt + craneLib + rustToolchain + surrealdbPkg + linuxRuntimeLibs + devGraphicsLibs + commonArgs + minneVersion + minne-pkg + minne-pkg-windows + minne-release + minne-release-windows + dockerImage + vmSmokeTest + xwinCargoCache + ; +} diff --git a/nix/minne-release.nix b/nix/minne-release.nix index 1cdc542..7c794c2 100644 --- a/nix/minne-release.nix +++ b/nix/minne-release.nix @@ -2,7 +2,14 @@ { lib, stdenv, - platform ? (if stdenv.isLinux then "linux" else if stdenv.isDarwin then "darwin" else "windows"), + platform ? ( + if stdenv.isLinux then + "linux" + else if stdenv.isDarwin then + "darwin" + else + "windows" + ), patchelf ? null, xz, unzip ? null, @@ -28,7 +35,11 @@ }: let archiveName = "main-${targetTriple}"; - binaries = ["main" "server" "worker"]; + binaries = [ + "main" + "server" + "worker" + ]; copyBinary = '' if [ -f "${minne-pkg}/bin/.$b-wrapped" ]; then @@ -48,10 +59,7 @@ let mkLinux = let isAarch64 = lib.hasSuffix "aarch64-unknown-linux-gnu" targetTriple; - dynamicLinker = - if isAarch64 - then "ld-linux-aarch64.so.1" - else "ld-linux-x86-64.so.2"; + dynamicLinker = if isAarch64 then "ld-linux-aarch64.so.1" else "ld-linux-x86-64.so.2"; copySharedLibs = pkg: '' if [ -d "${pkg}/lib" ]; then @@ -63,7 +71,10 @@ let pname = "main"; version = minneVersion; - nativeBuildInputs = [patchelf xz]; + nativeBuildInputs = [ + patchelf + xz + ]; dontUnpack = true; dontStrip = true; @@ -138,7 +149,7 @@ let pname = "main"; version = minneVersion; - nativeBuildInputs = [xz]; + nativeBuildInputs = [ xz ]; dontUnpack = true; dontStrip = true; @@ -202,7 +213,10 @@ let pname = "main"; version = minneVersion; - nativeBuildInputs = [unzip zip]; + nativeBuildInputs = [ + unzip + zip + ]; dontUnpack = true; dontStrip = true; @@ -243,7 +257,11 @@ let }; }; in -if platform == "linux" then mkLinux -else if platform == "darwin" then mkDarwin -else if platform == "windows" then mkWindows -else throw "minne-release: unknown platform ${platform}" +if platform == "linux" then + mkLinux +else if platform == "darwin" then + mkDarwin +else if platform == "windows" then + mkWindows +else + throw "minne-release: unknown platform ${platform}" diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 0000000..4bbd189 --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,57 @@ +# Crane packages, release archives, and flake apps. +{ ... }: +{ + perSystem = + { + pkgs, + lib, + minneCtx, + ... + }: + let + inherit (minneCtx) + minne-pkg + minne-pkg-windows + minne-release + minne-release-windows + dockerImage + xwinCargoCache + ; + in + { + packages = { + inherit minne-pkg dockerImage; + default = minne-pkg; + } + // lib.optionalAttrs (minne-release != null) { + minne-release = minne-release; + } + // lib.optionalAttrs (minne-release-windows != null) { + inherit xwinCargoCache; + minne-release-windows = minne-release-windows; + }; + + apps = { + main = { + type = "app"; + program = "${minne-pkg}/bin/main"; + meta.description = "Minne main server — API, web UI, and background worker"; + }; + worker = { + type = "app"; + program = "${minne-pkg}/bin/worker"; + meta.description = "Minne standalone background worker (ingestion, indexing, maintenance)"; + }; + server = { + type = "app"; + program = "${minne-pkg}/bin/server"; + meta.description = "Minne API-only server (no background worker)"; + }; + default = { + type = "app"; + program = "${minne-pkg}/bin/main"; + meta.description = "Minne main server — API, web UI, and background worker"; + }; + }; + }; +} diff --git a/nix/rust-toolchain.nix b/nix/rust-toolchain.nix new file mode 100644 index 0000000..5831f57 --- /dev/null +++ b/nix/rust-toolchain.nix @@ -0,0 +1,23 @@ +# Shared Rust toolchain for dev shell, crane builds, and cross-compilation. +{ + fenix, + system, + toolchainFile, + # Manifest hash for https://static.rust-lang.org/dist/channel-rust-.toml + manifestSha256 ? "sha256-SDu4snEWjuZU475PERvu+iO50Mi39KVjqCeJeNvpguU=", +}: +let + fenixPkgs = fenix.packages.${system}; + toolchain = fenixPkgs.fromToolchainFile { + file = toolchainFile; + sha256 = manifestSha256; + }; + parsed = builtins.fromTOML (builtins.readFile toolchainFile); + rustVersion = parsed.toolchain.channel; + windowsTarget = "x86_64-pc-windows-msvc"; +in +{ + inherit rustVersion toolchain windowsTarget; + + mkCraneLib = pkgs: craneLib: craneLib.overrideToolchain (_: toolchain); +} diff --git a/nix/surrealdb-binary.nix b/nix/surrealdb-binary.nix new file mode 100644 index 0000000..6467348 --- /dev/null +++ b/nix/surrealdb-binary.nix @@ -0,0 +1,41 @@ +# Pinned SurrealDB release binary (avoids building nixpkgs surrealdb from source). +{ + lib, + stdenv, + fetchurl, + autoPatchelfHook, + openssl, + rocksdb, +}: +stdenv.mkDerivation { + pname = "surrealdb"; + version = "2.6.5"; + + src = fetchurl { + url = "https://github.com/surrealdb/surrealdb/releases/download/v2.6.5/surreal-v2.6.5.linux-amd64.tgz"; + hash = "sha256-kp1z9GxPtZ8jeBDm/m2lTBdWBk8+2NfR9qlw6P3zj7A="; + }; + + nativeBuildInputs = [ autoPatchelfHook ]; + buildInputs = [ + openssl + rocksdb + stdenv.cc.cc.lib + ]; + + sourceRoot = "."; + + installPhase = '' + runHook preInstall + install -Dm755 surreal $out/bin/surreal + runHook postInstall + ''; + + meta = { + description = "SurrealDB prebuilt binary pinned for local dev and VM smoke tests"; + homepage = "https://surrealdb.com/"; + license = lib.licenses.bsl11; + platforms = [ "x86_64-linux" ]; + sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ]; + }; +} diff --git a/nix/vm-smoke-test.nix b/nix/vm-smoke-test.nix new file mode 100644 index 0000000..1f7e8bb --- /dev/null +++ b/nix/vm-smoke-test.nix @@ -0,0 +1,72 @@ +# NixOS VM smoke test for the packaged Minne server. +{ + lib, + pkgs, + minne-pkg, + surrealdb, +}: +pkgs.testers.nixosTest { + name = "minne-smoke"; + + nodes.machine = { + virtualisation.memorySize = 4096; + + systemd.services.surrealdb = { + description = "SurrealDB for Minne smoke test"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + preStart = "mkdir -p /var/lib/surrealdb"; + serviceConfig = { + Type = "simple"; + ExecStart = "${surrealdb}/bin/surreal start --bind 127.0.0.1:8000 --user root_user --pass root_password rocksdb:/var/lib/surrealdb/db"; + }; + }; + + systemd.services.minne = { + description = "Minne server smoke test"; + wantedBy = [ "multi-user.target" ]; + after = [ + "surrealdb.service" + "network.target" + ]; + requires = [ "surrealdb.service" ]; + preStart = '' + for i in $(seq 1 60); do + if ${pkgs.netcat}/bin/nc -z 127.0.0.1 8000; then + exit 0 + fi + sleep 1 + done + echo "surrealdb did not become ready on port 8000" >&2 + exit 1 + ''; + serviceConfig = { + Type = "simple"; + ExecStart = "${minne-pkg}/bin/main"; + Environment = [ + "SURREALDB_ADDRESS=ws://127.0.0.1:8000" + "SURREALDB_USERNAME=root_user" + "SURREALDB_PASSWORD=root_password" + "SURREALDB_NAMESPACE=test" + "SURREALDB_DATABASE=test" + "OPENAI_API_KEY=test-key" + "HTTP_PORT=3000" + "STORAGE=local" + "DATA_DIR=/var/lib/minne" + "EMBEDDING_BACKEND=hashed" + "RUST_LOG=info" + "INDEX_REBUILD_INTERVAL_SECS=0" + ]; + StateDirectory = "minne"; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("surrealdb.service") + machine.wait_for_unit("minne.service") + machine.wait_for_open_port(3000) + machine.succeed("curl -sf http://127.0.0.1:3000/api/v1/live") + machine.succeed("curl -sf http://127.0.0.1:3000/api/v1/ready") + ''; +} diff --git a/nix/windows-cross.nix b/nix/windows-cross.nix index 937079e..51ff6ad 100644 --- a/nix/windows-cross.nix +++ b/nix/windows-cross.nix @@ -6,7 +6,8 @@ cacert, writeText, writeShellScript, -}: let +}: +let cmakeOverride = writeText "override.cmake" '' # macOS paths usually start with /Users/*. Unfortunately, clang-cl interprets # paths starting with /U as macro undefines, so we need to put a -- before the @@ -109,14 +110,18 @@ exec "$real_clang_cl" "$@" ''; -in { +in +{ inherit clangClWrapper; xwinCargoCache = stdenv.mkDerivation { pname = "cargo-xwin-cache"; version = "17.2"; - nativeBuildInputs = [xwin cacert]; + nativeBuildInputs = [ + xwin + cacert + ]; preferLocalBuild = true; allowSubstitutes = false; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 760f4ff..366914a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,4 @@ [toolchain] channel = "1.91.1" -components = ["rustfmt", "clippy"] -profile = "default" +components = ["rustc", "cargo", "clippy", "rustfmt", "rust-analyzer"] targets = ["x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]