/ flake.nix
flake.nix
1 { 2 inputs = { 3 nixpkgs = { 4 url = "github:nixos/nixpkgs/nixos-24.11"; 5 }; 6 nixpkgs-old = { 7 url = "github:nixos/nixpkgs/nixos-23.05"; 8 }; 9 flake-utils.url = "github:numtide/flake-utils"; 10 fenix = { 11 url = "github:nix-community/fenix"; 12 inputs.nixpkgs.follows = "nixpkgs"; 13 }; 14 flakebox = { 15 url = "github:dpc/flakebox?rev=28e43cc8d5c67b0cc13cca1499fbe3fe6a5196f5"; 16 inputs.nixpkgs.follows = "nixpkgs"; 17 inputs.fenix.follows = "fenix"; 18 }; 19 cargo-deluxe = { 20 url = "github:rustshop/cargo-deluxe?rev=4acc6488d02f032434a5a1341f21f20d328bba40"; 21 inputs.nixpkgs.follows = "nixpkgs"; 22 }; 23 bundlers = { 24 url = "github:NixOS/bundlers?rev=ea1e72ad1dbb0864fd55b3ba52ed166cd190afa2"; 25 inputs.nixpkgs.follows = "nixpkgs"; 26 }; 27 advisory-db = { 28 url = "github:rustsec/advisory-db"; 29 flake = false; 30 }; 31 }; 32 33 outputs = 34 { 35 self, 36 nixpkgs, 37 nixpkgs-old, 38 flake-utils, 39 flakebox, 40 cargo-deluxe, 41 advisory-db, 42 bundlers, 43 ... 44 }: 45 let 46 # overlay combining all overlays we use 47 overlayAll = nixpkgs.lib.composeManyExtensions [ 48 (import ./nix/overlays/wasm-bindgen.nix) 49 (import ./nix/overlays/cargo-nextest.nix) 50 (import ./nix/overlays/esplora-electrs.nix) 51 (import ./nix/overlays/clightning.nix) 52 (import ./nix/overlays/darwin-compile-fixes.nix) 53 (import ./nix/overlays/cargo-honggfuzz.nix) 54 (import ./nix/overlays/trustedcoin.nix) 55 ]; 56 in 57 { 58 overlays = { 59 all = overlayAll; 60 wasm-bindgen = import ./nix/overlays/wasm-bindgen.nix; 61 darwin-compile-fixes = import ./nix/overlays/darwin-compile-fixes.nix; 62 cargo-honggfuzz = import ./nix/overlays/cargo-honggfuzz.nix; 63 }; 64 65 bundlers = bundlers.bundlers; 66 67 nixosModules = { 68 fedimintd = import ./nix/modules/fedimintd.nix; 69 }; 70 } 71 // flake-utils.lib.eachDefaultSystem ( 72 system: 73 let 74 pkgs-old = import nixpkgs-old { 75 inherit system; 76 overlays = [ 77 overlayAll 78 ]; 79 }; 80 pkgs = import nixpkgs { 81 inherit system; 82 overlays = [ 83 overlayAll 84 85 (final: prev: { 86 cargo-deluxe = cargo-deluxe.packages.${system}.default; 87 # use old CLN, until https://github.com/NixOS/nixpkgs/pull/358063 possibly 88 # resolves some python on darwin breakage 89 # https://github.com/NixOS/nixpkgs/blob/470e6e641f412894086b6df2f204847bd3905f17/pkgs/development/python-modules/jaraco-path/default.nix#L34 90 clightning = pkgs-old.clightning; 91 }) 92 ]; 93 }; 94 95 lib = pkgs.lib; 96 97 stdenv = pkgs.stdenv; 98 99 flakeboxLib = flakebox.lib.${system} { 100 # customizations will go here in the future 101 config = { 102 direnv.enable = false; 103 104 toolchain.components = [ 105 "rustc" 106 "cargo" 107 "clippy" 108 "rust-analyzer" 109 "rust-src" 110 "llvm-tools" 111 ]; 112 113 just.rules.clippy = { 114 content = lib.mkForce '' 115 # run `cargo clippy` on everything 116 clippy *ARGS="--locked --offline --workspace --all-targets": 117 cargo clippy {{ARGS}} 118 119 # run `cargo clippy --fix` on everything 120 clippy-fix *ARGS="--locked --offline --workspace --all-targets": 121 cargo clippy {{ARGS}} --fix 122 ''; 123 }; 124 125 motd = { 126 enable = true; 127 command = '' 128 >&2 echo "🚧 In an enfort to improve documentation, we now require all structs and" 129 >&2 echo "🚧 and public methods to be documented with a docstring." 130 >&2 echo "🚧 See https://github.com/fedimint/fedimint/issues/3807" 131 ''; 132 }; 133 # we have our own weird CI workflows 134 github.ci.enable = false; 135 just.importPaths = [ "justfile.fedimint.just" ]; 136 # we have a custom final check 137 just.rules.final-check.enable = false; 138 git.pre-commit.trailing_newline = false; 139 git.pre-commit.hooks = { 140 check_forbidden_dependencies = builtins.readFile ./nix/check-forbidden-deps.sh; 141 }; 142 git.pre-commit.hooks = { 143 cargo-sort = builtins.readFile ./nix/check-cargo-sort.sh; 144 }; 145 }; 146 }; 147 148 toolchainArgs = lib.optionalAttrs stdenv.isLinux { 149 # TODO: we seem to be hitting some miscompilation(?) with 150 # the new (as of nixos-24.11 default: clang 18), which causes 151 # fedimint-cli segfault randomly, but only in Nix sandbox. 152 # Supper weird. 153 stdenv = pkgs.clang14Stdenv; 154 clang = pkgs.llvmPackages_14.clang; 155 libclang = pkgs.llvmPackages_14.libclang.lib; 156 clang-unwrapped = pkgs.llvmPackages_14.clang-unwrapped; 157 }; 158 159 stdTargets = flakeboxLib.mkStdTargets { }; 160 stdToolchains = flakeboxLib.mkStdToolchains toolchainArgs; 161 162 # toolchains for the native build (default shell) 163 toolchainNative = flakeboxLib.mkFenixToolchain ( 164 toolchainArgs 165 // { 166 targets = ( 167 pkgs.lib.getAttrs [ 168 "default" 169 "wasm32-unknown" 170 ] stdTargets 171 ); 172 } 173 ); 174 175 # toolchains for the native + wasm build 176 toolchainWasm = flakeboxLib.mkFenixToolchain ( 177 toolchainArgs 178 // { 179 defaultTarget = "wasm32-unknown-unknown"; 180 targets = ( 181 pkgs.lib.getAttrs [ 182 "default" 183 "wasm32-unknown" 184 ] stdTargets 185 ); 186 187 args = { 188 nativeBuildInputs = [ 189 pkgs.wasm-bindgen-cli 190 pkgs.geckodriver 191 pkgs.wasm-pack 192 ] ++ lib.optionals (stdenv.isLinux) [ pkgs.firefox ]; 193 }; 194 } 195 ); 196 197 # toolchains for the native + wasm build 198 toolchainAll = flakeboxLib.mkFenixToolchain ( 199 toolchainArgs 200 // { 201 targets = ( 202 pkgs.lib.getAttrs ( 203 [ 204 "default" 205 "aarch64-android" 206 "x86_64-android" 207 "arm-android" 208 "armv7-android" 209 "wasm32-unknown" 210 ] 211 ++ lib.optionals pkgs.stdenv.isDarwin [ 212 "aarch64-ios" 213 "aarch64-ios-sim" 214 "x86_64-ios" 215 ] 216 ) stdTargets 217 ); 218 } 219 ); 220 # Replace placeholder git hash in a binary 221 # 222 # To avoid impurity, we use a git hash placeholder when building binaries 223 # and then replace them with the real git hash in the binaries themselves. 224 replaceGitHash = 225 let 226 # the hash we will set if the tree is dirty; 227 dirtyHashPrefix = builtins.substring 0 16 self.dirtyRev; 228 dirtyHashSuffix = builtins.substring (40 - 16) 16 self.dirtyRev; 229 # the string needs to be 40 characters, like the original, 230 # so to denote `-dirty` we replace the middle with zeros 231 dirtyHash = "${dirtyHashPrefix}00000000${dirtyHashSuffix}"; 232 in 233 { 234 package, 235 name, 236 placeholder, 237 gitHash ? if (self ? rev) then self.rev else dirtyHash, 238 }: 239 stdenv.mkDerivation { 240 inherit system; 241 inherit name; 242 243 # some bundlers want `pname` here, instead of `name` 244 pname = name; 245 246 dontUnpack = true; 247 dontStrip = !pkgs.stdenv.isDarwin; 248 249 installPhase = '' 250 cp -a ${package} $out 251 for path in `find $out -type f -executable`; do 252 # need to use a temporary file not to overwrite source as we are reading it 253 bbe -e 's/${placeholder}/${gitHash}/' $path -o ./tmp || exit 1 254 chmod +w $path 255 # use cat to keep all the original permissions etc as they were 256 cat ./tmp > "$path" 257 chmod -w $path 258 done 259 ''; 260 261 buildInputs = [ pkgs.bbe ]; 262 }; 263 264 craneMultiBuild = import nix/flakebox.nix { 265 inherit 266 pkgs 267 flakeboxLib 268 advisory-db 269 replaceGitHash 270 ; 271 272 # Yes, you're seeing right. We're passing result of this call as an argument 273 # to it. 274 inherit craneMultiBuild; 275 276 toolchains = stdToolchains // { 277 "wasm32-unknown" = toolchainWasm; 278 }; 279 profiles = [ 280 "dev" 281 "ci" 282 "test" 283 "release" 284 ]; 285 }; 286 287 devShells = 288 289 let 290 commonShellArgs = 291 craneMultiBuild.commonEnvsShell 292 // craneMultiBuild.commonArgs 293 // { 294 toolchain = toolchainNative; 295 buildInputs = craneMultiBuild.commonArgs.buildInputs; 296 nativeBuildInputs = 297 craneMultiBuild.commonArgs.nativeBuildInputs 298 ++ [ 299 pkgs.cargo-udeps 300 pkgs.cargo-audit 301 pkgs.cargo-deny 302 pkgs.cargo-sort 303 pkgs.parallel 304 pkgs.nixfmt-rfc-style 305 pkgs.just 306 pkgs.time 307 pkgs.gawk 308 309 (pkgs.writeShellScriptBin "git-recommit" "exec git commit --edit -F <(cat \"$(git rev-parse --git-path COMMIT_EDITMSG)\" | grep -v -E '^#.*') \"$@\"") 310 311 # This is required to prevent a mangled bash shell in nix develop 312 # see: https://discourse.nixos.org/t/interactive-bash-with-nix-develop-flake/15486 313 (pkgs.hiPrio pkgs.bashInteractive) 314 pkgs.tmux 315 pkgs.tmuxinator 316 pkgs.mprocs 317 pkgs.docker-compose 318 pkgs.tokio-console 319 pkgs.git 320 321 # Nix 322 pkgs.nixfmt-rfc-style 323 pkgs.shellcheck 324 pkgs.nil 325 pkgs.convco 326 pkgs.nodePackages.bash-language-server 327 pkgs.sccache 328 ] 329 ++ lib.optionals (!stdenv.isAarch64 && !stdenv.isDarwin) [ pkgs.semgrep ] 330 ++ lib.optionals (!stdenv.isDarwin) [ 331 # broken on MacOS? 332 pkgs.cargo-workspaces 333 334 # marked as broken on MacOS 335 pkgs.cargo-llvm-cov 336 ]; 337 338 shellHook = '' 339 export REPO_ROOT="$(git rev-parse --show-toplevel)" 340 export PATH="$REPO_ROOT/bin:$PATH" 341 342 # workaround https://github.com/rust-lang/cargo/issues/11020 343 cargo_cmd_bins=( $(ls $HOME/.cargo/bin/cargo-{clippy,udeps,llvm-cov} 2>/dev/null) ) 344 if (( ''${#cargo_cmd_bins[@]} != 0 )); then 345 >&2 echo "⚠️ Detected binaries that might conflict with reproducible environment: ''${cargo_cmd_bins[@]}" 1>&2 346 >&2 echo " Considering deleting them. See https://github.com/rust-lang/cargo/issues/11020 for details" 1>&2 347 fi 348 349 # Note: the string escaping necessary here (Nix's multi-line string and shell's) is mind-twisting. 350 if [ -n "$TMUX" ]; then 351 # if [ "$(tmux show-options -A default-command)" == 'default-command* \'\''' ]; then 352 if [ "$(tmux show-options -A default-command)" == 'bla' ]; then 353 echo 354 >&2 echo "⚠️ tmux's 'default-command' not set" 355 >&2 echo " ️ Please add 'set -g default-command \"\''${SHELL}\"' to your '$HOME/.tmux.conf' for tmuxinator test setup to work correctly" 356 fi 357 fi 358 359 export RUSTC_WRAPPER=${pkgs.sccache}/bin/sccache 360 export CARGO_BUILD_TARGET_DIR="''${CARGO_BUILD_TARGET_DIR:-''${REPO_ROOT}/target-nix}" 361 export FM_DISCOVER_API_VERSION_TIMEOUT=10 362 [ -f "$REPO_ROOT/.shrc.local" ] && source "$REPO_ROOT/.shrc.local" 363 364 if [ ''${#TMPDIR} -ge 40 ]; then 365 >&2 echo "⚠️ TMPDIR too long. This might lead to problems running tests and regtest fed. Will try to use /tmp/ instead" 366 # Note: this seems to work fine in `nix develop`, but doesn't work on some `direnv` implementations (doesn't work for dpc at least) 367 export TMPDIR="/tmp" 368 fi 369 370 if [ "$(ulimit -Sn)" -lt "1024" ]; then 371 >&2 echo "⚠️ ulimit too small. Run 'ulimit -Sn 1024' to avoid problems running tests" 372 fi 373 374 if [ -z "$(git config --global merge.ours.driver)" ]; then 375 >&2 echo "⚠️ Recommended to run 'git config --global merge.ours.driver true' to enable better lock file handling. See https://blog.aspect.dev/easier-merges-on-lockfiles for more info" 376 fi 377 ''; 378 }; 379 in 380 { 381 # The default shell - meant to developers working on the project, 382 # so notably not building any project binaries, but including all 383 # the settings and tools necessary to build and work with the codebase. 384 default = flakeboxLib.mkDevShell (commonShellArgs // { }); 385 386 fuzz = flakeboxLib.mkDevShell ( 387 commonShellArgs 388 // { 389 nativeBuildInputs = 390 with pkgs; 391 commonShellArgs.nativeBuildInputs 392 ++ [ 393 cargo-hongfuzz 394 libbfd_2_38 395 libunwind.dev 396 libopcodes_2_38 397 pkgsStatic.libblocksruntime 398 lldb 399 clang 400 ]; 401 402 } 403 ); 404 405 lint = flakeboxLib.mkLintShell { nativeBuildInputs = [ pkgs.cargo-sort ]; }; 406 407 # Like `cross` but only with wasm 408 crossWasm = flakeboxLib.mkDevShell ( 409 commonShellArgs 410 // { 411 toolchain = toolchainWasm; 412 413 nativeBuildInputs = 414 commonShellArgs.nativeBuildInputs or [ ] 415 ++ [ 416 pkgs.wasm-pack 417 pkgs.wasm-bindgen-cli 418 pkgs.geckodriver 419 ] 420 ++ lib.optionals (stdenv.isLinux) [ pkgs.firefox ]; 421 } 422 ); 423 424 replit = pkgs.mkShell { 425 nativeBuildInputs = with pkgs; [ 426 pkg-config 427 openssl 428 ]; 429 }; 430 431 bootstrap = pkgs.mkShell { nativeBuildInputs = with pkgs; [ cachix ]; }; 432 } 433 // 434 lib.attrsets.optionalAttrs 435 (lib.lists.elem system [ 436 "x86_64-linux" 437 "x86_64-darwin" 438 "aarch64-darwin" 439 ]) 440 { 441 # Shell with extra stuff to support cross-compilation with `cargo build --target <target>` 442 # 443 # This will pull extra stuff so to save time and download time to most common developers, 444 # was moved into another shell. 445 cross = flakeboxLib.mkDevShell ( 446 commonShellArgs 447 // craneMultiBuild.commonEnvsCrossShell 448 // { 449 toolchain = toolchainAll; 450 shellHook = '' 451 export REPO_ROOT="$(git rev-parse --show-toplevel)" 452 export PATH="$REPO_ROOT/bin:$PATH" 453 ''; 454 } 455 ); 456 }; 457 in 458 { 459 inherit devShells; 460 461 # Technically nested sets are not allowed in `packages`, so we can 462 # dump the nested things here. They'll work the same way for most 463 # purposes (like `nix build`). 464 legacyPackages = craneMultiBuild; 465 466 packages = { 467 inherit (craneMultiBuild) 468 gatewayd 469 fedimint-dbtool 470 gateway-cli 471 fedimint-cli 472 fedimintd 473 fedimint-load-test-tool 474 ; 475 inherit (craneMultiBuild) 476 client-pkgs 477 gateway-pkgs 478 fedimint-pkgs 479 devimint 480 ; 481 482 wasmBundle = craneMultiBuild.wasm32-unknown.release.wasmBundle; 483 }; 484 485 lib = { 486 inherit replaceGitHash devShells; 487 }; 488 } 489 ); 490 491 nixConfig = { 492 extra-substituters = [ "https://fedimint.cachix.org" ]; 493 extra-trusted-public-keys = [ 494 "fedimint.cachix.org-1:FpJJjy1iPVlvyv4OMiN5y9+/arFLPcnZhZVVCHCDYTs=" 495 ]; 496 }; 497 }