├── .envrc ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── README.md ├── failing ├── .envrc └── flake.nix ├── flake.lock ├── flake.nix ├── garnix.yaml ├── justfile ├── module ├── flake-module.nix └── flake.nix ├── rust-toolchain.toml └── src ├── check ├── caches.rs ├── direnv.rs ├── flake_enabled.rs ├── max_jobs.rs ├── min_nix_version.rs ├── mod.rs ├── rosetta.rs ├── system.rs └── trusted_users.rs ├── lib.rs ├── logging.rs ├── main.rs ├── report.rs └── traits.rs /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: "CI" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | jobs: 8 | checks: 9 | runs-on: ${{ matrix.system }} 10 | permissions: 11 | contents: read 12 | strategy: 13 | matrix: 14 | system: [ x86_64-linux, aarch64-darwin, x86_64-darwin ] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Build 18 | run: nixci --extra-access-tokens ${{ secrets.GITHUB_TOKEN }} build --systems "${{ matrix.system }}" 19 | - name: Run 20 | run: nix --option system ${{ matrix.system }} run 21 | if: "!(matrix.system == 'x86_64-darwin')" # Skip rosetta runs (that will fail anyway) 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .direnv 3 | result 4 | /just-flake.just -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.4.0](https://github.com/juspay/nix-health/compare/0.3.0...0.4.0) (2024-07-10) 4 | 5 | ### Features 6 | 7 | * **lib:** Expose `print_returning_exit_code` (#25) 8 | ([b9c70a9](https://github.com/juspay/nix-health/commit/b9c70a9506823bdcc1d54c14b7c56d299b3a5c6a)), 9 | closes [#25](https://github.com/juspay/nix-health/issues/25) 10 | * build linux static executable 11 | ([78b95e8](https://github.com/juspay/nix-health/commit/78b95e8528282ef3f88b2ed29c0f5fc0cebbaa07)) 12 | * Add flake-module to run nix-health in devShell shellHook 13 | ([2f8d8dc](https://github.com/juspay/nix-health/commit/2f8d8dc30121923192c78a8f5152c5c89fdf1809)) 14 | 15 | ### Fixes 16 | 17 | * build failure on intel mac 18 | ([91e9bcf](https://github.com/juspay/nix-health/commit/91e9bcfd60d672074951d534d7b51f609dda1e94)) 19 | 20 | ## 0.3.0 (2024-07-10) 21 | 22 | ### Fixes 23 | 24 | * **nix-health:** use `direnv status --json` & create `direnv` crate (#123) 25 | ([f7762d7](https://github.com/juspay/nix-health/commit/f7762d7fec28f3091289fb03b3ad171cfb923f87)), 26 | closes [#123](https://github.com/juspay/nix-health/issues/123) 27 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "android-tzdata" 31 | version = "0.1.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 34 | 35 | [[package]] 36 | name = "android_system_properties" 37 | version = "0.1.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 40 | dependencies = [ 41 | "libc", 42 | ] 43 | 44 | [[package]] 45 | name = "anstream" 46 | version = "0.6.13" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" 49 | dependencies = [ 50 | "anstyle", 51 | "anstyle-parse", 52 | "anstyle-query", 53 | "anstyle-wincon", 54 | "colorchoice", 55 | "utf8parse", 56 | ] 57 | 58 | [[package]] 59 | name = "anstyle" 60 | version = "1.0.6" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" 63 | 64 | [[package]] 65 | name = "anstyle-parse" 66 | version = "0.2.3" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 69 | dependencies = [ 70 | "utf8parse", 71 | ] 72 | 73 | [[package]] 74 | name = "anstyle-query" 75 | version = "1.0.2" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 78 | dependencies = [ 79 | "windows-sys 0.52.0", 80 | ] 81 | 82 | [[package]] 83 | name = "anstyle-wincon" 84 | version = "3.0.2" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 87 | dependencies = [ 88 | "anstyle", 89 | "windows-sys 0.52.0", 90 | ] 91 | 92 | [[package]] 93 | name = "anyhow" 94 | version = "1.0.81" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" 97 | 98 | [[package]] 99 | name = "autocfg" 100 | version = "1.2.0" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" 103 | 104 | [[package]] 105 | name = "backtrace" 106 | version = "0.3.71" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 109 | dependencies = [ 110 | "addr2line", 111 | "cc", 112 | "cfg-if", 113 | "libc", 114 | "miniz_oxide", 115 | "object", 116 | "rustc-demangle", 117 | ] 118 | 119 | [[package]] 120 | name = "base64" 121 | version = "0.21.7" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 124 | 125 | [[package]] 126 | name = "bitflags" 127 | version = "1.3.2" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 130 | 131 | [[package]] 132 | name = "bitflags" 133 | version = "2.5.0" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 136 | 137 | [[package]] 138 | name = "bumpalo" 139 | version = "3.15.4" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" 142 | 143 | [[package]] 144 | name = "bytes" 145 | version = "1.6.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 148 | 149 | [[package]] 150 | name = "bytesize" 151 | version = "1.3.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" 154 | dependencies = [ 155 | "serde", 156 | ] 157 | 158 | [[package]] 159 | name = "cc" 160 | version = "1.0.90" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" 163 | 164 | [[package]] 165 | name = "cfg-if" 166 | version = "1.0.0" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 169 | 170 | [[package]] 171 | name = "chrono" 172 | version = "0.4.37" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" 175 | dependencies = [ 176 | "android-tzdata", 177 | "iana-time-zone", 178 | "num-traits", 179 | "serde", 180 | "windows-targets 0.52.4", 181 | ] 182 | 183 | [[package]] 184 | name = "clap" 185 | version = "4.5.4" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" 188 | dependencies = [ 189 | "clap_builder", 190 | "clap_derive", 191 | ] 192 | 193 | [[package]] 194 | name = "clap_builder" 195 | version = "4.5.2" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" 198 | dependencies = [ 199 | "anstream", 200 | "anstyle", 201 | "clap_lex", 202 | "strsim 0.11.1", 203 | ] 204 | 205 | [[package]] 206 | name = "clap_derive" 207 | version = "4.5.4" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" 210 | dependencies = [ 211 | "heck", 212 | "proc-macro2", 213 | "quote", 214 | "syn", 215 | ] 216 | 217 | [[package]] 218 | name = "clap_lex" 219 | version = "0.7.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 222 | 223 | [[package]] 224 | name = "colorchoice" 225 | version = "1.0.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 228 | 229 | [[package]] 230 | name = "colored" 231 | version = "2.1.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 234 | dependencies = [ 235 | "lazy_static", 236 | "windows-sys 0.48.0", 237 | ] 238 | 239 | [[package]] 240 | name = "core-foundation-sys" 241 | version = "0.8.6" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 244 | 245 | [[package]] 246 | name = "crossbeam-deque" 247 | version = "0.8.5" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 250 | dependencies = [ 251 | "crossbeam-epoch", 252 | "crossbeam-utils", 253 | ] 254 | 255 | [[package]] 256 | name = "crossbeam-epoch" 257 | version = "0.9.18" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 260 | dependencies = [ 261 | "crossbeam-utils", 262 | ] 263 | 264 | [[package]] 265 | name = "crossbeam-utils" 266 | version = "0.8.19" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" 269 | 270 | [[package]] 271 | name = "darling" 272 | version = "0.20.8" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" 275 | dependencies = [ 276 | "darling_core", 277 | "darling_macro", 278 | ] 279 | 280 | [[package]] 281 | name = "darling_core" 282 | version = "0.20.8" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" 285 | dependencies = [ 286 | "fnv", 287 | "ident_case", 288 | "proc-macro2", 289 | "quote", 290 | "strsim 0.10.0", 291 | "syn", 292 | ] 293 | 294 | [[package]] 295 | name = "darling_macro" 296 | version = "0.20.8" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" 299 | dependencies = [ 300 | "darling_core", 301 | "quote", 302 | "syn", 303 | ] 304 | 305 | [[package]] 306 | name = "deranged" 307 | version = "0.3.11" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 310 | dependencies = [ 311 | "powerfmt", 312 | "serde", 313 | ] 314 | 315 | [[package]] 316 | name = "direnv" 317 | version = "0.1.1" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "691d98072428846b881361c9afae2867f082ae7122b068daf3e5c9eebdc61802" 320 | dependencies = [ 321 | "cfg-if", 322 | "semver", 323 | "serde", 324 | "serde_json", 325 | "serde_repr", 326 | "thiserror", 327 | "which", 328 | ] 329 | 330 | [[package]] 331 | name = "either" 332 | version = "1.10.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" 335 | 336 | [[package]] 337 | name = "equivalent" 338 | version = "1.0.1" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 341 | 342 | [[package]] 343 | name = "errno" 344 | version = "0.3.8" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 347 | dependencies = [ 348 | "libc", 349 | "windows-sys 0.52.0", 350 | ] 351 | 352 | [[package]] 353 | name = "fnv" 354 | version = "1.0.7" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 357 | 358 | [[package]] 359 | name = "form_urlencoded" 360 | version = "1.2.1" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 363 | dependencies = [ 364 | "percent-encoding", 365 | ] 366 | 367 | [[package]] 368 | name = "getrandom" 369 | version = "0.2.12" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 372 | dependencies = [ 373 | "cfg-if", 374 | "libc", 375 | "wasi", 376 | ] 377 | 378 | [[package]] 379 | name = "gimli" 380 | version = "0.28.1" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 383 | 384 | [[package]] 385 | name = "hashbrown" 386 | version = "0.12.3" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 389 | 390 | [[package]] 391 | name = "hashbrown" 392 | version = "0.14.3" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 395 | 396 | [[package]] 397 | name = "heck" 398 | version = "0.5.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 401 | 402 | [[package]] 403 | name = "hermit-abi" 404 | version = "0.3.9" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 407 | 408 | [[package]] 409 | name = "hex" 410 | version = "0.4.3" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 413 | 414 | [[package]] 415 | name = "home" 416 | version = "0.5.9" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" 419 | dependencies = [ 420 | "windows-sys 0.52.0", 421 | ] 422 | 423 | [[package]] 424 | name = "human-panic" 425 | version = "1.2.3" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "c4f016c89920bbb30951a8405ecacbb4540db5524313b9445736e7e1855cf370" 428 | dependencies = [ 429 | "anstream", 430 | "anstyle", 431 | "backtrace", 432 | "os_info", 433 | "serde", 434 | "serde_derive", 435 | "toml", 436 | "uuid", 437 | ] 438 | 439 | [[package]] 440 | name = "iana-time-zone" 441 | version = "0.1.60" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 444 | dependencies = [ 445 | "android_system_properties", 446 | "core-foundation-sys", 447 | "iana-time-zone-haiku", 448 | "js-sys", 449 | "wasm-bindgen", 450 | "windows-core", 451 | ] 452 | 453 | [[package]] 454 | name = "iana-time-zone-haiku" 455 | version = "0.1.2" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 458 | dependencies = [ 459 | "cc", 460 | ] 461 | 462 | [[package]] 463 | name = "ident_case" 464 | version = "1.0.1" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 467 | 468 | [[package]] 469 | name = "idna" 470 | version = "0.5.0" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 473 | dependencies = [ 474 | "unicode-bidi", 475 | "unicode-normalization", 476 | ] 477 | 478 | [[package]] 479 | name = "indexmap" 480 | version = "1.9.3" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 483 | dependencies = [ 484 | "autocfg", 485 | "hashbrown 0.12.3", 486 | "serde", 487 | ] 488 | 489 | [[package]] 490 | name = "indexmap" 491 | version = "2.2.6" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 494 | dependencies = [ 495 | "equivalent", 496 | "hashbrown 0.14.3", 497 | "serde", 498 | ] 499 | 500 | [[package]] 501 | name = "is_proc_translated" 502 | version = "0.1.1" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "c91b17261e5e2c0d44e0fd72790fbdbf7dc2cd5af12b120fa24ea589f5e57f4a" 505 | dependencies = [ 506 | "libc", 507 | ] 508 | 509 | [[package]] 510 | name = "itoa" 511 | version = "1.0.11" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 514 | 515 | [[package]] 516 | name = "js-sys" 517 | version = "0.3.69" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 520 | dependencies = [ 521 | "wasm-bindgen", 522 | ] 523 | 524 | [[package]] 525 | name = "lazy_static" 526 | version = "1.4.0" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 529 | 530 | [[package]] 531 | name = "libc" 532 | version = "0.2.153" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 535 | 536 | [[package]] 537 | name = "linux-raw-sys" 538 | version = "0.4.13" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 541 | 542 | [[package]] 543 | name = "lock_api" 544 | version = "0.4.11" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 547 | dependencies = [ 548 | "autocfg", 549 | "scopeguard", 550 | ] 551 | 552 | [[package]] 553 | name = "log" 554 | version = "0.4.21" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 557 | 558 | [[package]] 559 | name = "matchers" 560 | version = "0.1.0" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 563 | dependencies = [ 564 | "regex-automata 0.1.10", 565 | ] 566 | 567 | [[package]] 568 | name = "memchr" 569 | version = "2.7.2" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 572 | 573 | [[package]] 574 | name = "miniz_oxide" 575 | version = "0.7.2" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 578 | dependencies = [ 579 | "adler", 580 | ] 581 | 582 | [[package]] 583 | name = "mio" 584 | version = "0.8.11" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 587 | dependencies = [ 588 | "libc", 589 | "wasi", 590 | "windows-sys 0.48.0", 591 | ] 592 | 593 | [[package]] 594 | name = "nix_health" 595 | version = "0.4.1" 596 | dependencies = [ 597 | "anyhow", 598 | "bytesize", 599 | "cfg-if", 600 | "clap", 601 | "colored", 602 | "direnv", 603 | "human-panic", 604 | "nix_rs", 605 | "regex", 606 | "semver", 607 | "serde", 608 | "serde_json", 609 | "serde_repr", 610 | "serde_with", 611 | "thiserror", 612 | "tokio", 613 | "tracing", 614 | "tracing-subscriber", 615 | "url", 616 | "which", 617 | ] 618 | 619 | [[package]] 620 | name = "nix_rs" 621 | version = "0.5.0" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "da25f502c0280182deca738d14d878c77e5886d5d9af60e8d51c150afbd9b7e5" 624 | dependencies = [ 625 | "bytesize", 626 | "cfg-if", 627 | "colored", 628 | "is_proc_translated", 629 | "os_info", 630 | "regex", 631 | "serde", 632 | "serde_json", 633 | "serde_with", 634 | "shell-words", 635 | "sysinfo", 636 | "thiserror", 637 | "tokio", 638 | "tracing", 639 | "url", 640 | ] 641 | 642 | [[package]] 643 | name = "ntapi" 644 | version = "0.4.1" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" 647 | dependencies = [ 648 | "winapi", 649 | ] 650 | 651 | [[package]] 652 | name = "nu-ansi-term" 653 | version = "0.46.0" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 656 | dependencies = [ 657 | "overload", 658 | "winapi", 659 | ] 660 | 661 | [[package]] 662 | name = "num-conv" 663 | version = "0.1.0" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 666 | 667 | [[package]] 668 | name = "num-traits" 669 | version = "0.2.18" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" 672 | dependencies = [ 673 | "autocfg", 674 | ] 675 | 676 | [[package]] 677 | name = "num_cpus" 678 | version = "1.16.0" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 681 | dependencies = [ 682 | "hermit-abi", 683 | "libc", 684 | ] 685 | 686 | [[package]] 687 | name = "object" 688 | version = "0.32.2" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 691 | dependencies = [ 692 | "memchr", 693 | ] 694 | 695 | [[package]] 696 | name = "once_cell" 697 | version = "1.19.0" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 700 | 701 | [[package]] 702 | name = "os_info" 703 | version = "3.8.2" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" 706 | dependencies = [ 707 | "log", 708 | "serde", 709 | "windows-sys 0.52.0", 710 | ] 711 | 712 | [[package]] 713 | name = "overload" 714 | version = "0.1.1" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 717 | 718 | [[package]] 719 | name = "parking_lot" 720 | version = "0.12.1" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 723 | dependencies = [ 724 | "lock_api", 725 | "parking_lot_core", 726 | ] 727 | 728 | [[package]] 729 | name = "parking_lot_core" 730 | version = "0.9.9" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 733 | dependencies = [ 734 | "cfg-if", 735 | "libc", 736 | "redox_syscall", 737 | "smallvec", 738 | "windows-targets 0.48.5", 739 | ] 740 | 741 | [[package]] 742 | name = "percent-encoding" 743 | version = "2.3.1" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 746 | 747 | [[package]] 748 | name = "pin-project-lite" 749 | version = "0.2.14" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 752 | 753 | [[package]] 754 | name = "powerfmt" 755 | version = "0.2.0" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 758 | 759 | [[package]] 760 | name = "proc-macro2" 761 | version = "1.0.79" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" 764 | dependencies = [ 765 | "unicode-ident", 766 | ] 767 | 768 | [[package]] 769 | name = "quote" 770 | version = "1.0.35" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 773 | dependencies = [ 774 | "proc-macro2", 775 | ] 776 | 777 | [[package]] 778 | name = "rayon" 779 | version = "1.10.0" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 782 | dependencies = [ 783 | "either", 784 | "rayon-core", 785 | ] 786 | 787 | [[package]] 788 | name = "rayon-core" 789 | version = "1.12.1" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 792 | dependencies = [ 793 | "crossbeam-deque", 794 | "crossbeam-utils", 795 | ] 796 | 797 | [[package]] 798 | name = "redox_syscall" 799 | version = "0.4.1" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 802 | dependencies = [ 803 | "bitflags 1.3.2", 804 | ] 805 | 806 | [[package]] 807 | name = "regex" 808 | version = "1.10.4" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" 811 | dependencies = [ 812 | "aho-corasick", 813 | "memchr", 814 | "regex-automata 0.4.6", 815 | "regex-syntax 0.8.3", 816 | ] 817 | 818 | [[package]] 819 | name = "regex-automata" 820 | version = "0.1.10" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 823 | dependencies = [ 824 | "regex-syntax 0.6.29", 825 | ] 826 | 827 | [[package]] 828 | name = "regex-automata" 829 | version = "0.4.6" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 832 | dependencies = [ 833 | "aho-corasick", 834 | "memchr", 835 | "regex-syntax 0.8.3", 836 | ] 837 | 838 | [[package]] 839 | name = "regex-syntax" 840 | version = "0.6.29" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 843 | 844 | [[package]] 845 | name = "regex-syntax" 846 | version = "0.8.3" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" 849 | 850 | [[package]] 851 | name = "rustc-demangle" 852 | version = "0.1.23" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 855 | 856 | [[package]] 857 | name = "rustix" 858 | version = "0.38.32" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" 861 | dependencies = [ 862 | "bitflags 2.5.0", 863 | "errno", 864 | "libc", 865 | "linux-raw-sys", 866 | "windows-sys 0.52.0", 867 | ] 868 | 869 | [[package]] 870 | name = "ryu" 871 | version = "1.0.17" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 874 | 875 | [[package]] 876 | name = "scopeguard" 877 | version = "1.2.0" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 880 | 881 | [[package]] 882 | name = "semver" 883 | version = "1.0.22" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" 886 | dependencies = [ 887 | "serde", 888 | ] 889 | 890 | [[package]] 891 | name = "serde" 892 | version = "1.0.197" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" 895 | dependencies = [ 896 | "serde_derive", 897 | ] 898 | 899 | [[package]] 900 | name = "serde_derive" 901 | version = "1.0.197" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" 904 | dependencies = [ 905 | "proc-macro2", 906 | "quote", 907 | "syn", 908 | ] 909 | 910 | [[package]] 911 | name = "serde_json" 912 | version = "1.0.115" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" 915 | dependencies = [ 916 | "itoa", 917 | "ryu", 918 | "serde", 919 | ] 920 | 921 | [[package]] 922 | name = "serde_repr" 923 | version = "0.1.18" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" 926 | dependencies = [ 927 | "proc-macro2", 928 | "quote", 929 | "syn", 930 | ] 931 | 932 | [[package]] 933 | name = "serde_spanned" 934 | version = "0.6.5" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" 937 | dependencies = [ 938 | "serde", 939 | ] 940 | 941 | [[package]] 942 | name = "serde_with" 943 | version = "3.7.0" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" 946 | dependencies = [ 947 | "base64", 948 | "chrono", 949 | "hex", 950 | "indexmap 1.9.3", 951 | "indexmap 2.2.6", 952 | "serde", 953 | "serde_derive", 954 | "serde_json", 955 | "serde_with_macros", 956 | "time", 957 | ] 958 | 959 | [[package]] 960 | name = "serde_with_macros" 961 | version = "3.7.0" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" 964 | dependencies = [ 965 | "darling", 966 | "proc-macro2", 967 | "quote", 968 | "syn", 969 | ] 970 | 971 | [[package]] 972 | name = "sharded-slab" 973 | version = "0.1.7" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 976 | dependencies = [ 977 | "lazy_static", 978 | ] 979 | 980 | [[package]] 981 | name = "shell-words" 982 | version = "1.1.0" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" 985 | 986 | [[package]] 987 | name = "signal-hook-registry" 988 | version = "1.4.1" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 991 | dependencies = [ 992 | "libc", 993 | ] 994 | 995 | [[package]] 996 | name = "smallvec" 997 | version = "1.13.2" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1000 | 1001 | [[package]] 1002 | name = "socket2" 1003 | version = "0.5.6" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" 1006 | dependencies = [ 1007 | "libc", 1008 | "windows-sys 0.52.0", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "strsim" 1013 | version = "0.10.0" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1016 | 1017 | [[package]] 1018 | name = "strsim" 1019 | version = "0.11.1" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1022 | 1023 | [[package]] 1024 | name = "syn" 1025 | version = "2.0.58" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" 1028 | dependencies = [ 1029 | "proc-macro2", 1030 | "quote", 1031 | "unicode-ident", 1032 | ] 1033 | 1034 | [[package]] 1035 | name = "sysinfo" 1036 | version = "0.29.11" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" 1039 | dependencies = [ 1040 | "cfg-if", 1041 | "core-foundation-sys", 1042 | "libc", 1043 | "ntapi", 1044 | "once_cell", 1045 | "rayon", 1046 | "winapi", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "thiserror" 1051 | version = "1.0.58" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" 1054 | dependencies = [ 1055 | "thiserror-impl", 1056 | ] 1057 | 1058 | [[package]] 1059 | name = "thiserror-impl" 1060 | version = "1.0.58" 1061 | source = "registry+https://github.com/rust-lang/crates.io-index" 1062 | checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" 1063 | dependencies = [ 1064 | "proc-macro2", 1065 | "quote", 1066 | "syn", 1067 | ] 1068 | 1069 | [[package]] 1070 | name = "thread_local" 1071 | version = "1.1.8" 1072 | source = "registry+https://github.com/rust-lang/crates.io-index" 1073 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1074 | dependencies = [ 1075 | "cfg-if", 1076 | "once_cell", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "time" 1081 | version = "0.3.34" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" 1084 | dependencies = [ 1085 | "deranged", 1086 | "itoa", 1087 | "num-conv", 1088 | "powerfmt", 1089 | "serde", 1090 | "time-core", 1091 | "time-macros", 1092 | ] 1093 | 1094 | [[package]] 1095 | name = "time-core" 1096 | version = "0.1.2" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 1099 | 1100 | [[package]] 1101 | name = "time-macros" 1102 | version = "0.2.17" 1103 | source = "registry+https://github.com/rust-lang/crates.io-index" 1104 | checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" 1105 | dependencies = [ 1106 | "num-conv", 1107 | "time-core", 1108 | ] 1109 | 1110 | [[package]] 1111 | name = "tinyvec" 1112 | version = "1.6.0" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1115 | dependencies = [ 1116 | "tinyvec_macros", 1117 | ] 1118 | 1119 | [[package]] 1120 | name = "tinyvec_macros" 1121 | version = "0.1.1" 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" 1123 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1124 | 1125 | [[package]] 1126 | name = "tokio" 1127 | version = "1.37.0" 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" 1129 | checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" 1130 | dependencies = [ 1131 | "backtrace", 1132 | "bytes", 1133 | "libc", 1134 | "mio", 1135 | "num_cpus", 1136 | "parking_lot", 1137 | "pin-project-lite", 1138 | "signal-hook-registry", 1139 | "socket2", 1140 | "tokio-macros", 1141 | "windows-sys 0.48.0", 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "tokio-macros" 1146 | version = "2.2.0" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" 1149 | dependencies = [ 1150 | "proc-macro2", 1151 | "quote", 1152 | "syn", 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "toml" 1157 | version = "0.8.12" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" 1160 | dependencies = [ 1161 | "serde", 1162 | "serde_spanned", 1163 | "toml_datetime", 1164 | "toml_edit", 1165 | ] 1166 | 1167 | [[package]] 1168 | name = "toml_datetime" 1169 | version = "0.6.5" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 1172 | dependencies = [ 1173 | "serde", 1174 | ] 1175 | 1176 | [[package]] 1177 | name = "toml_edit" 1178 | version = "0.22.9" 1179 | source = "registry+https://github.com/rust-lang/crates.io-index" 1180 | checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" 1181 | dependencies = [ 1182 | "indexmap 2.2.6", 1183 | "serde", 1184 | "serde_spanned", 1185 | "toml_datetime", 1186 | ] 1187 | 1188 | [[package]] 1189 | name = "tracing" 1190 | version = "0.1.40" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1193 | dependencies = [ 1194 | "pin-project-lite", 1195 | "tracing-attributes", 1196 | "tracing-core", 1197 | ] 1198 | 1199 | [[package]] 1200 | name = "tracing-attributes" 1201 | version = "0.1.27" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 1204 | dependencies = [ 1205 | "proc-macro2", 1206 | "quote", 1207 | "syn", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "tracing-core" 1212 | version = "0.1.32" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1215 | dependencies = [ 1216 | "once_cell", 1217 | "valuable", 1218 | ] 1219 | 1220 | [[package]] 1221 | name = "tracing-log" 1222 | version = "0.2.0" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1225 | dependencies = [ 1226 | "log", 1227 | "once_cell", 1228 | "tracing-core", 1229 | ] 1230 | 1231 | [[package]] 1232 | name = "tracing-subscriber" 1233 | version = "0.3.18" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 1236 | dependencies = [ 1237 | "matchers", 1238 | "nu-ansi-term", 1239 | "once_cell", 1240 | "regex", 1241 | "sharded-slab", 1242 | "smallvec", 1243 | "thread_local", 1244 | "tracing", 1245 | "tracing-core", 1246 | "tracing-log", 1247 | ] 1248 | 1249 | [[package]] 1250 | name = "unicode-bidi" 1251 | version = "0.3.15" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 1254 | 1255 | [[package]] 1256 | name = "unicode-ident" 1257 | version = "1.0.12" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1260 | 1261 | [[package]] 1262 | name = "unicode-normalization" 1263 | version = "0.1.23" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 1266 | dependencies = [ 1267 | "tinyvec", 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "url" 1272 | version = "2.5.0" 1273 | source = "registry+https://github.com/rust-lang/crates.io-index" 1274 | checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" 1275 | dependencies = [ 1276 | "form_urlencoded", 1277 | "idna", 1278 | "percent-encoding", 1279 | "serde", 1280 | ] 1281 | 1282 | [[package]] 1283 | name = "utf8parse" 1284 | version = "0.2.1" 1285 | source = "registry+https://github.com/rust-lang/crates.io-index" 1286 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 1287 | 1288 | [[package]] 1289 | name = "uuid" 1290 | version = "1.8.0" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" 1293 | dependencies = [ 1294 | "getrandom", 1295 | ] 1296 | 1297 | [[package]] 1298 | name = "valuable" 1299 | version = "0.1.0" 1300 | source = "registry+https://github.com/rust-lang/crates.io-index" 1301 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 1302 | 1303 | [[package]] 1304 | name = "wasi" 1305 | version = "0.11.0+wasi-snapshot-preview1" 1306 | source = "registry+https://github.com/rust-lang/crates.io-index" 1307 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1308 | 1309 | [[package]] 1310 | name = "wasm-bindgen" 1311 | version = "0.2.92" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 1314 | dependencies = [ 1315 | "cfg-if", 1316 | "wasm-bindgen-macro", 1317 | ] 1318 | 1319 | [[package]] 1320 | name = "wasm-bindgen-backend" 1321 | version = "0.2.92" 1322 | source = "registry+https://github.com/rust-lang/crates.io-index" 1323 | checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 1324 | dependencies = [ 1325 | "bumpalo", 1326 | "log", 1327 | "once_cell", 1328 | "proc-macro2", 1329 | "quote", 1330 | "syn", 1331 | "wasm-bindgen-shared", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "wasm-bindgen-macro" 1336 | version = "0.2.92" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 1339 | dependencies = [ 1340 | "quote", 1341 | "wasm-bindgen-macro-support", 1342 | ] 1343 | 1344 | [[package]] 1345 | name = "wasm-bindgen-macro-support" 1346 | version = "0.2.92" 1347 | source = "registry+https://github.com/rust-lang/crates.io-index" 1348 | checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 1349 | dependencies = [ 1350 | "proc-macro2", 1351 | "quote", 1352 | "syn", 1353 | "wasm-bindgen-backend", 1354 | "wasm-bindgen-shared", 1355 | ] 1356 | 1357 | [[package]] 1358 | name = "wasm-bindgen-shared" 1359 | version = "0.2.92" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 1362 | 1363 | [[package]] 1364 | name = "which" 1365 | version = "4.4.2" 1366 | source = "registry+https://github.com/rust-lang/crates.io-index" 1367 | checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" 1368 | dependencies = [ 1369 | "either", 1370 | "home", 1371 | "once_cell", 1372 | "rustix", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "winapi" 1377 | version = "0.3.9" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1380 | dependencies = [ 1381 | "winapi-i686-pc-windows-gnu", 1382 | "winapi-x86_64-pc-windows-gnu", 1383 | ] 1384 | 1385 | [[package]] 1386 | name = "winapi-i686-pc-windows-gnu" 1387 | version = "0.4.0" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1390 | 1391 | [[package]] 1392 | name = "winapi-x86_64-pc-windows-gnu" 1393 | version = "0.4.0" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1396 | 1397 | [[package]] 1398 | name = "windows-core" 1399 | version = "0.52.0" 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" 1401 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1402 | dependencies = [ 1403 | "windows-targets 0.52.4", 1404 | ] 1405 | 1406 | [[package]] 1407 | name = "windows-sys" 1408 | version = "0.48.0" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1411 | dependencies = [ 1412 | "windows-targets 0.48.5", 1413 | ] 1414 | 1415 | [[package]] 1416 | name = "windows-sys" 1417 | version = "0.52.0" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1420 | dependencies = [ 1421 | "windows-targets 0.52.4", 1422 | ] 1423 | 1424 | [[package]] 1425 | name = "windows-targets" 1426 | version = "0.48.5" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1429 | dependencies = [ 1430 | "windows_aarch64_gnullvm 0.48.5", 1431 | "windows_aarch64_msvc 0.48.5", 1432 | "windows_i686_gnu 0.48.5", 1433 | "windows_i686_msvc 0.48.5", 1434 | "windows_x86_64_gnu 0.48.5", 1435 | "windows_x86_64_gnullvm 0.48.5", 1436 | "windows_x86_64_msvc 0.48.5", 1437 | ] 1438 | 1439 | [[package]] 1440 | name = "windows-targets" 1441 | version = "0.52.4" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 1444 | dependencies = [ 1445 | "windows_aarch64_gnullvm 0.52.4", 1446 | "windows_aarch64_msvc 0.52.4", 1447 | "windows_i686_gnu 0.52.4", 1448 | "windows_i686_msvc 0.52.4", 1449 | "windows_x86_64_gnu 0.52.4", 1450 | "windows_x86_64_gnullvm 0.52.4", 1451 | "windows_x86_64_msvc 0.52.4", 1452 | ] 1453 | 1454 | [[package]] 1455 | name = "windows_aarch64_gnullvm" 1456 | version = "0.48.5" 1457 | source = "registry+https://github.com/rust-lang/crates.io-index" 1458 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1459 | 1460 | [[package]] 1461 | name = "windows_aarch64_gnullvm" 1462 | version = "0.52.4" 1463 | source = "registry+https://github.com/rust-lang/crates.io-index" 1464 | checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" 1465 | 1466 | [[package]] 1467 | name = "windows_aarch64_msvc" 1468 | version = "0.48.5" 1469 | source = "registry+https://github.com/rust-lang/crates.io-index" 1470 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1471 | 1472 | [[package]] 1473 | name = "windows_aarch64_msvc" 1474 | version = "0.52.4" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" 1477 | 1478 | [[package]] 1479 | name = "windows_i686_gnu" 1480 | version = "0.48.5" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1483 | 1484 | [[package]] 1485 | name = "windows_i686_gnu" 1486 | version = "0.52.4" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" 1489 | 1490 | [[package]] 1491 | name = "windows_i686_msvc" 1492 | version = "0.48.5" 1493 | source = "registry+https://github.com/rust-lang/crates.io-index" 1494 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1495 | 1496 | [[package]] 1497 | name = "windows_i686_msvc" 1498 | version = "0.52.4" 1499 | source = "registry+https://github.com/rust-lang/crates.io-index" 1500 | checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" 1501 | 1502 | [[package]] 1503 | name = "windows_x86_64_gnu" 1504 | version = "0.48.5" 1505 | source = "registry+https://github.com/rust-lang/crates.io-index" 1506 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1507 | 1508 | [[package]] 1509 | name = "windows_x86_64_gnu" 1510 | version = "0.52.4" 1511 | source = "registry+https://github.com/rust-lang/crates.io-index" 1512 | checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" 1513 | 1514 | [[package]] 1515 | name = "windows_x86_64_gnullvm" 1516 | version = "0.48.5" 1517 | source = "registry+https://github.com/rust-lang/crates.io-index" 1518 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1519 | 1520 | [[package]] 1521 | name = "windows_x86_64_gnullvm" 1522 | version = "0.52.4" 1523 | source = "registry+https://github.com/rust-lang/crates.io-index" 1524 | checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" 1525 | 1526 | [[package]] 1527 | name = "windows_x86_64_msvc" 1528 | version = "0.48.5" 1529 | source = "registry+https://github.com/rust-lang/crates.io-index" 1530 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1531 | 1532 | [[package]] 1533 | name = "windows_x86_64_msvc" 1534 | version = "0.52.4" 1535 | source = "registry+https://github.com/rust-lang/crates.io-index" 1536 | checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 1537 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nix_health" 3 | version = "0.4.1" 4 | license = "Apache-2.0" 5 | repository = "https://github.com/juspay/nix-health" 6 | description = "Check the health of your Nix setup" 7 | edition = "2021" 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [[bin]] 13 | name = "nix-health" 14 | path = "src/main.rs" 15 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 17 | 18 | [dependencies] 19 | serde = { version = "1.0.197", features = ["derive"] } 20 | cfg-if = "1" 21 | clap = { version = "4.3", features = ["derive", "env"] } 22 | regex = "1.9.3" 23 | thiserror = "1.0" 24 | serde_json = "1.0" 25 | serde_with = { version = "3.2", features = ["json"] } 26 | tokio = { version = "1.29", features = ["full"] } 27 | tracing = "0.1" 28 | tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } 29 | url = { version = "2.4", features = ["serde"] } 30 | nix_rs = "0.5.0" 31 | direnv = "0.1.1" 32 | human-panic = "1.1.5" 33 | anyhow = { version = "1.0.75" } 34 | colored = { version = "2.0" } 35 | which = { version = "4.4.2" } 36 | bytesize = { version = "1.3.0", features = ["serde"] } 37 | semver = { version = "1.0.22", features = ["serde"] } 38 | serde_repr = "0.1.18" 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: /health 3 | --- 4 | 5 | # Nix Health 6 | 7 | [nix-health](https://github.com/juspay/nix-health)[^1] is a program that checks the health of your Nix install. Furthermore, individual projects can configure their own health checks in their `flake.nix`. For example, the nammayatri project checks that [the cachix cache is in use](https://github.com/nammayatri/nammayatri/blob/2201f618af69dc78070fefeb4f082420b1d226cc/flake.nix#L29-L31). 8 | 9 | [^1]: nix-health originally began as a script which is now deprecated. 10 | 11 | ![](https://github.com/juspay/nix-browser/assets/3998/abbbc54b-d888-42fb-a2a8-31d9ae142d6a) 12 | 13 | ## Checks performed 14 | 15 | | Check | Configurable in `flake.nix`? | 16 | | --- | --- | 17 | | Flakes are enabled | - | 18 | | Nix version is not too old | Yes | 19 | | Nix runs natively (no rosetta)[^2] | Yes | 20 | | Builds use multiple cores (`max-jobs`) | Yes | 21 | | Nix Caches in use | Yes | 22 | | $USER is in `trusted-users` | - | 23 | | Direnv: installed and activated | Yes | 24 | | Min RAM / Disk space | Yes | 25 | 26 | [^2]: This check is only performed on macOS with Apple Silicon. 27 | 28 | Note that some checks are considered non-essential. For eg., the disk space check looks for 1TB+ disk space, but if the user is on a laptop with 256GB SSD, the check will report a warning instead of failing. This can also be configured in per-project basis from `flake.nix` (see below). 29 | 30 | ## Usage 31 | 32 | >[!note] 33 | >nix-health `0.3.0` is [available](https://github.com/NixOS/nixpkgs/pull/303283) on nixpkgs. 34 | 35 | To run the development version, 36 | 37 | ```bash 38 | # NOTE: You may have to add `--extra-experimental-features "flakes nix-command"` 39 | # if flakes are not already enabled 40 | nix --accept-flake-config run github:juspay/nix-health 41 | ``` 42 | 43 | To run nix-health along with health check configuration specified in a project flake, pass that flake as an argument. For eg., to run nix-health with additional checks from the nammayatri project, run: 44 | 45 | ```bash 46 | # The argument can be any flake URL (including a local path) 47 | nix --accept-flake-config run github:juspay/nix-health github:nammayatri/nammayatri 48 | ``` 49 | 50 | ## Configuring in `flake.nix` {#conf} 51 | 52 | To add project specific health checks or configure health checks, add the following flake output: 53 | 54 | ```nix 55 | { 56 | outputs = inputs: { 57 | nix-health.default = { 58 | # Add configuration here 59 | caches.required = [ "https://ourproject.cachix.org" ]; 60 | }; 61 | }; 62 | } 63 | ``` 64 | 65 | To see all available configuration options, run `nix-health --dump-schema`. This will dump the schema of the configuration in JSON format. Convert that to a Nix attrset to see what can be added under the `nix-health.default` attrset of your flake. 66 | 67 | ```sh-session 68 | $ nix-health --dump-schema > schema.json 69 | $ nix eval --impure --expr 'builtins.fromJSON (builtins.readFile ./schema.json)' | nix run nixpkgs#alejandra -- --quiet 70 | { 71 | caches = {required = ["https://cache.nixos.org/"];}; 72 | direnv = { 73 | enable = true; 74 | required = false; 75 | }; 76 | flake-enabled = {}; 77 | max-jobs = {}; 78 | nix-version = {min-required = "2.13.0";}; 79 | rosetta = { 80 | enable = true; 81 | required = true; 82 | }; 83 | system = { 84 | enable = true; 85 | min_disk_space = "1024.0 GB"; 86 | min_ram = null; 87 | required = false; 88 | }; 89 | trusted-users = {}; 90 | } 91 | ``` 92 | 93 | ### Adding devShell check 94 | 95 | You can automatically run `nix-health` whenever your Nix dev shell starts. To do this, import the flake module in your flake and use it in your devShell: 96 | 97 | ```nix 98 | { 99 | inputs = { 100 | # NOTE: refers to ./module flake. 101 | nix-health.url = "github:juspay/nix-health?dir=module"; 102 | }; 103 | outputs = inputs: 104 | inputs.flake-parts.lib.mkFlake { inherit inputs; } { 105 | imports = [ 106 | inputs.nix-health.flakeModule 107 | ]; 108 | perSystem = { config, pkgs, ... }: { 109 | devShells.default = pkgs.mkShell { 110 | inputsFrom = [ 111 | config.nix-health.outputs.devShell 112 | ] 113 | }; 114 | }; 115 | }; 116 | } 117 | ``` 118 | 119 | Now suppose you have Nix 2.18 installed, but your project requires 2.19 or above due to the following config in its `flake.nix`: 120 | 121 | ```nix 122 | flake.nix-health.default = { 123 | nix-version.min-required = "2.19.0"; 124 | }; 125 | ``` 126 | 127 | you can expect the devShell to print a giant message like this: 128 | 129 | image 130 | 131 | Note that you will still be dropped into the Nix dev shell (there's no way to abrupt the launching of a dev Shell). 132 | -------------------------------------------------------------------------------- /failing/.envrc: -------------------------------------------------------------------------------- 1 | # Just a dummy .envrc for testing flake.nix checks 2 | invalid -------------------------------------------------------------------------------- /failing/flake.nix: -------------------------------------------------------------------------------- 1 | # Just a flake.nix to configure nix-health to fail all possible checks 2 | # 3 | # Used for testing purposes; run as: 4 | # just watch-nix-health ./crates/nix_health/failing/ 5 | 6 | { 7 | outputs = _: { 8 | nix-health.default = { 9 | caches.required = [ "https://unknown.cachix.org" "https://example.com" ]; 10 | nix-version.min-required = "9.99.99"; 11 | system = { 12 | min_ram = "512GB"; 13 | min_disk_space = "64TB"; 14 | }; 15 | }; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "crane": { 4 | "inputs": { 5 | "nixpkgs": [ 6 | "rust-flake", 7 | "nixpkgs" 8 | ] 9 | }, 10 | "locked": { 11 | "lastModified": 1718474113, 12 | "narHash": "sha256-UKrfy/46YF2TRnxTtKCYzqf2f5ZPRRWwKCCJb7O5X8U=", 13 | "owner": "ipetkov", 14 | "repo": "crane", 15 | "rev": "0095fd8ea00ae0a9e6014f39c375e40c2fbd3386", 16 | "type": "github" 17 | }, 18 | "original": { 19 | "owner": "ipetkov", 20 | "repo": "crane", 21 | "type": "github" 22 | } 23 | }, 24 | "flake-parts": { 25 | "inputs": { 26 | "nixpkgs-lib": "nixpkgs-lib" 27 | }, 28 | "locked": { 29 | "lastModified": 1698882062, 30 | "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", 31 | "owner": "hercules-ci", 32 | "repo": "flake-parts", 33 | "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", 34 | "type": "github" 35 | }, 36 | "original": { 37 | "owner": "hercules-ci", 38 | "repo": "flake-parts", 39 | "type": "github" 40 | } 41 | }, 42 | "flake-utils": { 43 | "inputs": { 44 | "systems": "systems" 45 | }, 46 | "locked": { 47 | "lastModified": 1705309234, 48 | "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", 49 | "owner": "numtide", 50 | "repo": "flake-utils", 51 | "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", 52 | "type": "github" 53 | }, 54 | "original": { 55 | "owner": "numtide", 56 | "repo": "flake-utils", 57 | "type": "github" 58 | } 59 | }, 60 | "just-flake": { 61 | "locked": { 62 | "lastModified": 1713316411, 63 | "narHash": "sha256-NkJfU6H+6vgHkPtZ2ESbZ/h2wnsDQrZvB4vbdUIBx8Q=", 64 | "owner": "juspay", 65 | "repo": "just-flake", 66 | "rev": "0e33952a4bcd16cd54ee3aba8111606c237d4526", 67 | "type": "github" 68 | }, 69 | "original": { 70 | "owner": "juspay", 71 | "repo": "just-flake", 72 | "type": "github" 73 | } 74 | }, 75 | "nixpkgs": { 76 | "locked": { 77 | "lastModified": 1716509168, 78 | "narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=", 79 | "owner": "nixos", 80 | "repo": "nixpkgs", 81 | "rev": "bfb7a882678e518398ce9a31a881538679f6f092", 82 | "type": "github" 83 | }, 84 | "original": { 85 | "owner": "nixos", 86 | "ref": "nixos-unstable", 87 | "repo": "nixpkgs", 88 | "type": "github" 89 | } 90 | }, 91 | "nixpkgs-lib": { 92 | "locked": { 93 | "dir": "lib", 94 | "lastModified": 1698611440, 95 | "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", 96 | "owner": "NixOS", 97 | "repo": "nixpkgs", 98 | "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", 99 | "type": "github" 100 | }, 101 | "original": { 102 | "dir": "lib", 103 | "owner": "NixOS", 104 | "ref": "nixos-unstable", 105 | "repo": "nixpkgs", 106 | "type": "github" 107 | } 108 | }, 109 | "nixpkgs_2": { 110 | "locked": { 111 | "lastModified": 1706487304, 112 | "narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=", 113 | "owner": "NixOS", 114 | "repo": "nixpkgs", 115 | "rev": "90f456026d284c22b3e3497be980b2e47d0b28ac", 116 | "type": "github" 117 | }, 118 | "original": { 119 | "owner": "NixOS", 120 | "ref": "nixpkgs-unstable", 121 | "repo": "nixpkgs", 122 | "type": "github" 123 | } 124 | }, 125 | "nixpkgs_3": { 126 | "locked": { 127 | "lastModified": 1712122226, 128 | "narHash": "sha256-pmgwKs8Thu1WETMqCrWUm0CkN1nmCKX3b51+EXsAZyY=", 129 | "owner": "nixos", 130 | "repo": "nixpkgs", 131 | "rev": "08b9151ed40350725eb40b1fe96b0b86304a654b", 132 | "type": "github" 133 | }, 134 | "original": { 135 | "owner": "nixos", 136 | "ref": "nixos-unstable", 137 | "repo": "nixpkgs", 138 | "type": "github" 139 | } 140 | }, 141 | "root": { 142 | "inputs": { 143 | "flake-parts": "flake-parts", 144 | "just-flake": "just-flake", 145 | "nixpkgs": "nixpkgs", 146 | "rust-flake": "rust-flake", 147 | "systems": "systems_2", 148 | "treefmt-nix": "treefmt-nix" 149 | } 150 | }, 151 | "rust-flake": { 152 | "inputs": { 153 | "crane": "crane", 154 | "nixpkgs": [ 155 | "nixpkgs" 156 | ], 157 | "rust-overlay": "rust-overlay" 158 | }, 159 | "locked": { 160 | "lastModified": 1718723475, 161 | "narHash": "sha256-sTNWxStNuvjLFIR1P412Ovk+sEKJsAP31ARxraCf7iI=", 162 | "owner": "juspay", 163 | "repo": "rust-flake", 164 | "rev": "f2333101b401e82013b2c6966a3727bdd23c86e0", 165 | "type": "github" 166 | }, 167 | "original": { 168 | "owner": "juspay", 169 | "repo": "rust-flake", 170 | "type": "github" 171 | } 172 | }, 173 | "rust-overlay": { 174 | "inputs": { 175 | "flake-utils": "flake-utils", 176 | "nixpkgs": "nixpkgs_2" 177 | }, 178 | "locked": { 179 | "lastModified": 1715480255, 180 | "narHash": "sha256-gEZl8nYidQwqJhOigJ91JDjoBFoPEWVsd82AKnaE7Go=", 181 | "owner": "oxalica", 182 | "repo": "rust-overlay", 183 | "rev": "d690205a4f01ec0930303c4204e5063958e51255", 184 | "type": "github" 185 | }, 186 | "original": { 187 | "owner": "oxalica", 188 | "repo": "rust-overlay", 189 | "type": "github" 190 | } 191 | }, 192 | "systems": { 193 | "locked": { 194 | "lastModified": 1681028828, 195 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 196 | "owner": "nix-systems", 197 | "repo": "default", 198 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 199 | "type": "github" 200 | }, 201 | "original": { 202 | "owner": "nix-systems", 203 | "repo": "default", 204 | "type": "github" 205 | } 206 | }, 207 | "systems_2": { 208 | "locked": { 209 | "lastModified": 1681028828, 210 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 211 | "owner": "nix-systems", 212 | "repo": "default", 213 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 214 | "type": "github" 215 | }, 216 | "original": { 217 | "owner": "nix-systems", 218 | "repo": "default", 219 | "type": "github" 220 | } 221 | }, 222 | "treefmt-nix": { 223 | "inputs": { 224 | "nixpkgs": "nixpkgs_3" 225 | }, 226 | "locked": { 227 | "lastModified": 1699786194, 228 | "narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=", 229 | "owner": "numtide", 230 | "repo": "treefmt-nix", 231 | "rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1", 232 | "type": "github" 233 | }, 234 | "original": { 235 | "owner": "numtide", 236 | "repo": "treefmt-nix", 237 | "type": "github" 238 | } 239 | } 240 | }, 241 | "root": "root", 242 | "version": 7 243 | } 244 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | nixConfig = { 3 | # https://garnix.io/docs/caching 4 | extra-substituters = "https://cache.garnix.io"; 5 | extra-trusted-public-keys = "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="; 6 | }; 7 | inputs = { 8 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 9 | flake-parts.url = "github:hercules-ci/flake-parts"; 10 | systems.url = "github:nix-systems/default"; 11 | rust-flake.url = "github:juspay/rust-flake"; 12 | rust-flake.inputs.nixpkgs.follows = "nixpkgs"; 13 | just-flake.url = "github:juspay/just-flake"; 14 | 15 | # Dev tools 16 | treefmt-nix.url = "github:numtide/treefmt-nix"; 17 | }; 18 | 19 | outputs = inputs: 20 | inputs.flake-parts.lib.mkFlake { inherit inputs; } { 21 | systems = import inputs.systems; 22 | imports = [ 23 | inputs.treefmt-nix.flakeModule 24 | inputs.just-flake.flakeModule 25 | inputs.rust-flake.flakeModules.default 26 | inputs.rust-flake.flakeModules.nixpkgs 27 | ./module/flake-module.nix 28 | ]; 29 | perSystem = { config, self', pkgs, lib, system, ... }: { 30 | rust-project.crane.args = { 31 | buildInputs = lib.optionals pkgs.stdenv.isDarwin ( 32 | with pkgs.darwin.apple_sdk.frameworks; [ 33 | IOKit 34 | # apple_sdk refers to SDK version 10.12. To compile for `x86_64-darwin` we need 11.0 35 | # see: https://github.com/NixOS/nixpkgs/pull/261683#issuecomment-1772935802 36 | pkgs.darwin.apple_sdk_11_0.frameworks.CoreFoundation 37 | ] 38 | ); 39 | nativeBuildInputs = with pkgs; [ 40 | nix # Tests need nix cli 41 | ]; 42 | } // lib.optionalAttrs pkgs.stdenv.isLinux { 43 | CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; 44 | CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static"; 45 | }; 46 | 47 | just-flake.features = { 48 | treefmt.enable = true; 49 | rust.enable = true; 50 | convco.enable = true; 51 | }; 52 | 53 | # Add your auto-formatters here. 54 | # cf. https://numtide.github.io/treefmt/ 55 | treefmt.config = { 56 | projectRootFile = "flake.nix"; 57 | programs = { 58 | nixpkgs-fmt.enable = true; 59 | rustfmt.enable = true; 60 | }; 61 | }; 62 | 63 | devShells.default = pkgs.mkShell { 64 | name = "nix-health"; 65 | packages = [ pkgs.cargo-watch pkgs.convco ]; 66 | inputsFrom = [ 67 | self'.devShells.nix_health 68 | config.treefmt.build.devShell 69 | config.nix-health.outputs.devShell 70 | config.just-flake.outputs.devShell 71 | ]; 72 | }; 73 | packages.default = self'.packages.nix_health.overrideAttrs ({ 74 | meta.mainProgram = "nix-health"; 75 | }); 76 | }; 77 | 78 | flake = { 79 | flakeModule = ./module/flake-module.nix; 80 | nix-health.default = { 81 | nix-version.min-required = "2.17.0"; 82 | }; 83 | }; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /garnix.yaml: -------------------------------------------------------------------------------- 1 | # Only building to provide package cache 2 | builds: 3 | include: 4 | - "packages.aarch64-darwin.default" 5 | - "packages.x86_64-darwin.default" 6 | - "packages.x86_64-linux.default" 7 | branch: main 8 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | # See flake.nix (just-flake) 2 | import 'just-flake.just' 3 | 4 | # Display the list of recipes 5 | default: 6 | @just --list 7 | 8 | # Run and re-run after re-compiling on change 9 | run: 10 | cargo watch -x run 11 | -------------------------------------------------------------------------------- /module/flake-module.nix: -------------------------------------------------------------------------------- 1 | { self, lib, flake-parts-lib, ... }: 2 | 3 | let 4 | inherit (flake-parts-lib) 5 | mkPerSystemOption; 6 | in 7 | { 8 | options = { 9 | perSystem = mkPerSystemOption 10 | ({ config, pkgs, ... }: { 11 | options.nix-health.outputs.devShell = lib.mkOption { 12 | type = lib.types.package; 13 | description = '' 14 | Add a shellHook for running nix-health on the flake. 15 | ''; 16 | default = pkgs.mkShell { 17 | shellHook = '' 18 | # Must use a subshell so that 'trap' handles only nix-health 19 | # crashes. 20 | ( 21 | trap "${lib.getExe pkgs.toilet} NIX UNHEALTHY --filter gay -f smmono9" ERR 22 | 23 | ${lib.getExe pkgs.nix-health} --quiet . 24 | ) 25 | ''; 26 | }; 27 | }; 28 | }); 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /module/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | outputs = _: { 3 | flakeModule = ./flake-module.nix; 4 | }; 5 | } 6 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | targets = [ "x86_64-unknown-linux-musl" ] 4 | -------------------------------------------------------------------------------- /src/check/caches.rs: -------------------------------------------------------------------------------- 1 | use nix_rs::info; 2 | use serde::{Deserialize, Serialize}; 3 | use url::Url; 4 | 5 | use crate::traits::*; 6 | 7 | /// Check that [nix_rs::config::NixConfig::substituters] is set to a good value. 8 | #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] 9 | #[serde(rename_all = "kebab-case")] 10 | #[serde(default)] 11 | pub struct Caches { 12 | pub required: Vec, 13 | } 14 | 15 | impl Default for Caches { 16 | fn default() -> Self { 17 | Caches { 18 | required: vec![Url::parse("https://cache.nixos.org").unwrap()], 19 | } 20 | } 21 | } 22 | 23 | impl Checkable for Caches { 24 | fn check( 25 | &self, 26 | nix_info: &info::NixInfo, 27 | _: Option<&nix_rs::flake::url::FlakeUrl>, 28 | ) -> Vec { 29 | let val = &nix_info.nix_config.substituters.value; 30 | let missing_caches = self 31 | .required 32 | .iter() 33 | .filter(|required_cache| !val.contains(required_cache)) 34 | .collect::>(); 35 | let result = if missing_caches.is_empty() { 36 | CheckResult::Green 37 | } else { 38 | CheckResult::Red { 39 | msg: format!( 40 | "You are missing some required caches: {}", 41 | missing_caches 42 | .iter() 43 | .map(|url| url.to_string()) 44 | .collect::>() 45 | .join(" ") 46 | ), 47 | suggestion: format!( 48 | "Caches can be added in your {} (see https://nixos.wiki/wiki/Binary_Cache#Using_a_binary_cache). Cachix caches can also be added using `nix run nixpkgs#cachix use `.", 49 | nix_info.nix_env.os.nix_config_label() 50 | ) 51 | } 52 | }; 53 | let check = Check { 54 | title: "Nix Caches in use".to_string(), 55 | info: format!( 56 | "substituters = {}", 57 | val.iter() 58 | .map(|url| url.to_string()) 59 | .collect::>() 60 | .join(" ") 61 | ), 62 | result, 63 | required: true, 64 | }; 65 | vec![check] 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/check/direnv.rs: -------------------------------------------------------------------------------- 1 | use nix_rs::{flake::url::FlakeUrl, info}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::traits::{Check, CheckResult, Checkable}; 5 | 6 | /// Check if direnv is installed 7 | #[derive(Debug, Serialize, Deserialize, Clone)] 8 | #[serde(default, rename_all = "kebab-case")] 9 | pub struct Direnv { 10 | pub(crate) enable: bool, 11 | /// Whether to produce [Check::required] checks 12 | pub(crate) required: bool, 13 | } 14 | 15 | impl Default for Direnv { 16 | fn default() -> Self { 17 | Self { 18 | enable: true, 19 | required: false, 20 | } 21 | } 22 | } 23 | 24 | impl Checkable for Direnv { 25 | fn check(&self, _nix_info: &info::NixInfo, flake_url: Option<&FlakeUrl>) -> Vec { 26 | let mut checks = vec![]; 27 | if !self.enable { 28 | return checks; 29 | } 30 | 31 | let direnv_install_result = direnv::DirenvInstall::detect(); 32 | checks.push(install_check(&direnv_install_result, self.required)); 33 | 34 | match direnv_install_result.as_ref() { 35 | Err(_) => return checks, 36 | Ok(direnv_install) => { 37 | // If direnv is installed, check for version and then allowed_check 38 | // This check is currently only relevant if the flake is local and an `.envrc` exists. 39 | match flake_url.as_ref().and_then(|url| url.as_local_path()) { 40 | None => {} 41 | Some(local_path) => { 42 | if local_path.join(".envrc").exists() { 43 | checks.push(allowed_check(direnv_install, local_path, self.required)); 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | checks 51 | } 52 | } 53 | 54 | /// [Check] that direnv was installed. 55 | fn install_check( 56 | direnv_install_result: &Result, 57 | required: bool, 58 | ) -> Check { 59 | let setup_url = "https://nixos.asia/en/direnv#setup"; 60 | Check { 61 | title: "Direnv installation".to_string(), 62 | info: format!( 63 | "direnv location = {:?}", 64 | direnv_install_result.as_ref().ok().map(|s| &s.bin_path) 65 | ), 66 | result: match direnv_install_result { 67 | Ok(direnv_install) if is_path_in_nix_store(&direnv_install.canonical_path) => { 68 | CheckResult::Green 69 | } 70 | Ok(direnv_install) => CheckResult::Red { 71 | msg: format!( 72 | "direnv is installed outside of Nix ({:?})", 73 | &direnv_install.canonical_path 74 | ), 75 | suggestion: format!( 76 | "Install direnv via Nix, it will also manage shell integration. See <{}>", 77 | setup_url 78 | ), 79 | }, 80 | Err(e) => CheckResult::Red { 81 | msg: format!("Unable to locate direnv ({})", e), 82 | suggestion: format!("Install direnv <{}>", setup_url), 83 | }, 84 | }, 85 | required, 86 | } 87 | } 88 | 89 | /// Check that the path is in the Nix store (usually /nix/store) 90 | fn is_path_in_nix_store(path: &std::path::Path) -> bool { 91 | path.starts_with("/nix/store") 92 | } 93 | 94 | /// [Check] that direnv was allowed on the local flake 95 | fn allowed_check( 96 | direnv_install: &direnv::DirenvInstall, 97 | local_flake: &std::path::Path, 98 | required: bool, 99 | ) -> Check { 100 | let suggestion = format!("Run `direnv allow` under `{}`", local_flake.display()); 101 | let direnv_allowed = direnv_install 102 | .status(local_flake) 103 | .map(|status| status.state.is_allowed()); 104 | Check { 105 | title: "Direnv allowed".to_string(), 106 | info: format!("Local flake: {:?} (has .envrc and is allowed)", local_flake), 107 | result: match direnv_allowed { 108 | Ok(true) => CheckResult::Green, 109 | Ok(false) => CheckResult::Red { 110 | msg: "direnv was not allowed on this project".to_string(), 111 | suggestion, 112 | }, 113 | Err(e) => CheckResult::Red { 114 | msg: format!("Unable to check direnv status: {}", e), 115 | suggestion, 116 | }, 117 | }, 118 | required, 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/check/flake_enabled.rs: -------------------------------------------------------------------------------- 1 | use nix_rs::info; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::traits::*; 5 | 6 | /// Check that [nix_rs::config::NixConfig::experimental_features] is set to a good value. 7 | #[derive(Debug, Default, Serialize, Deserialize, Clone)] 8 | #[serde(default)] 9 | pub struct FlakeEnabled {} 10 | 11 | impl Checkable for FlakeEnabled { 12 | fn check( 13 | &self, 14 | nix_info: &info::NixInfo, 15 | _: Option<&nix_rs::flake::url::FlakeUrl>, 16 | ) -> Vec { 17 | let val = &nix_info.nix_config.experimental_features.value; 18 | let check = Check { 19 | title: "Flakes Enabled".to_string(), 20 | info: format!("experimental-features = {}", val.join(" ")), 21 | result: if val.contains(&"flakes".to_string()) 22 | && val.contains(&"nix-command".to_string()) 23 | { 24 | CheckResult::Green 25 | } else { 26 | CheckResult::Red { 27 | msg: "Nix flakes are not enabled".into(), 28 | suggestion: "See https://nixos.wiki/wiki/Flakes#Enable_flakes".into(), 29 | } 30 | }, 31 | required: true, 32 | }; 33 | vec![check] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/check/max_jobs.rs: -------------------------------------------------------------------------------- 1 | use nix_rs::info; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::traits::*; 5 | 6 | /// Check that [nix_rs::config::NixConfig::max_jobs] is set to a good value. 7 | #[derive(Debug, Default, Serialize, Deserialize, Clone)] 8 | #[serde(default)] 9 | pub struct MaxJobs {} 10 | 11 | impl Checkable for MaxJobs { 12 | fn check( 13 | &self, 14 | nix_info: &info::NixInfo, 15 | _: Option<&nix_rs::flake::url::FlakeUrl>, 16 | ) -> Vec { 17 | let max_jobs = nix_info.nix_config.max_jobs.value; 18 | let check = Check { 19 | title: "Max Jobs".to_string(), 20 | info: format!("max-jobs = {}", max_jobs), 21 | result: if max_jobs > 1 { 22 | CheckResult::Green 23 | } else { 24 | CheckResult::Red { 25 | msg: "You are using only 1 CPU core for nix builds".into(), 26 | suggestion: format!( 27 | "Set `max-jobs = auto` in {}", 28 | nix_info.nix_env.os.nix_config_label() 29 | ), 30 | } 31 | }, 32 | required: true, 33 | }; 34 | vec![check] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/check/min_nix_version.rs: -------------------------------------------------------------------------------- 1 | use nix_rs::version::NixVersion; 2 | 3 | use nix_rs::info; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::traits::*; 7 | 8 | /// Check that [nix_rs::version::NixVersion] is set to a good value. 9 | #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] 10 | #[serde(default, rename_all = "kebab-case")] 11 | pub struct MinNixVersion { 12 | pub min_required: NixVersion, 13 | } 14 | 15 | impl Default for MinNixVersion { 16 | fn default() -> Self { 17 | MinNixVersion { 18 | min_required: NixVersion { 19 | major: 2, 20 | minor: 16, 21 | patch: 0, 22 | }, 23 | } 24 | } 25 | } 26 | 27 | impl Checkable for MinNixVersion { 28 | fn check( 29 | &self, 30 | nix_info: &info::NixInfo, 31 | _: Option<&nix_rs::flake::url::FlakeUrl>, 32 | ) -> Vec { 33 | let val = &nix_info.nix_version; 34 | let check = Check { 35 | title: "Minimum Nix Version".to_string(), 36 | info: format!("nix version = {}", val), 37 | result: if val >= &self.min_required { 38 | CheckResult::Green 39 | } else { 40 | CheckResult::Red { 41 | msg: format!("Your Nix version ({}) is too old; we require at least {}", val, self.min_required), 42 | suggestion: "See https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-upgrade-nix.html".into(), 43 | } 44 | }, 45 | required: true, 46 | }; 47 | vec![check] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/check/mod.rs: -------------------------------------------------------------------------------- 1 | //! Individual Nix checks 2 | pub mod caches; 3 | pub mod direnv; 4 | pub mod flake_enabled; 5 | pub mod max_jobs; 6 | pub mod min_nix_version; 7 | pub mod rosetta; 8 | pub mod system; 9 | pub mod trusted_users; 10 | -------------------------------------------------------------------------------- /src/check/rosetta.rs: -------------------------------------------------------------------------------- 1 | use nix_rs::{ 2 | env::{AppleEmulation, MacOSArch, OS}, 3 | info, 4 | }; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use crate::traits::{Check, CheckResult, Checkable}; 8 | 9 | /// Check if Nix is being run under rosetta emulation 10 | /// 11 | /// Enabled only on ARM macs. 12 | #[derive(Debug, Serialize, Deserialize, Clone)] 13 | #[serde(default)] 14 | pub struct Rosetta { 15 | enable: bool, 16 | required: bool, 17 | } 18 | 19 | impl Default for Rosetta { 20 | fn default() -> Self { 21 | Self { 22 | enable: true, 23 | required: true, 24 | } 25 | } 26 | } 27 | 28 | impl Checkable for Rosetta { 29 | fn check( 30 | &self, 31 | nix_info: &info::NixInfo, 32 | _: Option<&nix_rs::flake::url::FlakeUrl>, 33 | ) -> Vec { 34 | let mut checks = vec![]; 35 | if let (true, Some(emulation)) = (self.enable, get_apple_emulation(&nix_info.nix_env.os)) { 36 | let check = Check { 37 | title: "Rosetta Not Active".to_string(), 38 | info: format!("apple emulation = {:?}", emulation), 39 | result: if emulation == AppleEmulation::Rosetta { 40 | CheckResult::Red { 41 | msg: "Rosetta emulation will slow down Nix builds".to_string(), 42 | suggestion: "Remove rosetta, see the comment by @hruan here: https://developer.apple.com/forums/thread/669486".to_string(), 43 | } 44 | } else { 45 | CheckResult::Green 46 | }, 47 | required: self.required, 48 | }; 49 | checks.push(check); 50 | }; 51 | checks 52 | } 53 | } 54 | 55 | /// Return [AppleEmulation]. Return None if not an ARM mac. 56 | 57 | fn get_apple_emulation(system: &OS) -> Option { 58 | match system { 59 | OS::MacOS { 60 | nix_darwin: _, 61 | arch: MacOSArch::Arm64(apple_emulation), 62 | } => Some(apple_emulation.clone()), 63 | _ => None, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/check/system.rs: -------------------------------------------------------------------------------- 1 | use bytesize::ByteSize; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::traits::{Check, CheckResult, Checkable}; 5 | 6 | /// Check if the system has enough resources 7 | #[derive(Debug, Serialize, Deserialize, Clone)] 8 | #[serde(default)] 9 | pub struct System { 10 | enable: bool, 11 | required: bool, 12 | /// Minimum required RAM memory 13 | min_ram: Option, 14 | /// Minimum required disk space 15 | min_disk_space: Option, 16 | } 17 | 18 | impl Default for System { 19 | fn default() -> Self { 20 | Self { 21 | enable: true, 22 | required: false, 23 | // RAM requirements vary between projects. 24 | min_ram: None, 25 | // 1TiB is recommended for nix 26 | min_disk_space: Some(ByteSize::gb(1024)), 27 | } 28 | } 29 | } 30 | 31 | impl Checkable for System { 32 | fn check( 33 | &self, 34 | nix_info: &nix_rs::info::NixInfo, 35 | _: Option<&nix_rs::flake::url::FlakeUrl>, 36 | ) -> Vec { 37 | let mut checks = vec![]; 38 | let nix_env = &nix_info.nix_env; 39 | if self.enable { 40 | if let Some(min_ram) = self.min_ram { 41 | checks.push(Check { 42 | title: "RAM".to_string(), 43 | info: format!( 44 | "min ram = {:?}; total = {:?}", 45 | min_ram, nix_env.total_memory 46 | ), 47 | result: if nix_env.total_memory < min_ram { 48 | CheckResult::Red { 49 | msg: format!("Total memory is less than {}", min_ram), 50 | suggestion: "Please add more memory to your system".to_string(), 51 | } 52 | } else { 53 | CheckResult::Green 54 | }, 55 | required: self.required, 56 | }); 57 | }; 58 | if let Some(min_disk_space) = self.min_disk_space { 59 | checks.push(Check { 60 | title: "Disk Space".to_string(), 61 | info: format!( 62 | "min disk space = {:?}; total = {:?}", 63 | min_disk_space, nix_env.total_disk_space 64 | ), 65 | result: if nix_env.total_disk_space < min_disk_space { 66 | CheckResult::Red { 67 | msg: format!("Total disk space is less than {}", min_disk_space), 68 | suggestion: 69 | "The Nix store tends to use a lot of disk space. Please add more disk space" 70 | .to_string(), 71 | } 72 | } else { 73 | CheckResult::Green 74 | }, 75 | required: self.required, 76 | }); 77 | }; 78 | }; 79 | checks 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/check/trusted_users.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use nix_rs::config::TrustedUserValue; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::traits::*; 7 | 8 | /// Check that [crate::nix::config::NixConfig::trusted_users] is set to a good value. 9 | #[derive(Debug, Default, Serialize, Deserialize, Clone)] 10 | #[serde(default)] 11 | pub struct TrustedUsers {} 12 | 13 | impl Checkable for TrustedUsers { 14 | fn check( 15 | &self, 16 | nix_info: &nix_rs::info::NixInfo, 17 | _: Option<&nix_rs::flake::url::FlakeUrl>, 18 | ) -> Vec { 19 | let result = if is_current_user_trusted(nix_info) { 20 | CheckResult::Green 21 | } else { 22 | let current_user = &nix_info.nix_env.current_user; 23 | let msg = format!("User '{}' not present in trusted_users", current_user); 24 | let suggestion = match nix_info.nix_env.os.nix_system_config_label() { 25 | Some(conf_label) => format!( 26 | r#"Add `nix.trustedUsers = [ "root" "{}" ];` to your {}"#, 27 | current_user, conf_label, 28 | ), 29 | None => format!( 30 | r#"Set `trusted-users = root {}` in /etc/nix/nix.conf and then restart the Nix daemon using `sudo pkill nix-daemon`"#, 31 | current_user 32 | ), 33 | }; 34 | CheckResult::Red { msg, suggestion } 35 | }; 36 | let check = Check { 37 | title: "Trusted Users".to_string(), 38 | info: format!( 39 | "trusted-users = {}", 40 | TrustedUserValue::display_original(&nix_info.nix_config.trusted_users.value) 41 | ), 42 | result, 43 | required: true, 44 | }; 45 | vec![check] 46 | } 47 | } 48 | 49 | /// Check that [crate::nix::config::NixConfig::trusted_users] is set to a good 50 | /// value such that the current user is trusted. 51 | fn is_current_user_trusted(nix_info: &nix_rs::info::NixInfo) -> bool { 52 | let current_user = &nix_info.nix_env.current_user; 53 | let current_user_groups: HashSet<&String> = 54 | nix_info.nix_env.current_user_groups.iter().collect(); 55 | nix_info 56 | .nix_config 57 | .trusted_users 58 | .value 59 | .iter() 60 | .any(|x| match x { 61 | TrustedUserValue::Group(x) => current_user_groups.contains(&x), 62 | TrustedUserValue::User(x) => x == current_user, 63 | TrustedUserValue::All => true, 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Health checks for the user's Nix install 2 | 3 | pub mod check; 4 | pub mod logging; 5 | pub mod report; 6 | pub mod traits; 7 | 8 | use colored::Colorize; 9 | 10 | use check::direnv::Direnv; 11 | use nix_rs::command::NixCmd; 12 | use nix_rs::flake::eval::nix_eval_attr_json; 13 | use nix_rs::flake::url::FlakeUrl; 14 | use serde::{Deserialize, Serialize}; 15 | use tracing::instrument; 16 | 17 | use self::check::{ 18 | caches::Caches, flake_enabled::FlakeEnabled, max_jobs::MaxJobs, min_nix_version::MinNixVersion, 19 | rosetta::Rosetta, trusted_users::TrustedUsers, 20 | }; 21 | 22 | /// Nix Health check information for user's install 23 | /// 24 | /// Each field represents an individual check which satisfies the [Checkable] trait. 25 | #[derive(Debug, Default, Serialize, Deserialize, Clone)] 26 | #[serde(default, rename_all = "kebab-case")] 27 | pub struct NixHealth { 28 | pub max_jobs: MaxJobs, 29 | pub caches: Caches, 30 | pub flake_enabled: FlakeEnabled, 31 | pub nix_version: MinNixVersion, 32 | pub system: check::system::System, 33 | pub trusted_users: TrustedUsers, 34 | pub rosetta: Rosetta, 35 | pub direnv: Direnv, 36 | } 37 | 38 | impl<'a> IntoIterator for &'a NixHealth { 39 | type Item = &'a dyn traits::Checkable; 40 | type IntoIter = std::vec::IntoIter; 41 | 42 | /// Return an iterator to iterate on the fields of [NixHealth] 43 | fn into_iter(self) -> Self::IntoIter { 44 | let items: Vec = vec![ 45 | &self.rosetta, 46 | &self.nix_version, 47 | &self.flake_enabled, 48 | &self.system, 49 | &self.max_jobs, 50 | &self.caches, 51 | &self.trusted_users, 52 | &self.direnv, 53 | ]; 54 | items.into_iter() 55 | } 56 | } 57 | 58 | impl NixHealth { 59 | /// Create [NixHealth] using configuration from the given flake 60 | /// 61 | /// Fallback to using the default health check config if the flake doesn't 62 | /// override it. 63 | pub async fn from_flake(url: &FlakeUrl) -> Result { 64 | nix_eval_attr_json( 65 | &NixCmd::default(), 66 | &url.with_fully_qualified_root_attr("nix-health"), 67 | true, 68 | ) 69 | .await 70 | } 71 | 72 | /// Run all checks and collect the results 73 | #[instrument(skip_all)] 74 | pub fn run_checks( 75 | &self, 76 | nix_info: &nix_rs::info::NixInfo, 77 | flake_url: Option, 78 | ) -> Vec { 79 | tracing::info!("🩺 Running health checks"); 80 | self.into_iter() 81 | .flat_map(|c| c.check(nix_info, flake_url.as_ref())) 82 | .collect() 83 | } 84 | 85 | pub fn print_report_returning_exit_code(checks: &[traits::Check]) -> i32 { 86 | let mut res = AllChecksResult::new(); 87 | for check in checks { 88 | match &check.result { 89 | traits::CheckResult::Green => { 90 | tracing::info!( 91 | "✅ {}\n {}", 92 | check.title.green().bold(), 93 | check.info.blue() 94 | ); 95 | } 96 | traits::CheckResult::Red { msg, suggestion } => { 97 | res.register_failure(check.required); 98 | if check.required { 99 | tracing::error!( 100 | "❌ {}\n {}\n {}\n {}", 101 | check.title.red().bold(), 102 | check.info.blue(), 103 | msg.yellow(), 104 | suggestion 105 | ); 106 | } else { 107 | tracing::warn!( 108 | "🟧 {}\n {}\n {}\n {}", 109 | check.title.yellow().bold(), 110 | check.info.blue(), 111 | msg.yellow(), 112 | suggestion 113 | ); 114 | } 115 | } 116 | } 117 | } 118 | res.report() 119 | } 120 | 121 | pub fn schema() -> Result { 122 | serde_json::to_string_pretty(&NixHealth::default()) 123 | } 124 | } 125 | 126 | /// A convenient type to aggregate check failures, and summary report at end. 127 | enum AllChecksResult { 128 | Pass, 129 | PassSomeFail, 130 | Fail, 131 | } 132 | 133 | impl AllChecksResult { 134 | fn new() -> Self { 135 | AllChecksResult::Pass 136 | } 137 | 138 | fn register_failure(&mut self, required: bool) { 139 | if required { 140 | *self = AllChecksResult::Fail; 141 | } else if matches!(self, AllChecksResult::Pass) { 142 | *self = AllChecksResult::PassSomeFail; 143 | } 144 | } 145 | 146 | /// Print a summary report of the checks and return the exit code 147 | fn report(self) -> i32 { 148 | match self { 149 | AllChecksResult::Pass => { 150 | tracing::info!("{}", "✅ All checks passed".green().bold()); 151 | 0 152 | } 153 | AllChecksResult::PassSomeFail => { 154 | tracing::warn!( 155 | "{}, {}", 156 | "✅ Required checks passed".green().bold(), 157 | "but some non-required checks failed".yellow().bold() 158 | ); 159 | 0 160 | } 161 | AllChecksResult::Fail => { 162 | tracing::error!("{}", "❌ Some required checks failed".red().bold()); 163 | 1 164 | } 165 | } 166 | } 167 | } 168 | 169 | #[cfg(test)] 170 | mod tests { 171 | use crate::check::{caches::Caches, min_nix_version::MinNixVersion}; 172 | 173 | #[test] 174 | fn test_json_deserialize_empty() { 175 | let json = r#"{}"#; 176 | let v: super::NixHealth = serde_json::from_str(json).unwrap(); 177 | assert_eq!(v.nix_version, MinNixVersion::default()); 178 | assert_eq!(v.caches, Caches::default()); 179 | println!("{:?}", v); 180 | } 181 | 182 | #[test] 183 | fn test_json_deserialize_nix_version() { 184 | let json = r#"{ "nix-version": { "min-required": "2.17.0" } }"#; 185 | let v: super::NixHealth = serde_json::from_str(json).unwrap(); 186 | assert_eq!(v.nix_version.min_required.to_string(), "2.17.0"); 187 | assert_eq!(v.caches, Caches::default()); 188 | } 189 | 190 | #[test] 191 | fn test_json_deserialize_caches() { 192 | let json = r#"{ "caches": { "required": ["https://foo.cachix.org"] } }"#; 193 | let v: super::NixHealth = serde_json::from_str(json).unwrap(); 194 | assert_eq!( 195 | v.caches.required, 196 | vec![url::Url::parse("https://foo.cachix.org").unwrap()] 197 | ); 198 | assert_eq!(v.nix_version, MinNixVersion::default()); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/logging.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io; 3 | 4 | use tracing::{Event, Level, Subscriber}; 5 | use tracing_subscriber::fmt::format; 6 | use tracing_subscriber::{ 7 | fmt::{FmtContext, FormatEvent, FormatFields}, 8 | registry::LookupSpan, 9 | }; 10 | 11 | /// A [tracing_subscriber] event formatter that suppresses everything but the 12 | /// log message. 13 | struct BareFormatter; 14 | 15 | impl FormatEvent for BareFormatter 16 | where 17 | S: Subscriber + for<'a> LookupSpan<'a>, 18 | N: for<'a> FormatFields<'a> + 'static, 19 | { 20 | fn format_event( 21 | &self, 22 | ctx: &FmtContext<'_, S, N>, 23 | mut writer: format::Writer<'_>, 24 | event: &Event<'_>, 25 | ) -> fmt::Result { 26 | ctx.field_format().format_fields(writer.by_ref(), event)?; 27 | writeln!(writer) 28 | } 29 | } 30 | 31 | pub fn setup_logging(quiet: bool) { 32 | let env_filter = if quiet { 33 | "nix_health=warn,nix_rs=error" 34 | } else { 35 | "nix_health=info,nix_rs=error" 36 | }; 37 | let builder = tracing_subscriber::fmt() 38 | .with_writer(io::stderr) 39 | .with_max_level(Level::INFO) 40 | .with_env_filter(env_filter); 41 | 42 | builder.event_format(BareFormatter).init(); 43 | } 44 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use clap::{command, Parser}; 3 | use nix_health::{traits::Check, NixHealth}; 4 | use nix_rs::{command::NixCmd, env::NixEnv, flake::url::FlakeUrl, info::NixInfo}; 5 | 6 | #[derive(Parser)] 7 | #[command(author, version, about, long_about = None)] 8 | pub struct Args { 9 | /// Include health checks defined in the given flake 10 | pub flake_url: Option, 11 | 12 | /// Be quiet by outputting only failed checks 13 | #[arg(long = "quiet", short = 'q')] 14 | pub quiet: bool, 15 | 16 | /// Dump the config schema of the health checks (useful when adding them to 17 | /// a flake.nix) 18 | #[arg(long = "dump-schema")] 19 | pub dump_schema: bool, 20 | } 21 | 22 | #[tokio::main] 23 | async fn main() -> anyhow::Result<()> { 24 | human_panic::setup_panic!(); 25 | let args = Args::parse(); 26 | 27 | nix_health::logging::setup_logging(args.quiet); 28 | 29 | if args.dump_schema { 30 | tracing::info!("{}", NixHealth::schema()?); 31 | return Ok(()); 32 | } 33 | 34 | let checks = run_checks(args.flake_url).await?; 35 | 36 | let exit_code = NixHealth::print_report_returning_exit_code(&checks); 37 | 38 | std::process::exit(exit_code) 39 | } 40 | 41 | /// Run health checks, taking current directory flake into account if there is 42 | /// one. 43 | async fn run_checks(flake_url: Option) -> anyhow::Result> { 44 | let nix_info = NixInfo::from_nix(&NixCmd::default()) 45 | .await 46 | .with_context(|| "Unable to gather nix info")?; 47 | let nix_env = NixEnv::detect() 48 | .await 49 | .with_context(|| "Unable to gather system info")?; 50 | let action_msg = format!( 51 | "🩺️ Checking the health of your Nix setup ({} on {})", 52 | &nix_info.nix_config.system.value, &nix_env.os 53 | ); 54 | let health: NixHealth = match flake_url.as_ref() { 55 | Some(flake_url) => { 56 | tracing::info!("{}, using config from flake '{}':", action_msg, flake_url); 57 | NixHealth::from_flake(flake_url).await 58 | } 59 | None => { 60 | tracing::info!("{}:", action_msg); 61 | Ok(NixHealth::default()) 62 | } 63 | }?; 64 | let checks = health.run_checks(&nix_info, flake_url.clone()); 65 | Ok(checks) 66 | } 67 | -------------------------------------------------------------------------------- /src/report.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Health report 4 | /// 5 | /// If you just want the binary indicator, use `Report` (see 6 | /// [NoDetails]). If you want the report with details regarding the problem, use 7 | /// `Report` (see [WithDetails]). 8 | #[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize, Clone)] 9 | pub enum Report { 10 | /// Green means everything is fine 11 | Green, 12 | /// Red means something is wrong. `T` holds information about what's wrong. 13 | Red(T), 14 | } 15 | 16 | #[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize, Clone)] 17 | pub struct NoDetails; 18 | 19 | impl Report { 20 | pub fn is_green(&self) -> bool { 21 | match self { 22 | Report::Green => true, 23 | Report::Red(_) => false, 24 | } 25 | } 26 | 27 | pub fn is_red(&self) -> bool { 28 | !self.is_green() 29 | } 30 | } 31 | 32 | /// Details regarding a failed report 33 | #[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize, Clone)] 34 | pub struct WithDetails { 35 | /// A short message describing the problem 36 | pub msg: String, 37 | /// A suggestion for how to fix the problem 38 | pub suggestion: String, 39 | } // TODO: Should this be Markdown? 40 | 41 | impl Report { 42 | /// Return the report without the details 43 | pub fn without_details(&self) -> Report { 44 | match self { 45 | Report::Green => Report::Green, 46 | Report::Red(_) => Report::Red(NoDetails), 47 | } 48 | } 49 | /// Return the problem details if there is one. 50 | pub fn get_red_details(&self) -> Option { 51 | match self { 52 | Report::Green => None, 53 | Report::Red(details) => Some(details.clone()), 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Types that can do specific "health check" for Nix 4 | 5 | pub trait Checkable { 6 | /// Run and create the health check 7 | /// 8 | /// NOTE: Some checks may perform impure actions (IO, etc.). Returning an 9 | /// empty vector indicates that the check is skipped on this environment. 10 | fn check( 11 | &self, 12 | nix_info: &nix_rs::info::NixInfo, 13 | // The flake against which the check is being run 14 | // 15 | // If None, the check is run against the current environment, with no 16 | // specific configuration from a flake. 17 | flake: Option<&nix_rs::flake::url::FlakeUrl>, 18 | ) -> Vec; 19 | } 20 | 21 | /// A health check 22 | #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] 23 | pub struct Check { 24 | /// A user-facing title of this check 25 | /// 26 | /// This value is expected to be unique across all checks. 27 | pub title: String, 28 | 29 | /// The user-facing information used to conduct this check 30 | /// TODO: Use Markdown 31 | pub info: String, 32 | 33 | /// The result of running this check 34 | pub result: CheckResult, 35 | 36 | /// Whether this check is mandatory 37 | /// 38 | /// Failures are considered non-critical if this is false. 39 | pub required: bool, 40 | } 41 | 42 | /// The result of a health [Check] 43 | #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] 44 | pub enum CheckResult { 45 | /// The check passed 46 | Green, 47 | 48 | /// The check failed 49 | Red { 50 | /// TODO: Use markdown 51 | msg: String, 52 | /// TODO: Use markdown 53 | suggestion: String, 54 | }, 55 | } 56 | 57 | impl CheckResult { 58 | /// When the check is green (ie., healthy) 59 | pub fn green(&self) -> bool { 60 | matches!(self, Self::Green) 61 | } 62 | } 63 | --------------------------------------------------------------------------------