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