diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7838c9a53..683e4e1e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,8 @@ jobs: - uses: DeterminateSystems/magic-nix-cache-action@main - name: Check rustfmt run: nix develop --command check-rustfmt + - name: Check Clippy + run: nix develop --command check-clippy - name: Check Spelling run: nix develop --command check-spelling - name: Check nixpkgs-fmt formatting diff --git a/Cargo.lock b/Cargo.lock index f37a6fb8b..77d4a7ecf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -34,30 +43,29 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -68,28 +76,28 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] @@ -111,9 +119,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -126,9 +134,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.2" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "bitflags" @@ -138,21 +146,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -166,18 +174,21 @@ dependencies = [ [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ "serde", ] [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -187,58 +198,56 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "winapi", + "windows-targets", ] [[package]] name = "clap" -version = "4.3.8" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9394150f5b4273a1763355bd1c2ec54cc5a2593f790587bcd6b2c947cfa9211" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.8" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", - "bitflags 1.3.2", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "color-eyre" @@ -292,9 +301,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "darling" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -302,27 +311,37 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] name = "darling_macro" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.22", + "syn 2.0.38", +] + +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", + "serde", ] [[package]] @@ -353,7 +372,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -369,21 +388,21 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -401,33 +420,28 @@ dependencies = [ ] [[package]] -name = "erased-serde" -version = "0.3.25" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2b0c2380453a92ea8b6c8e5f64ecaafccddde8ceab55ff7a8ac1029f894569" -dependencies = [ - "serde", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "errno" -version = "0.3.1" +name = "erased-serde" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", + "serde", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "errno" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "cc", "libc", + "windows-sys", ] [[package]] @@ -442,23 +456,20 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "filetime" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", - "windows-sys 0.48.0", + "redox_syscall 0.3.5", + "windows-sys", ] [[package]] @@ -511,7 +522,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] @@ -554,22 +565,11 @@ dependencies = [ "wasi", ] -[[package]] -name = "ghost" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77ac7b51b8e6313251737fcef4b1c01a2ea102bde68415b62c0ee9268fec357" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.22", -] - [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "glob" @@ -579,17 +579,17 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" -version = "0.3.20" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "fnv", "futures-core", "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -602,6 +602,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" + [[package]] name = "heck" version = "0.4.1" @@ -619,18 +625,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -638,13 +635,22 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + [[package]] name = "http" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "fnv", "itoa", ] @@ -655,7 +661,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "http", "pin-project-lite", ] @@ -668,9 +674,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" @@ -678,7 +684,7 @@ version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "futures-channel", "futures-core", "futures-util", @@ -689,7 +695,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -698,10 +704,11 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", "rustls", @@ -761,38 +768,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] [[package]] -name = "instant" -version = "0.1.12" +name = "indexmap" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ - "cfg-if", + "equivalent", + "hashbrown 0.14.1", + "serde", ] [[package]] name = "inventory" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0539b5de9241582ce6bd6b0ba7399313560151e58c9aaf8b74b711b1bdce644" -dependencies = [ - "ghost", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] +checksum = "e1be380c410bf0595e94992a648ea89db4dd3f3354ba54af206fd2a68cf5ac8e" [[package]] name = "iovec" @@ -805,20 +800,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" - -[[package]] -name = "is-terminal" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" -dependencies = [ - "hermit-abi 0.3.1", - "rustix 0.38.3", - "windows-sys 0.48.0", -] +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is_ci" @@ -828,9 +812,9 @@ checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" @@ -849,9 +833,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "line-wrap" @@ -864,15 +848,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" @@ -886,9 +864,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lzma-sys" @@ -907,14 +885,14 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mime" @@ -924,9 +902,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] @@ -939,43 +917,44 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "nix" -version = "0.26.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "cfg-if", "libc", - "static_assertions", ] [[package]] name = "nix-config-parser" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a94767d6f881412ae02a87a4b6136b4079cbda9d3eab8f4ae272c9db740902c" +checksum = "383d96c6f2c44fc706e7a523743434465d62db109b7c8364b642f35853475d67" dependencies = [ + "indexmap 2.0.2", "serde", "thiserror", ] [[package]] name = "nix-installer" -version = "0.10.1-unreleased" +version = "0.14.0" dependencies = [ "async-trait", - "bytes 1.4.0", + "bytes 1.5.0", "clap", "color-eyre", "dirs", "dyn-clone", "eyre", "glob", + "indexmap 2.0.2", "is_ci", "nix", "nix-config-parser", @@ -1019,28 +998,28 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.3", "libc", ] [[package]] name = "object" -version = "0.30.4" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -1118,9 +1097,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1136,18 +1115,24 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plist" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" +checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" dependencies = [ "base64", - "indexmap", + "indexmap 1.9.3", "line-wrap", "quick-xml", "serde", "time", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1156,27 +1141,27 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" -version = "0.28.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1242,11 +1227,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.4" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ - "regex-syntax 0.7.2", + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -1258,6 +1246,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -1266,18 +1265,18 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64", - "bytes 1.4.0", + "bytes 1.5.0", "encoding_rs", "futures-core", "futures-util", @@ -1299,6 +1298,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-rustls", "tokio-socks", @@ -1335,36 +1335,22 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.37.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.3" +version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.3", - "windows-sys 0.48.0", + "linux-raw-sys", + "windows-sys", ] [[package]] name = "rustls" -version = "0.21.2" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", @@ -1386,18 +1372,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64", ] [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ "ring", "untrusted", @@ -1405,15 +1391,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safemem" @@ -1432,18 +1418,18 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -1457,9 +1443,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -1470,9 +1456,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -1480,38 +1466,38 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.164" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.99" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1532,14 +1518,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.0.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" +checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" dependencies = [ "base64", "chrono", "hex", - "indexmap", + "indexmap 1.9.3", + "indexmap 2.0.2", "serde", "serde_json", "serde_with_macros", @@ -1548,21 +1535,21 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.0.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" +checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -1578,18 +1565,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" @@ -1602,16 +1589,20 @@ dependencies = [ ] [[package]] -name = "spin" -version = "0.5.2" +name = "socket2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys", +] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "spin" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "strsim" @@ -1630,15 +1621,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.25.0" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9f3bd7d2e45dcc5e265fbb88d6513e4747d8ef9444cf01a533119bce28a157" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] @@ -1664,9 +1655,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.22" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -1687,11 +1678,32 @@ dependencies = [ "walkdir", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tar" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" dependencies = [ "filetime", "libc", @@ -1700,22 +1712,21 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.8" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" [[package]] name = "tempfile" -version = "3.6.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ - "autocfg", "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.20", - "windows-sys 0.48.0", + "rustix", + "windows-sys", ] [[package]] @@ -1731,22 +1742,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] @@ -1761,11 +1772,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.22" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ + "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -1773,15 +1786,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -1803,22 +1816,22 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.2" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ - "autocfg", - "bytes 1.4.0", + "backtrace", + "bytes 1.5.0", "libc", "mio", "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.4", "tokio-macros", "tracing", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1840,7 +1853,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] @@ -1867,11 +1880,11 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "futures-core", "futures-sink", "pin-project-lite", @@ -1887,11 +1900,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1899,20 +1911,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -1965,9 +1977,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typetag" -version = "0.2.8" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6898cc6f6a32698cc3e14d5632a14d2b23ed9f7b11e6b8e05ce685990acc22" +checksum = "80960fd143d4c96275c0e60b08f14b81fbb468e79bc0ef8fbda69fb0afafae43" dependencies = [ "erased-serde", "inventory", @@ -1978,13 +1990,13 @@ dependencies = [ [[package]] name = "typetag-impl" -version = "0.2.8" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3e1c30cedd24fc597f7d37a721efdbdc2b1acae012c1ef1218f4c7c2c0f3e7" +checksum = "bfc13d450dc4a695200da3074dacf43d449b968baee95e341920e47f61a3b40f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", ] [[package]] @@ -1995,9 +2007,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2016,9 +2028,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -2034,9 +2046,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.3.4" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "serde", ] @@ -2049,9 +2061,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -2093,7 +2105,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -2127,7 +2139,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2140,9 +2152,9 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-streams" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" dependencies = [ "futures-util", "js-sys", @@ -2163,13 +2175,14 @@ dependencies = [ [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -2190,9 +2203,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -2212,21 +2225,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -2238,117 +2236,76 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys", ] [[package]] name = "xattr" -version = "0.2.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index fe8871cac..2be5ecf95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "nix-installer" description = "The Determinate Nix Installer" -version = "0.10.1-unreleased" +version = "0.14.0" edition = "2021" resolver = "2" license = "LGPL-2.1" @@ -31,7 +31,7 @@ clap = { version = "4", features = ["std", "color", "usage", "help", "error-cont color-eyre = { version = "0.6.2", default-features = false, features = [ "track-caller", "issue-url", "tracing-error", "capture-spantrace", "color-spantrace" ], optional = true } eyre = { version = "0.6.8", default-features = false, features = [ "track-caller" ], optional = true } glob = { version = "0.3.0", default-features = false } -nix = { version = "0.26.0", default-features = false, features = ["user", "fs", "process", "term"] } +nix = { version = "0.27.0", default-features = false, features = ["user", "fs", "process", "term"] } owo-colors = { version = "3.5.0", default-features = false, features = [ "supports-colors" ] } reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls-native-roots", "stream", "socks"] } serde = { version = "1.0.144", default-features = false, features = [ "std", "derive" ] } @@ -57,10 +57,11 @@ uuid = { version = "1.2.2", features = ["serde"] } os-release = { version = "0.1.0", default-features = false } is_ci = { version = "1.1.1", default-features = false, optional = true } strum = { version = "0.25.0", features = ["derive"] } -nix-config-parser = { version = "0.1.2", features = ["serde"] } +nix-config-parser = { version = "0.2", features = ["serde"] } which = "4.4.0" sysctl = "0.5.4" walkdir = "2.3.3" +indexmap = { version = "2.0.2", features = ["serde"] } [dev-dependencies] eyre = { version = "0.6.8", default-features = false, features = [ "track-caller" ] } diff --git a/README.md b/README.md index f36b054d9..f4bc80d17 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,14 @@ If you used the **Determinate Nix Installer**, report issues at https://github.c [![Crates.io](https://img.shields.io/crates/v/nix-installer)](https://crates.io/crates/nix-installer) [![Docs.rs](https://img.shields.io/docsrs/nix-installer)](https://docs.rs/nix-installer/latest/nix_installer/) -`nix-installer` is an opinionated alternative to the [official Nix install scripts](https://nixos.org/download.html). +A fast, friendly, and reliable tool to help you use Nix with Flakes everywhere. ```bash curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install ``` -The `nix-installer` tool is ready to use in a number of environments: +The `nix-installer` has successfully completed over 1,000,000 installs in a number of environments, including [Github Actions](#as-a-github-action): | Platform | Multi User | `root` only | Maturity | |------------------------------|:------------------:|:-----------:|:-----------------:| @@ -35,27 +35,6 @@ The `nix-installer` tool is ready to use in a number of environments: > **Note** > On **MacOS only**, removing users and/or groups may fail if there are no users who are logged in graphically. -## Installation Differences - -Differing from the current official [Nix](https://github.com/NixOS/nix) installer scripts: - -* an installation receipt (for uninstalling) is stored at `/nix/receipt.json` as well as a copy of the install binary at `/nix/nix-installer` -* `nix-channel --update` is not run, `~/.nix-channels` is not provisioned -* `NIX_SSL_CERT_FILE` is set in the various shell profiles if the `ssl-cert-file` argument is used. - -## Motivations - -The current Nix install scripts do an excellent job, however they are difficult to maintain. Subtle differences in the shell implementations and certain characteristics of bash scripts make it difficult to make meaningful changes to the installer. - -Our team wishes to experiment with the idea of an installer in a more structured language and see if this is a worthwhile alternative. Along the way, we are also exploring a few other ideas, such as: - -* offering users a chance to review an accurate, calculated install plan -* having 'planners' which can create appropriate install plans -* keeping an installation receipt for uninstallation -* offering users with a failing install the chance to do a best-effort revert -* doing whatever tasks we can in parallel - -So far, our explorations have been quite fruitful, so we wanted to share and keep exploring. ## Usage @@ -70,11 +49,9 @@ Or, to download a platform specific Installer binary yourself: ```bash $ curl -sL -o nix-installer https://install.determinate.systems/nix/nix-installer-x86_64-linux $ chmod +x nix-installer +$ ./nix-installer ``` -> **Note** -> `nix-installer` will elevate itself if needed using `sudo`. If you use `doas` or `please` you may need to elevate `nix-installer` yourself. - `nix-installer` installs Nix by following a *plan* made by a *planner*. Review the available planners: ```bash @@ -128,8 +105,17 @@ $ curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/ni $ NIX_BUILD_GROUP_NAME=nixbuilder ./nix-installer install linux-multi --nix-build-group-id 4000 ``` +### Upgrading Nix + +You can upgrade Nix (to the version specified [here](https://raw.githubusercontent.com/NixOS/nixpkgs/master/nixos/modules/installer/tools/nix-fallback-paths.nix)) by running: + +``` +sudo -i nix upgrade-nix +``` + +Alternatively, you can [uninstall](#uninstalling) and [reinstall](#usage) with a different version of the `nix-installer`. -## Uninstalling +### Uninstalling You can remove a `nix-installer`-installed Nix by running @@ -138,7 +124,7 @@ You can remove a `nix-installer`-installed Nix by running ``` -## As a Github Action +### As a Github Action You can use the [`nix-installer-action`](https://github.com/DeterminateSystems/nix-installer-action) Github Action like so: @@ -160,7 +146,7 @@ jobs: run: nix build . ``` -## Without systemd (Linux only) +### Without systemd (Linux only) > **Warning** > When `--init none` is used, _only_ `root` or users who can elevate to `root` privileges can run Nix: @@ -175,7 +161,7 @@ If you don't use [systemd], you can still install Nix by explicitly specifying t curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install linux --init none ``` -## In a container +### In a container In Docker/Podman containers or WSL2 instances where an init (like `systemd`) is not present, pass `--init none`. @@ -238,15 +224,29 @@ podman rmi $IMAGE On some container tools, such as `docker`, `sandbox = false` can be omitted. Omitting it will negatively impact compatibility with container tools like `podman`. -## In WSL2 +### In WSL2 + +We **strongly recommend** [enabling systemd](https://ubuntu.com/blog/ubuntu-wsl-enable-systemd), then installing Nix as normal: -If [systemd is enabled](https://ubuntu.com/blog/ubuntu-wsl-enable-systemd) it's possible to install Nix as normal using the command at the top of this document: ```bash curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install ``` -If systemd is not enabled, pass `--init none` at the end of the command: +If [WSLg][wslg] is enabled, you can do things like open a Linux Firefox from Windows on Powershell: + +```powershell +wsl nix run nixpkgs#firefox +``` + +To use some OpenGL applications, you can use [`nixGL`][nixgl] (note that some applications, such as `blender`, may not work): + +```powershell +wsl nix run --impure github:guibou/nixGL nix run nixpkgs#obs-studio +``` + + +If enabling system is not an option, pass `--init none` at the end of the command: > **Warning** > When `--init none` is used, _only_ `root` or users who can elevate to `root` privileges can run Nix: @@ -260,7 +260,7 @@ If systemd is not enabled, pass `--init none` at the end of the command: curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install linux --init none ``` -## Skip confirmation +### Skip confirmation If you'd like to bypass the confirmation step, you can apply the `--no-confirm` flag: @@ -270,6 +270,46 @@ curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix This is especially useful when using the installer in non-interactive scripts. + +## Quirks + +While `nix-installer` tries to provide a comprehensive and unquirky experience, there are unfortunately some issues which may require manual intervention or operator choices. + +### Using MacOS after removing `nix` while `nix-darwin` was still installed, network requests fail + +If `nix` was previously uninstalled without uninstalling `nix-darwin` first, users may experience errors similar to this: + +```bash +$ nix shell nixpkgs#curl +error: unable to download 'https://cache.nixos.org/g8bqlgmpa4yg601w561qy2n576i6g0vh.narinfo': Problem with the SSL CA cert (path? access rights?) (77) +``` + +This occurs because `nix-darwin` provisions an `org.nixos.activate-system` service which remains after Nix is uninstalled. +The `org.nixos.activate-system` service in this state interacts with the newly installed Nix and changes the SSL certificates it uses to be a broken symlink. + +```bash +$ ls -lah /etc/ssl/certs +total 0 +drwxr-xr-x 3 root wheel 96B Oct 17 08:26 . +drwxr-xr-x 6 root wheel 192B Sep 16 06:28 .. +lrwxr-xr-x 1 root wheel 41B Oct 17 08:26 ca-certificates.crt -> /etc/static/ssl/certs/ca-certificates.crt +``` + +The problem is compounded by the matter that the [`nix-darwin` uninstaller](https://github.com/LnL7/nix-darwin#uninstalling) will not work after uninstalling Nix, since it uses Nix and requires network connectivity. + +It's possible to resolve this situation by removing the `org.nixos.activate-system` service and the `ca-certificates`: + +```bash +$ sudo rm /Library/LaunchDaemons/org.nixos.activate-system.plist +$ sudo launchctl bootout system/org.nixos.activate-system +$ /nix/nix-installer uninstall +$ sudo rm /etc/ssl/certs/ca-certificates.crt +``` + +Then run the `nix-installer` again, and it should work. + +Up-to-date versions of the `nix-installer` will refuse to uninstall until `nix-darwin` is uninstalled first, helping mitigate this problem. + ## Building a binary Since you'll be using `nix-installer` to install Nix on systems without Nix, the default build is a static binary. @@ -300,7 +340,7 @@ nix build -L "github:determinatesystems/nix-installer/$NIX_INSTALLER_TAG#nix-ins Then copy the `result/bin/nix-installer` to the machine you wish to run it on. -You can also add `nix-installer` to a system without Nix via `cargo`: +You can also add `nix-installer` to a system without Nix via `cargo`, there are no system dependencies to worry about: ```bash # to build and run a local copy @@ -323,7 +363,7 @@ To make this build portable, pass ` --target x86_64-unknown-linux-musl`. ## As a library > **Warning** -> Use as a library is still experimental, if you're using this, please let us know and we can make a path to stablization. +> Use as a library is still experimental. This feature is likely to be removed in the future without an advocate. If you're using this, please let us know and we can make a path to stablization. Add `nix-installer` to your dependencies: @@ -374,52 +414,36 @@ curl -sSf -L https://github.com/DeterminateSystems/nix-installer/releases/downlo ./nix-installer install ``` -## Quirks -While `nix-installer` tries to provide a comprehensive and unquirky experience, there are unfortunately some issues which may require manual intervention or operator choices. +## Installation Differences -### Using MacOS remote SSH builders, Nix binaries are not on `$PATH` +Differing from the upstream [Nix](https://github.com/NixOS/nix) installer scripts: -When connecting to a Mac remote SSH builder users may sometimes see this error: +* an installation receipt (for uninstalling) is stored at `/nix/receipt.json` as well as a copy of the install binary at `/nix/nix-installer` +* `nix-channel --update` is not run, `~/.nix-channels` is not provisioned +* `ssl-cert-file` is set in `/etc/nix/nix.conf` if the `ssl-cert-file` argument is used. + +## Motivations + +The existing upstream scripts do a good job, however they are difficult to maintain. + +Subtle differences in the shell implementations and tool used in the scripts make it difficult to make meaningful changes to the installer. + +The Determinate Nix installer has numerous advantages: + +* survives macOS upgrades +* keeping an installation receipt for easy uninstallation +* offering users a chance to review an accurate, calculated install plan +* having 'planners' which can create appropriate install plans for complicated targets +* offering users with a failing install the chance to do a best-effort revert +* improving performance by maximizing parallel operations +* supporting a expanded test suite including 'curing' cases +* supporting SELinux and OSTree based distributions without asking users to make compromises +* operating as a single, static binary with external dependencies such as `openssl`, only calling existing system tools (like `useradd`) where necessary +* As a MacOS remote build target, ensures `nix` is not absent from path + +It has been wonderful to collaborate with other participants in the Nix Installer Working Group and members of the broader community. The working group maintains a [foundation owned fork of the installer](https://github.com/nixos/experimental-nix-installer/). -```bash -$ nix store ping --store "ssh://$USER@$HOST" -Store URL: ssh://$USER@$HOST -zsh:1: command not found: nix-store -error: cannot connect to '$USER@$HOST' -``` - -The way MacOS populates the `PATH` environment differs from other environments. ([Some background](https://gist.github.com/Linerre/f11ad4a6a934dcf01ee8415c9457e7b2)) - -There are two possible workarounds for this: - -* **(Preferred)** Update the remote builder URL to include the `remote-program` parameter pointing to `nix-store`. For example: - ```bash - nix store ping --store "ssh://$USER@$HOST?remote-program=/nix/var/nix/profiles/default/bin/nix-store" - ``` - If you are unsure where the `nix-store` binary is located, run `which nix-store` on the remote. -* Update `/etc/zshenv` on the remote so that `zsh` populates the Nix path for every shell, even those that are neither *interactive* or *login*: - ```bash - # Nix - if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then - . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' - fi - # End Nix - ``` -
- This strategy has some behavioral caveats, namely, $PATH may have unexpected contents - - For example, if `$PATH` gets unset then a script invoked, `$PATH` may not be as empty as expected: - ```bash - $ cat example.sh - #! /bin/zsh - echo $PATH - $ PATH= ./example.sh - /Users/ephemeraladmin/.nix-profile/bin:/nix/var/nix/profiles/default/bin: - ``` - This strategy results in Nix's paths being present on `$PATH` twice and may have a minor impact on performance. - -
## Diagnostics @@ -439,6 +463,7 @@ Here is a table of the [diagnostic data we collect][diagnosticdata]: | `is_ci` | Whether the installer is being used in CI (e.g. GitHub Actions). | | `action` | Either `Install` or `Uninstall`. | | `status` | One of `Success`, `Failure`, `Pending`, or `Cancelled`. | +| `attribution` | Optionally defined by the user, associate the diagnostics of this run to the provided value. | | `failure_chain` | A high level description of what the failure was, if any. For example: `Command("diskutil")` if the command `diskutil list` failed. | To disable diagnostic reporting, set the diagnostics URL to an empty string by passing `--diagnostic-endpoint=""` or setting `NIX_INSTALLER_DIAGNOSTIC_ENDPOINT=""`. @@ -449,3 +474,5 @@ You can read the full privacy policy for [Determinate Systems][detsys], the crea [diagnosticdata]: https://github.com/DeterminateSystems/nix-installer/blob/f9f927840d532b71f41670382a30cfcbea2d8a35/src/diagnostics.rs#L29-L43 [privacy]: https://determinate.systems/privacy [systemd]: https://systemd.io +[wslg]: https://github.com/microsoft/wslg +[nixgl]: https://github.com/guibou/nixGL diff --git a/flake.lock b/flake.lock index eef483d9c..c4849219d 100644 --- a/flake.lock +++ b/flake.lock @@ -8,20 +8,31 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1687760804, - "narHash": "sha256-4aJlNuAI+AjrUid9hmjdqwJxT7y/HCHptC2dtDmuEWU=", - "owner": "nix-community", - "repo": "fenix", - "rev": "2102665784dba3f11314e37b4027794ccd324f1d", - "type": "github" + "narHash": "sha256-0dZpggYjjmWEk+rGixiBHOHuQfLzEzNfrtjSig04s6Q=", + "rev": "9ccae1754eec0341b640d5705302ac0923d22875", + "revCount": 1618, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/nix-community/fenix/0.1.1618%2Brev-9ccae1754eec0341b640d5705302ac0923d22875/018aea4c-03c9-7734-95d5-b84cc8881e3d/source.tar.gz" }, "original": { - "owner": "nix-community", - "repo": "fenix", - "type": "github" + "type": "tarball", + "url": "https://flakehub.com/f/nix-community/fenix/0.1.1584.tar.gz" } }, "flake-compat": { + "locked": { + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "revCount": 57, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://flakehub.com/f/edolstra/flake-compat/1.0.0.tar.gz" + } + }, + "flake-compat_2": { "flake": false, "locked": { "lastModified": 1673956053, @@ -60,11 +71,11 @@ ] }, "locked": { - "lastModified": 1686572087, - "narHash": "sha256-jXTut7ZSYqLEgm/nTk7TuVL2ExahTip605bLINklAnQ=", + "lastModified": 1694081375, + "narHash": "sha256-vzJXOUnmkMCm3xw8yfPP5m8kypQ3BhAIRe4RRCWpzy8=", "owner": "nix-community", "repo": "naersk", - "rev": "8507af04eb40c5520bd35d9ce6f9d2342cea5ad1", + "rev": "3f976d822b7b37fc6fb8e6f157c2dd05e7e94e89", "type": "github" }, "original": { @@ -75,37 +86,35 @@ }, "nix": { "inputs": { + "flake-compat": "flake-compat_2", "lowdown-src": "lowdown-src", "nixpkgs": "nixpkgs", "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1674678482, - "narHash": "sha256-MtVatZVsV+dtjdD4AC4bztrnDFas+WZYHzQMt41FwzU=", - "owner": "nixos", - "repo": "nix", - "rev": "435a16b5556f4171b4204a3f65c9dedf215f168c", - "type": "github" + "narHash": "sha256-WNmifcTsN9aG1ONkv+l2BC4sHZZxtNKy0keqBHXXQ7w=", + "rev": "f5f4de6a550327b4b1a06123c2e450f1b92c73b6", + "revCount": 14900, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/NixOS/nix/2.18.1/018af406-b173-7112-9c1c-82f5b645e9d3/source.tar.gz" }, "original": { - "owner": "nixos", - "ref": "2.13.2", - "repo": "nix", - "type": "github" + "type": "tarball", + "url": "https://flakehub.com/f/NixOS/nix/2.18.0.tar.gz" } }, "nixpkgs": { "locked": { - "lastModified": 1670461440, - "narHash": "sha256-jy1LB8HOMKGJEGXgzFRLDU1CBGL0/LlkolgnqIsF0D8=", + "lastModified": 1695283060, + "narHash": "sha256-CJz71xhCLlRkdFUSQEL0pIAAfcnWFXMzd9vXhPrnrEg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "04a75b2eecc0acf6239acf9dd04485ff8d14f425", + "rev": "31ed632c692e6a36cfc18083b88ece892f863ed4", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-22.11-small", + "ref": "nixos-23.05-small", "repo": "nixpkgs", "type": "github" } @@ -128,18 +137,15 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1687701825, - "narHash": "sha256-aMC9hqsf+4tJL7aJWSdEUurW2TsjxtDcJBwM9Y4FIYM=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "07059ee2fa34f1598758839b9af87eae7f7ae6ea", - "type": "github" + "narHash": "sha256-9NJcFF9CEYPvHJ5ckE8kvINvI84SZZ87PvqMbH6pro0=", + "rev": "5e4c2ada4fcd54b99d56d7bd62f384511a7e2593", + "revCount": 534806, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.534806%2Brev-5e4c2ada4fcd54b99d56d7bd62f384511a7e2593/018b29e9-ae6d-72f2-993b-19cb9a64a3b5/source.tar.gz" }, "original": { - "owner": "nixos", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" + "type": "tarball", + "url": "https://flakehub.com/f/NixOS/nixpkgs/0.1.0.tar.gz" } }, "root": { @@ -154,11 +160,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1687732103, - "narHash": "sha256-5Jn/Nj/xgcjTT289Itng55GLUBTEIULPndl/XrGkUwQ=", + "lastModified": 1696050837, + "narHash": "sha256-2K3Aq4gjPZBDnkAMJaMA4ElE+BNbmrqtSBWtt9kPGaM=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "4a2ceeff0fb53de168691b0f55d9808d221b867e", + "rev": "0840038f02daec6ba3238f05d8caa037d28701a0", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 3ca9d8643..c38feb808 100644 --- a/flake.nix +++ b/flake.nix @@ -2,10 +2,10 @@ description = "The Determinate Nix Installer"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1.0.tar.gz"; fenix = { - url = "github:nix-community/fenix"; + url = "https://flakehub.com/f/nix-community/fenix/0.1.1584.tar.gz"; inputs.nixpkgs.follows = "nixpkgs"; }; @@ -15,11 +15,11 @@ }; nix = { - url = "github:nixos/nix/2.13.2"; + url = "https://flakehub.com/f/NixOS/nix/2.18.0.tar.gz"; # Omitting `inputs.nixpkgs.follows = "nixpkgs";` on purpose }; - flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; + flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.0.0.tar.gz"; }; outputs = @@ -66,7 +66,7 @@ }; sharedAttrs = { pname = "nix-installer"; - version = "0.10.1-unreleased"; + version = "0.14.0"; src = builtins.path { name = "nix-installer-source"; path = self; @@ -138,16 +138,19 @@ cargo-outdated cacert cargo-audit + cargo-watch nixpkgs-fmt check.check-rustfmt check.check-spelling check.check-nixpkgs-fmt check.check-editorconfig check.check-semver + check.check-clippy ] ++ lib.optionals (pkgs.stdenv.isDarwin) (with pkgs; [ libiconv darwin.apple_sdk.frameworks.Security + darwin.apple_sdk.frameworks.SystemConfiguration ]) ++ lib.optionals (pkgs.stdenv.isLinux) (with pkgs; [ checkpolicy diff --git a/nix/check.nix b/nix/check.nix index 6c1d6e4e8..1ca091821 100644 --- a/nix/check.nix +++ b/nix/check.nix @@ -50,4 +50,13 @@ in cargo-semver-checks semver-checks check-release ''; }); + # Clippy + check-clippy = (writeShellApplication { + name = "check-clippy"; + runtimeInputs = with pkgs; [ cargo clippy rustc ]; + text = '' + cargo clippy + ''; + }); + } diff --git a/nix/tests/vm-test/default.nix b/nix/tests/vm-test/default.nix index d806da6a8..63eb2a543 100644 --- a/nix/tests/vm-test/default.nix +++ b/nix/tests/vm-test/default.nix @@ -4,7 +4,7 @@ let nix-installer-install = '' NIX_PATH=$(readlink -f nix.tar.xz) - RUST_BACKTRACE="full" ./nix-installer install --nix-package-url "file://$NIX_PATH" --no-confirm --logger pretty --log-directive nix_installer=trace + RUST_BACKTRACE="full" ./nix-installer install --nix-package-url "file://$NIX_PATH" --no-confirm --logger pretty --log-directive nix_installer=info ''; nix-installer-install-quiet = '' NIX_PATH=$(readlink -f nix.tar.xz) @@ -135,7 +135,7 @@ let install-no-start-daemon = { install = '' NIX_PATH=$(readlink -f nix.tar.xz) - RUST_BACKTRACE="full" ./nix-installer install linux --nix-package-url "file://$NIX_PATH" --no-confirm --logger pretty --log-directive nix_installer=trace --no-start-daemon + RUST_BACKTRACE="full" ./nix-installer install linux --nix-package-url "file://$NIX_PATH" --no-confirm --logger pretty --log-directive nix_installer=info --no-start-daemon ''; check = '' set -ex @@ -162,7 +162,7 @@ let install-daemonless = { install = '' NIX_PATH=$(readlink -f nix.tar.xz) - RUST_BACKTRACE="full" ./nix-installer install linux --nix-package-url "file://$NIX_PATH" --no-confirm --logger pretty --log-directive nix_installer=trace --init none + RUST_BACKTRACE="full" ./nix-installer install linux --nix-package-url "file://$NIX_PATH" --no-confirm --logger pretty --log-directive nix_installer=info --init none ''; check = '' set -ex @@ -212,11 +212,27 @@ let uninstall = installCases.install-default.uninstall; uninstallCheck = installCases.install-default.uninstallCheck; }; - cure-self-linux-broken-missing-group = { + cure-self-linux-broken-missing-users = { + preinstall = '' + ${nix-installer-install-quiet} + sudo mv /nix/receipt.json /nix/old-receipt.json + sudo userdel nixbld1 + sudo userdel nixbld3 + sudo userdel nixbld16 + ''; + install = installCases.install-default.install; + check = installCases.install-default.check; + uninstall = installCases.install-default.uninstall; + uninstallCheck = installCases.install-default.uninstallCheck; + }; + cure-self-linux-broken-missing-users-and-group = { preinstall = '' NIX_PATH=$(readlink -f nix.tar.xz) RUST_BACKTRACE="full" ./nix-installer install --nix-package-url "file://$NIX_PATH" --no-confirm sudo mv /nix/receipt.json /nix/old-receipt.json + for i in {1..32}; do + sudo userdel "nixbld''${i}" + done sudo groupdel nixbld ''; install = installCases.install-default.install; @@ -358,10 +374,13 @@ let ''; in { - uninstall-groups-missing = { + uninstall-users-and-groups-missing = { install = installCases.install-default.install; check = installCases.install-default.check; preuninstall = '' + for i in $(seq 1 32); do + sudo userdel nixbld$i + done sudo groupdel nixbld ''; uninstall = uninstallFailExpected; @@ -376,15 +395,6 @@ let uninstall = uninstallFailExpected; uninstallCheck = installCases.install-default.uninstallCheck; }; - uninstall-shell-profile-clobbered = { - install = installCases.install-default.install; - check = installCases.install-default.check; - preuninstall = '' - sudo rm -rf /etc/bashrc - ''; - uninstall = uninstallFailExpected; - uninstallCheck = installCases.install-default.uninstallCheck; - }; }; disableSELinux = "sudo setenforce 0"; diff --git a/src/action/base/create_file.rs b/src/action/base/create_file.rs index 31cf16d49..18667ff2b 100644 --- a/src/action/base/create_file.rs +++ b/src/action/base/create_file.rs @@ -266,6 +266,10 @@ impl Action for CreateFile { buf: _, force: _, } = self; + // The user already deleted it + if !path.exists() { + return Ok(()); + } remove_file(&path) .await diff --git a/src/action/base/create_or_insert_into_file.rs b/src/action/base/create_or_insert_into_file.rs index 3deedd299..cf0e23749 100644 --- a/src/action/base/create_or_insert_into_file.rs +++ b/src/action/base/create_or_insert_into_file.rs @@ -339,6 +339,11 @@ impl Action for CreateOrInsertIntoFile { buf, position: _, } = self; + // The user already deleted it + if !path.exists() { + return Ok(()); + } + let mut file = OpenOptions::new() .create(false) .write(true) diff --git a/src/action/base/create_or_merge_nix_config.rs b/src/action/base/create_or_merge_nix_config.rs index 250ac0be1..4a0239cf8 100644 --- a/src/action/base/create_or_merge_nix_config.rs +++ b/src/action/base/create_or_merge_nix_config.rs @@ -409,10 +409,10 @@ impl Action for CreateOrMergeNixConfig { new_config.push('\n'); } - new_config.push_str(&format!( - "# Generated by https://github.com/DeterminateSystems/nix-installer, version {version}.\n", - version = env!("CARGO_PKG_VERSION"), - )); + new_config + .push_str("# Generated by https://github.com/DeterminateSystems/nix-installer.\n"); + new_config.push_str("# See `/nix/nix-installer --version` for the version details.\n"); + new_config.push_str("\n"); for (name, value) in merged_nix_config.settings() { new_config.push_str(name); diff --git a/src/action/base/fetch_and_unpack_nix.rs b/src/action/base/fetch_and_unpack_nix.rs index 9ab95550a..280982300 100644 --- a/src/action/base/fetch_and_unpack_nix.rs +++ b/src/action/base/fetch_and_unpack_nix.rs @@ -7,6 +7,7 @@ use tracing::{span, Span}; use crate::{ action::{Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction}, parse_ssl_cert, + settings::UrlOrPath, }; /** @@ -14,7 +15,7 @@ Fetch a URL to the given path */ #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct FetchAndUnpackNix { - url: Url, + url_or_path: UrlOrPath, dest: PathBuf, proxy: Option, ssl_cert_file: Option, @@ -23,7 +24,7 @@ pub struct FetchAndUnpackNix { impl FetchAndUnpackNix { #[tracing::instrument(level = "debug", skip_all)] pub async fn plan( - url: Url, + url_or_path: UrlOrPath, dest: PathBuf, proxy: Option, ssl_cert_file: Option, @@ -31,10 +32,12 @@ impl FetchAndUnpackNix { // TODO(@hoverbear): Check URL exists? // TODO(@hoverbear): Check tempdir exists - match url.scheme() { - "https" | "http" | "file" => (), - _ => return Err(Self::error(FetchUrlError::UnknownUrlScheme)), - }; + if let UrlOrPath::Url(url) = &url_or_path { + match url.scheme() { + "https" | "http" | "file" => (), + _ => return Err(Self::error(ActionErrorKind::UnknownUrlScheme)), + } + } if let Some(proxy) = &proxy { match proxy.scheme() { @@ -48,7 +51,7 @@ impl FetchAndUnpackNix { } Ok(Self { - url, + url_or_path, dest, proxy, ssl_cert_file, @@ -64,14 +67,14 @@ impl Action for FetchAndUnpackNix { ActionTag("fetch_and_unpack_nix") } fn tracing_synopsis(&self) -> String { - format!("Fetch `{}` to `{}`", self.url, self.dest.display()) + format!("Fetch `{}` to `{}`", self.url_or_path, self.dest.display()) } fn tracing_span(&self) -> Span { let span = span!( tracing::Level::DEBUG, "fetch_and_unpack_nix", - url = tracing::field::display(&self.url), + url_or_path = tracing::field::display(&self.url_or_path), proxy = tracing::field::Empty, ssl_cert_file = tracing::field::Empty, dest = tracing::field::display(self.dest.display()), @@ -94,47 +97,60 @@ impl Action for FetchAndUnpackNix { #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { - let bytes = match self.url.scheme() { - "https" | "http" => { - let mut buildable_client = reqwest::Client::builder(); - if let Some(proxy) = &self.proxy { - buildable_client = buildable_client.proxy( - reqwest::Proxy::all(proxy.clone()) - .map_err(FetchUrlError::Reqwest) - .map_err(Self::error)?, - ) - } - if let Some(ssl_cert_file) = &self.ssl_cert_file { - let ssl_cert = parse_ssl_cert(ssl_cert_file).await.map_err(Self::error)?; - buildable_client = buildable_client.add_root_certificate(ssl_cert); - } - let client = buildable_client - .build() - .map_err(FetchUrlError::Reqwest) - .map_err(Self::error)?; - let req = client - .get(self.url.clone()) - .build() - .map_err(FetchUrlError::Reqwest) - .map_err(Self::error)?; - let res = client - .execute(req) - .await - .map_err(FetchUrlError::Reqwest) - .map_err(Self::error)?; - res.bytes() - .await - .map_err(FetchUrlError::Reqwest) - .map_err(Self::error)? + let bytes = match &self.url_or_path { + UrlOrPath::Url(url) => { + let bytes = match url.scheme() { + "https" | "http" => { + let mut buildable_client = reqwest::Client::builder(); + if let Some(proxy) = &self.proxy { + buildable_client = buildable_client.proxy( + reqwest::Proxy::all(proxy.clone()) + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)?, + ) + } + if let Some(ssl_cert_file) = &self.ssl_cert_file { + let ssl_cert = + parse_ssl_cert(ssl_cert_file).await.map_err(Self::error)?; + buildable_client = buildable_client.add_root_certificate(ssl_cert); + } + let client = buildable_client + .build() + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)?; + let req = client + .get(url.clone()) + .build() + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)?; + let res = client + .execute(req) + .await + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)?; + res.bytes() + .await + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)? + }, + "file" => { + let buf = tokio::fs::read(url.path()) + .await + .map_err(|e| ActionErrorKind::Read(PathBuf::from(url.path()), e)) + .map_err(Self::error)?; + Bytes::from(buf) + }, + _ => return Err(Self::error(ActionErrorKind::UnknownUrlScheme)), + }; + bytes }, - "file" => { - let buf = tokio::fs::read(self.url.path()) + UrlOrPath::Path(path) => { + let buf = tokio::fs::read(path) .await - .map_err(|e| ActionErrorKind::Read(PathBuf::from(self.url.path()), e)) + .map_err(|e| ActionErrorKind::Read(PathBuf::from(path), e)) .map_err(Self::error)?; Bytes::from(buf) }, - _ => return Err(Self::error(FetchUrlError::UnknownUrlScheme)), }; // TODO(@Hoverbear): Pick directory @@ -167,16 +183,8 @@ impl Action for FetchAndUnpackNix { #[non_exhaustive] #[derive(Debug, thiserror::Error)] pub enum FetchUrlError { - #[error("Request error")] - Reqwest( - #[from] - #[source] - reqwest::Error, - ), #[error("Unarchiving error")] Unarchive(#[source] std::io::Error), - #[error("Unknown url scheme, `file://`, `https://` and `http://` supported")] - UnknownUrlScheme, #[error("Unknown proxy scheme, `https://`, `socks5://`, and `http://` supported")] UnknownProxyScheme, } diff --git a/src/action/base/move_unpacked_nix.rs b/src/action/base/move_unpacked_nix.rs index 875e3903e..37e034ff8 100644 --- a/src/action/base/move_unpacked_nix.rs +++ b/src/action/base/move_unpacked_nix.rs @@ -1,5 +1,4 @@ use std::{ - fs::Permissions, os::unix::prelude::PermissionsExt, path::{Path, PathBuf}, }; @@ -110,13 +109,21 @@ impl Action for MoveUnpackedNix { .map_err(|e| ActionErrorKind::Rename(entry.path(), entry_dest.to_owned(), e)) .map_err(Self::error)?; - let perms: Permissions = PermissionsExt::from_mode(0o555); for entry_item in WalkDir::new(&entry_dest) .into_iter() .filter_map(Result::ok) .filter(|e| !e.file_type().is_symlink()) { - tokio::fs::set_permissions(&entry_item.path(), perms.clone()) + let path = entry_item.path(); + + let mut perms = path + .metadata() + .map_err(|e| ActionErrorKind::GetMetadata(path.to_owned(), e)) + .map_err(Self::error)? + .permissions(); + perms.set_readonly(true); + + tokio::fs::set_permissions(path, perms.clone()) .await .map_err(|e| { ActionErrorKind::SetPermissions( diff --git a/src/action/common/configure_init_service.rs b/src/action/common/configure_init_service.rs index 71a3536d9..1997abccf 100644 --- a/src/action/common/configure_init_service.rs +++ b/src/action/common/configure_init_service.rs @@ -182,7 +182,7 @@ impl Action for ConfigureInitService { execute_command( Command::new("launchctl") .process_group(0) - .args(&["load", "-w"]) + .args(["load", "-w"]) .arg(DARWIN_NIX_DAEMON_DEST) .stdin(std::process::Stdio::null()), ) @@ -192,7 +192,7 @@ impl Action for ConfigureInitService { let domain = "system"; let service = "org.nixos.nix-daemon"; - let is_disabled = crate::action::macos::service_is_disabled(&domain, &service) + let is_disabled = crate::action::macos::service_is_disabled(domain, service) .await .map_err(Self::error)?; if is_disabled { @@ -395,7 +395,7 @@ impl Action for ConfigureInitService { .arg(DARWIN_NIX_DAEMON_DEST), ) .await - .map_err(|e| Self::error(e))?; + .map_err(Self::error)?; }, #[cfg(target_os = "linux")] InitSystem::Systemd => { diff --git a/src/action/common/configure_nix.rs b/src/action/common/configure_nix.rs index b3225db39..9088c22e3 100644 --- a/src/action/common/configure_nix.rs +++ b/src/action/common/configure_nix.rs @@ -43,6 +43,7 @@ impl ConfigureNix { }; let place_nix_configuration = PlaceNixConfiguration::plan( settings.nix_build_group_name.clone(), + settings.proxy.clone(), settings.ssl_cert_file.clone(), settings.extra_conf.clone(), settings.force, diff --git a/src/action/common/configure_shell_profile.rs b/src/action/common/configure_shell_profile.rs index f0ba421c0..d6fc90017 100644 --- a/src/action/common/configure_shell_profile.rs +++ b/src/action/common/configure_shell_profile.rs @@ -45,11 +45,11 @@ impl ConfigureShellProfile { let profile_target_path = Path::new(profile_target); if let Some(parent) = profile_target_path.parent() { if !parent.exists() { - tracing::trace!( - "Did not plan to edit `{}` as its parent folder does not exist.", - profile_target.display(), + create_directories.push( + CreateDirectory::plan(parent, None, None, 0o0755, false) + .await + .map_err(Self::error)?, ); - continue; } create_or_insert_files.push( CreateOrInsertIntoFile::plan( @@ -60,7 +60,8 @@ impl ConfigureShellProfile { shell_buf.to_string(), create_or_insert_into_file::Position::Beginning, ) - .await?, + .await + .map_err(Self::error)?, ); } } diff --git a/src/action/common/place_nix_configuration.rs b/src/action/common/place_nix_configuration.rs index 5c0809350..8c1da8b08 100644 --- a/src/action/common/place_nix_configuration.rs +++ b/src/action/common/place_nix_configuration.rs @@ -1,10 +1,13 @@ use tracing::{span, Span}; +use url::Url; use crate::action::base::create_or_merge_nix_config::CreateOrMergeNixConfigError; use crate::action::base::{CreateDirectory, CreateOrMergeNixConfig}; use crate::action::{ Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction, }; +use crate::parse_ssl_cert; +use crate::settings::UrlOrPathOrString; use std::path::PathBuf; const NIX_CONF_FOLDER: &str = "/etc/nix"; @@ -23,11 +26,64 @@ impl PlaceNixConfiguration { #[tracing::instrument(level = "debug", skip_all)] pub async fn plan( nix_build_group_name: String, + proxy: Option, ssl_cert_file: Option, - extra_conf: Vec, + extra_conf: Vec, force: bool, ) -> Result, ActionError> { - let extra_conf = extra_conf.join("\n"); + let mut extra_conf_text = vec![]; + for extra in extra_conf { + let buf = match &extra { + UrlOrPathOrString::Url(url) => match url.scheme() { + "https" | "http" => { + let mut buildable_client = reqwest::Client::builder(); + if let Some(proxy) = &proxy { + buildable_client = buildable_client.proxy( + reqwest::Proxy::all(proxy.clone()) + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)?, + ) + } + if let Some(ssl_cert_file) = &ssl_cert_file { + let ssl_cert = + parse_ssl_cert(ssl_cert_file).await.map_err(Self::error)?; + buildable_client = buildable_client.add_root_certificate(ssl_cert); + } + let client = buildable_client + .build() + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)?; + let req = client + .get(url.clone()) + .build() + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)?; + let res = client + .execute(req) + .await + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)?; + res.text() + .await + .map_err(ActionErrorKind::Reqwest) + .map_err(Self::error)? + }, + "file" => tokio::fs::read_to_string(url.path()) + .await + .map_err(|e| ActionErrorKind::Read(PathBuf::from(url.path()), e)) + .map_err(Self::error)?, + _ => return Err(Self::error(ActionErrorKind::UnknownUrlScheme)), + }, + UrlOrPathOrString::Path(path) => tokio::fs::read_to_string(path) + .await + .map_err(|e| ActionErrorKind::Read(PathBuf::from(path), e)) + .map_err(Self::error)?, + UrlOrPathOrString::String(string) => string.clone(), + }; + extra_conf_text.push(buf) + } + + let extra_conf = extra_conf_text.join("\n"); let mut nix_config = nix_config_parser::NixConfig::parse_string(extra_conf, None) .map_err(CreateOrMergeNixConfigError::ParseNixConfig) .map_err(Self::error)?; @@ -37,9 +93,9 @@ impl PlaceNixConfiguration { #[cfg(not(feature = "nix-community"))] { - use std::collections::hash_map::Entry; + use indexmap::map::Entry; - let experimental_features = ["nix-command", "flakes", "auto-allocate-uids"]; + let experimental_features = ["nix-command", "flakes", "repl-flake"]; match settings.entry("experimental-features".to_string()) { Entry::Occupied(mut slot) => { let slot_mut = slot.get_mut(); @@ -63,25 +119,20 @@ impl PlaceNixConfiguration { "bash-prompt-prefix".to_string(), "(nix:$name)\\040".to_string(), ); + settings.insert("max-jobs".to_string(), "auto".to_string()); + if let Some(ssl_cert_file) = ssl_cert_file { + let ssl_cert_file_canonical = ssl_cert_file + .canonicalize() + .map_err(|e| Self::error(ActionErrorKind::Canonicalize(ssl_cert_file, e)))?; + settings.insert( + "ssl-cert-file".to_string(), + ssl_cert_file_canonical.display().to_string(), + ); + } settings.insert( "extra-nix-path".to_string(), "nixpkgs=flake:nixpkgs".to_string(), ); - - // Auto-allocate uids is broken on Mac. Tools like `whoami` don't work. - // e.g. https://github.com/NixOS/nix/issues/8444 - #[cfg(not(target_os = "macos"))] - settings.insert("auto-allocate-uids".to_string(), "true".to_string()); - } - - if let Some(ssl_cert_file) = ssl_cert_file { - let ssl_cert_file_canonical = ssl_cert_file - .canonicalize() - .map_err(|e| Self::error(ActionErrorKind::Canonicalize(ssl_cert_file, e)))?; - settings.insert( - "ssl-cert-file".to_string(), - ssl_cert_file_canonical.display().to_string(), - ); } let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force) diff --git a/src/action/macos/configure_remote_building.rs b/src/action/macos/configure_remote_building.rs new file mode 100644 index 000000000..3d9e03690 --- /dev/null +++ b/src/action/macos/configure_remote_building.rs @@ -0,0 +1,96 @@ +use crate::action::base::{create_or_insert_into_file, CreateOrInsertIntoFile}; +use crate::action::{Action, ActionDescription, ActionError, ActionTag, StatefulAction}; + +use std::path::Path; +use tracing::{span, Instrument, Span}; + +const PROFILE_NIX_FILE_SHELL: &str = "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"; + +/** +Configure macOS's zshenv to load the Nix environment when ForceCommand is used. +This enables remote building, which requires `ssh host nix` to work. + */ +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct ConfigureRemoteBuilding { + create_or_insert_into_file: StatefulAction, +} + +impl ConfigureRemoteBuilding { + #[tracing::instrument(level = "debug", skip_all)] + pub async fn plan() -> Result, ActionError> { + let shell_buf = format!( + r#" +# Set up Nix only on SSH connections +# See: https://github.com/DeterminateSystems/nix-installer/pull/714 +if [ -e '{PROFILE_NIX_FILE_SHELL}' ] && [ -n "${{SSH_CONNECTION}}" ] && [ "${{SHLVL}}" -eq 1 ]; then + . '{PROFILE_NIX_FILE_SHELL}' +fi +# End Nix +"# + ); + + let create_or_insert_into_file = CreateOrInsertIntoFile::plan( + Path::new("/etc/zshenv"), + None, + None, + 0o644, + shell_buf.to_string(), + create_or_insert_into_file::Position::Beginning, + ) + .await + .map_err(Self::error)?; + + Ok(Self { + create_or_insert_into_file, + } + .into()) + } +} + +#[async_trait::async_trait] +#[typetag::serde(name = "configure_remote_building")] +impl Action for ConfigureRemoteBuilding { + fn action_tag() -> ActionTag { + ActionTag("configure_remote_building") + } + fn tracing_synopsis(&self) -> String { + "Configuring zsh to support using Nix in non-interactive shells".to_string() + } + + fn tracing_span(&self) -> Span { + span!(tracing::Level::DEBUG, "configure_remote_building",) + } + + fn execute_description(&self) -> Vec { + vec![ActionDescription::new( + self.tracing_synopsis(), + vec!["Update `/etc/zshenv` to import Nix".to_string()], + )] + } + + #[tracing::instrument(level = "debug", skip_all)] + async fn execute(&mut self) -> Result<(), ActionError> { + let span = tracing::Span::current().clone(); + self.create_or_insert_into_file + .try_execute() + .instrument(span) + .await + .map_err(Self::error)?; + + Ok(()) + } + + fn revert_description(&self) -> Vec { + vec![ActionDescription::new( + "Remove the Nix configuration from zsh's non-login shells".to_string(), + vec!["Update `/etc/zshenv` to no longer import Nix".to_string()], + )] + } + + #[tracing::instrument(level = "debug", skip_all)] + async fn revert(&mut self) -> Result<(), ActionError> { + self.create_or_insert_into_file.try_revert().await?; + + Ok(()) + } +} diff --git a/src/action/macos/create_apfs_volume.rs b/src/action/macos/create_apfs_volume.rs index 1a15d86a0..401ebd865 100644 --- a/src/action/macos/create_apfs_volume.rs +++ b/src/action/macos/create_apfs_volume.rs @@ -1,3 +1,4 @@ +use std::io::Cursor; use std::path::{Path, PathBuf}; use tokio::process::Command; @@ -7,7 +8,7 @@ use crate::action::{ActionError, ActionTag, StatefulAction}; use crate::execute_command; use crate::action::{Action, ActionDescription}; -use crate::os::darwin::DiskUtilApfsListOutput; +use crate::os::darwin::{DiskUtilApfsListOutput, DiskUtilInfoOutput}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateApfsVolume { @@ -122,6 +123,38 @@ impl Action for CreateApfsVolume { #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { + let currently_mounted = { + let buf = execute_command( + Command::new("/usr/sbin/diskutil") + .process_group(0) + .args(["info", "-plist"]) + .arg(&self.name) + .stdin(std::process::Stdio::null()), + ) + .await + .map_err(Self::error)? + .stdout; + let the_plist: DiskUtilInfoOutput = + plist::from_reader(Cursor::new(buf)).map_err(Self::error)?; + + the_plist.mount_point.is_some() + }; + + // Unmounts the volume before attempting to remove it, avoiding 'in use' errors + // https://github.com/DeterminateSystems/nix-installer/issues/647 + if !currently_mounted { + execute_command( + Command::new("/usr/sbin/diskutil") + .process_group(0) + .args(["unmount", "force", &self.name]) + .stdin(std::process::Stdio::null()), + ) + .await + .map_err(Self::error)?; + } else { + tracing::debug!("Volume was already unmounted, can skip unmounting") + } + execute_command( Command::new("/usr/sbin/diskutil") .process_group(0) diff --git a/src/action/macos/create_nix_hook_service.rs b/src/action/macos/create_nix_hook_service.rs new file mode 100644 index 000000000..6ae5d1d34 --- /dev/null +++ b/src/action/macos/create_nix_hook_service.rs @@ -0,0 +1,226 @@ +use serde::{Deserialize, Serialize}; +use tracing::{span, Span}; + +use std::path::PathBuf; +use tokio::{ + fs::{remove_file, OpenOptions}, + io::AsyncWriteExt, + process::Command, +}; + +use crate::{ + action::{Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction}, + execute_command, +}; + +/** Create a plist for a `launchctl` service to re-add Nix to the zshrc after upgrades. + */ +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateNixHookService { + path: PathBuf, + service_label: String, + needs_bootout: bool, +} + +impl CreateNixHookService { + #[tracing::instrument(level = "debug", skip_all)] + pub async fn plan() -> Result, ActionError> { + let mut this = Self { + path: PathBuf::from( + "/Library/LaunchDaemons/systems.determinate.nix-installer.nix-hook.plist", + ), + service_label: "systems.determinate.nix-installer.nix-hook".into(), + needs_bootout: false, + }; + + // If the service is currently loaded or running, we need to unload it during execute (since we will then recreate it and reload it) + // This `launchctl` command may fail if the service isn't loaded + let mut check_loaded_command = Command::new("launchctl"); + check_loaded_command.process_group(0); + check_loaded_command.arg("print"); + check_loaded_command.arg(format!("system/{}", this.service_label)); + tracing::trace!( + command = format!("{:?}", check_loaded_command.as_std()), + "Executing" + ); + let check_loaded_output = check_loaded_command + .output() + .await + .map_err(|e| ActionErrorKind::command(&check_loaded_command, e)) + .map_err(Self::error)?; + this.needs_bootout = check_loaded_output.status.success(); + if this.needs_bootout { + tracing::debug!( + "Detected loaded service `{}` which needs unload before replacing `{}`", + this.service_label, + this.path.display(), + ); + } + + if this.path.exists() { + let discovered_plist: LaunchctlHookPlist = + plist::from_file(&this.path).map_err(Self::error)?; + let expected_plist = generate_plist(&this.service_label) + .await + .map_err(Self::error)?; + if discovered_plist != expected_plist { + tracing::trace!( + ?discovered_plist, + ?expected_plist, + "Parsed plists not equal" + ); + return Err(Self::error(CreateNixHookServiceError::DifferentPlist { + expected: expected_plist, + discovered: discovered_plist, + path: this.path.clone(), + })); + } + + tracing::debug!("Creating file `{}` already complete", this.path.display()); + return Ok(StatefulAction::completed(this)); + } + + Ok(StatefulAction::uncompleted(this)) + } +} + +#[async_trait::async_trait] +#[typetag::serde(name = "create_nix_hook_service")] +impl Action for CreateNixHookService { + fn action_tag() -> ActionTag { + ActionTag("create_nix_hook_service") + } + fn tracing_synopsis(&self) -> String { + format!( + "{maybe_unload} a `launchctl` plist to put Nix into your PATH", + maybe_unload = if self.needs_bootout { + "Unload, then recreate" + } else { + "Create" + } + ) + } + + fn tracing_span(&self) -> Span { + let span = span!( + tracing::Level::DEBUG, + "create_nix_hook_service", + path = tracing::field::display(self.path.display()), + buf = tracing::field::Empty, + ); + + span + } + + fn execute_description(&self) -> Vec { + vec![ActionDescription::new(self.tracing_synopsis(), vec![])] + } + + #[tracing::instrument(level = "debug", skip_all)] + async fn execute(&mut self) -> Result<(), ActionError> { + let Self { + path, + service_label, + needs_bootout, + } = self; + + if *needs_bootout { + execute_command( + Command::new("launchctl") + .process_group(0) + .arg("bootout") + .arg(format!("system/{service_label}")), + ) + .await + .map_err(Self::error)?; + } + + let generated_plist = generate_plist(service_label).await.map_err(Self::error)?; + + let mut options = OpenOptions::new(); + options.create(true).write(true).read(true); + + let mut file = options + .open(&path) + .await + .map_err(|e| Self::error(ActionErrorKind::Open(path.to_owned(), e)))?; + + let mut buf = Vec::new(); + plist::to_writer_xml(&mut buf, &generated_plist).map_err(Self::error)?; + file.write_all(&buf) + .await + .map_err(|e| Self::error(ActionErrorKind::Write(path.to_owned(), e)))?; + + Ok(()) + } + + fn revert_description(&self) -> Vec { + vec![ActionDescription::new( + format!("Delete file `{}`", self.path.display()), + vec![format!("Delete file `{}`", self.path.display())], + )] + } + + #[tracing::instrument(level = "debug", skip_all)] + async fn revert(&mut self) -> Result<(), ActionError> { + remove_file(&self.path) + .await + .map_err(|e| Self::error(ActionErrorKind::Remove(self.path.to_owned(), e)))?; + + Ok(()) + } +} + +/// This function must be able to operate at both plan and execute time. +async fn generate_plist(service_label: &str) -> Result { + let plist = LaunchctlHookPlist { + keep_alive: KeepAliveOpts { + successful_exit: false, + }, + label: service_label.into(), + program_arguments: vec![ + "/bin/sh".into(), + "-c".into(), + "/bin/wait4path /nix/nix-installer && /nix/nix-installer repair".into(), + ], + standard_error_path: "/nix/.nix-installer-hook.err.log".into(), + standard_out_path: "/nix/.nix-installer-hook.out.log".into(), + }; + + Ok(plist) +} + +#[derive(Deserialize, Clone, Debug, Serialize, PartialEq)] +#[serde(rename_all = "PascalCase")] +pub struct LaunchctlHookPlist { + label: String, + program_arguments: Vec, + keep_alive: KeepAliveOpts, + standard_error_path: String, + standard_out_path: String, +} + +#[derive(Deserialize, Clone, Debug, Serialize, PartialEq)] +#[serde(rename_all = "PascalCase")] +pub struct KeepAliveOpts { + successful_exit: bool, +} + +#[non_exhaustive] +#[derive(Debug, thiserror::Error)] +pub enum CreateNixHookServiceError { + #[error( + "`{path}` exists and contains content different than expected. Consider removing the file." + )] + DifferentPlist { + expected: LaunchctlHookPlist, + discovered: LaunchctlHookPlist, + path: PathBuf, + }, +} + +impl From for ActionErrorKind { + fn from(val: CreateNixHookServiceError) -> Self { + ActionErrorKind::Custom(Box::new(val)) + } +} diff --git a/src/action/macos/create_volume_service.rs b/src/action/macos/create_volume_service.rs index fbf2b71f3..60d836c54 100644 --- a/src/action/macos/create_volume_service.rs +++ b/src/action/macos/create_volume_service.rs @@ -311,7 +311,7 @@ pub enum CreateVolumeServiceError { }, #[error("UUID for APFS volume labelled `{0}` was not found")] CannotDetermineUuid(String), - #[error("An APFS volume labelled `{1}` does not exist, but there exists an fstab entry for that volume, as well as a service file at `{0}`. Consider removing the line containing `/nix` from the `/etc/fstab` and running `rm {0}`")] + #[error("An APFS volume labelled `{1}` does not exist, but there exists an fstab entry for that volume, as well as a service file at `{0}`. Consider removing the line containing `/nix` from the `/etc/fstab` and running `sudo rm {0}`")] VolumeDoesNotExistButVolumeServiceAndFstabEntryDoes(PathBuf, String), } diff --git a/src/action/macos/enable_ownership.rs b/src/action/macos/enable_ownership.rs index f39e528d0..ef5e69476 100644 --- a/src/action/macos/enable_ownership.rs +++ b/src/action/macos/enable_ownership.rs @@ -68,7 +68,7 @@ impl Action for EnableOwnership { let the_plist: DiskUtilInfoOutput = plist::from_reader(Cursor::new(buf)).map_err(Self::error)?; - the_plist.global_permissions_enabled + !the_plist.global_permissions_enabled }; if should_enable_ownership { diff --git a/src/action/macos/encrypt_apfs_volume.rs b/src/action/macos/encrypt_apfs_volume.rs index 52db64313..78b8f005e 100644 --- a/src/action/macos/encrypt_apfs_volume.rs +++ b/src/action/macos/encrypt_apfs_volume.rs @@ -252,11 +252,11 @@ impl Action for EncryptApfsVolume { #[derive(thiserror::Error, Debug)] pub enum EncryptApfsVolumeError { - #[error("The keychain has an existing password for a non-existing \"{0}\" volume on disk `{1}`, consider removing the password with `security delete-generic-password -a \"{0}\" -s \"Nix Store\" -l \"{1} encryption password\" -D \"Encrypted volume password\"`")] + #[error("The keychain has an existing password for a non-existing \"{0}\" volume on disk `{1}`, consider removing the password with `sudo security delete-generic-password -a \"{0}\" -s \"Nix Store\" -l \"{1} encryption password\" -D \"Encrypted volume password\"`. Note that it's possible to have several passwords stored, so you may need to run this command several times until receiving the message `The specified item could not be found in the keychain.`")] ExistingPasswordFound(String, PathBuf), - #[error("The keychain lacks a password for the already existing \"{0}\" volume on disk `{1}`, consider removing the volume with `diskutil apfs deleteVolume \"{0}\"` (if you receive error -69888, you may need to run `launchctl bootout system/org.nixos.darwin-store` and `launchctl bootout system/org.nixos.nix-daemon` first)")] + #[error("The keychain lacks a password for the already existing \"{0}\" volume on disk `{1}`, consider removing the volume with `diskutil apfs deleteVolume \"{0}\"` (if you receive error -69888, you may need to run `sudo launchctl bootout system/org.nixos.darwin-store` and `sudo launchctl bootout system/org.nixos.nix-daemon` first)")] MissingPasswordForExistingVolume(String, PathBuf), - #[error("The existing APFS volume \"{0}\" on disk `{1}` is not encrypted but it should be, consider removing the volume with `diskutil apfs deleteVolume \"{0}\"` (if you receive error -69888, you may need to run `launchctl bootout system/org.nixos.darwin-store` and `launchctl bootout system/org.nixos.nix-daemon` first)")] + #[error("The existing APFS volume \"{0}\" on disk `{1}` is not encrypted but it should be, consider removing the volume with `diskutil apfs deleteVolume \"{0}\"` (if you receive error -69888, you may need to run `sudo launchctl bootout system/org.nixos.darwin-store` and `sudo launchctl bootout system/org.nixos.nix-daemon` first)")] ExistingVolumeNotEncrypted(String, PathBuf), } diff --git a/src/action/macos/mod.rs b/src/action/macos/mod.rs index d1e5ef488..7ad01fb90 100644 --- a/src/action/macos/mod.rs +++ b/src/action/macos/mod.rs @@ -2,8 +2,10 @@ */ pub(crate) mod bootstrap_launchctl_service; +pub(crate) mod configure_remote_building; pub(crate) mod create_apfs_volume; pub(crate) mod create_fstab_entry; +pub(crate) mod create_nix_hook_service; pub(crate) mod create_nix_volume; pub(crate) mod create_synthetic_objects; pub(crate) mod create_volume_service; @@ -15,7 +17,9 @@ pub(crate) mod set_tmutil_exclusions; pub(crate) mod unmount_apfs_volume; pub use bootstrap_launchctl_service::BootstrapLaunchctlService; +pub use configure_remote_building::ConfigureRemoteBuilding; pub use create_apfs_volume::CreateApfsVolume; +pub use create_nix_hook_service::CreateNixHookService; pub use create_nix_volume::{CreateNixVolume, NIX_VOLUME_MOUNTD_DEST}; pub use create_synthetic_objects::CreateSyntheticObjects; pub use create_volume_service::CreateVolumeService; diff --git a/src/action/mod.rs b/src/action/mod.rs index d86039159..c9ef0998a 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -156,6 +156,7 @@ impl Planner for MyPlanner { #[cfg(feature = "diagnostics")] async fn diagnostic_data(&self) -> Result { Ok(nix_installer::diagnostics::DiagnosticData::new( + self.common.diagnostic_attribution.clone(), self.common.diagnostic_endpoint.clone(), self.typetag_name().into(), self.configured_settings() @@ -198,7 +199,7 @@ use std::{error::Error, process::Output}; use tokio::task::JoinError; use tracing::Span; -use crate::{error::HasExpectedErrors, CertificateError}; +use crate::{error::HasExpectedErrors, settings::UrlOrPathError, CertificateError}; /// An action which can be reverted or completed, with an action state /// @@ -421,6 +422,8 @@ pub enum ActionErrorKind { std::path::PathBuf, #[source] std::io::Error, ), + #[error("Getting filesystem metadata for `{0}` on `{1}`")] + GetMetadata(std::path::PathBuf, #[source] std::io::Error), #[error("Set mode `{0:#o}` on `{1}`")] SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error), #[error("Remove file `{0}`")] @@ -557,6 +560,16 @@ pub enum ActionErrorKind { SystemdMissing, #[error("`{command}` failed, message: {message}")] DiskUtilInfoError { command: String, message: String }, + #[error(transparent)] + UrlOrPathError(#[from] UrlOrPathError), + #[error("Request error")] + Reqwest( + #[from] + #[source] + reqwest::Error, + ), + #[error("Unknown url scheme")] + UnknownUrlScheme, } impl ActionErrorKind { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 7f33ea386..37ded7302 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -19,9 +19,11 @@ pub trait CommandExecute { async fn execute(self) -> eyre::Result; } -/// The Determinate Nix installer -/// -/// Plans a Nix install, prompts for confirmation, then executes it +/** +The Determinate Nix installer + +A fast, friendly, and reliable tool to help you use Nix with Flakes everywhere. +*/ #[derive(Debug, Parser)] #[clap(version)] pub struct NixInstallerCli { @@ -45,6 +47,7 @@ impl CommandExecute for NixInstallerCli { NixInstallerSubcommand::Plan(plan) => plan.execute().await, NixInstallerSubcommand::SelfTest(self_test) => self_test.execute().await, NixInstallerSubcommand::Install(install) => install.execute().await, + NixInstallerSubcommand::Repair(restore_shell) => restore_shell.execute().await, NixInstallerSubcommand::Uninstall(revert) => revert.execute().await, } } diff --git a/src/cli/subcommand/install.rs b/src/cli/subcommand/install.rs index aaa18a7b0..a72c05b4a 100644 --- a/src/cli/subcommand/install.rs +++ b/src/cli/subcommand/install.rs @@ -30,9 +30,13 @@ const EXISTING_INCOMPATIBLE_PLAN_GUIDANCE: &str = "\ If you are using `nix-installer` in an automated curing process and seeing this message, consider pinning the version you use via https://github.com/DeterminateSystems/nix-installer#accessing-other-versions.\ "; -/// Execute an install (possibly using an existing plan) -/// -/// To pass custom options, select a planner, for example `nix-installer install linux-multi --help` +/** +Install Nix using a planner + +By default, an appropriate planner is heuristically determined based on the system. + +Some planners have additional options which can be set from the planner's subcommand. +*/ #[derive(Debug, Parser)] #[command(args_conflicts_with_subcommands = true)] pub struct Install { @@ -124,8 +128,8 @@ impl CommandExecute for Install { eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}` which used different planner settings, try uninstalling the existing install with `{uninstall_command}`").red()); return Ok(ExitCode::FAILURE) } - eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}`, with the same settings, already completed, try uninstalling (`{uninstall_command}`) and reinstalling if Nix isn't working").red()); - return Ok(ExitCode::FAILURE) + eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}`, with the same settings, already completed. Try uninstalling (`{uninstall_command}`) and reinstalling if Nix isn't working").red()); + return Ok(ExitCode::SUCCESS) }, None => { let res = planner.plan().await; @@ -176,7 +180,7 @@ impl CommandExecute for Install { return Ok(ExitCode::FAILURE) } if existing_receipt.actions.iter().all(|v| v.state == ActionState::Completed) { - eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}`, with the same settings, already completed, try uninstalling (`{uninstall_command}`) and reinstalling if Nix isn't working").yellow()); + eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}`, with the same settings, already completed. Try uninstalling (`{uninstall_command}`) and reinstalling if Nix isn't working").yellow()); return Ok(ExitCode::SUCCESS) } existing_receipt diff --git a/src/cli/subcommand/mod.rs b/src/cli/subcommand/mod.rs index 466bbfc0f..ce8f42435 100644 --- a/src/cli/subcommand/mod.rs +++ b/src/cli/subcommand/mod.rs @@ -2,15 +2,19 @@ mod plan; use plan::Plan; mod install; use install::Install; +mod repair; +use repair::Repair; mod uninstall; use uninstall::Uninstall; mod self_test; use self_test::SelfTest; +#[allow(clippy::large_enum_variant)] #[derive(Debug, clap::Subcommand)] pub enum NixInstallerSubcommand { - Plan(Plan), Install(Install), + Repair(Repair), Uninstall(Uninstall), SelfTest(SelfTest), + Plan(Plan), } diff --git a/src/cli/subcommand/plan.rs b/src/cli/subcommand/plan.rs index 2666487bf..a97fcdf54 100644 --- a/src/cli/subcommand/plan.rs +++ b/src/cli/subcommand/plan.rs @@ -1,6 +1,6 @@ use std::{path::PathBuf, process::ExitCode}; -use crate::{error::HasExpectedErrors, BuiltinPlanner}; +use crate::{cli::ensure_root, error::HasExpectedErrors, BuiltinPlanner}; use clap::Parser; use eyre::WrapErr; @@ -8,7 +8,11 @@ use owo_colors::OwoColorize; use crate::cli::CommandExecute; -/// Plan an install that can be repeated on an identical host later +/** +Emit a JSON install plan that can be manually edited before execution + +Primarily intended for development, debugging, and handling install cases. +*/ #[derive(Debug, Parser)] pub struct Plan { #[clap(subcommand)] @@ -28,6 +32,8 @@ impl CommandExecute for Plan { async fn execute(self) -> eyre::Result { let Self { planner, output } = self; + ensure_root()?; + let planner = match planner { Some(planner) => planner, None => BuiltinPlanner::default().await?, diff --git a/src/cli/subcommand/repair.rs b/src/cli/subcommand/repair.rs new file mode 100644 index 000000000..243ff20ad --- /dev/null +++ b/src/cli/subcommand/repair.rs @@ -0,0 +1,60 @@ +use std::process::ExitCode; + +use crate::{ + action::common::ConfigureShellProfile, + cli::{ensure_root, CommandExecute}, + planner::{PlannerError, ShellProfileLocations}, +}; +use clap::{ArgAction, Parser}; + +/** +Update the shell profiles to make Nix usable after system upgrades. +*/ +#[derive(Debug, Parser)] +#[command(args_conflicts_with_subcommands = true)] +pub struct Repair { + #[clap( + long, + env = "NIX_INSTALLER_NO_CONFIRM", + action(ArgAction::SetTrue), + default_value = "false", + global = true + )] + pub no_confirm: bool, +} + +#[async_trait::async_trait] +impl CommandExecute for Repair { + #[tracing::instrument(level = "trace", skip_all)] + async fn execute(self) -> eyre::Result { + let Self { no_confirm: _ } = self; + + ensure_root()?; + + let mut reconfigure = ConfigureShellProfile::plan(ShellProfileLocations::default()) + .await + .map_err(PlannerError::Action)? + .boxed(); + + if let Err(err) = reconfigure.try_execute().await { + println!("{:#?}", err); + return Ok(ExitCode::FAILURE); + } + // TODO: Using `cfg` based on OS is not a long term solution. + // Make this read the planner from the `/nix/receipt.json` to determine which tasks to run. + #[cfg(target_os = "macos")] + { + let mut reconfigure = crate::action::macos::ConfigureRemoteBuilding::plan() + .await + .map_err(PlannerError::Action)? + .boxed(); + + if let Err(err) = reconfigure.try_execute().await { + println!("{:#?}", err); + return Ok(ExitCode::FAILURE); + } + } + + Ok(ExitCode::SUCCESS) + } +} diff --git a/src/cli/subcommand/self_test.rs b/src/cli/subcommand/self_test.rs index ab17d30f9..7f3da8b55 100644 --- a/src/cli/subcommand/self_test.rs +++ b/src/cli/subcommand/self_test.rs @@ -4,7 +4,7 @@ use clap::Parser; use crate::{cli::CommandExecute, NixInstallerError}; -/// Run a self test of Nix to ensure that the install worked. +/// Run a self test of Nix to ensure that an install is working #[derive(Debug, Parser)] pub struct SelfTest {} diff --git a/src/cli/subcommand/uninstall.rs b/src/cli/subcommand/uninstall.rs index d10367ee3..fcbcc900a 100644 --- a/src/cli/subcommand/uninstall.rs +++ b/src/cli/subcommand/uninstall.rs @@ -7,7 +7,7 @@ use std::{ use crate::{ cli::{ensure_root, interaction::PromptChoice, signal_channel}, error::HasExpectedErrors, - plan::RECEIPT_LOCATION, + plan::{current_version, RECEIPT_LOCATION}, InstallPlan, NixInstallerError, }; use clap::{ArgAction, Parser}; @@ -17,7 +17,7 @@ use rand::Rng; use crate::cli::{interaction, CommandExecute}; -/// Uninstall a previously installed Nix (only `nix-installer` done installs supported) +/// Uninstall a previously `nix-installer` installed Nix #[derive(Debug, Parser)] pub struct Uninstall { #[clap( @@ -111,7 +111,48 @@ impl CommandExecute for Uninstall { let install_receipt_string = tokio::fs::read_to_string(receipt) .await .wrap_err("Reading receipt")?; - let mut plan: InstallPlan = serde_json::from_str(&install_receipt_string)?; + + let mut plan: InstallPlan = match serde_json::from_str(&install_receipt_string) { + Ok(plan) => plan, + Err(plan_err) => { + #[derive(serde::Deserialize)] + struct MinimalPlan { + version: semver::Version, + } + let minimal_plan: Result = + serde_json::from_str(&install_receipt_string); + match minimal_plan { + Ok(minimal_plan) => { + return Err(plan_err).wrap_err_with(|| { + let plan_version = minimal_plan.version; + let current_version = current_version().map(|v| v.to_string()).unwrap_or_else(|_| env!("CARGO_PKG_VERSION").to_string()); + format!( + "\ + Unable to parse plan, this plan was created by `nix-installer` version `{plan_version}`, this is `nix-installer` version `{current_version}`\n\ + To uninstall, either run `/nix/nix-installer uninstall` or `curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix/tag/{plan_version} | sh -s -- uninstall`\ + ").red().to_string() + }); + }, + Err(_minimal_plan_err) => return Err(plan_err)?, + } + }, + }; + + if let Err(e) = plan.check_compatible() { + let version = plan.version; + eprintln!( + "{}", + format!("\ + {e}\n\ + \n\ + Found existing plan in `{RECEIPT_LOCATION}` which was created by a version incompatible `nix-installer`.\n\ + \n + To uninstall, either run `/nix/nix-installer uninstall` or `curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix/tag/${version} | sh -s -- uninstall`\n\ + \n\ + ").red() + ); + return Ok(ExitCode::FAILURE); + } if let Err(err) = plan.pre_uninstall_check().await { if let Some(expected) = err.expected() { diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 138832860..294f579b7 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -34,6 +34,7 @@ pub enum DiagnosticAction { /// A report sent to an endpoint #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct DiagnosticReport { + pub attribution: Option, pub version: String, pub planner: String, pub configured_settings: Vec, @@ -50,6 +51,7 @@ pub struct DiagnosticReport { /// A preparation of data to be sent to the `endpoint`. #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Default)] pub struct DiagnosticData { + attribution: Option, version: String, planner: String, configured_settings: Vec, @@ -65,6 +67,7 @@ pub struct DiagnosticData { impl DiagnosticData { pub fn new( + attribution: Option, endpoint: Option, planner: String, configured_settings: Vec, @@ -81,6 +84,7 @@ impl DiagnosticData { let is_ci = is_ci::cached() || std::env::var("NIX_INSTALLER_CI").unwrap_or_else(|_| "0".into()) == "1"; Ok(Self { + attribution, endpoint, version: env!("CARGO_PKG_VERSION").into(), planner, @@ -131,6 +135,7 @@ impl DiagnosticData { pub fn report(&self, action: DiagnosticAction, status: DiagnosticStatus) -> DiagnosticReport { let Self { + attribution, version, planner, configured_settings, @@ -143,6 +148,7 @@ impl DiagnosticData { failure_chain, } = self; DiagnosticReport { + attribution: attribution.clone(), version: version.clone(), planner: planner.clone(), configured_settings: configured_settings.clone(), diff --git a/src/plan.rs b/src/plan.rs index 62e180979..6543f0cbc 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -228,7 +228,7 @@ impl InstallPlan { .await?; } - Err(err) + tracing::warn!("{err:?}") } else { #[cfg(feature = "diagnostics")] if let Some(diagnostic_data) = &self.diagnostic_data { @@ -240,9 +240,9 @@ impl InstallPlan { ) .await?; } - - Ok(()) } + + Ok(()) } #[tracing::instrument(level = "debug", skip_all)] @@ -425,7 +425,7 @@ async fn write_receipt(plan: InstallPlan) -> Result<(), NixInstallerError> { Result::<(), NixInstallerError>::Ok(()) } -fn current_version() -> Result { +pub fn current_version() -> Result { let nix_installer_version_str = env!("CARGO_PKG_VERSION"); Version::from_str(nix_installer_version_str).map_err(|e| { NixInstallerError::InvalidCurrentVersion(nix_installer_version_str.to_string(), e) diff --git a/src/planner/linux.rs b/src/planner/linux.rs index 66438c719..21a2ba176 100644 --- a/src/planner/linux.rs +++ b/src/planner/linux.rs @@ -17,7 +17,7 @@ use which::which; use super::ShellProfileLocations; -/// A planner for Linux installs +/// A planner for traditional, mutable Linux systems like Debian, RHEL, or Arch #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "cli", derive(clap::Parser))] pub struct Linux { @@ -77,6 +77,13 @@ impl Planner for Linux { ); } + plan.push( + CreateDirectory::plan("/etc/tmpfiles.d", None, None, 0o0755, false) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + plan.push( ConfigureInitService::plan(self.init.init, self.init.start_daemon) .await @@ -97,8 +104,8 @@ impl Planner for Linux { let Self { settings, init } = self; let mut map = HashMap::default(); - map.extend(settings.settings()?.into_iter()); - map.extend(init.settings()?.into_iter()); + map.extend(settings.settings()?); + map.extend(init.settings()?); Ok(map) } @@ -122,6 +129,7 @@ impl Planner for Linux { #[cfg(feature = "diagnostics")] async fn diagnostic_data(&self) -> Result { Ok(crate::diagnostics::DiagnosticData::new( + self.settings.diagnostic_attribution.clone(), self.settings.diagnostic_endpoint.clone(), self.typetag_name().into(), self.configured_settings() diff --git a/src/planner/macos.rs b/src/planner/macos.rs index 048dd5acc..a35a91a5f 100644 --- a/src/planner/macos.rs +++ b/src/planner/macos.rs @@ -12,7 +12,9 @@ use crate::{ action::{ base::RemoveDirectory, common::{ConfigureInitService, ConfigureNix, CreateUsersAndGroups, ProvisionNix}, - macos::{CreateNixVolume, SetTmutilExclusions}, + macos::{ + ConfigureRemoteBuilding, CreateNixHookService, CreateNixVolume, SetTmutilExclusions, + }, StatefulAction, }, execute_command, @@ -23,7 +25,7 @@ use crate::{ Action, BuiltinPlanner, }; -/// A planner for MacOS (Darwin) installs +/// A planner for MacOS (Darwin) systems #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "cli", derive(clap::Parser))] pub struct Macos { @@ -122,15 +124,14 @@ impl Planner for Macos { let stdout = String::from_utf8_lossy(&output.stdout); let stdout_trimmed = stdout.trim(); - if stdout_trimmed == "true" { - true - } else { - false - } + + stdout_trimmed == "true" }, }; - Ok(vec![ + let mut plan = vec![]; + + plan.push( CreateNixVolume::plan( root_disk.unwrap(), /* We just ensured it was populated */ self.volume_label.clone(), @@ -140,33 +141,63 @@ impl Planner for Macos { .await .map_err(PlannerError::Action)? .boxed(), + ); + plan.push( ProvisionNix::plan(&self.settings) .await .map_err(PlannerError::Action)? .boxed(), - // Auto-allocate uids is broken on Mac. Tools like `whoami` don't work. - // e.g. https://github.com/NixOS/nix/issues/8444 + ); + // Auto-allocate uids is broken on Mac. Tools like `whoami` don't work. + // e.g. https://github.com/NixOS/nix/issues/8444 + plan.push( CreateUsersAndGroups::plan(self.settings.clone()) .await .map_err(PlannerError::Action)? .boxed(), + ); + plan.push( SetTmutilExclusions::plan(vec![PathBuf::from("/nix/store"), PathBuf::from("/nix/var")]) .await .map_err(PlannerError::Action)? .boxed(), + ); + plan.push( ConfigureNix::plan(ShellProfileLocations::default(), &self.settings) .await .map_err(PlannerError::Action)? .boxed(), + ); + plan.push( + ConfigureRemoteBuilding::plan() + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + + if self.settings.modify_profile { + plan.push( + CreateNixHookService::plan() + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + } + + plan.push( ConfigureInitService::plan(InitSystem::Launchd, true) .await .map_err(PlannerError::Action)? .boxed(), + ); + plan.push( RemoveDirectory::plan(crate::settings::SCRATCH_DIR) .await .map_err(PlannerError::Action)? .boxed(), - ]) + ); + + Ok(plan) } fn settings(&self) -> Result, InstallSettingsError> { @@ -179,7 +210,7 @@ impl Planner for Macos { } = self; let mut map = HashMap::default(); - map.extend(settings.settings()?.into_iter()); + map.extend(settings.settings()?); map.insert("volume_encrypt".into(), serde_json::to_value(encrypt)?); map.insert("volume_label".into(), serde_json::to_value(volume_label)?); map.insert("root_disk".into(), serde_json::to_value(root_disk)?); @@ -210,6 +241,7 @@ impl Planner for Macos { #[cfg(feature = "diagnostics")] async fn diagnostic_data(&self) -> Result { Ok(crate::diagnostics::DiagnosticData::new( + self.settings.diagnostic_attribution.clone(), self.settings.diagnostic_endpoint.clone(), self.typetag_name().into(), self.configured_settings() @@ -233,9 +265,9 @@ impl Planner for Macos { } } -impl Into for Macos { - fn into(self) -> BuiltinPlanner { - BuiltinPlanner::Macos(self) +impl From for BuiltinPlanner { + fn from(val: Macos) -> Self { + BuiltinPlanner::Macos(val) } } diff --git a/src/planner/mod.rs b/src/planner/mod.rs index 8db515a40..97e72503f 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -72,6 +72,7 @@ impl Planner for MyPlanner { #[cfg(feature = "diagnostics")] async fn diagnostic_data(&self) -> Result { Ok(nix_installer::diagnostics::DiagnosticData::new( + self.common.diagnostic_attribution.clone(), self.common.diagnostic_endpoint.clone(), self.typetag_name().into(), self.configured_settings() @@ -107,6 +108,8 @@ pub mod linux; #[cfg(target_os = "macos")] pub mod macos; #[cfg(target_os = "linux")] +pub mod ostree; +#[cfg(target_os = "linux")] pub mod steam_deck; use std::{collections::HashMap, path::PathBuf, string::FromUtf8Error}; @@ -163,14 +166,17 @@ dyn_clone::clone_trait_object!(Planner); #[cfg_attr(feature = "cli", derive(clap::Subcommand))] pub enum BuiltinPlanner { #[cfg(target_os = "linux")] - /// A planner for Linux installs + /// A planner for traditional, mutable Linux systems like Debian, RHEL, or Arch Linux(linux::Linux), - /// A planner MacOS (Darwin) for installs - #[cfg(target_os = "macos")] - Macos(macos::Macos), - /// A planner suitable for the Valve Steam Deck running SteamOS + /// A planner for the Valve Steam Deck running SteamOS #[cfg(target_os = "linux")] SteamDeck(steam_deck::SteamDeck), + /// A planner suitable for immutable systems using ostree, such as Fedora Silverblue + #[cfg(target_os = "linux")] + Ostree(ostree::Ostree), + /// A planner for MacOS (Darwin) systems + #[cfg(target_os = "macos")] + Macos(macos::Macos), } impl BuiltinPlanner { @@ -179,15 +185,7 @@ impl BuiltinPlanner { use target_lexicon::{Architecture, OperatingSystem}; match (Architecture::host(), OperatingSystem::host()) { #[cfg(target_os = "linux")] - (Architecture::X86_64, OperatingSystem::Linux) => { - let os_release = os_release::OsRelease::new().ok(); - match os_release { - Some(os_release) if os_release.id == "steamos" => { - Ok(Self::SteamDeck(steam_deck::SteamDeck::default().await?)) - }, - _ => Ok(Self::Linux(linux::Linux::default().await?)), - } - }, + (Architecture::X86_64, OperatingSystem::Linux) => Self::detect_linux_distro().await, #[cfg(target_os = "linux")] (Architecture::X86_32(_), OperatingSystem::Linux) => { Ok(Self::Linux(linux::Linux::default().await?)) @@ -210,6 +208,26 @@ impl BuiltinPlanner { } } + #[cfg(target_os = "linux")] + async fn detect_linux_distro() -> Result { + let is_steam_deck = + os_release::OsRelease::new().is_ok_and(|os_release| os_release.id == "steamos"); + if is_steam_deck { + return Ok(Self::SteamDeck(steam_deck::SteamDeck::default().await?)); + } + + let is_ostree = std::process::Command::new("ostree") + .arg("remote") + .arg("list") + .output() + .is_ok_and(|output| output.status.success()); + if is_ostree { + return Ok(Self::Ostree(ostree::Ostree::default().await?)); + } + + Ok(Self::Linux(linux::Linux::default().await?)) + } + pub async fn from_common_settings(settings: CommonSettings) -> Result { let mut built = Self::default().await?; match &mut built { @@ -217,6 +235,8 @@ impl BuiltinPlanner { BuiltinPlanner::Linux(inner) => inner.settings = settings, #[cfg(target_os = "linux")] BuiltinPlanner::SteamDeck(inner) => inner.settings = settings, + #[cfg(target_os = "linux")] + BuiltinPlanner::Ostree(inner) => inner.settings = settings, #[cfg(target_os = "macos")] BuiltinPlanner::Macos(inner) => inner.settings = settings, } @@ -231,6 +251,8 @@ impl BuiltinPlanner { BuiltinPlanner::Linux(inner) => inner.configured_settings().await, #[cfg(target_os = "linux")] BuiltinPlanner::SteamDeck(inner) => inner.configured_settings().await, + #[cfg(target_os = "linux")] + BuiltinPlanner::Ostree(inner) => inner.configured_settings().await, #[cfg(target_os = "macos")] BuiltinPlanner::Macos(inner) => inner.configured_settings().await, } @@ -242,6 +264,8 @@ impl BuiltinPlanner { BuiltinPlanner::Linux(planner) => InstallPlan::plan(planner).await, #[cfg(target_os = "linux")] BuiltinPlanner::SteamDeck(planner) => InstallPlan::plan(planner).await, + #[cfg(target_os = "linux")] + BuiltinPlanner::Ostree(planner) => InstallPlan::plan(planner).await, #[cfg(target_os = "macos")] BuiltinPlanner::Macos(planner) => InstallPlan::plan(planner).await, } @@ -252,6 +276,8 @@ impl BuiltinPlanner { BuiltinPlanner::Linux(i) => i.boxed(), #[cfg(target_os = "linux")] BuiltinPlanner::SteamDeck(i) => i.boxed(), + #[cfg(target_os = "linux")] + BuiltinPlanner::Ostree(i) => i.boxed(), #[cfg(target_os = "macos")] BuiltinPlanner::Macos(i) => i.boxed(), } @@ -263,6 +289,8 @@ impl BuiltinPlanner { BuiltinPlanner::Linux(i) => i.typetag_name(), #[cfg(target_os = "linux")] BuiltinPlanner::SteamDeck(i) => i.typetag_name(), + #[cfg(target_os = "linux")] + BuiltinPlanner::Ostree(i) => i.typetag_name(), #[cfg(target_os = "macos")] BuiltinPlanner::Macos(i) => i.typetag_name(), } @@ -274,6 +302,8 @@ impl BuiltinPlanner { BuiltinPlanner::Linux(i) => i.settings(), #[cfg(target_os = "linux")] BuiltinPlanner::SteamDeck(i) => i.settings(), + #[cfg(target_os = "linux")] + BuiltinPlanner::Ostree(i) => i.settings(), #[cfg(target_os = "macos")] BuiltinPlanner::Macos(i) => i.settings(), } @@ -288,6 +318,8 @@ impl BuiltinPlanner { BuiltinPlanner::Linux(i) => i.diagnostic_data().await, #[cfg(target_os = "linux")] BuiltinPlanner::SteamDeck(i) => i.diagnostic_data().await, + #[cfg(target_os = "linux")] + BuiltinPlanner::Ostree(i) => i.diagnostic_data().await, #[cfg(target_os = "macos")] BuiltinPlanner::Macos(i) => i.diagnostic_data().await, } diff --git a/src/planner/ostree.rs b/src/planner/ostree.rs new file mode 100644 index 000000000..fa246a443 --- /dev/null +++ b/src/planner/ostree.rs @@ -0,0 +1,347 @@ +use crate::{ + action::{ + base::{CreateDirectory, CreateFile, RemoveDirectory}, + common::{ConfigureInitService, ConfigureNix, CreateUsersAndGroups, ProvisionNix}, + linux::{ProvisionSelinux, StartSystemdUnit, SystemctlDaemonReload}, + StatefulAction, + }, + error::HasExpectedErrors, + planner::{Planner, PlannerError}, + settings::CommonSettings, + settings::{InitSystem, InstallSettingsError}, + Action, BuiltinPlanner, +}; +use std::{collections::HashMap, path::PathBuf}; + +use super::{ + linux::{ + check_nix_not_already_installed, check_not_nixos, check_not_wsl1, check_systemd_active, + detect_selinux, + }, + ShellProfileLocations, +}; + +/// A planner suitable for immutable systems using ostree, such as Fedora Silverblue +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "cli", derive(clap::Parser))] +pub struct Ostree { + /// Where `/nix` will be bind mounted to. + #[cfg_attr(feature = "cli", clap(long, default_value = "/var/home/nix"))] + persistence: PathBuf, + #[cfg_attr(feature = "cli", clap(flatten))] + pub settings: CommonSettings, +} + +#[async_trait::async_trait] +#[typetag::serde(name = "ostree")] +impl Planner for Ostree { + async fn default() -> Result { + Ok(Self { + persistence: PathBuf::from("/var/home/nix"), + settings: CommonSettings::default().await?, + }) + } + + async fn plan(&self) -> Result>>, PlannerError> { + let has_selinux = detect_selinux().await?; + let mut plan = vec![ + // Primarily for uninstall + SystemctlDaemonReload::plan() + .await + .map_err(PlannerError::Action)? + .boxed(), + ]; + + plan.push( + CreateDirectory::plan(&self.persistence, None, None, 0o0755, true) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + + let nix_directory_buf = "\ + [Unit]\n\ + Description=Enable mount points in / for ostree\n\ + ConditionPathExists=!/nix\n\ + DefaultDependencies=no\n\ + Requires=local-fs-pre.target\n\ + After=local-fs-pre.target\n\ + [Service]\n\ + Type=oneshot\n\ + ExecStartPre=chattr -i /\n\ + ExecStart=mkdir -p /nix\n\ + ExecStopPost=chattr +i /\n\ + " + .to_string(); + let nix_directory_unit = CreateFile::plan( + "/etc/systemd/system/nix-directory.service", + None, + None, + 0o0644, + nix_directory_buf, + false, + ) + .await + .map_err(PlannerError::Action)?; + plan.push(nix_directory_unit.boxed()); + + let create_bind_mount_buf = format!( + "\ + [Unit]\n\ + Description=Mount `{persistence}` on `/nix`\n\ + PropagatesStopTo=nix-daemon.service\n\ + PropagatesStopTo=nix-directory.service\n\ + After=nix-directory.service\n\ + Requires=nix-directory.service\n\ + ConditionPathIsDirectory=/nix\n\ + DefaultDependencies=no\n\ + \n\ + [Mount]\n\ + What={persistence}\n\ + Where=/nix\n\ + Type=none\n\ + DirectoryMode=0755\n\ + Options=bind\n\ + \n\ + [Install]\n\ + RequiredBy=nix-daemon.service\n\ + RequiredBy=nix-daemon.socket\n + ", + persistence = self.persistence.display(), + ); + let create_bind_mount_unit = CreateFile::plan( + "/etc/systemd/system/nix.mount", + None, + None, + 0o0644, + create_bind_mount_buf, + false, + ) + .await + .map_err(PlannerError::Action)?; + plan.push(create_bind_mount_unit.boxed()); + + let ensure_symlinked_units_resolve_buf = "\ + [Unit]\n\ + Description=Ensure Nix related units which are symlinked resolve\n\ + After=nix.mount\n\ + Requires=nix.mount\n\ + DefaultDependencies=no\n\ + \n\ + [Service]\n\ + Type=oneshot\n\ + RemainAfterExit=yes\n\ + ExecStart=/usr/bin/systemctl daemon-reload\n\ + ExecStart=/usr/bin/systemctl restart --no-block nix-daemon.socket\n\ + \n\ + [Install]\n\ + WantedBy=sysinit.target\n\ + " + .to_string(); + let ensure_symlinked_units_resolve_unit = CreateFile::plan( + "/etc/systemd/system/ensure-symlinked-units-resolve.service", + None, + None, + 0o0644, + ensure_symlinked_units_resolve_buf, + false, + ) + .await + .map_err(PlannerError::Action)?; + plan.push(ensure_symlinked_units_resolve_unit.boxed()); + + // We need to remove this path since it's part of the read-only install. + let mut shell_profile_locations = ShellProfileLocations::default(); + if let Some(index) = shell_profile_locations + .fish + .vendor_confd_prefixes + .iter() + .position(|v| *v == PathBuf::from("/usr/share/fish/")) + { + shell_profile_locations + .fish + .vendor_confd_prefixes + .remove(index); + } + + plan.push( + StartSystemdUnit::plan("nix.mount".to_string(), false) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + + plan.push( + ProvisionNix::plan(&self.settings.clone()) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + plan.push( + CreateUsersAndGroups::plan(self.settings.clone()) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + plan.push( + ConfigureNix::plan(shell_profile_locations, &self.settings) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + + if has_selinux { + plan.push( + ProvisionSelinux::plan("/etc/nix-installer/selinux/packages/nix.pp".into()) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + } + + plan.push( + CreateDirectory::plan("/etc/tmpfiles.d", None, None, 0o0755, false) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + + plan.push( + ConfigureInitService::plan(InitSystem::Systemd, true) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + plan.push( + StartSystemdUnit::plan("ensure-symlinked-units-resolve.service".to_string(), true) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + plan.push( + RemoveDirectory::plan(crate::settings::SCRATCH_DIR) + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + plan.push( + SystemctlDaemonReload::plan() + .await + .map_err(PlannerError::Action)? + .boxed(), + ); + + Ok(plan) + } + + fn settings(&self) -> Result, InstallSettingsError> { + let Self { + persistence, + settings, + } = self; + let mut map = HashMap::default(); + + map.extend(settings.settings()?); + map.insert( + "persistence".to_string(), + serde_json::to_value(persistence)?, + ); + + Ok(map) + } + + async fn configured_settings( + &self, + ) -> Result, PlannerError> { + let default = Self::default().await?.settings()?; + let configured = self.settings()?; + + let mut settings: HashMap = HashMap::new(); + for (key, value) in configured.iter() { + if default.get(key) != Some(value) { + settings.insert(key.clone(), value.clone()); + } + } + + Ok(settings) + } + + #[cfg(feature = "diagnostics")] + async fn diagnostic_data(&self) -> Result { + Ok(crate::diagnostics::DiagnosticData::new( + self.settings.diagnostic_attribution.clone(), + self.settings.diagnostic_endpoint.clone(), + self.typetag_name().into(), + self.configured_settings() + .await? + .into_keys() + .collect::>(), + self.settings.ssl_cert_file.clone(), + )?) + } + async fn pre_uninstall_check(&self) -> Result<(), PlannerError> { + check_not_wsl1()?; + + check_systemd_active()?; + + Ok(()) + } + + async fn pre_install_check(&self) -> Result<(), PlannerError> { + check_not_nixos()?; + + check_nix_not_already_installed().await?; + + check_not_wsl1()?; + + check_systemd_active()?; + + Ok(()) + } +} + +impl From for BuiltinPlanner { + fn from(val: Ostree) -> Self { + BuiltinPlanner::Ostree(val) + } +} + +#[non_exhaustive] +#[derive(Debug, thiserror::Error)] +pub enum OstreeError { + #[error( + "\ + systemd was not active.\n\ + \n\ + If it will be started later consider, passing `--no-start-daemon`.\n\ + \n\ + To use a `root`-only Nix install, consider passing `--init none`." + )] + SystemdNotActive, + #[error( + "\ + systemd was not active.\n\ + \n\ + On WSL2, systemd is not enabled by default. Consider enabling it by adding it to your `/etc/wsl.conf` with `echo -e '[boot]\\nsystemd=true'` then restarting WSL2 with `wsl.exe --shutdown` and re-entering the WSL shell. For more information, see https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/.\n\ + \n\ + If it will be started later consider, passing `--no-start-daemon`.\n\ + \n\ + To use a `root`-only Nix install, consider passing `--init none`." + )] + Wsl2SystemdNotActive, +} + +impl HasExpectedErrors for OstreeError { + fn expected<'a>(&'a self) -> Option> { + match self { + OstreeError::SystemdNotActive => Some(Box::new(self)), + OstreeError::Wsl2SystemdNotActive => Some(Box::new(self)), + } + } +} + +impl From for PlannerError { + fn from(v: OstreeError) -> PlannerError { + PlannerError::Custom(Box::new(v)) + } +} diff --git a/src/planner/steam_deck.rs b/src/planner/steam_deck.rs index 10f9efd33..915da62ad 100644 --- a/src/planner/steam_deck.rs +++ b/src/planner/steam_deck.rs @@ -117,6 +117,7 @@ use crate::{ use super::ShellProfileLocations; +/// A planner for the Valve Steam Deck running SteamOS #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "cli", derive(clap::Parser))] pub struct SteamDeck { @@ -359,7 +360,7 @@ impl Planner for SteamDeck { } = self; let mut map = HashMap::default(); - map.extend(settings.settings()?.into_iter()); + map.extend(settings.settings()?); map.insert( "persistence".to_string(), serde_json::to_value(persistence)?, @@ -387,6 +388,7 @@ impl Planner for SteamDeck { #[cfg(feature = "diagnostics")] async fn diagnostic_data(&self) -> Result { Ok(crate::diagnostics::DiagnosticData::new( + self.settings.diagnostic_attribution.clone(), self.settings.diagnostic_endpoint.clone(), self.typetag_name().into(), self.configured_settings() diff --git a/src/settings.rs b/src/settings.rs index aded61c02..31529c61e 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,28 +1,31 @@ /*! Configurable knobs and their related errors */ -use std::{collections::HashMap, path::PathBuf}; +use std::{collections::HashMap, fmt::Display, path::PathBuf, str::FromStr}; #[cfg(feature = "cli")] -use clap::ArgAction; +use clap::{ + error::{ContextKind, ContextValue}, + ArgAction, +}; use url::Url; pub const SCRATCH_DIR: &str = "/nix/temp-install-dir"; /// Default [`nix_package_url`](CommonSettings::nix_package_url) for Linux x86_64 pub const NIX_X64_64_LINUX_URL: &str = - "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz"; + "https://releases.nixos.org/nix/nix-2.18.1/nix-2.18.1-x86_64-linux.tar.xz"; /// Default [`nix_package_url`](CommonSettings::nix_package_url) for Linux x86 (32 bit) pub const NIX_I686_LINUX_URL: &str = - "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-i686-linux.tar.xz"; + "https://releases.nixos.org/nix/nix-2.18.1/nix-2.18.1-i686-linux.tar.xz"; /// Default [`nix_package_url`](CommonSettings::nix_package_url) for Linux aarch64 pub const NIX_AARCH64_LINUX_URL: &str = - "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-aarch64-linux.tar.xz"; + "https://releases.nixos.org/nix/nix-2.18.1/nix-2.18.1-aarch64-linux.tar.xz"; /// Default [`nix_package_url`](CommonSettings::nix_package_url) for Darwin x86_64 pub const NIX_X64_64_DARWIN_URL: &str = - "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-darwin.tar.xz"; + "https://releases.nixos.org/nix/nix-2.18.1/nix-2.18.1-x86_64-darwin.tar.xz"; /// Default [`nix_package_url`](CommonSettings::nix_package_url) for Darwin aarch64 pub const NIX_AARCH64_DARWIN_URL: &str = - "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-aarch64-darwin.tar.xz"; + "https://releases.nixos.org/nix/nix-2.18.1/nix-2.18.1-aarch64-darwin.tar.xz"; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "cli", derive(clap::ValueEnum))] @@ -142,7 +145,7 @@ pub struct CommonSettings { /// The Nix package URL #[cfg_attr( feature = "cli", - clap(long, env = "NIX_INSTALLER_NIX_PACKAGE_URL", global = true) + clap(long, env = "NIX_INSTALLER_NIX_PACKAGE_URL", global = true, value_parser = clap::value_parser!(UrlOrPath)) )] #[cfg_attr( all(target_os = "macos", target_arch = "x86_64", feature = "cli"), @@ -174,19 +177,19 @@ pub struct CommonSettings { default_value = NIX_AARCH64_LINUX_URL, ) )] - pub nix_package_url: Url, + pub nix_package_url: UrlOrPath, /// The proxy to use (if any), valid proxy bases are `https://$URL`, `http://$URL` and `socks5://$URL` #[cfg_attr(feature = "cli", clap(long, env = "NIX_INSTALLER_PROXY"))] pub proxy: Option, - /// An SSL cert to use (if any), used for fetching Nix and sets `NIX_SSL_CERT_FILE` for Nix + /// An SSL cert to use (if any), used for fetching Nix and sets `ssl-cert-file` in `/etc/nix/nix.conf` #[cfg_attr(feature = "cli", clap(long, env = "NIX_INSTALLER_SSL_CERT_FILE"))] pub ssl_cert_file: Option, /// Extra configuration lines for `/etc/nix.conf` #[cfg_attr(feature = "cli", clap(long, action = ArgAction::Append, num_args = 0.., env = "NIX_INSTALLER_EXTRA_CONF", global = true))] - pub extra_conf: Vec, + pub extra_conf: Vec, /// If `nix-installer` should forcibly recreate files it finds existing #[cfg_attr( @@ -201,12 +204,26 @@ pub struct CommonSettings { )] pub force: bool, + #[cfg(feature = "diagnostics")] + /// Relate the install diagnostic to a specific value + #[cfg_attr( + feature = "cli", + clap( + long, + default_value = None, + env = "NIX_INSTALLER_DIAGNOSTIC_ATTRIBUTION", + global = true + ) + )] + pub diagnostic_attribution: Option, + #[cfg(feature = "diagnostics")] /// The URL or file path for an installation diagnostic to be sent /// /// Sample of the data sent: /// /// { + /// "attribution": null, /// "version": "0.4.0", /// "planner": "linux", /// "configured_settings": [ "modify_profile" ], @@ -297,6 +314,8 @@ impl CommonSettings { force: false, ssl_cert_file: Default::default(), #[cfg(feature = "diagnostics")] + diagnostic_attribution: None, + #[cfg(feature = "diagnostics")] diagnostic_endpoint: Some("https://install.determinate.systems/nix/diagnostic".into()), }) } @@ -315,6 +334,8 @@ impl CommonSettings { extra_conf, force, ssl_cert_file, + #[cfg(feature = "diagnostics")] + diagnostic_attribution: _, #[cfg(feature = "diagnostics")] diagnostic_endpoint, } = self; @@ -362,6 +383,7 @@ impl CommonSettings { Ok(map) } } + #[cfg(target_os = "linux")] async fn linux_detect_systemd_started() -> bool { use std::process::Stdio; @@ -494,6 +516,153 @@ pub enum InstallSettingsError { ), #[error("No supported init system found")] InitNotSupported, + #[error(transparent)] + UrlOrPath(#[from] UrlOrPathError), +} + +#[derive(Debug, thiserror::Error)] +pub enum UrlOrPathError { + #[error("Error parsing URL `{0}`")] + Url(String, #[source] url::ParseError), + #[error("The specified path `{0}` does not exist")] + PathDoesNotExist(PathBuf), + #[error("Error fetching URL `{0}`")] + Reqwest(Url, #[source] reqwest::Error), + #[error("I/O error when accessing `{0}`")] + Io(PathBuf, #[source] std::io::Error), +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, Clone)] +pub enum UrlOrPath { + Url(Url), + Path(PathBuf), +} + +impl Display for UrlOrPath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + UrlOrPath::Url(url) => f.write_fmt(format_args!("{url}")), + UrlOrPath::Path(path) => f.write_fmt(format_args!("{}", path.display())), + } + } +} + +impl FromStr for UrlOrPath { + type Err = UrlOrPathError; + + fn from_str(s: &str) -> Result { + match Url::parse(s) { + Ok(url) => Ok(UrlOrPath::Url(url)), + Err(url::ParseError::RelativeUrlWithoutBase) => { + // This is most likely a relative path (`./boop` or `boop`) + // or an absolute path (`/boop`) + // + // So we'll see if such a path exists, and if so, use it + let path = PathBuf::from(s); + if path.exists() { + Ok(UrlOrPath::Path(path)) + } else { + Err(UrlOrPathError::PathDoesNotExist(path)) + } + }, + Err(e) => Err(UrlOrPathError::Url(s.to_string(), e)), + } + } +} + +#[cfg(feature = "cli")] +impl clap::builder::TypedValueParser for UrlOrPath { + type Value = UrlOrPath; + + fn parse_ref( + &self, + cmd: &clap::Command, + _arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + let value_str = value.to_str().ok_or_else(|| { + let mut err = clap::Error::new(clap::error::ErrorKind::InvalidValue); + err.insert( + ContextKind::InvalidValue, + ContextValue::String(format!("`{value:?}` not a UTF-8 string")), + ); + err + })?; + match UrlOrPath::from_str(value_str) { + Ok(v) => Ok(v), + Err(from_str_error) => { + let mut err = clap::Error::new(clap::error::ErrorKind::InvalidValue).with_cmd(cmd); + err.insert( + clap::error::ContextKind::Custom, + clap::error::ContextValue::String(from_str_error.to_string()), + ); + Err(err) + }, + } + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, Clone)] +pub enum UrlOrPathOrString { + Url(Url), + Path(PathBuf), + String(String), +} + +impl FromStr for UrlOrPathOrString { + type Err = url::ParseError; + + fn from_str(s: &str) -> Result { + match Url::parse(s) { + Ok(url) => Ok(UrlOrPathOrString::Url(url)), + Err(url::ParseError::RelativeUrlWithoutBase) => { + // This is most likely a relative path (`./boop` or `boop`) + // or an absolute path (`/boop`) + // + // So we'll see if such a path exists, and if so, use it + let path = PathBuf::from(s); + if path.exists() { + Ok(UrlOrPathOrString::Path(path)) + } else { + // The path doesn't exist, so the user is providing us with a string + Ok(UrlOrPathOrString::String(s.into())) + } + }, + Err(e) => Err(e), + } + } +} + +#[cfg(feature = "cli")] +impl clap::builder::TypedValueParser for UrlOrPathOrString { + type Value = UrlOrPathOrString; + + fn parse_ref( + &self, + cmd: &clap::Command, + _arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + let value_str = value.to_str().ok_or_else(|| { + let mut err = clap::Error::new(clap::error::ErrorKind::InvalidValue); + err.insert( + ContextKind::InvalidValue, + ContextValue::String(format!("`{value:?}` not a UTF-8 string")), + ); + err + })?; + match UrlOrPathOrString::from_str(value_str) { + Ok(v) => Ok(v), + Err(from_str_error) => { + let mut err = clap::Error::new(clap::error::ErrorKind::InvalidValue).with_cmd(cmd); + err.insert( + clap::error::ContextKind::Custom, + clap::error::ContextValue::String(from_str_error.to_string()), + ); + Err(err) + }, + } + } } #[cfg(feature = "diagnostics")] @@ -503,3 +672,48 @@ impl crate::diagnostics::ErrorDiagnostic for InstallSettingsError { static_str.to_string() } } + +#[cfg(test)] +mod tests { + use super::{FromStr, PathBuf, Url, UrlOrPath, UrlOrPathOrString}; + + #[test] + fn url_or_path_or_string_parses() -> Result<(), Box> { + assert_eq!( + UrlOrPathOrString::from_str("https://boop.bleat")?, + UrlOrPathOrString::Url(Url::from_str("https://boop.bleat")?), + ); + assert_eq!( + UrlOrPathOrString::from_str("file:///boop/bleat")?, + UrlOrPathOrString::Url(Url::from_str("file:///boop/bleat")?), + ); + // The file *must* exist! + assert_eq!( + UrlOrPathOrString::from_str(file!())?, + UrlOrPathOrString::Path(PathBuf::from_str(file!())?), + ); + assert_eq!( + UrlOrPathOrString::from_str("Boop")?, + UrlOrPathOrString::String(String::from("Boop")), + ); + Ok(()) + } + + #[test] + fn url_or_path_parses() -> Result<(), Box> { + assert_eq!( + UrlOrPath::from_str("https://boop.bleat")?, + UrlOrPath::Url(Url::from_str("https://boop.bleat")?), + ); + assert_eq!( + UrlOrPath::from_str("file:///boop/bleat")?, + UrlOrPath::Url(Url::from_str("file:///boop/bleat")?), + ); + // The file *must* exist! + assert_eq!( + UrlOrPath::from_str(file!())?, + UrlOrPath::Path(PathBuf::from_str(file!())?), + ); + Ok(()) + } +} diff --git a/tests/fixtures/linux/linux.json b/tests/fixtures/linux/linux.json index 681eedf4e..ad285084e 100644 --- a/tests/fixtures/linux/linux.json +++ b/tests/fixtures/linux/linux.json @@ -1,5 +1,5 @@ { - "version": "0.10.1-unreleased", + "version": "0.14.0", "actions": [ { "action": { @@ -18,7 +18,9 @@ "action": "provision_nix", "fetch_nix": { "action": { - "url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz", + "url_or_path": { + "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz" + }, "dest": "/nix/temp-install-dir", "proxy": null, "ssl_cert_file": null @@ -229,6 +231,17 @@ ] }, "create_directories": [ + { + "action": { + "path": "/etc/fish/conf.d", + "user": null, + "group": null, + "mode": 493, + "is_mountpoint": false, + "force_prune_on_revert": false + }, + "state": "Completed" + }, { "action": { "path": "/usr/share/fish/vendor_conf.d", @@ -286,6 +299,28 @@ }, "state": "Uncompleted" }, + { + "action": { + "path": "/etc/zsh/zshrc", + "user": null, + "group": null, + "mode": 420, + "buf": "\n# Nix\nif [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then\n . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'\nfi\n# End Nix\n\n \n", + "position": "Beginning" + }, + "state": "Uncompleted" + }, + { + "action": { + "path": "/etc/fish/conf.d/nix.fish", + "user": null, + "group": null, + "mode": 420, + "buf": "\n# Nix\nif test -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.fish'\n . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.fish'\nend\n# End Nix\n\n", + "position": "Beginning" + }, + "state": "Uncompleted" + }, { "action": { "path": "/usr/share/fish/vendor_conf.d/nix.fish", @@ -336,6 +371,18 @@ }, "state": "Uncompleted" }, + { + "action": { + "action": "create_directory", + "path": "/etc/tmpfiles.d", + "user": null, + "group": null, + "mode": 493, + "is_mountpoint": false, + "force_prune_on_revert": false + }, + "state": "Uncompleted" + }, { "action": { "action": "configure_init_service", @@ -362,7 +409,9 @@ "nix_build_user_count": 0, "nix_build_user_prefix": "nixbld", "nix_build_user_id_base": 30000, - "nix_package_url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz", + "nix_package_url": { + "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz" + }, "proxy": null, "ssl_cert_file": null, "extra_conf": [], @@ -375,7 +424,7 @@ } }, "diagnostic_data": { - "version": "0.10.1-unreleased", + "version": "0.14.0", "planner": "linux", "configured_settings": [], "os_name": "Ubuntu", diff --git a/tests/fixtures/linux/steam-deck.json b/tests/fixtures/linux/steam-deck.json index bbd3e609f..0610ff0be 100644 --- a/tests/fixtures/linux/steam-deck.json +++ b/tests/fixtures/linux/steam-deck.json @@ -1,5 +1,5 @@ { - "version": "0.10.1-unreleased", + "version": "0.14.0", "actions": [ { "action": { @@ -62,7 +62,9 @@ "action": "provision_nix", "fetch_nix": { "action": { - "url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz", + "url_or_path": { + "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz" + }, "dest": "/nix/temp-install-dir", "proxy": null, "ssl_cert_file": null @@ -391,7 +393,9 @@ "nix_build_user_count": 0, "nix_build_user_prefix": "nixbld", "nix_build_user_id_base": 30000, - "nix_package_url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz", + "nix_package_url": { + "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz" + }, "proxy": null, "ssl_cert_file": null, "extra_conf": [], @@ -400,7 +404,7 @@ } }, "diagnostic_data": { - "version": "0.10.1-unreleased", + "version": "0.14.0", "planner": "steam-deck", "configured_settings": [], "os_name": "Ubuntu", diff --git a/tests/fixtures/macos/macos.json b/tests/fixtures/macos/macos.json index 8147a5351..3ce5d7e05 100644 --- a/tests/fixtures/macos/macos.json +++ b/tests/fixtures/macos/macos.json @@ -1,5 +1,5 @@ { - "version": "0.10.1-unreleased", + "version": "0.14.0", "actions": [ { "action": { @@ -88,7 +88,9 @@ "action": "provision_nix", "fetch_nix": { "action": { - "url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-aarch64-darwin.tar.xz", + "url_or_path": { + "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-darwin.tar.xz" + }, "dest": "/nix/temp-install-dir", "proxy": null, "ssl_cert_file": null @@ -418,7 +420,9 @@ "nix_build_user_count": 32, "nix_build_user_prefix": "_nixbld", "nix_build_user_id_base": 300, - "nix_package_url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-aarch64-darwin.tar.xz", + "nix_package_url": { + "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-darwin.tar.xz" + }, "proxy": null, "ssl_cert_file": null, "extra_conf": [], @@ -431,7 +435,7 @@ "root_disk": "disk3" }, "diagnostic_data": { - "version": "0.10.1-unreleased", + "version": "0.14.0", "planner": "macos", "configured_settings": [], "os_name": "unknown",