/ 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  }