difftreelog
refactor use nix bindings instead of REPL
in: trunk
33 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -22,9 +22,9 @@
[[package]]
name = "adler2"
-version = "2.0.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aead"
@@ -63,9 +63,9 @@
[[package]]
name = "age"
-version = "0.11.0"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2020562e68d7a02c2743707b262c62484b340a296924a5e4146d5a0a96ca8103"
+checksum = "57fc171f4874fa10887e47088f81a55fcf030cd421aa31ec2b370cafebcc608a"
dependencies = [
"aes",
"aes-gcm",
@@ -127,12 +127,13 @@
[[package]]
name = "alejandra"
-version = "3.1.0"
-source = "git+https://github.com/kamadorueda/alejandra#264e23546663a5676a77174cab31340a81aa2cc0"
+version = "4.0.0"
+source = "git+https://github.com/kamadorueda/alejandra#c68bef57c1db3add865493d9cb741a14618bdc28"
dependencies = [
"mimalloc",
"rnix",
"rowan",
+ "serde",
]
[[package]]
@@ -152,9 +153,9 @@
[[package]]
name = "anstream"
-version = "0.6.18"
+version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -167,43 +168,44 @@
[[package]]
name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
[[package]]
name = "anstyle-parse"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.1.2"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.6"
+version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
+checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [
"anstyle",
- "windows-sys 0.59.0",
+ "once_cell_polyfill",
+ "windows-sys 0.60.2",
]
[[package]]
name = "anyhow"
-version = "1.0.93"
+version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
+checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "arc-swap"
@@ -241,9 +243,9 @@
[[package]]
name = "async-trait"
-version = "0.1.88"
+version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
+checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
@@ -258,9 +260,9 @@
[[package]]
name = "autocfg"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "axum"
@@ -283,8 +285,8 @@
"pin-project-lite",
"rustversion",
"serde",
- "sync_wrapper 1.0.1",
- "tower 0.5.1",
+ "sync_wrapper",
+ "tower 0.5.2",
"tower-layer",
"tower-service",
]
@@ -304,16 +306,16 @@
"mime",
"pin-project-lite",
"rustversion",
- "sync_wrapper 1.0.1",
+ "sync_wrapper",
"tower-layer",
"tower-service",
]
[[package]]
name = "backtrace"
-version = "0.3.74"
+version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
@@ -321,7 +323,7 @@
"miniz_oxide",
"object",
"rustc-demangle",
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -338,15 +340,15 @@
[[package]]
name = "base64ct"
-version = "1.6.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
[[package]]
name = "basic-toml"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
+checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
dependencies = [
"serde",
]
@@ -380,10 +382,30 @@
]
[[package]]
+name = "bindgen"
+version = "0.72.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.13.0",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash 2.1.1",
+ "shlex",
+ "syn",
+]
+
+[[package]]
name = "bitflags"
-version = "2.9.1"
+version = "2.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
dependencies = [
"serde",
]
@@ -418,15 +440,15 @@
[[package]]
name = "bumpalo"
-version = "3.16.0"
+version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytecount"
-version = "0.6.8"
+version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
+checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "byteorder"
@@ -436,9 +458,9 @@
[[package]]
name = "bytes"
-version = "1.8.0"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cbc"
@@ -450,28 +472,28 @@
]
[[package]]
-name = "cbitset"
-version = "0.2.0"
+name = "cc"
+version = "1.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29b6ad25ae296159fb0da12b970b2fe179b234584d7cd294c891e2bbb284466b"
+checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc"
dependencies = [
- "num-traits",
+ "shlex",
]
[[package]]
-name = "cc"
-version = "1.2.1"
+name = "cexpr"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
- "shlex",
+ "nom 7.1.3",
]
[[package]]
name = "cfg-if"
-version = "1.0.0"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "cfg_aliases"
@@ -530,10 +552,21 @@
]
[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
name = "clap"
-version = "4.5.21"
+version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
+checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
dependencies = [
"clap_builder",
"clap_derive",
@@ -541,9 +574,9 @@
[[package]]
name = "clap_builder"
-version = "4.5.21"
+version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
+checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
dependencies = [
"anstream",
"anstyle",
@@ -551,23 +584,23 @@
"strsim",
"terminal_size",
"unicase",
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
]
[[package]]
name = "clap_complete"
-version = "4.5.38"
+version = "4.5.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01"
+checksum = "4d9501bd3f5f09f7bbee01da9a511073ed30a80cd7a509f1214bb74eadea71ad"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
-version = "4.5.18"
+version = "4.5.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
+checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
dependencies = [
"heck",
"proc-macro2",
@@ -577,27 +610,38 @@
[[package]]
name = "clap_lex"
-version = "0.7.3"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
+
+[[package]]
+name = "codespan-reporting"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
+dependencies = [
+ "serde",
+ "termcolor",
+ "unicode-width 0.2.1",
+]
[[package]]
name = "colorchoice"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "console"
-version = "0.15.8"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
+checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d"
dependencies = [
"encode_unicode",
- "lazy_static",
"libc",
- "unicode-width 0.1.11",
- "windows-sys 0.52.0",
+ "once_cell",
+ "unicode-width 0.2.1",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -632,26 +676,20 @@
[[package]]
name = "countme"
-version = "2.0.4"
+version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "328b822bdcba4d4e402be8d9adb6eebf269f969f8eadef977a553ff3c4fbcb58"
+checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
[[package]]
name = "cpufeatures"
-version = "0.2.15"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
-name = "crossbeam-utils"
-version = "0.8.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
-
-[[package]]
name = "crossterm"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -664,7 +702,7 @@
"filedescriptor",
"mio",
"parking_lot",
- "rustix 1.0.7",
+ "rustix 1.0.8",
"signal-hook",
"signal-hook-mio",
"winapi",
@@ -691,6 +729,22 @@
]
[[package]]
+name = "ctor"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb"
+dependencies = [
+ "ctor-proc-macro",
+ "dtor",
+]
+
+[[package]]
+name = "ctor-proc-macro"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2"
+
+[[package]]
name = "ctr"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -727,24 +781,72 @@
]
[[package]]
-name = "dashmap"
-version = "6.1.0"
+name = "cxx"
+version = "1.0.173"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c64ed3da1c337cbaae7223cdcff8b4dddf698d188cd3eaddd1116f6b0295950"
+dependencies = [
+ "cc",
+ "cxxbridge-cmd",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "foldhash",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.173"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
+checksum = "ae0a26a75a05551f5ae3d75b3557543d06682284eaa7419113162d602cb45766"
dependencies = [
- "cfg-if",
- "crossbeam-utils",
- "hashbrown 0.14.5",
- "lock_api",
- "once_cell",
- "parking_lot_core",
+ "cc",
+ "codespan-reporting",
+ "indexmap 2.11.0",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
]
[[package]]
+name = "cxxbridge-cmd"
+version = "1.0.173"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "952d408b6002b7db4b36da07c682a9cbb34f2db0efa03e976ae50a388414e16c"
+dependencies = [
+ "clap",
+ "codespan-reporting",
+ "indexmap 2.11.0",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.173"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccbd201b471c75c6abb6321cace706d1982d270e308b891c11a3262d320f5265"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.173"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bea8b915bbc4cb4288f242aa7ca18b23ecc6965e4d6e7c1b07905e3fe2e0c41"
+dependencies = [
+ "indexmap 2.11.0",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
+[[package]]
name = "der"
-version = "0.7.9"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid",
"zeroize",
@@ -752,9 +854,9 @@
[[package]]
name = "deranged"
-version = "0.4.0"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
+checksum = "75d7cc94194b4dd0fa12845ef8c911101b7f37633cda14997a6e82099aa0b693"
dependencies = [
"powerfmt",
"serde",
@@ -814,6 +916,21 @@
]
[[package]]
+name = "dtor"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e58a0764cddb55ab28955347b45be00ade43d4d6f3ba4bf3dc354e4ec9432934"
+dependencies = [
+ "dtor-proc-macro",
+]
+
+[[package]]
+name = "dtor-proc-macro"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5"
+
+[[package]]
name = "ed25519"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -825,9 +942,9 @@
[[package]]
name = "ed25519-dalek"
-version = "2.1.1"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
dependencies = [
"curve25519-dalek",
"ed25519",
@@ -839,37 +956,58 @@
[[package]]
name = "either"
-version = "1.13.0"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "encode_unicode"
-version = "0.3.6"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
+
+[[package]]
+name = "env_filter"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "log",
+]
[[package]]
name = "equivalent"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
-version = "0.3.12"
+version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
+checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
name = "fastrand"
-version = "2.2.0"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fiat-crypto"
@@ -879,9 +1017,9 @@
[[package]]
name = "filedescriptor"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e"
+checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d"
dependencies = [
"libc",
"thiserror 1.0.69",
@@ -899,9 +1037,9 @@
[[package]]
name = "fixedbitset"
-version = "0.4.2"
+version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "fleet"
@@ -963,7 +1101,7 @@
"nixlike",
"nom 8.0.0",
"openssh",
- "rand 0.9.1",
+ "rand 0.9.2",
"serde",
"serde_json",
"tabled",
@@ -985,7 +1123,7 @@
"ed25519-dalek",
"fleet-shared",
"hex",
- "rand 0.9.1",
+ "rand 0.9.2",
"x25519-dalek",
]
@@ -1035,7 +1173,7 @@
"fluent-syntax",
"intl-memoizer",
"intl_pluralrules",
- "rustc-hash",
+ "rustc-hash 1.1.0",
"self_cell 0.10.3",
"smallvec",
"unic-langid",
@@ -1066,6 +1204,12 @@
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1166,13 +1310,13 @@
[[package]]
name = "getrandom"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]]
@@ -1184,7 +1328,7 @@
"cfg-if",
"libc",
"r-efi",
- "wasi 0.14.2+wasi-0.2.4",
+ "wasi 0.14.3+wasi-0.2.4",
]
[[package]]
@@ -1204,10 +1348,16 @@
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
+name = "glob"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
+
+[[package]]
name = "h2"
-version = "0.4.6"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205"
+checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
dependencies = [
"atomic-waker",
"bytes",
@@ -1215,7 +1365,7 @@
"futures-core",
"futures-sink",
"http",
- "indexmap 2.6.0",
+ "indexmap 2.11.0",
"slab",
"tokio",
"tokio-util",
@@ -1224,12 +1374,6 @@
[[package]]
name = "hashbrown"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
-
-[[package]]
-name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
@@ -1242,9 +1386,9 @@
[[package]]
name = "hashbrown"
-version = "0.15.1"
+version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
[[package]]
name = "heck"
@@ -1254,15 +1398,9 @@
[[package]]
name = "hermit-abi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
-
-[[package]]
-name = "hermit-abi"
-version = "0.4.0"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "hex"
@@ -1310,9 +1448,9 @@
[[package]]
name = "http"
-version = "1.1.0"
+version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [
"bytes",
"fnv",
@@ -1331,12 +1469,12 @@
[[package]]
name = "http-body-util"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
- "futures-util",
+ "futures-core",
"http",
"http-body",
"pin-project-lite",
@@ -1344,9 +1482,9 @@
[[package]]
name = "httparse"
-version = "1.9.5"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
@@ -1362,13 +1500,14 @@
[[package]]
name = "hyper"
-version = "1.5.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
+checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
dependencies = [
+ "atomic-waker",
"bytes",
"futures-channel",
- "futures-util",
+ "futures-core",
"h2",
"http",
"http-body",
@@ -1376,6 +1515,7 @@
"httpdate",
"itoa",
"pin-project-lite",
+ "pin-utils",
"smallvec",
"tokio",
"want",
@@ -1396,18 +1536,20 @@
[[package]]
name = "hyper-util"
-version = "0.1.10"
+version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
+checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
dependencies = [
"bytes",
"futures-channel",
+ "futures-core",
"futures-util",
"http",
"http-body",
"hyper",
+ "libc",
"pin-project-lite",
- "socket2",
+ "socket2 0.6.0",
"tokio",
"tower-service",
"tracing",
@@ -1415,9 +1557,9 @@
[[package]]
name = "i18n-config"
-version = "0.4.7"
+version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e88074831c0be5b89181b05e6748c4915f77769ecc9a4c372f88b169a8509c9"
+checksum = "3e06b90c8a0d252e203c94344b21e35a30f3a3a85dc7db5af8f8df9f3e0c63ef"
dependencies = [
"basic-toml",
"log",
@@ -1429,9 +1571,9 @@
[[package]]
name = "i18n-embed"
-version = "0.15.2"
+version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7839d8c7bb8da7bd58c1112d3a1aeb7f178ff3df4ae87783e758ca3bfb750b7"
+checksum = "669ffc2c93f97e6ddf06ddbe999fcd6782e3342978bb85f7d3c087c7978404c4"
dependencies = [
"arc-swap",
"fluent",
@@ -1439,7 +1581,6 @@
"fluent-syntax",
"i18n-embed-impl",
"intl-memoizer",
- "lazy_static",
"log",
"parking_lot",
"rust-embed",
@@ -1450,17 +1591,15 @@
[[package]]
name = "i18n-embed-fl"
-version = "0.9.2"
+version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6e9571c3cba9eba538eaa5ee40031b26debe76f0c7e17bafc97ea57a76cd82e"
+checksum = "04b2969d0b3fc6143776c535184c19722032b43e6a642d710fa3f88faec53c2d"
dependencies = [
- "dashmap",
"find-crate",
"fluent",
"fluent-syntax",
"i18n-config",
"i18n-embed",
- "lazy_static",
"proc-macro-error2",
"proc-macro2",
"quote",
@@ -1484,14 +1623,15 @@
[[package]]
name = "iana-time-zone"
-version = "0.1.61"
+version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
+ "log",
"wasm-bindgen",
"windows-core",
]
@@ -1517,24 +1657,24 @@
[[package]]
name = "indexmap"
-version = "2.6.0"
+version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
+checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
dependencies = [
"equivalent",
- "hashbrown 0.15.1",
+ "hashbrown 0.15.5",
]
[[package]]
name = "indicatif"
-version = "0.17.9"
+version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281"
+checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd"
dependencies = [
"console",
- "number_prefix",
"portable-atomic",
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
+ "unit-prefix",
"vt100",
"web-time",
]
@@ -1547,9 +1687,9 @@
[[package]]
name = "inout"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"block-padding",
"generic-array",
@@ -1557,9 +1697,9 @@
[[package]]
name = "intl-memoizer"
-version = "0.5.2"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda"
+checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f"
dependencies = [
"type-map",
"unic-langid",
@@ -1575,6 +1715,17 @@
]
[[package]]
+name = "io-uring"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
name = "io_tee"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1582,13 +1733,13 @@
[[package]]
name = "is-terminal"
-version = "0.4.13"
+version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
+checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [
- "hermit-abi 0.4.0",
+ "hermit-abi",
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -1623,16 +1774,17 @@
[[package]]
name = "itoa"
-version = "1.0.11"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
-version = "0.3.72"
+version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
+ "once_cell",
"wasm-bindgen",
]
@@ -1647,27 +1799,46 @@
[[package]]
name = "libc"
-version = "0.2.174"
+version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
+checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
+name = "libloading"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.53.3",
+]
+
+[[package]]
name = "libm"
-version = "0.2.11"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
+checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "libmimalloc-sys"
-version = "0.1.39"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
+checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870"
dependencies = [
"cc",
"libc",
]
[[package]]
+name = "link-cplusplus"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1675,9 +1846,9 @@
[[package]]
name = "linux-raw-sys"
-version = "0.4.14"
+version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
@@ -1687,15 +1858,15 @@
[[package]]
name = "litrs"
-version = "0.4.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
[[package]]
name = "lock_api"
-version = "0.4.12"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
@@ -1703,17 +1874,17 @@
[[package]]
name = "log"
-version = "0.4.22"
+version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "matchers"
-version = "0.1.0"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
- "regex-automata 0.1.10",
+ "regex-automata",
]
[[package]]
@@ -1724,24 +1895,24 @@
[[package]]
name = "memchr"
-version = "2.7.4"
+version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "memoffset"
-version = "0.6.5"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "mimalloc"
-version = "0.1.43"
+version = "0.1.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
+checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8"
dependencies = [
"libmimalloc-sys",
]
@@ -1760,31 +1931,30 @@
[[package]]
name = "miniz_oxide"
-version = "0.8.0"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
-version = "1.0.2"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
+checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
- "hermit-abi 0.3.9",
"libc",
"log",
- "wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys 0.52.0",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+ "windows-sys 0.59.0",
]
[[package]]
name = "multimap"
-version = "0.10.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
+checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
[[package]]
name = "nix"
@@ -1804,17 +1974,24 @@
dependencies = [
"anyhow",
"better-command",
+ "bindgen",
+ "ctor",
+ "cxx",
+ "cxx-build",
"futures",
"itertools 0.14.0",
"nixlike",
+ "pkg-config",
"r2d2",
"regex",
"serde",
"serde_json",
- "thiserror 2.0.12",
+ "test-log",
+ "thiserror 2.0.16",
"tokio",
"tokio-util",
"tracing",
+ "tracing-indicatif",
"unindent",
]
@@ -1829,7 +2006,7 @@
"serde",
"serde-transcode",
"serde_json",
- "thiserror 2.0.12",
+ "thiserror 2.0.16",
]
[[package]]
@@ -1853,12 +2030,11 @@
[[package]]
name = "nu-ansi-term"
-version = "0.46.0"
+version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
dependencies = [
- "overload",
- "winapi",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1915,25 +2091,25 @@
]
[[package]]
-name = "number_prefix"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
-
-[[package]]
name = "object"
-version = "0.36.5"
+version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
-version = "1.20.2"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "opaque-debug"
@@ -1951,24 +2127,18 @@
"once_cell",
"shell-escape",
"tempfile",
- "thiserror 2.0.12",
+ "thiserror 2.0.16",
"tokio",
]
[[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
-
-[[package]]
name = "owo-colors"
-version = "4.2.1"
+version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec"
+checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e"
dependencies = [
"supports-color 2.1.0",
- "supports-color 3.0.1",
+ "supports-color 3.0.2",
]
[[package]]
@@ -1979,14 +2149,14 @@
dependencies = [
"bytecount",
"fnv",
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
]
[[package]]
name = "parking_lot"
-version = "0.12.3"
+version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -1994,15 +2164,15 @@
[[package]]
name = "parking_lot_core"
-version = "0.9.10"
+version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -2050,9 +2220,9 @@
[[package]]
name = "pem"
-version = "3.0.4"
+version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae"
+checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3"
dependencies = [
"base64 0.22.1",
"serde",
@@ -2060,34 +2230,34 @@
[[package]]
name = "percent-encoding"
-version = "2.3.1"
+version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "petgraph"
-version = "0.6.5"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
dependencies = [
"fixedbitset",
- "indexmap 2.6.0",
+ "indexmap 2.11.0",
]
[[package]]
name = "pin-project"
-version = "1.1.7"
+version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
+checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
-version = "1.1.7"
+version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
+checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
@@ -2096,9 +2266,9 @@
[[package]]
name = "pin-project-lite"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
@@ -2128,6 +2298,12 @@
]
[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2152,9 +2328,9 @@
[[package]]
name = "portable-atomic"
-version = "1.9.0"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
+checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "powerfmt"
@@ -2164,18 +2340,18 @@
[[package]]
name = "ppv-lite86"
-version = "0.2.20"
+version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "prettyplease"
-version = "0.2.25"
+version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
@@ -2205,18 +2381,18 @@
[[package]]
name = "proc-macro2"
-version = "1.0.89"
+version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
+checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "prost"
-version = "0.13.3"
+version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f"
+checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
dependencies = [
"bytes",
"prost-derive",
@@ -2224,13 +2400,12 @@
[[package]]
name = "prost-build"
-version = "0.13.3"
+version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15"
+checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
dependencies = [
- "bytes",
"heck",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"log",
"multimap",
"once_cell",
@@ -2245,12 +2420,12 @@
[[package]]
name = "prost-derive"
-version = "0.13.3"
+version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5"
+checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
dependencies = [
"anyhow",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"proc-macro2",
"quote",
"syn",
@@ -2258,18 +2433,18 @@
[[package]]
name = "prost-types"
-version = "0.13.3"
+version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670"
+checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
dependencies = [
"prost",
]
[[package]]
name = "quote"
-version = "1.0.37"
+version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
@@ -2304,9 +2479,9 @@
[[package]]
name = "rand"
-version = "0.9.1"
+version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
@@ -2338,7 +2513,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
- "getrandom 0.2.15",
+ "getrandom 0.2.16",
]
[[package]]
@@ -2352,9 +2527,9 @@
[[package]]
name = "rcgen"
-version = "0.13.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54077e1872c46788540de1ea3d7f4ccb1983d12f9aa909b234468676c1a36779"
+checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
dependencies = [
"pem",
"ring",
@@ -2365,68 +2540,52 @@
[[package]]
name = "redox_syscall"
-version = "0.5.7"
+version = "0.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
+checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
-version = "1.11.1"
+version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax 0.6.29",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
name = "regex-automata"
-version = "0.4.9"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.8.5",
+ "regex-syntax",
]
[[package]]
name = "regex-syntax"
-version = "0.6.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.5"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
name = "ring"
-version = "0.17.8"
+version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
- "getrandom 0.2.15",
+ "getrandom 0.2.16",
"libc",
- "spin",
"untrusted",
"windows-sys 0.52.0",
]
@@ -2455,20 +2614,18 @@
[[package]]
name = "rnix"
-version = "0.10.2"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8024a523e8836f1a5d051203dc00d833357fee94e351b51348dfaeca5364daa9"
+checksum = "6f15e00b0ab43abd70d50b6f8cd021290028f9b7fdd7cdfa6c35997173bc1ba9"
dependencies = [
- "cbitset",
"rowan",
- "smol_str",
]
[[package]]
name = "ron"
-version = "0.10.1"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f"
+checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468"
dependencies = [
"base64 0.22.1",
"bitflags",
@@ -2479,22 +2636,22 @@
[[package]]
name = "rowan"
-version = "0.12.6"
+version = "0.15.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1b36e449f3702f3b0c821411db1cbdf30fb451726a9456dce5dabcd44420043"
+checksum = "d4f1e4a001f863f41ea8d0e6a0c34b356d5b733db50dadab3efef640bafb779b"
dependencies = [
"countme",
- "hashbrown 0.9.1",
+ "hashbrown 0.14.5",
"memoffset",
- "rustc-hash",
+ "rustc-hash 1.1.0",
"text-size",
]
[[package]]
name = "rsa"
-version = "0.9.6"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
+checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b"
dependencies = [
"const-oid",
"digest",
@@ -2512,9 +2669,9 @@
[[package]]
name = "rust-embed"
-version = "8.5.0"
+version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
+checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
@@ -2523,9 +2680,9 @@
[[package]]
name = "rust-embed-impl"
-version = "8.5.0"
+version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
+checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c"
dependencies = [
"proc-macro2",
"quote",
@@ -2536,9 +2693,9 @@
[[package]]
name = "rust-embed-utils"
-version = "8.5.0"
+version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
+checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594"
dependencies = [
"sha2",
"walkdir",
@@ -2546,9 +2703,9 @@
[[package]]
name = "rustc-demangle"
-version = "0.1.24"
+version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]]
name = "rustc-hash"
@@ -2557,6 +2714,12 @@
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2567,35 +2730,35 @@
[[package]]
name = "rustix"
-version = "0.38.40"
+version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags",
"errno",
"libc",
- "linux-raw-sys 0.4.14",
- "windows-sys 0.52.0",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
]
[[package]]
name = "rustix"
-version = "1.0.7"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
+checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys 0.9.4",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
name = "rustls"
-version = "0.23.17"
+version = "0.23.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e"
+checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
dependencies = [
"log",
"once_cell",
@@ -2617,15 +2780,18 @@
[[package]]
name = "rustls-pki-types"
-version = "1.10.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
+checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
+dependencies = [
+ "zeroize",
+]
[[package]]
name = "rustls-webpki"
-version = "0.102.8"
+version = "0.103.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
+checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
dependencies = [
"ring",
"rustls-pki-types",
@@ -2634,15 +2800,15 @@
[[package]]
name = "rustversion"
-version = "1.0.18"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
-version = "1.0.18"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa20"
@@ -2678,6 +2844,12 @@
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
+name = "scratch"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2"
+
+[[package]]
name = "scrypt"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2703,20 +2875,20 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d"
dependencies = [
- "self_cell 1.0.4",
+ "self_cell 1.2.0",
]
[[package]]
name = "self_cell"
-version = "1.0.4"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a"
+checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
[[package]]
name = "semver"
-version = "1.0.23"
+version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[package]]
name = "serde"
@@ -2738,9 +2910,9 @@
[[package]]
name = "serde_bytes"
-version = "0.11.15"
+version = "0.11.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a"
+checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96"
dependencies = [
"serde",
]
@@ -2758,9 +2930,9 @@
[[package]]
name = "serde_json"
-version = "1.0.140"
+version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
dependencies = [
"itoa",
"memchr",
@@ -2770,9 +2942,9 @@
[[package]]
name = "sha2"
-version = "0.10.8"
+version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -2802,9 +2974,9 @@
[[package]]
name = "signal-hook"
-version = "0.3.17"
+version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
@@ -2823,9 +2995,9 @@
[[package]]
name = "signal-hook-registry"
-version = "1.4.2"
+version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
dependencies = [
"libc",
]
@@ -2842,36 +3014,34 @@
[[package]]
name = "slab"
-version = "0.4.9"
+version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
-dependencies = [
- "autocfg",
-]
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "smallvec"
-version = "1.13.2"
+version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
-name = "smol_str"
-version = "0.1.24"
+name = "socket2"
+version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9"
+checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
- "serde",
+ "libc",
+ "windows-sys 0.52.0",
]
[[package]]
name = "socket2"
-version = "0.5.7"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2914,18 +3084,18 @@
[[package]]
name = "supports-color"
-version = "3.0.1"
+version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77"
+checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6"
dependencies = [
"is_ci",
]
[[package]]
name = "syn"
-version = "2.0.87"
+version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
+checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
@@ -2934,15 +3104,9 @@
[[package]]
name = "sync_wrapper"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
-
-[[package]]
-name = "sync_wrapper"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
[[package]]
name = "tabled"
@@ -2970,25 +3134,34 @@
[[package]]
name = "tempfile"
-version = "3.20.0"
+version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
+checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e"
dependencies = [
"fastrand",
"getrandom 0.3.3",
"once_cell",
- "rustix 1.0.7",
- "windows-sys 0.59.0",
+ "rustix 1.0.8",
+ "windows-sys 0.60.2",
]
[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
name = "terminal_size"
-version = "0.4.0"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
+checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0"
dependencies = [
- "rustix 0.38.40",
- "windows-sys 0.59.0",
+ "rustix 1.0.8",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -3003,12 +3176,34 @@
]
[[package]]
+name = "test-log"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e33b98a582ea0be1168eba097538ee8dd4bbe0f2b01b22ac92ea30054e5be7b"
+dependencies = [
+ "env_logger",
+ "test-log-macros",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "test-log-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "451b374529930d7601b1eef8d32bc79ae870b6079b069401709c2a8bf9e75f36"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "testing_table"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f8daae29995a24f65619e19d8d31dea5b389f3d853d8bf297bbf607cd0014cc"
dependencies = [
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
]
[[package]]
@@ -3056,11 +3251,11 @@
[[package]]
name = "thiserror"
-version = "2.0.12"
+version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
dependencies = [
- "thiserror-impl 2.0.12",
+ "thiserror-impl 2.0.16",
]
[[package]]
@@ -3076,9 +3271,9 @@
[[package]]
name = "thiserror-impl"
-version = "2.0.12"
+version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
dependencies = [
"proc-macro2",
"quote",
@@ -3087,19 +3282,18 @@
[[package]]
name = "thread_local"
-version = "1.1.8"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
- "once_cell",
]
[[package]]
name = "time"
-version = "0.3.41"
+version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
+checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3"
dependencies = [
"deranged",
"num-conv",
@@ -3111,15 +3305,15 @@
[[package]]
name = "time-core"
-version = "0.1.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
+checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68"
[[package]]
name = "time-macros"
-version = "0.2.22"
+version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
+checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c"
dependencies = [
"num-conv",
"time-core",
@@ -3127,28 +3321,31 @@
[[package]]
name = "tinystr"
-version = "0.7.6"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
dependencies = [
"displaydoc",
+ "zerovec",
]
[[package]]
name = "tokio"
-version = "1.45.1"
+version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
+checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [
"backtrace",
"bytes",
+ "io-uring",
"libc",
"mio",
"pin-project-lite",
"signal-hook-registry",
- "socket2",
+ "slab",
+ "socket2 0.6.0",
"tokio-macros",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -3164,20 +3361,19 @@
[[package]]
name = "tokio-rustls"
-version = "0.26.0"
+version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
+checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
dependencies = [
"rustls",
- "rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-stream"
-version = "0.1.16"
+version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
+checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -3187,9 +3383,9 @@
[[package]]
name = "tokio-util"
-version = "0.7.15"
+version = "0.7.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
+checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
dependencies = [
"bytes",
"futures-core",
@@ -3229,7 +3425,7 @@
"pin-project",
"prost",
"rustls-pemfile",
- "socket2",
+ "socket2 0.5.10",
"tokio",
"tokio-rustls",
"tokio-stream",
@@ -3275,14 +3471,14 @@
[[package]]
name = "tower"
-version = "0.5.1"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
"pin-project-lite",
- "sync_wrapper 0.1.2",
+ "sync_wrapper",
"tower-layer",
"tower-service",
]
@@ -3318,9 +3514,9 @@
[[package]]
name = "tracing"
-version = "0.1.40"
+version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-attributes",
@@ -3329,9 +3525,9 @@
[[package]]
name = "tracing-attributes"
-version = "0.1.27"
+version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
@@ -3340,9 +3536,9 @@
[[package]]
name = "tracing-core"
-version = "0.1.32"
+version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
dependencies = [
"once_cell",
"valuable",
@@ -3350,9 +3546,9 @@
[[package]]
name = "tracing-indicatif"
-version = "0.3.6"
+version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "069580424efe11d97c3fef4197fa98c004fa26672cc71ad8770d224e23b1951d"
+checksum = "04d4e11e0e27acef25a47f27e9435355fecdc488867fa2bc90e75b0700d2823d"
dependencies = [
"indicatif",
"tracing",
@@ -3373,9 +3569,9 @@
[[package]]
name = "tracing-serde"
-version = "0.1.3"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
+checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
dependencies = [
"serde",
"tracing-core",
@@ -3383,14 +3579,14 @@
[[package]]
name = "tracing-subscriber"
-version = "0.3.18"
+version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
- "regex",
+ "regex-automata",
"serde",
"serde_json",
"sharded-slab",
@@ -3410,33 +3606,33 @@
[[package]]
name = "type-map"
-version = "0.5.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f"
+checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90"
dependencies = [
- "rustc-hash",
+ "rustc-hash 2.1.1",
]
[[package]]
name = "typenum"
-version = "1.17.0"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "unic-langid"
-version = "0.9.5"
+version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44"
+checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05"
dependencies = [
"unic-langid-impl",
]
[[package]]
name = "unic-langid-impl"
-version = "0.9.5"
+version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5"
+checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658"
dependencies = [
"serde",
"tinystr",
@@ -3444,15 +3640,15 @@
[[package]]
name = "unicase"
-version = "2.8.0"
+version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
+checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
-version = "1.0.13"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-segmentation"
@@ -3462,15 +3658,15 @@
[[package]]
name = "unicode-width"
-version = "0.1.11"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-width"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
[[package]]
name = "unicode_categories"
@@ -3485,6 +3681,12 @@
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
[[package]]
+name = "unit-prefix"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817"
+
+[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3508,9 +3710,9 @@
[[package]]
name = "valuable"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "version_check"
@@ -3526,7 +3728,7 @@
dependencies = [
"itoa",
"log",
- "unicode-width 0.1.11",
+ "unicode-width 0.1.14",
"vte",
]
@@ -3572,39 +3774,39 @@
[[package]]
name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
-version = "0.14.2+wasi-0.2.4"
+version = "0.14.3+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
dependencies = [
- "wit-bindgen-rt",
+ "wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
-version = "0.2.95"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
+ "rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.95"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
- "once_cell",
"proc-macro2",
"quote",
"syn",
@@ -3613,9 +3815,9 @@
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.95"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3623,9 +3825,9 @@
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.95"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
@@ -3636,9 +3838,12 @@
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.95"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
[[package]]
name = "web-time"
@@ -3659,7 +3864,7 @@
"either",
"home",
"once_cell",
- "rustix 0.38.40",
+ "rustix 0.38.44",
]
[[package]]
@@ -3680,11 +3885,11 @@
[[package]]
name = "winapi-util"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -3695,11 +3900,37 @@
[[package]]
name = "windows-core"
-version = "0.52.0"
+version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
- "windows-targets",
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
@@ -3709,12 +3940,30 @@
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
+name = "windows-result"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -3723,23 +3972,49 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.3",
+]
+
+[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.0",
+ "windows_aarch64_msvc 0.53.0",
+ "windows_i686_gnu 0.53.0",
+ "windows_i686_gnullvm 0.53.0",
+ "windows_i686_msvc 0.53.0",
+ "windows_x86_64_gnu 0.53.0",
+ "windows_x86_64_gnullvm 0.53.0",
+ "windows_x86_64_msvc 0.53.0",
]
[[package]]
@@ -3749,55 +4024,100 @@
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
+
+[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
+
+[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
+name = "windows_i686_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
+
+[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
+
+[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
+name = "windows_i686_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
+
+[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
+
+[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
+
+[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
-name = "wit-bindgen-rt"
-version = "0.39.0"
+name = "windows_x86_64_msvc"
+version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
-dependencies = [
- "bitflags",
-]
+checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+
+[[package]]
+name = "wit-bindgen"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
[[package]]
name = "wsl"
@@ -3834,19 +4154,18 @@
[[package]]
name = "zerocopy"
-version = "0.7.35"
+version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
- "byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.7.35"
+version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
@@ -3854,6 +4173,12 @@
]
[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+
+[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3872,3 +4197,12 @@
"quote",
"syn",
]
+
+[[package]]
+name = "zerovec"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
+dependencies = [
+ "zerofrom",
+]
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,7 +11,7 @@
nix-eval = { path = "./crates/nix-eval" }
nixlike = { path = "./crates/nixlike" }
-age = { version = "0.11", features = ["ssh", "plugin"] }
+age = { version = "0.11", features = ["plugin", "ssh"] }
anyhow = "1.0"
clap = { version = "4.5", features = ["derive", "env", "unicode", "wrap_help"] }
clap_complete = "4.5"
cmds/fleet/Cargo.tomldiffbeforeafterboth--- a/cmds/fleet/Cargo.toml
+++ b/cmds/fleet/Cargo.toml
@@ -43,7 +43,7 @@
fleet-base = { version = "0.1.0", path = "../../crates/fleet-base" }
human-repr = { version = "1.1", optional = true }
-indicatif = { version = "0.17", optional = true }
+indicatif = { version = "0.18", optional = true }
nom = "8.0.0"
tracing-indicatif = { version = "0.3", optional = true }
cmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/build_systems.rs
+++ b/cmds/fleet/src/cmds/build_systems.rs
@@ -1,13 +1,13 @@
use std::{env::current_dir, os::unix::fs::symlink, path::PathBuf};
-use anyhow::{Result, anyhow};
+use anyhow::Result;
use clap::Parser;
use fleet_base::{
deploy::{DeployAction, deploy_task, upload_task},
host::{Config, DeployKind, GenerationStorage},
opts::FleetOpts,
};
-use nix_eval::{NixBuildBatch, nix_go};
+use nix_eval::nix_go;
use tokio::task::LocalSet;
use tracing::{Instrument, error, field, info, info_span, warn};
@@ -32,17 +32,15 @@
config: Config,
hostname: String,
build_attr: &str,
- batch: Option<NixBuildBatch>,
+ // batch: Option<NixBuildBatch>,
) -> Result<PathBuf> {
info!("building");
let host = config.host(&hostname).await?;
// let action = Action::from(self.subcommand.clone());
let nixos = host.nixos_config().await?;
let drv = nix_go!(nixos.system.build[{ build_attr }]);
- let outputs = drv.build_maybe_batch(batch).await?;
- let out_output = outputs
- .get("out")
- .ok_or_else(|| anyhow!("system build should produce \"out\" output"))?;
+ // let outputs = drv.build_maybe_batch(batch).await?;
+ let out_output = drv.build("out").await?;
// We already have system profiles for backups.
if !host.local {
@@ -56,11 +54,11 @@
config.data().gc_root_prefix
),
)
- .arg(out_output);
+ .arg(&out_output);
cmd.sudo().run_nix().await?;
}
- Ok(out_output.clone())
+ Ok(out_output)
}
impl BuildSystems {
@@ -68,21 +66,20 @@
let hosts = opts.filter_skipped(config.list_hosts().await?).await?;
let set = LocalSet::new();
let build_attr = self.build_attr.clone();
- let batch = (hosts.len() > 1).then(|| {
- config
- .nix_session
- .new_build_batch("build-hosts".to_string())
- });
+ // let batch = (hosts.len() > 1).then(|| {
+ // config
+ // .nix_session
+ // .new_build_batch("build-hosts".to_string())
+ // });
for host in hosts {
let config = config.clone();
let span = info_span!("build", host = field::display(&host.name));
let hostname = host.name;
let build_attr = build_attr.clone();
- let batch = batch.clone();
+ // let batch = batch.clone();
set.spawn_local(
(async move {
- let built = match build_task(config, hostname.clone(), &build_attr, batch).await
- {
+ let built = match build_task(config, hostname.clone(), &build_attr).await {
Ok(path) => path,
Err(e) => {
error!("failed to deploy host: {}", e);
@@ -91,7 +88,7 @@
};
// TODO: Handle error
let mut out = current_dir().expect("cwd exists");
- out.push(format!("built-{}", hostname));
+ out.push(format!("built-{hostname}"));
info!("linking iso image to {:?}", out);
if let Err(e) = symlink(built, out) {
@@ -101,7 +98,6 @@
.instrument(span),
);
}
- drop(batch);
set.await;
Ok(())
}
@@ -111,17 +107,16 @@
pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> {
let hosts = opts.filter_skipped(config.list_hosts().await?).await?;
let set = LocalSet::new();
- let batch = (hosts.len() > 1).then(|| {
- config
- .nix_session
- .new_build_batch("deploy-hosts".to_string())
- });
+ // let batch = (hosts.len() > 1).then(|| {
+ // config
+ // .nix_session
+ // .new_build_batch("deploy-hosts".to_string())
+ // });
for host in hosts.into_iter() {
let config = config.clone();
let span = info_span!("deploy", host = field::display(&host.name));
let hostname = host.name.clone();
let opts = opts.clone();
- let batch = batch.clone();
if let Some(deploy_kind) = opts.action_attr::<DeployKind>(&host, "deploy_kind").await? {
host.set_deploy_kind(deploy_kind);
};
@@ -131,15 +126,14 @@
set.spawn_local(
(async move {
- let built =
- match build_task(config.clone(), hostname.clone(), "toplevel", batch).await
- {
- Ok(path) => path,
- Err(e) => {
- error!("failed to build host system closure: {}", e);
- return;
- }
- };
+ let built = match build_task(config.clone(), hostname.clone(), "toplevel").await
+ {
+ Ok(path) => path,
+ Err(e) => {
+ error!("failed to build host system closure: {:#}", e);
+ return;
+ }
+ };
let deploy_kind = match host.deploy_kind().await {
Ok(v) => v,
@@ -187,7 +181,6 @@
.instrument(span),
);
}
- drop(batch);
set.await;
Ok(())
}
cmds/fleet/src/cmds/info.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/info.rs
+++ b/cmds/fleet/src/cmds/info.rs
@@ -38,7 +38,8 @@
'host: for host in config.list_hosts().await? {
if !tagged.is_empty() {
let config = &config.config_field;
- let tags: Vec<String> = nix_go_json!(config.hosts[{ host.name }].tags);
+ let host_name = &host.name;
+ let tags: Vec<String> = nix_go_json!(config.hosts[host_name].tags);
for tag in tagged {
if !tags.contains(tag) {
continue 'host;
cmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/secrets/mod.rs
+++ b/cmds/fleet/src/cmds/secrets/mod.rs
@@ -2,6 +2,7 @@
collections::{BTreeMap, BTreeSet, HashSet},
io::{self, Read, Write, stdin, stdout},
path::PathBuf,
+ slice,
};
use age::Recipient;
@@ -14,7 +15,7 @@
opts::FleetOpts,
};
use fleet_shared::SecretData;
-use nix_eval::{NixBuildBatch, Value, nix_go, nix_go_json};
+use nix_eval::{NixType, Value, nix_go, nix_go_json};
use owo_colors::OwoColorize;
use serde::Deserialize;
use tabled::{Table, Tabled};
@@ -159,7 +160,7 @@
}
#[allow(clippy::too_many_arguments)]
-#[tracing::instrument(skip(config, secret, field, prefer_identities, batch))]
+#[tracing::instrument(skip(config, secret, field, prefer_identities))]
async fn maybe_regenerate_shared_secret(
secret_name: &str,
config: &Config,
@@ -168,7 +169,7 @@
expected_owners: &[String],
expected_generation_data: serde_json::Value,
prefer_identities: &[String],
- batch: Option<NixBuildBatch>,
+ // batch: Option<NixBuildBatch>,
) -> Result<FleetSharedSecret> {
let original_set = secret.owners.clone();
@@ -206,12 +207,12 @@
field,
expected_owners.to_vec(),
expected_generation_data,
- batch,
+ // batch,
)
.await?;
Ok(generated)
} else {
- drop(batch);
+ // drop(batch);
let identity_holder = if !prefer_identities.is_empty() {
prefer_identities
.iter()
@@ -263,7 +264,7 @@
default_generator: Value,
expected_owners: &[String],
expected_generation_data: serde_json::Value,
- batch: Option<NixBuildBatch>,
+ // batch: Option<NixBuildBatch>,
) -> Result<FleetSecret> {
let generator = nix_go!(secret.generator);
let on: Option<String> = nix_go_json!(default_generator.impureOn);
@@ -284,17 +285,16 @@
recipients.push(key);
}
let generators = nix_go!(mk_secret_generators(Obj { recipients }));
- let pkgs_and_generators = nix_go!(on_pkgs + generators);
+ // FIXME: Apparently, // operator is slow in nix
+ let pkgs_and_generators = on_pkgs.attrs_update(generators)?;
let call_package = nix_go!(nixpkgs.lib.callPackageWith(pkgs_and_generators));
let generator = nix_go!(call_package(generator)(Obj {}));
- let generator = generator.build_maybe_batch(batch).await?;
- let generator = generator
- .get("out")
- .ok_or_else(|| anyhow!("missing generateImpure out"))?;
- let generator = host.remote_derivation(generator).await?;
+ // let generator = generator.build_maybe_batch(batch).await?;
+ let generator = generator.build("out").await?;
+ let generator = host.remote_derivation(&generator).await?;
let out_parent = host.mktemp_dir().await?;
let out = format!("{out_parent}/out");
@@ -347,21 +347,21 @@
secret: Value,
expected_owners: &[String],
expected_generation_data: serde_json::Value,
- batch: Option<NixBuildBatch>,
+ // batch: Option<NixBuildBatch>,
) -> Result<FleetSecret> {
let generator = nix_go!(secret.generator);
// Can't properly check on nix module system level
{
- let gen_ty = generator.type_of().await?;
- if gen_ty == "null" {
+ let gen_ty = generator.type_of()?;
+ if matches!(gen_ty, NixType::Null) {
bail!("secret has no generator defined, can't automatically generate it.");
}
- if gen_ty == "set" {
- if !generator.has_field("__functor").await? {
- bail!("generator should be functor, got {gen_ty}");
+ if matches!(gen_ty, NixType::Attrs) {
+ if !generator.has_field("__functor")? {
+ bail!("generator should be functor, got {gen_ty:?}");
}
- } else if gen_ty != "lambda" {
- bail!("generator should be functor, got {gen_ty}");
+ } else if matches!(gen_ty, NixType::Function) {
+ bail!("generator should be functor, got {gen_ty:?}");
}
}
let nixpkgs = &config.nixpkgs;
@@ -378,7 +378,7 @@
let generators = nix_go!(default_mk_secret_generators(Obj {
recipients: <Vec<String>>::new(),
}));
- let pkgs_and_generators = nix_go!(default_pkgs + generators);
+ let pkgs_and_generators = default_pkgs.clone().attrs_update(generators)?;
let call_package = nix_go!(nixpkgs.lib.callPackageWith(pkgs_and_generators));
let default_generator = nix_go!(call_package(generator)(Obj {}));
@@ -394,7 +394,7 @@
default_generator,
expected_owners,
expected_generation_data,
- batch,
+ // batch,
)
.await
}
@@ -416,7 +416,7 @@
secret: Value,
expected_owners: Vec<String>,
expected_generation_data: serde_json::Value,
- batch: Option<NixBuildBatch>,
+ // batch: Option<NixBuildBatch>,
) -> Result<FleetSharedSecret> {
// let owners: Vec<String> = nix_go_json!(secret.expectedOwners);
Ok(FleetSharedSecret {
@@ -426,7 +426,7 @@
secret,
&expected_owners,
expected_generation_data,
- batch,
+ // batch,
)
.await?,
owners: expected_owners,
@@ -722,7 +722,8 @@
}
let config_field = &config.config_field;
- let field = nix_go!(config_field.sharedSecrets[{ name }]);
+ let name_clone = name.clone();
+ let field = nix_go!(config_field.sharedSecrets[name_clone]);
let expected_generation_data = nix_go_json!(field.expectedGenerationData);
let updated = maybe_regenerate_shared_secret(
@@ -733,7 +734,7 @@
&target_machines,
expected_generation_data,
&prefer_identities,
- None,
+ // None,
)
.await?;
config.replace_shared(name, updated);
@@ -746,7 +747,7 @@
let stored_shared_set = config.list_shared().into_iter().collect::<HashSet<_>>();
{
// Generate missing shared
- let shared_batch = None;
+ // let shared_batch = None;
let _span = info_span!("shared").entered();
let expected_shared_set = config
.list_configured_shared()
@@ -771,7 +772,7 @@
secret,
expected_owners,
expected_generation_data,
- shared_batch.clone(),
+ // shared_batch.clone(),
)
.in_current_span()
.await?;
@@ -779,7 +780,7 @@
}
}
if !skip_hosts {
- let hosts_batch = None;
+ // let hosts_batch = None;
for host in config.list_hosts().await? {
if opts.should_skip(&host).await? {
continue;
@@ -805,9 +806,9 @@
config,
missing,
secret,
- &[host.name.clone()],
+ slice::from_ref(&host.name),
expected_generation_data,
- hosts_batch.clone(),
+ // hosts_batch.clone(),
)
.in_current_span()
.await
@@ -831,9 +832,9 @@
config,
&name,
secret,
- &[host.name.clone()],
+ slice::from_ref(&host.name),
expected_generation_data,
- hosts_batch.clone(),
+ // hosts_batch.clone(),
)
.in_current_span()
.await
@@ -874,7 +875,7 @@
&expected_owners,
expected_generation_data,
&prefer_identities,
- None,
+ // None,
)
.await?,
);
cmds/fleet/src/cmds/tf.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/tf.rs
+++ b/cmds/fleet/src/cmds/tf.rs
@@ -42,8 +42,7 @@
debug!("generating terraform configs");
let system = &config.local_system;
let config = &config.config_field;
- let data: HashMap<String, PathBuf> = nix_go!(config.tf({ system })).build().await?;
- let data = &data["out"];
+ let data: PathBuf = nix_go!(config.tf({ system })).build("out").await?;
let data = fs::read(&data).await?;
create_dir_all(&dir).await?;
cmds/fleet/src/main.rsdiffbeforeafterboth--- a/cmds/fleet/src/main.rs
+++ b/cmds/fleet/src/main.rs
@@ -23,6 +23,7 @@
use human_repr::HumanCount;
#[cfg(feature = "indicatif")]
use indicatif::{ProgressState, ProgressStyle};
+use nix_eval::{gc_register_my_thread, gc_unregister_my_thread, init_libraries};
use tracing::{Instrument, error, info, info_span};
#[cfg(feature = "indicatif")]
use tracing_indicatif::IndicatifLayer;
@@ -183,21 +184,31 @@
}
setup_logging();
- async_main(opts)
-}
-#[tokio::main]
-async fn async_main(opts: RootOpts) -> ExitCode {
- if let Err(e) = main_real(opts).await {
- error!("{e:#}");
- return ExitCode::FAILURE;
- }
- ExitCode::SUCCESS
+ init_libraries();
+
+ tokio::runtime::Builder::new_multi_thread()
+ .enable_all()
+ .on_thread_start(|| {
+ gc_register_my_thread();
+ })
+ .on_thread_stop(|| {
+ gc_unregister_my_thread();
+ })
+ .build()
+ .expect("failed to build runtime")
+ .block_on(async {
+ if let Err(e) = main_real(opts).await {
+ error!("{e:#}");
+ ExitCode::FAILURE
+ } else {
+ ExitCode::SUCCESS
+ }
+ })
+ // async_main(opts)
}
async fn main_real(opts: RootOpts) -> Result<()> {
- nix_eval::init_tokio();
-
let nix_args = std::env::var_os("NIX_ARGS")
.map(|a| extra_args::parse_os(&a))
.transpose()?
crates/fleet-base/src/host.rsdiffbeforeafterboth1use std::{2 cell::OnceCell,3 collections::BTreeSet,4 ffi::{OsStr, OsString},5 fmt::Display,6 io::Write,7 ops::Deref,8 path::PathBuf,9 str::FromStr,10 sync::{Arc, Mutex, MutexGuard, OnceLock},11};1213use anyhow::{Context, Result, anyhow, bail, ensure};14use fleet_shared::SecretData;15use nix_eval::{NixSession, Value, nix_go, nix_go_json, util::assert_warn};16use openssh::SessionBuilder;17use serde::de::DeserializeOwned;18use tabled::Tabled;19use tempfile::NamedTempFile;20use time::{UtcDateTime, format_description};21use tracing::warn;2223use crate::{24 command::MyCommand,25 fleetdata::{FleetData, FleetSecret, FleetSharedSecret},26};2728pub struct FleetConfigInternals {29 /// Fleet project directory, containing fleet.nix file.30 pub directory: PathBuf,31 /// builtins.currentSystem32 pub local_system: String,33 pub data: Mutex<FleetData>,34 pub nix_args: Vec<OsString>,35 /// fleet_config.config36 pub config_field: Value,37 // TODO: Remove with connectivity refactor38 pub localhost: String,3940 /// import nixpkgs {system = local};41 pub default_pkgs: Value,42 /// inputs.nixpkgs43 pub nixpkgs: Value,4445 pub nix_session: NixSession,46}4748// TODO: Make field not pub49#[derive(Clone)]50pub struct Config(pub Arc<FleetConfigInternals>);5152impl Deref for Config {53 type Target = FleetConfigInternals;5455 fn deref(&self) -> &Self::Target {56 &self.057 }58}5960#[derive(Clone, Copy, Debug)]61pub enum EscalationStrategy {62 Sudo,63 Run0,64 Su,65}6667#[derive(Clone, PartialEq, Copy, Debug)]68pub enum DeployKind {69 /// NixOS => NixOS managed by fleet70 UpgradeToFleet,71 /// NixOS managed by fleet => NixOS managed by fleet72 Fleet,73 /// Remote host has /mnt, /mnt/boot mounted,74 /// generated config is added to fleet configuration.75 NixosInstall,76 /// Remote host has some system and nix installed in multi-user mode (/nix is owned by root),77 /// generated config is added to fleet configuration,78 /// and /etc/NIXOS_LUSTRATE exists, fleet will perform the rest.79 NixosLustrate,80}8182impl FromStr for DeployKind {83 type Err = anyhow::Error;84 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {85 match s {86 "upgrade-to-fleet" => Ok(Self::UpgradeToFleet),87 "fleet" => Ok(Self::Fleet),88 "nixos-install" => Ok(Self::NixosInstall),89 "nixos-lustrate" => Ok(Self::NixosLustrate),90 v => bail!(91 "unknown deploy_kind: {v}; expected on of \"upgrade-to-fleet\", \"fleet\", \"nixos-install\", \"nixos-lustrate\""92 ),93 }94 }95}96pub struct ConfigHost {97 config: Config,98 pub name: String,99 groups: OnceCell<Vec<String>>,100101 // TODO: Both of those values are taken from host opts, there should be a cleaner way to specify it102 deploy_kind: OnceCell<DeployKind>,103 session_destination: OnceCell<String>,104105 pub host_config: Option<Value>,106 pub nixos_config: OnceCell<Value>,107 pub nixos_unchecked_config: OnceCell<Value>,108 pub pkgs_override: Option<Value>,109110 // TODO: Move command helpers away with connectivity refactor111 pub local: bool,112 pub session: OnceLock<Arc<openssh::Session>>,113}114115#[derive(Debug, Clone, Copy)]116pub enum GenerationStorage {117 Deployer,118 Machine,119 Pusher,120}121impl GenerationStorage {122 fn prefix(&self) -> &'static str {123 match self {124 GenerationStorage::Deployer => "deployer.",125 GenerationStorage::Machine => "",126 GenerationStorage::Pusher => "pusher.",127 }128 }129}130131#[derive(Tabled, Debug)]132pub struct Generation {133 #[tabled(rename = "ID", format("{}", self.rollback_id()))]134 pub id: u32,135 #[tabled(rename = "Current")]136 pub current: bool,137 #[tabled(rename = "Created at")]138 pub datetime: UtcDateTime,139 #[tabled(format = "{:?}")]140 pub store_path: PathBuf,141 #[tabled(skip)]142 pub location: GenerationStorage,143}144impl Generation {145 pub fn rollback_id(&self) -> String {146 format!("{}{}", self.location.prefix(), self.id)147 }148}149150fn parse_generation_line(g: &str) -> Option<Generation> {151 let mut parts = g.split_whitespace();152 let id = parts.next()?;153 let id: u32 = id.parse().ok()?;154 let date = parts.next()?;155 let time = parts.next()?;156 let current = if let Some(current) = parts.next() {157 if current == "(current)" {158 Some(true)159 } else {160 None161 }162 } else {163 Some(false)164 };165 let current = current?;166 if parts.next().is_some() {167 warn!("unexpected text after generation: {g}");168 }169170 let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")171 .expect("valid format");172 let datetime = UtcDateTime::parse(&format!("{date} {time}"), &format).ok()?;173174 Some(Generation {175 id,176 current,177 datetime,178 store_path: PathBuf::new(),179 location: GenerationStorage::Machine,180 })181}182// TODO: Move command helpers away with connectivity refactor183impl ConfigHost {184 pub async fn list_generations(&self, profile: &str) -> Result<Vec<Generation>> {185 let mut cmd = self.cmd("nix-env").await?;186 cmd.comparg("--profile", format!("/nix/var/nix/profiles/{profile}"))187 .arg("--list-generations")188 .env("TZ", "UTC");189 // Sudo is required because --list-generations tries to acquire profile lock190 let data = cmd.sudo().run_string().await?;191 let mut generations = data192 .split('\n')193 .map(|e| e.trim())194 .filter(|&l| !l.is_empty())195 .filter_map(|g| {196 let generation = parse_generation_line(g);197 if generation.is_none() {198 warn!("bad generation: {g}");199 };200 generation201 })202 .collect::<Vec<_>>();203 for ele in generations.iter_mut() {204 let mut cmd = self.cmd("readlink").await?;205 cmd.arg("--")206 .arg(format!("/nix/var/nix/profiles/{profile}-{}-link", ele.id));207 let path = cmd.run_string().await?;208 ele.store_path = PathBuf::from(path.trim_end_matches("\n"));209 }210211 Ok(generations)212 }213214 pub fn set_session_destination(&self, dest: String) {215 self.session_destination216 .set(dest)217 .expect("session destination is already set")218 }219 pub fn set_deploy_kind(&self, kind: DeployKind) {220 self.deploy_kind221 .set(kind)222 .expect("deploy kind is already set");223 }224 pub async fn deploy_kind(&self) -> Result<DeployKind> {225 if let Some(kind) = self.deploy_kind.get() {226 return Ok(*kind);227 }228 let is_fleet_managed = match self.file_exists("/etc/FLEET_HOST").await {229 Ok(v) => v,230 Err(e) => {231 bail!("failed to query remote system kind: {}", e);232 }233 };234 if !is_fleet_managed {235 bail!(indoc::indoc! {"236 host is not marked as managed by fleet237 if you're not trying to lustrate/install system from scratch,238 you should either239 1. manually create /etc/FLEET_HOST file on the target host,240 2. use ?deploy_kind=fleet host argument if you're upgrading from older version of fleet241 3. use ?deploy_kind=upgrade_to_fleet if you're upgrading from plain nixos to fleet-managed nixos242 "});243 }244 // TOCTOU is possible245 let _ = self.deploy_kind.set(DeployKind::Fleet);246 Ok(*self.deploy_kind.get().expect("deploy kind is just set"))247 }248 pub async fn escalation_strategy(&self) -> Result<EscalationStrategy> {249 // Prefer sudo, as run0 has some gotchas with polkit250 // and too many repeating prompts.251 if (self.find_in_path("sudo").await).is_ok() {252 return Ok(EscalationStrategy::Sudo);253 }254 if (self.find_in_path("run0").await).is_ok() {255 return Ok(EscalationStrategy::Run0);256 }257 Ok(EscalationStrategy::Su)258 }259 async fn open_session(&self) -> Result<Arc<openssh::Session>> {260 assert!(!self.local, "do not open ssh connection to local session");261 // FIXME: TOCTOU262 if let Some(session) = &self.session.get() {263 return Ok((*session).clone());264 };265 let session = SessionBuilder::default();266267 let dest = self.session_destination.get().unwrap_or(&self.name);268 let session = session269 .connect(&dest)270 .await271 .map_err(|e| anyhow!("ssh error while connecting to {}: {e:#?}", self.name))?;272 let session = Arc::new(session);273 self.session.set(session.clone()).expect("TOCTOU happened");274 Ok(session)275 }276 pub async fn mktemp_dir(&self) -> Result<String> {277 let mut cmd = self.cmd("mktemp").await?;278 cmd.arg("-d");279 let path = cmd.run_string().await?;280 Ok(path.trim_end().to_owned())281 }282 pub async fn file_exists(&self, path: impl AsRef<OsStr>) -> Result<bool> {283 let mut cmd = self.cmd("sh").await?;284 cmd.arg("-c")285 .arg("test -e \"$1\" && echo true || echo false")286 .arg("_")287 .arg(path);288 cmd.run_value().await289 }290 pub async fn read_file_bin(&self, path: impl AsRef<OsStr>) -> Result<Vec<u8>> {291 let mut cmd = self.cmd("cat").await?;292 cmd.arg(path);293 cmd.run_bytes().await294 }295 pub async fn read_file_text(&self, path: impl AsRef<OsStr>) -> Result<String> {296 let mut cmd = self.cmd("cat").await?;297 cmd.arg(path);298 cmd.run_string().await299 }300 pub async fn read_dir(&self, path: impl AsRef<OsStr>) -> Result<Vec<String>> {301 let mut cmd = self.cmd("ls").await?;302 cmd.arg(path);303 let out = cmd.run_string().await?;304 let mut lines = out.split('\n');305 if let Some(last) = lines.next_back() {306 ensure!(last.is_empty(), "output of ls should end with newline");307 }308 Ok(lines.map(ToOwned::to_owned).collect())309 }310 #[allow(dead_code)]311 pub async fn read_file_json<D: DeserializeOwned>(&self, path: impl AsRef<OsStr>) -> Result<D> {312 let text = self.read_file_text(path).await?;313 Ok(serde_json::from_str(&text)?)314 }315 pub async fn read_env(&self, env: &str) -> Result<String> {316 let mut cmd = self.cmd("printenv").await?;317 cmd.arg(env);318 cmd.run_string().await319 }320 pub async fn find_in_path(&self, command: &str) -> Result<String> {321 // // `which` is not a part of coreutils, and it might not exist on machine.322 // let path = self.read_env("PATH").await?;323 // // Assuming delimiter is :, we don't work with windows host, this check will be much324 // // more sophisticated in remowt backend (and quicker, since actual PATH search will be done on remote machine)325 // for ele in path.split(':') {326 // let test_path = format!("{ele}/{cmd}");327 // test -x etc328 // }329 // let mut cmd = self.cmd("printenv").await?;330 // cmd.arg(env);331 // Ok(cmd.run_string().await?)332 // Assuming this is an environment issue if which doesn't exist, will be fixed with remowt.333 let mut cmd = self334 .cmd_escalation(335 // Not used336 EscalationStrategy::Su,337 "which",338 )339 .await?;340 cmd.arg(command);341 cmd.run_string().await342 }343 pub async fn read_file_value<D: FromStr>(&self, path: impl AsRef<OsStr>) -> Result<D>344 where345 <D as FromStr>::Err: Display,346 {347 let text = self.read_file_text(path).await?;348 D::from_str(&text).map_err(|e| anyhow!("failed to parse value: {e}"))349 }350 pub async fn cmd(&self, cmd: impl AsRef<OsStr>) -> Result<MyCommand> {351 self.cmd_escalation(self.escalation_strategy().await?, cmd)352 .await353 }354 pub async fn cmd_escalation(355 &self,356 escalation: EscalationStrategy,357 cmd: impl AsRef<OsStr>,358 ) -> Result<MyCommand> {359 if self.local {360 Ok(MyCommand::new(escalation, cmd))361 } else {362 let session = self.open_session().await?;363 Ok(MyCommand::new_on(escalation, cmd, session))364 }365 }366 pub async fn nix_cmd(&self) -> Result<MyCommand> {367 let mut nix = self.cmd("nix").await?;368 nix.args([369 "--extra-experimental-features",370 "nix-command",371 "--extra-experimental-features",372 "flakes",373 ]);374 Ok(nix)375 }376377 pub async fn decrypt(&self, data: SecretData) -> Result<Vec<u8>> {378 ensure!(data.encrypted, "secret is not encrypted");379 let mut cmd = self.cmd("fleet-install-secrets").await?;380 cmd.arg("decrypt").eqarg("--secret", data.to_string());381 let encoded = cmd382 .sudo()383 .run_string()384 .await385 .context("failed to call remote host for decrypt")?;386 let data: SecretData = encoded.parse().map_err(|e| anyhow!("{e}"))?;387 ensure!(!data.encrypted, "secret came out encrypted");388 Ok(data.data)389 }390 pub async fn reencrypt(&self, data: SecretData, targets: Vec<String>) -> Result<SecretData> {391 ensure!(data.encrypted, "secret is not encrypted");392 let mut cmd = self.cmd("fleet-install-secrets").await?;393 cmd.arg("reencrypt").eqarg("--secret", data.to_string());394 for target in targets {395 let key = self.config.key(&target).await?;396 cmd.eqarg("--targets", key);397 }398 let encoded = cmd399 .sudo()400 .run_string()401 .await402 .context("failed to call remote host for decrypt")?;403 let data: SecretData = encoded.parse().map_err(|e| anyhow!("{e}"))?;404 ensure!(data.encrypted, "secret came out not encrypted");405 Ok(data)406 }407 /// Returns path for futureproofing, as path might change i.e on conversion to CA408 pub async fn remote_derivation(&self, path: &PathBuf) -> Result<PathBuf> {409 if self.local {410 // Path is located locally, thus already trusted.411 return Ok(path.to_owned());412 }413 let mut nix = MyCommand::new(414 // Not used415 EscalationStrategy::Su,416 "nix",417 );418 nix.arg("copy").arg("--substitute-on-destination");419420 match self.deploy_kind().await? {421 DeployKind::Fleet | DeployKind::UpgradeToFleet | DeployKind::NixosLustrate => {422 nix.comparg("--to", format!("ssh-ng://{}", self.name));423 }424 DeployKind::NixosInstall => {425 nix426 // Signature checking makes no sense with remote-store store argument set, as we're not even interacting with remote nix daemon427 .arg("--no-check-sigs")428 .comparg(429 "--to",430 format!("ssh-ng://root@{}?remote-store=/mnt", self.name),431 );432 }433 }434 nix.arg(path);435 nix.run_nix().await.context("nix copy")?;436 Ok(path.to_owned())437 }438 pub async fn systemctl_stop(&self, name: &str) -> Result<()> {439 let mut cmd = self.cmd("systemctl").await?;440 cmd.arg("stop").arg(name);441 cmd.sudo().run().await442 }443 pub async fn systemctl_start(&self, name: &str) -> Result<()> {444 let mut cmd = self.cmd("systemctl").await?;445 cmd.arg("start").arg(name);446 cmd.sudo().run().await447 }448449 pub async fn rm_file(&self, path: impl AsRef<OsStr>, sudo: bool) -> Result<()> {450 let mut cmd = self.cmd("rm").await?;451 cmd.arg("-f").arg(path);452 if sudo {453 cmd = cmd.sudo()454 }455 cmd.run().await456 }457}458impl ConfigHost {459 // TOCTOU is possible here in case if config is changed, but this case is not handled anywhere anyway,460 // assuming getting tags always returns the same value.461 pub async fn tags(&self) -> Result<Vec<String>> {462 if let Some(v) = self.groups.get() {463 return Ok(v.clone());464 }465 let Some(host_config) = &self.host_config else {466 return Ok(vec![]);467 };468 let tags: Vec<String> = nix_go_json!(host_config.tags);469470 let _ = self.groups.set(tags.clone());471472 Ok(tags)473 }474 pub async fn nixos_config(&self) -> Result<Value> {475 if let Some(v) = self.nixos_config.get() {476 return Ok(v.clone());477 }478 let Some(host_config) = &self.host_config else {479 bail!("local host has no nixos_config");480 };481 let nixos_config = nix_go!(host_config.nixos.config);482 assert_warn("nixos config evaluation", &nixos_config).await?;483484 let _ = self.nixos_config.set(nixos_config.clone());485486 Ok(nixos_config)487 }488 pub async fn nixos_unchecked_config(&self) -> Result<Value> {489 if let Some(v) = self.nixos_unchecked_config.get() {490 return Ok(v.clone());491 }492 let Some(host_config) = &self.host_config else {493 bail!("local host has no nixos_config");494 };495 let nixos_config = nix_go!(host_config.nixos_unchecked.config);496497 let _ = self.nixos_unchecked_config.set(nixos_config.clone());498499 Ok(nixos_config)500 }501502 pub async fn list_configured_secrets(&self) -> Result<Vec<String>> {503 let nixos = self.nixos_unchecked_config().await?;504 let secrets = nix_go!(nixos.secrets);505 let mut out = Vec::new();506 for name in secrets.list_fields().await? {507 let secret = nix_go!(secrets[{ name }]);508 let is_shared: bool = nix_go_json!(secret.shared);509 if is_shared {510 continue;511 }512 out.push(name);513 }514 Ok(out)515 }516 pub async fn secret_field(&self, name: &str) -> Result<Value> {517 let nixos = self.nixos_unchecked_config().await?;518 Ok(nix_go!(nixos.secrets[{ name }]))519 }520521 /// Packages for this host, resolved with nixpkgs overlays522 pub async fn pkgs(&self) -> Result<Value> {523 if let Some(value) = &self.pkgs_override {524 return Ok(value.clone());525 }526 let Some(host_config) = &self.host_config else {527 bail!("local host has no host_config");528 };529 // TODO: Should nixos.options be cached?530 Ok(nix_go!(host_config.nixos.options._module.args.value.pkgs))531 }532}533534impl Config {535 pub async fn tagged_hostnames(&self, tag: &str) -> Result<Vec<String>> {536 let config = &self.config_field;537 let tagged: Vec<String> = nix_go_json!(config.taggedWith[{ tag }]);538 Ok(tagged)539 }540 pub async fn expand_owner_set(&self, owners: Vec<String>) -> Result<BTreeSet<String>> {541 let mut out = BTreeSet::new();542 for owner in owners {543 if let Some(tag) = owner.strip_prefix('@') {544 let hosts = self.tagged_hostnames(tag).await?;545 out.extend(hosts);546 } else {547 out.insert(owner);548 }549 }550 Ok(out)551 }552 pub fn local_host(&self) -> ConfigHost {553 ConfigHost {554 config: self.clone(),555 name: "<virtual localhost>".to_owned(),556 host_config: None,557 nixos_config: OnceCell::new(),558 nixos_unchecked_config: OnceCell::new(),559 groups: {560 let cell = OnceCell::new();561 let _ = cell.set(vec![]);562 cell563 },564 pkgs_override: Some(self.default_pkgs.clone()),565566 local: true,567 session: OnceLock::new(),568 deploy_kind: OnceCell::new(),569 session_destination: OnceCell::new(),570 }571 }572573 pub async fn host(&self, name: &str) -> Result<ConfigHost> {574 let config = &self.config_field;575 let host_config = nix_go!(config.hosts[{ name }]);576577 Ok(ConfigHost {578 config: self.clone(),579 name: name.to_owned(),580 host_config: Some(host_config),581 nixos_config: OnceCell::new(),582 nixos_unchecked_config: OnceCell::new(),583 groups: OnceCell::new(),584 pkgs_override: None,585586 // TODO: Remove with connectivit refactor587 local: self.localhost == name,588 session: OnceLock::new(),589 deploy_kind: OnceCell::new(),590 session_destination: OnceCell::new(),591 })592 }593 pub async fn list_hosts(&self) -> Result<Vec<ConfigHost>> {594 let config = &self.config_field;595 let names = nix_go!(config.hosts).list_fields().await?;596 let mut out = vec![];597 for name in names {598 out.push(self.host(&name).await?);599 }600 Ok(out)601 }602 // TODO: Replace usages with .host().nixos_config603 pub async fn system_config(&self, host: &str) -> Result<Value> {604 let fleet_field = &self.config_field;605 Ok(nix_go!(fleet_field.hosts[{ host }].nixos.config))606 }607608 /// Shared secrets configured in fleet.nix or in flake609 pub async fn list_configured_shared(&self) -> Result<Vec<String>> {610 let config_field = &self.config_field;611 Ok(nix_go!(config_field.sharedSecrets).list_fields().await?)612 }613 /// Shared secrets configured in fleet.nix614 pub fn list_shared(&self) -> Vec<String> {615 let data = self.data();616 data.shared_secrets.keys().cloned().collect()617 }618 pub fn has_shared(&self, name: &str) -> bool {619 let data = self.data();620 data.shared_secrets.contains_key(name)621 }622 pub fn replace_shared(&self, name: String, shared: FleetSharedSecret) {623 let mut data = self.data_mut();624 data.shared_secrets.insert(name.to_owned(), shared);625 }626 pub fn remove_shared(&self, secret: &str) {627 let mut data = self.data_mut();628 data.shared_secrets.remove(secret);629 }630631 pub fn list_secrets(&self, host: &str) -> Vec<String> {632 let data = self.data();633 let Some(secrets) = data.host_secrets.get(host) else {634 return Vec::new();635 };636 secrets.keys().cloned().collect()637 }638639 pub fn has_secret(&self, host: &str, secret: &str) -> bool {640 let data = self.data();641 let Some(host_secrets) = data.host_secrets.get(host) else {642 return false;643 };644 host_secrets.contains_key(secret)645 }646 pub fn insert_secret(&self, host: &str, secret: String, value: FleetSecret) {647 let mut data = self.data_mut();648 let host_secrets = data.host_secrets.entry(host.to_owned()).or_default();649 host_secrets.insert(secret, value);650 }651652 pub fn host_secret(&self, host: &str, secret: &str) -> Result<FleetSecret> {653 let data = self.data();654 let Some(host_secrets) = data.host_secrets.get(host) else {655 bail!("no secrets for machine {host}");656 };657 let Some(secret) = host_secrets.get(secret) else {658 bail!("machine {host} has no secret {secret}");659 };660 Ok(secret.clone())661 }662 pub fn shared_secret(&self, secret: &str) -> Result<FleetSharedSecret> {663 let data = self.data();664 let Some(secret) = data.shared_secrets.get(secret) else {665 bail!("no shared secret {secret}");666 };667 Ok(secret.clone())668 }669 pub async fn shared_secret_expected_owners(&self, secret: &str) -> Result<Vec<String>> {670 let config_field = &self.config_field;671 Ok(nix_go_json!(672 config_field.sharedSecrets[{ secret }].expectedOwners673 ))674 }675676 // TODO: Should this be something modifiable from other processes?677 // E.g terraform provider might want to update FleetData (e.g secrets),678 // and current implementation assumes only one process holds current fleet.nix679 // Given that it is no longer needs to be a file for nix evaluation,680 // maybe it can be a .nix file for persistence, but accessible only681 // thru some shared state controller? Might it be stored in terraform682 // state provider?683 pub fn data(&self) -> MutexGuard<FleetData> {684 self.data.lock().unwrap()685 }686 pub fn data_mut(&self) -> MutexGuard<FleetData> {687 self.data.lock().unwrap()688 }689 pub fn save(&self) -> Result<()> {690 let mut tempfile = NamedTempFile::new_in(self.directory.clone()).context("failed to create updated version of fleet.nix in the same directory as original.\nDo you have write access to it? Access only to the fleet.nix won't be enough, the directory is used for atomic overwrite operation.\nIt is not recommended to use fleet by root anyway, move fleet project to your home directory.")?;691 let data = nixlike::serialize(&self.data() as &FleetData)?;692 tempfile.write_all(693 format!(694 "# This file contains fleet state and shouldn't be edited by hand\n\n{}\n\n# vim: ts=2 et nowrap\n",695 data696 )697 .as_bytes(),698 )?;699 let mut fleet_data_path = self.directory.clone();700 fleet_data_path.push("fleet.nix");701 tempfile.persist(fleet_data_path)?;702 Ok(())703 }704}1use std::{2 cell::OnceCell,3 collections::BTreeSet,4 ffi::{OsStr, OsString},5 fmt::Display,6 io::Write,7 ops::Deref,8 path::PathBuf,9 str::FromStr,10 sync::{Arc, Mutex, MutexGuard, OnceLock},11};1213use anyhow::{Context, Result, anyhow, bail, ensure};14use fleet_shared::SecretData;15use nix_eval::{Value, nix_go, nix_go_json, util::assert_warn};16use openssh::SessionBuilder;17use serde::de::DeserializeOwned;18use tabled::Tabled;19use tempfile::NamedTempFile;20use time::{UtcDateTime, format_description};21use tracing::warn;2223use crate::{24 command::MyCommand,25 fleetdata::{FleetData, FleetSecret, FleetSharedSecret},26};2728pub struct FleetConfigInternals {29 /// Fleet project directory, containing fleet.nix file.30 pub directory: PathBuf,31 /// builtins.currentSystem32 pub local_system: String,33 pub data: Mutex<FleetData>,34 pub nix_args: Vec<OsString>,35 /// fleet_config.config36 pub config_field: Value,37 // TODO: Remove with connectivity refactor38 pub localhost: String,3940 /// import nixpkgs {system = local};41 pub default_pkgs: Value,42 /// inputs.nixpkgs43 pub nixpkgs: Value,44}4546// TODO: Make field not pub47#[derive(Clone)]48pub struct Config(pub Arc<FleetConfigInternals>);4950impl Deref for Config {51 type Target = FleetConfigInternals;5253 fn deref(&self) -> &Self::Target {54 &self.055 }56}5758#[derive(Clone, Copy, Debug)]59pub enum EscalationStrategy {60 Sudo,61 Run0,62 Su,63}6465#[derive(Clone, PartialEq, Copy, Debug)]66pub enum DeployKind {67 /// NixOS => NixOS managed by fleet68 UpgradeToFleet,69 /// NixOS managed by fleet => NixOS managed by fleet70 Fleet,71 /// Remote host has /mnt, /mnt/boot mounted,72 /// generated config is added to fleet configuration.73 NixosInstall,74 /// Remote host has some system and nix installed in multi-user mode (/nix is owned by root),75 /// generated config is added to fleet configuration,76 /// and /etc/NIXOS_LUSTRATE exists, fleet will perform the rest.77 NixosLustrate,78}7980impl FromStr for DeployKind {81 type Err = anyhow::Error;82 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {83 match s {84 "upgrade-to-fleet" => Ok(Self::UpgradeToFleet),85 "fleet" => Ok(Self::Fleet),86 "nixos-install" => Ok(Self::NixosInstall),87 "nixos-lustrate" => Ok(Self::NixosLustrate),88 v => bail!(89 "unknown deploy_kind: {v}; expected on of \"upgrade-to-fleet\", \"fleet\", \"nixos-install\", \"nixos-lustrate\""90 ),91 }92 }93}94pub struct ConfigHost {95 config: Config,96 pub name: String,97 groups: OnceCell<Vec<String>>,9899 // TODO: Both of those values are taken from host opts, there should be a cleaner way to specify it100 deploy_kind: OnceCell<DeployKind>,101 session_destination: OnceCell<String>,102103 pub host_config: Option<Value>,104 pub nixos_config: OnceCell<Value>,105 pub nixos_unchecked_config: OnceCell<Value>,106 pub pkgs_override: Option<Value>,107108 // TODO: Move command helpers away with connectivity refactor109 pub local: bool,110 pub session: OnceLock<Arc<openssh::Session>>,111}112113#[derive(Debug, Clone, Copy)]114pub enum GenerationStorage {115 Deployer,116 Machine,117 Pusher,118}119impl GenerationStorage {120 fn prefix(&self) -> &'static str {121 match self {122 GenerationStorage::Deployer => "deployer.",123 GenerationStorage::Machine => "",124 GenerationStorage::Pusher => "pusher.",125 }126 }127}128129#[derive(Tabled, Debug)]130pub struct Generation {131 #[tabled(rename = "ID", format("{}", self.rollback_id()))]132 pub id: u32,133 #[tabled(rename = "Current")]134 pub current: bool,135 #[tabled(rename = "Created at")]136 pub datetime: UtcDateTime,137 #[tabled(format = "{:?}")]138 pub store_path: PathBuf,139 #[tabled(skip)]140 pub location: GenerationStorage,141}142impl Generation {143 pub fn rollback_id(&self) -> String {144 format!("{}{}", self.location.prefix(), self.id)145 }146}147148fn parse_generation_line(g: &str) -> Option<Generation> {149 let mut parts = g.split_whitespace();150 let id = parts.next()?;151 let id: u32 = id.parse().ok()?;152 let date = parts.next()?;153 let time = parts.next()?;154 let current = if let Some(current) = parts.next() {155 if current == "(current)" {156 Some(true)157 } else {158 None159 }160 } else {161 Some(false)162 };163 let current = current?;164 if parts.next().is_some() {165 warn!("unexpected text after generation: {g}");166 }167168 let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")169 .expect("valid format");170 let datetime = UtcDateTime::parse(&format!("{date} {time}"), &format).ok()?;171172 Some(Generation {173 id,174 current,175 datetime,176 store_path: PathBuf::new(),177 location: GenerationStorage::Machine,178 })179}180// TODO: Move command helpers away with connectivity refactor181impl ConfigHost {182 pub async fn list_generations(&self, profile: &str) -> Result<Vec<Generation>> {183 let mut cmd = self.cmd("nix-env").await?;184 cmd.comparg("--profile", format!("/nix/var/nix/profiles/{profile}"))185 .arg("--list-generations")186 .env("TZ", "UTC");187 // Sudo is required because --list-generations tries to acquire profile lock188 let data = cmd.sudo().run_string().await?;189 let mut generations = data190 .split('\n')191 .map(|e| e.trim())192 .filter(|&l| !l.is_empty())193 .filter_map(|g| {194 let generation = parse_generation_line(g);195 if generation.is_none() {196 warn!("bad generation: {g}");197 };198 generation199 })200 .collect::<Vec<_>>();201 for ele in generations.iter_mut() {202 let mut cmd = self.cmd("readlink").await?;203 cmd.arg("--")204 .arg(format!("/nix/var/nix/profiles/{profile}-{}-link", ele.id));205 let path = cmd.run_string().await?;206 ele.store_path = PathBuf::from(path.trim_end_matches("\n"));207 }208209 Ok(generations)210 }211212 pub fn set_session_destination(&self, dest: String) {213 self.session_destination214 .set(dest)215 .expect("session destination is already set")216 }217 pub fn set_deploy_kind(&self, kind: DeployKind) {218 self.deploy_kind219 .set(kind)220 .expect("deploy kind is already set");221 }222 pub async fn deploy_kind(&self) -> Result<DeployKind> {223 if let Some(kind) = self.deploy_kind.get() {224 return Ok(*kind);225 }226 let is_fleet_managed = match self.file_exists("/etc/FLEET_HOST").await {227 Ok(v) => v,228 Err(e) => {229 bail!("failed to query remote system kind: {}", e);230 }231 };232 if !is_fleet_managed {233 bail!(234 "{}",235 indoc::indoc! {"236 host is not marked as managed by fleet237 if you're not trying to lustrate/install system from scratch,238 you should either239 1. manually create /etc/FLEET_HOST file on the target host,240 2. use ?deploy_kind=fleet host argument if you're upgrading from older version of fleet241 3. use ?deploy_kind=upgrade_to_fleet if you're upgrading from plain nixos to fleet-managed nixos242 "}243 );244 }245 // TOCTOU is possible246 let _ = self.deploy_kind.set(DeployKind::Fleet);247 Ok(*self.deploy_kind.get().expect("deploy kind is just set"))248 }249 pub async fn escalation_strategy(&self) -> Result<EscalationStrategy> {250 // Prefer sudo, as run0 has some gotchas with polkit251 // and too many repeating prompts.252 if (self.find_in_path("sudo").await).is_ok() {253 return Ok(EscalationStrategy::Sudo);254 }255 if (self.find_in_path("run0").await).is_ok() {256 return Ok(EscalationStrategy::Run0);257 }258 Ok(EscalationStrategy::Su)259 }260 async fn open_session(&self) -> Result<Arc<openssh::Session>> {261 assert!(!self.local, "do not open ssh connection to local session");262 // FIXME: TOCTOU263 if let Some(session) = &self.session.get() {264 return Ok((*session).clone());265 };266 let session = SessionBuilder::default();267268 let dest = self.session_destination.get().unwrap_or(&self.name);269 let session = session270 .connect(&dest)271 .await272 .map_err(|e| anyhow!("ssh error while connecting to {}: {e:#?}", self.name))?;273 let session = Arc::new(session);274 self.session.set(session.clone()).expect("TOCTOU happened");275 Ok(session)276 }277 pub async fn mktemp_dir(&self) -> Result<String> {278 let mut cmd = self.cmd("mktemp").await?;279 cmd.arg("-d");280 let path = cmd.run_string().await?;281 Ok(path.trim_end().to_owned())282 }283 pub async fn file_exists(&self, path: impl AsRef<OsStr>) -> Result<bool> {284 let mut cmd = self.cmd("sh").await?;285 cmd.arg("-c")286 .arg("test -e \"$1\" && echo true || echo false")287 .arg("_")288 .arg(path);289 cmd.run_value().await290 }291 pub async fn read_file_bin(&self, path: impl AsRef<OsStr>) -> Result<Vec<u8>> {292 let mut cmd = self.cmd("cat").await?;293 cmd.arg(path);294 cmd.run_bytes().await295 }296 pub async fn read_file_text(&self, path: impl AsRef<OsStr>) -> Result<String> {297 let mut cmd = self.cmd("cat").await?;298 cmd.arg(path);299 cmd.run_string().await300 }301 pub async fn read_dir(&self, path: impl AsRef<OsStr>) -> Result<Vec<String>> {302 let mut cmd = self.cmd("ls").await?;303 cmd.arg(path);304 let out = cmd.run_string().await?;305 let mut lines = out.split('\n');306 if let Some(last) = lines.next_back() {307 ensure!(last.is_empty(), "output of ls should end with newline");308 }309 Ok(lines.map(ToOwned::to_owned).collect())310 }311 #[allow(dead_code)]312 pub async fn read_file_json<D: DeserializeOwned>(&self, path: impl AsRef<OsStr>) -> Result<D> {313 let text = self.read_file_text(path).await?;314 Ok(serde_json::from_str(&text)?)315 }316 pub async fn read_env(&self, env: &str) -> Result<String> {317 let mut cmd = self.cmd("printenv").await?;318 cmd.arg(env);319 cmd.run_string().await320 }321 pub async fn find_in_path(&self, command: &str) -> Result<String> {322 // // `which` is not a part of coreutils, and it might not exist on machine.323 // let path = self.read_env("PATH").await?;324 // // Assuming delimiter is :, we don't work with windows host, this check will be much325 // // more sophisticated in remowt backend (and quicker, since actual PATH search will be done on remote machine)326 // for ele in path.split(':') {327 // let test_path = format!("{ele}/{cmd}");328 // test -x etc329 // }330 // let mut cmd = self.cmd("printenv").await?;331 // cmd.arg(env);332 // Ok(cmd.run_string().await?)333 // Assuming this is an environment issue if which doesn't exist, will be fixed with remowt.334 let mut cmd = self335 .cmd_escalation(336 // Not used337 EscalationStrategy::Su,338 "which",339 )340 .await?;341 cmd.arg(command);342 cmd.run_string().await343 }344 pub async fn read_file_value<D: FromStr>(&self, path: impl AsRef<OsStr>) -> Result<D>345 where346 <D as FromStr>::Err: Display,347 {348 let text = self.read_file_text(path).await?;349 D::from_str(&text).map_err(|e| anyhow!("failed to parse value: {e}"))350 }351 pub async fn cmd(&self, cmd: impl AsRef<OsStr>) -> Result<MyCommand> {352 self.cmd_escalation(self.escalation_strategy().await?, cmd)353 .await354 }355 pub async fn cmd_escalation(356 &self,357 escalation: EscalationStrategy,358 cmd: impl AsRef<OsStr>,359 ) -> Result<MyCommand> {360 if self.local {361 Ok(MyCommand::new(escalation, cmd))362 } else {363 let session = self.open_session().await?;364 Ok(MyCommand::new_on(escalation, cmd, session))365 }366 }367 pub async fn nix_cmd(&self) -> Result<MyCommand> {368 let mut nix = self.cmd("nix").await?;369 nix.args([370 "--extra-experimental-features",371 "nix-command",372 "--extra-experimental-features",373 "flakes",374 ]);375 Ok(nix)376 }377378 pub async fn decrypt(&self, data: SecretData) -> Result<Vec<u8>> {379 ensure!(data.encrypted, "secret is not encrypted");380 let mut cmd = self.cmd("fleet-install-secrets").await?;381 cmd.arg("decrypt").eqarg("--secret", data.to_string());382 let encoded = cmd383 .sudo()384 .run_string()385 .await386 .context("failed to call remote host for decrypt")?;387 let data: SecretData = encoded.parse().map_err(|e| anyhow!("{e}"))?;388 ensure!(!data.encrypted, "secret came out encrypted");389 Ok(data.data)390 }391 pub async fn reencrypt(&self, data: SecretData, targets: Vec<String>) -> Result<SecretData> {392 ensure!(data.encrypted, "secret is not encrypted");393 let mut cmd = self.cmd("fleet-install-secrets").await?;394 cmd.arg("reencrypt").eqarg("--secret", data.to_string());395 for target in targets {396 let key = self.config.key(&target).await?;397 cmd.eqarg("--targets", key);398 }399 let encoded = cmd400 .sudo()401 .run_string()402 .await403 .context("failed to call remote host for decrypt")?;404 let data: SecretData = encoded.parse().map_err(|e| anyhow!("{e}"))?;405 ensure!(data.encrypted, "secret came out not encrypted");406 Ok(data)407 }408 /// Returns path for futureproofing, as path might change i.e on conversion to CA409 pub async fn remote_derivation(&self, path: &PathBuf) -> Result<PathBuf> {410 if self.local {411 // Path is located locally, thus already trusted.412 return Ok(path.to_owned());413 }414 let mut nix = MyCommand::new(415 // Not used416 EscalationStrategy::Su,417 "nix",418 );419 nix.arg("copy").arg("--substitute-on-destination");420421 match self.deploy_kind().await? {422 DeployKind::Fleet | DeployKind::UpgradeToFleet | DeployKind::NixosLustrate => {423 nix.comparg("--to", format!("ssh-ng://{}", self.name));424 }425 DeployKind::NixosInstall => {426 nix427 // Signature checking makes no sense with remote-store store argument set, as we're not even interacting with remote nix daemon428 .arg("--no-check-sigs")429 .comparg(430 "--to",431 format!("ssh-ng://root@{}?remote-store=/mnt", self.name),432 );433 }434 }435 nix.arg(path);436 nix.run_nix().await.context("nix copy")?;437 Ok(path.to_owned())438 }439 pub async fn systemctl_stop(&self, name: &str) -> Result<()> {440 let mut cmd = self.cmd("systemctl").await?;441 cmd.arg("stop").arg(name);442 cmd.sudo().run().await443 }444 pub async fn systemctl_start(&self, name: &str) -> Result<()> {445 let mut cmd = self.cmd("systemctl").await?;446 cmd.arg("start").arg(name);447 cmd.sudo().run().await448 }449450 pub async fn rm_file(&self, path: impl AsRef<OsStr>, sudo: bool) -> Result<()> {451 let mut cmd = self.cmd("rm").await?;452 cmd.arg("-f").arg(path);453 if sudo {454 cmd = cmd.sudo()455 }456 cmd.run().await457 }458}459impl ConfigHost {460 // TOCTOU is possible here in case if config is changed, but this case is not handled anywhere anyway,461 // assuming getting tags always returns the same value.462 pub async fn tags(&self) -> Result<Vec<String>> {463 if let Some(v) = self.groups.get() {464 return Ok(v.clone());465 }466 let Some(host_config) = &self.host_config else {467 return Ok(vec![]);468 };469 let tags: Vec<String> = nix_go_json!(host_config.tags);470471 let _ = self.groups.set(tags.clone());472473 Ok(tags)474 }475 pub async fn nixos_config(&self) -> Result<Value> {476 if let Some(v) = self.nixos_config.get() {477 return Ok(v.clone());478 }479 let Some(host_config) = &self.host_config else {480 bail!("local host has no nixos_config");481 };482 let nixos_config = nix_go!(host_config.nixos.config);483 assert_warn("nixos config evaluation", &nixos_config).await?;484485 let _ = self.nixos_config.set(nixos_config.clone());486487 Ok(nixos_config)488 }489 pub async fn nixos_unchecked_config(&self) -> Result<Value> {490 if let Some(v) = self.nixos_unchecked_config.get() {491 return Ok(v.clone());492 }493 let Some(host_config) = &self.host_config else {494 bail!("local host has no nixos_config");495 };496 let nixos_config = nix_go!(host_config.nixos_unchecked.config);497498 let _ = self.nixos_unchecked_config.set(nixos_config.clone());499500 Ok(nixos_config)501 }502503 pub async fn list_configured_secrets(&self) -> Result<Vec<String>> {504 let nixos = self.nixos_unchecked_config().await?;505 let secrets = nix_go!(nixos.secrets);506 let mut out = Vec::new();507 for name in secrets.list_fields()? {508 let secret = secrets.get_field(&name)?;509 let is_shared: bool = nix_go_json!(secret.shared);510 if is_shared {511 continue;512 }513 out.push(name);514 }515 Ok(out)516 }517 pub async fn secret_field(&self, name: &str) -> Result<Value> {518 let nixos = self.nixos_unchecked_config().await?;519 Ok(nix_go!(nixos.secrets[{ name }]))520 }521522 /// Packages for this host, resolved with nixpkgs overlays523 pub async fn pkgs(&self) -> Result<Value> {524 if let Some(value) = &self.pkgs_override {525 return Ok(value.clone());526 }527 let Some(host_config) = &self.host_config else {528 bail!("local host has no host_config");529 };530 // TODO: Should nixos.options be cached?531 Ok(nix_go!(host_config.nixos.options._module.args.value.pkgs))532 }533}534535impl Config {536 pub async fn tagged_hostnames(&self, tag: &str) -> Result<Vec<String>> {537 let config = &self.config_field;538 let tagged: Vec<String> = nix_go_json!(config.taggedWith[{ tag }]);539 Ok(tagged)540 }541 pub async fn expand_owner_set(&self, owners: Vec<String>) -> Result<BTreeSet<String>> {542 let mut out = BTreeSet::new();543 for owner in owners {544 if let Some(tag) = owner.strip_prefix('@') {545 let hosts = self.tagged_hostnames(tag).await?;546 out.extend(hosts);547 } else {548 out.insert(owner);549 }550 }551 Ok(out)552 }553 pub fn local_host(&self) -> ConfigHost {554 ConfigHost {555 config: self.clone(),556 name: "<virtual localhost>".to_owned(),557 host_config: None,558 nixos_config: OnceCell::new(),559 nixos_unchecked_config: OnceCell::new(),560 groups: {561 let cell = OnceCell::new();562 let _ = cell.set(vec![]);563 cell564 },565 pkgs_override: Some(self.default_pkgs.clone()),566567 local: true,568 session: OnceLock::new(),569 deploy_kind: OnceCell::new(),570 session_destination: OnceCell::new(),571 }572 }573574 pub async fn host(&self, name: &str) -> Result<ConfigHost> {575 let config = &self.config_field;576 let host_config = nix_go!(config.hosts[{ name }]);577578 Ok(ConfigHost {579 config: self.clone(),580 name: name.to_owned(),581 host_config: Some(host_config),582 nixos_config: OnceCell::new(),583 nixos_unchecked_config: OnceCell::new(),584 groups: OnceCell::new(),585 pkgs_override: None,586587 // TODO: Remove with connectivit refactor588 local: self.localhost == name,589 session: OnceLock::new(),590 deploy_kind: OnceCell::new(),591 session_destination: OnceCell::new(),592 })593 }594 pub async fn list_hosts(&self) -> Result<Vec<ConfigHost>> {595 let config = &self.config_field;596 let names = nix_go!(config.hosts).list_fields()?;597 let mut out = vec![];598 for name in names {599 out.push(self.host(&name).await?);600 }601 Ok(out)602 }603 // TODO: Replace usages with .host().nixos_config604 pub async fn system_config(&self, host: &str) -> Result<Value> {605 let fleet_field = &self.config_field;606 Ok(nix_go!(fleet_field.hosts[{ host }].nixos.config))607 }608609 /// Shared secrets configured in fleet.nix or in flake610 pub async fn list_configured_shared(&self) -> Result<Vec<String>> {611 let config_field = &self.config_field;612 nix_go!(config_field.sharedSecrets).list_fields()613 }614 /// Shared secrets configured in fleet.nix615 pub fn list_shared(&self) -> Vec<String> {616 let data = self.data();617 data.shared_secrets.keys().cloned().collect()618 }619 pub fn has_shared(&self, name: &str) -> bool {620 let data = self.data();621 data.shared_secrets.contains_key(name)622 }623 pub fn replace_shared(&self, name: String, shared: FleetSharedSecret) {624 let mut data = self.data_mut();625 data.shared_secrets.insert(name.to_owned(), shared);626 }627 pub fn remove_shared(&self, secret: &str) {628 let mut data = self.data_mut();629 data.shared_secrets.remove(secret);630 }631632 pub fn list_secrets(&self, host: &str) -> Vec<String> {633 let data = self.data();634 let Some(secrets) = data.host_secrets.get(host) else {635 return Vec::new();636 };637 secrets.keys().cloned().collect()638 }639640 pub fn has_secret(&self, host: &str, secret: &str) -> bool {641 let data = self.data();642 let Some(host_secrets) = data.host_secrets.get(host) else {643 return false;644 };645 host_secrets.contains_key(secret)646 }647 pub fn insert_secret(&self, host: &str, secret: String, value: FleetSecret) {648 let mut data = self.data_mut();649 let host_secrets = data.host_secrets.entry(host.to_owned()).or_default();650 host_secrets.insert(secret, value);651 }652653 pub fn host_secret(&self, host: &str, secret: &str) -> Result<FleetSecret> {654 let data = self.data();655 let Some(host_secrets) = data.host_secrets.get(host) else {656 bail!("no secrets for machine {host}");657 };658 let Some(secret) = host_secrets.get(secret) else {659 bail!("machine {host} has no secret {secret}");660 };661 Ok(secret.clone())662 }663 pub fn shared_secret(&self, secret: &str) -> Result<FleetSharedSecret> {664 let data = self.data();665 let Some(secret) = data.shared_secrets.get(secret) else {666 bail!("no shared secret {secret}");667 };668 Ok(secret.clone())669 }670 pub async fn shared_secret_expected_owners(&self, secret: &str) -> Result<Vec<String>> {671 let config_field = &self.config_field;672 Ok(nix_go_json!(673 config_field.sharedSecrets[{ secret }].expectedOwners674 ))675 }676677 // TODO: Should this be something modifiable from other processes?678 // E.g terraform provider might want to update FleetData (e.g secrets),679 // and current implementation assumes only one process holds current fleet.nix680 // Given that it is no longer needs to be a file for nix evaluation,681 // maybe it can be a .nix file for persistence, but accessible only682 // thru some shared state controller? Might it be stored in terraform683 // state provider?684 pub fn data(&'_ self) -> MutexGuard<'_, FleetData> {685 self.data.lock().unwrap()686 }687 pub fn data_mut(&'_ self) -> MutexGuard<'_, FleetData> {688 self.data.lock().unwrap()689 }690 pub fn save(&self) -> Result<()> {691 let mut tempfile = NamedTempFile::new_in(self.directory.clone()).context("failed to create updated version of fleet.nix in the same directory as original.\nDo you have write access to it? Access only to the fleet.nix won't be enough, the directory is used for atomic overwrite operation.\nIt is not recommended to use fleet by root anyway, move fleet project to your home directory.")?;692 let data = nixlike::serialize(&self.data() as &FleetData)?;693 tempfile.write_all(694 format!(695 "# This file contains fleet state and shouldn't be edited by hand\n\n{data}\n\n# vim: ts=2 et nowrap\n"696 )697 .as_bytes(),698 )?;699 let mut fleet_data_path = self.directory.clone();700 fleet_data_path.push("fleet.nix");701 tempfile.persist(fleet_data_path)?;702 Ok(())703 }704}crates/fleet-base/src/opts.rsdiffbeforeafterboth--- a/crates/fleet-base/src/opts.rs
+++ b/crates/fleet-base/src/opts.rs
@@ -7,7 +7,7 @@
};
use anyhow::{Context, Result, bail};
-use nix_eval::{NixSessionPool, Value, nix_go, util::assert_warn};
+use nix_eval::{FetchSettings, FlakeReference, FlakeSettings, Value, nix_go, util::assert_warn};
use nom::{
Parser,
bytes::complete::take_while1,
@@ -210,30 +210,39 @@
std::fs::read_to_string(&fleet_data_path).context("reading fleet state (fleet.nix)")?;
let data: Mutex<FleetData> = nixlike::parse_str(&bytes)?;
- let pool = NixSessionPool::new(
- directory.as_os_str().to_owned(),
- nix_args.clone(),
- self.local_system.clone(),
- self.fail_fast,
- )
- .await?;
- let nix_session = pool.get().await?;
+ let mut fetch_settings = FetchSettings::new();
+ fetch_settings.set(c"warn-dirty", c"false");
+
+ // TODO: use correct directory, not cwd
+ let (mut flake, _) = FlakeReference::new(
+ directory
+ .to_str()
+ .ok_or_else(|| anyhow::anyhow!("fleet dir should have utf-8 path"))?,
+ &fetch_settings,
+ )?;
+ let flake = flake.lock(&fetch_settings)?;
+
+ let mut flake_settings = FlakeSettings::new()?;
+ let flake = flake.get_attrs(&mut flake_settings)?;
- let builtins_field = Value::binding(nix_session.clone(), "builtins").await?;
+ let builtins_field = Value::eval("builtins")?;
- let fleet_root = Value::binding(nix_session.clone(), "fleetConfigurations").await?;
- let fleet_field = nix_go!(fleet_root.default({ data }));
+ let fleet_root = flake.get_field("fleetConfigurations")?;
+ let data_val = Value::serialized(&data)?;
+ let fleet_field = nix_go!(fleet_root.default(data_val));
let config_field = nix_go!(fleet_field.config);
if assert {
- assert_warn("fleet config evaluation", &config_field).await?;
+ assert_warn("fleet config evaluation", &config_field)
+ .await
+ .context("failed to verify assertions")?;
}
let import = nix_go!(builtins_field.import);
let overlays = nix_go!(config_field.nixpkgs.overlays);
let nixpkgs = nix_go!(config_field.nixpkgs.buildUsing);
- let nixpkgs_imported = nix_go!(nixpkgs | import);
+ let nixpkgs_imported = nix_go!(import(nixpkgs));
let default_pkgs = nix_go!(nixpkgs_imported(Obj {
overlays,
@@ -241,7 +250,6 @@
}));
Ok(Config(Arc::new(FleetConfigInternals {
- nix_session,
directory,
data,
local_system: self.local_system.clone(),
crates/nix-eval/Cargo.tomldiffbeforeafterboth--- a/crates/nix-eval/Cargo.toml
+++ b/crates/nix-eval/Cargo.toml
@@ -16,12 +16,17 @@
tokio-util.workspace = true
tracing.workspace = true
+cxx = "1.0.168"
futures = "0.3.31"
itertools = "0.14.0"
r2d2 = "0.8.10"
regex = "1.11.1"
+test-log = { version = "0.2.18", features = ["trace"] }
unindent = "0.2.4"
+tracing-indicatif = "0.3.13"
+ctor = "0.5.0"
-# [build-dependencies]
-# bindgen = "0.69.4"
-# pkg-config = "0.3.30"
+[build-dependencies]
+bindgen = "0.72.0"
+cxx-build = "1.0.168"
+pkg-config = "0.3.30"
crates/nix-eval/build.rsdiffbeforeafterboth--- a/crates/nix-eval/build.rs
+++ b/crates/nix-eval/build.rs
@@ -1,30 +1,101 @@
-// use bindgen::callbacks::ParseCallbacks;
-// use std::path::PathBuf;
-//
-// #[derive(Debug)]
-// struct StripPrefix;
-// impl ParseCallbacks for StripPrefix {
-// fn item_name(&self, name: &str) -> Option<String> {
-// name.strip_prefix("nix_").map(ToOwned::to_owned)
-// }
-// }
+use bindgen::{
+ RustEdition,
+ callbacks::{ItemInfo, ParseCallbacks},
+};
+use std::path::PathBuf;
+#[derive(Debug)]
+struct StripPrefix;
+impl ParseCallbacks for StripPrefix {
+ fn item_name(&self, name: ItemInfo<'_>) -> Option<String> {
+ name.name.strip_prefix("nix_").map(ToOwned::to_owned)
+ }
+}
+
fn main() {
- //
- // let mut libnix = bindgen::builder().header_contents("nix.h", "
- // #define GC_THREADS
- // #include <gc/gc.h>
- // #include <nix_api_expr.h>
- // #include <nix_api_store.h>
- // #include <nix_api_util.h>
- // #include <nix_api_value.h>
- // ").parse_callbacks(Box::new(StripPrefix));
- //
- // for header in pkg_config::probe_library("nix-expr-c").expect("nix-expr-c").include_paths.into_iter().chain(pkg_config::probe_library("bdw-gc").expect("bdw-gc").include_paths.into_iter()) {
- // libnix = libnix.clang_arg(format!("-I{}", header.to_str().expect("path is utf-8")));
- // }
+ // Link nix C++ libraries for cxx
+ for lib in &[
+ "nix-util",
+ "nix-store",
+ "nix-expr",
+ "nix-flake",
+ "nix-fetchers",
+ "bdw-gc",
+ ] {
+ if let Ok(library) = pkg_config::probe_library(lib) {
+ for lib_path in library.libs {
+ println!("cargo:rustc-link-lib={lib_path}");
+ }
+ for search_path in library.link_paths {
+ println!("cargo:rustc-link-search=native={}", search_path.display());
+ }
+ }
+ }
+
+ cxx_build::bridge("src/logging.rs")
+ .file("src/logging.cc")
+ .std("c++20")
+ .shared_flag(true)
+ .compile("nix-eval-logging");
+ cxx_build::bridge("src/lib.rs")
+ .file("src/lib.cc")
+ .std("c++20")
+ .shared_flag(true)
+ .compile("nix-eval");
+
+ println!("cargo:rerun-if-changed=src/lib.cc");
+ println!("cargo:rerun-if-changed=src/lib.hh");
+ println!("cargo:rerun-if-changed=src/logging.cc");
+ println!("cargo:rerun-if-changed=src/logging.hh");
+
//
- // let mut out = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR is set by cargo"));
- // out.push("bindings.rs");
- // libnix.generate().expect("generate bindings").write_to_file(out).expect("write bindings");
+ let mut libnix = bindgen::builder()
+ .rust_edition(RustEdition::Edition2024)
+ .header_contents(
+ "nix.h",
+ "
+ #define GC_THREADS
+ #include <gc/gc.h>
+ #include <nix_api_expr.h>
+ #include <nix_api_store.h>
+ #include <nix_api_flake.h>
+ #include <nix_api_fetchers.h>
+ #include <nix_api_util.h>
+ #include <nix_api_value.h>
+ ",
+ )
+ .parse_callbacks(Box::new(StripPrefix));
+
+ for header in pkg_config::probe_library("nix-expr-c")
+ .expect("nix-expr-c")
+ .include_paths
+ .into_iter()
+ .chain(
+ pkg_config::probe_library("nix-flake-c")
+ .expect("nix-flake-c")
+ .include_paths
+ .into_iter(),
+ )
+ .chain(
+ pkg_config::probe_library("nix-fetchers-c")
+ .expect("nix-fetchers-c")
+ .include_paths
+ .into_iter(),
+ )
+ .chain(
+ pkg_config::probe_library("bdw-gc")
+ .expect("bdw-gc")
+ .include_paths
+ .into_iter(),
+ ) {
+ libnix = libnix.clang_arg(format!("-I{}", header.to_str().expect("path is utf-8")));
+ }
+
+ let mut out = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR is set by cargo"));
+ out.push("bindings.rs");
+ libnix
+ .generate()
+ .expect("generate bindings")
+ .write_to_file(out)
+ .expect("write bindings");
}
crates/nix-eval/src/lib.ccdiffbeforeafterboth--- /dev/null
+++ b/crates/nix-eval/src/lib.cc
@@ -0,0 +1,17 @@
+#include "nix-eval/src/lib.rs"
+#include "lib.hh"
+#include <nix/fetchers/fetch-settings.hh>
+#include <nix/util/ref.hh>
+#include <nix_api_fetchers.h>
+
+struct nix_fetchers_settings {
+ nix::ref<nix::fetchers::Settings> settings;
+};
+
+extern "C" {
+void set_fetcher_setting(nix_fetchers_settings *settings_struct,
+ const char *setting, const char *value) {
+ auto &settings_ref = settings_struct->settings;
+ bool result = settings_ref->set(setting, value);
+}
+}
crates/nix-eval/src/lib.hhdiffbeforeafterboth--- /dev/null
+++ b/crates/nix-eval/src/lib.hh
@@ -0,0 +1,7 @@
+#pragma once
+#include <nix_api_fetchers.h>
+
+extern "C" {
+void set_fetcher_setting(nix_fetchers_settings *settings, const char *setting,
+ const char *value);
+}
crates/nix-eval/src/lib.rsdiffbeforeafterboth--- a/crates/nix-eval/src/lib.rs
+++ b/crates/nix-eval/src/lib.rs
@@ -3,120 +3,892 @@
//!
//! Current api is awful, little effort was put into this implementation.
-use std::{collections::HashMap, path::PathBuf, sync::Arc};
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::ffi::{CStr, CString, c_char, c_int, c_uint, c_void};
+use std::fmt;
+use std::ptr::null_mut;
+use std::sync::LazyLock;
+use std::{collections::HashMap, path::PathBuf};
-pub use pool::NixSessionPool;
-use pool::NixSessionPoolInner;
-use r2d2::PooledConnection;
-pub use session::{Error, Result};
-use tokio::sync::{mpsc, oneshot};
-use tracing::instrument;
-pub use value::{Index, Value};
+use anyhow::{Context, bail};
+use serde::Serialize;
+use serde::de::DeserializeOwned;
+
+pub use anyhow::Result;
+
+use self::logging::nix_logging_cxx;
+use self::nix_cxx::set_fetcher_setting;
+use self::nix_raw::{
+ alloc_value, c_context, c_context_create, err_code, err_info_msg, eval_state_build,
+ eval_state_builder_new, expr_eval_from_string, fetchers_settings, fetchers_settings_free,
+ fetchers_settings_new, flake_lock, flake_lock_flags, flake_lock_flags_free,
+ flake_lock_flags_new, flake_reference_parse_flags, flake_reference_parse_flags_free,
+ flake_reference_parse_flags_new, flake_reference_parse_flags_set_base_directory,
+ flake_settings, flake_settings_free, flake_settings_new, init_bool, init_int, init_string,
+ locked_flake_free, locked_flake_get_output_attrs, set_err_msg, setting_set, state_free,
+ value_decref, value_force, value_incref,
+};
-mod pool;
-mod session;
mod value;
// Contains macros helpers
+pub mod logging;
#[doc(hidden)]
pub mod macros;
pub mod util;
-// #[allow(non_upper_case_globals, non_camel_case_types, non_snake_case)]
-// mod nix_raw {
-// include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-// }
-// fn init() {
-// nix_raw::libutil_init();
-// }
+#[allow(non_upper_case_globals, non_camel_case_types, non_snake_case)]
+mod nix_raw {
+ include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+}
+#[cxx::bridge]
+pub mod nix_cxx {
+ unsafe extern "C++" {
+ type nix_fetchers_settings;
+ include!("nix-eval/src/lib.hh");
+
+ unsafe fn set_fetcher_setting(
+ settings: *mut nix_fetchers_settings,
+ setting: *const c_char,
+ value: *const c_char,
+ );
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum NixType {
+ Thunk,
+ Int,
+ Float,
+ Bool,
+ String,
+ Path,
+ Null,
+ Attrs,
+ List,
+ Function,
+ External,
+}
+impl NixType {
+ fn from_int(c: c_uint) -> Self {
+ match c {
+ 0 => Self::Thunk,
+ 1 => Self::Int,
+ 2 => Self::Float,
+ 3 => Self::Bool,
+ 4 => Self::String,
+ 5 => Self::Path,
+ 6 => Self::Null,
+ 7 => Self::Attrs,
+ 8 => Self::List,
+ 9 => Self::Function,
+ 10 => Self::External,
+ _ => unreachable!("unknown nix type: {c}"),
+ }
+ }
+}
+
+#[derive(Debug)]
+#[repr(i32)]
+enum NixErrorKind {
+ Unknown = 1,
+ Overflow = 2,
+ Key = 3,
+ Generic = 4,
+}
+impl NixErrorKind {
+ fn from_int(v: c_int) -> Option<Self> {
+ Some(match v {
+ 0 => return None,
+ -1 => Self::Unknown,
+ -2 => Self::Overflow,
+ -3 => Self::Key,
+ -4 => Self::Generic,
+ _ => {
+ debug_assert!(false, "unexpected nix error kind: {v}");
+ Self::Unknown
+ }
+ })
+ }
+}
+
+pub fn gc_register_my_thread() {
+ assert_eq!(unsafe { nix_raw::GC_thread_is_registered() }, 0);
+
+ let mut sb = nix_raw::GC_stack_base {
+ mem_base: null_mut(),
+ };
+ let r = unsafe { nix_raw::GC_get_stack_base(&mut sb) };
+ if r as u32 != nix_raw::GC_SUCCESS {
+ panic!("failed to get thread stack base");
+ }
+ unsafe { nix_raw::GC_register_my_thread(&sb) };
+}
+pub fn gc_unregister_my_thread() {
+ assert_eq!(unsafe { nix_raw::GC_thread_is_registered() }, 1);
+
+ unsafe { nix_raw::GC_unregister_my_thread() };
+}
+
+struct ThreadRegisterGuard {}
+impl ThreadRegisterGuard {
+ fn new() -> Self {
+ gc_register_my_thread();
+ Self {}
+ }
+}
+impl Drop for ThreadRegisterGuard {
+ fn drop(&mut self) {
+ gc_unregister_my_thread();
+ }
+}
+
+struct NixContext(*mut c_context);
+impl NixContext {
+ fn set_err(&mut self, err: NixErrorKind, msg: &CStr) {
+ unsafe { set_err_msg(self.0, err as c_int, msg.as_ptr()) };
+ }
+ fn new() -> Self {
+ let ctx = unsafe { c_context_create() };
+ Self(ctx)
+ }
+ fn error_kind(&self) -> Option<NixErrorKind> {
+ let code = unsafe { err_code(self.0) };
+ NixErrorKind::from_int(code)
+ }
+ fn error<'t>(&self) -> Option<Cow<'t, str>> {
+ if let NixErrorKind::Generic = self.error_kind()? {
+ let mut err_out = String::new();
+ unsafe {
+ err_info_msg(
+ null_mut(),
+ self.0,
+ Some(copy_nix_str),
+ (&raw mut err_out).cast(),
+ )
+ };
+ return Some(Cow::Owned(err_out));
+ };
+
+ // TODO: Can throw error (resulting in panic) if unable to retrieve error. Should be able to resolve by passing context as a first argument,
+ // but it looks ugly
+ let str = unsafe { nix_raw::err_msg(null_mut(), self.0, null_mut()) };
+ Some(unsafe { CStr::from_ptr(str) }.to_string_lossy())
+
+ // TODO: There is also nix_err_info_msg, but I don't understand when it should be used
+ // Some(match self.error_kind()? {
+ // NixErrorKind::Generic => {
+ // }
+ // })
+ }
+ fn clean_err(&mut self) {
+ unsafe {
+ nix_raw::clear_err(self.0);
+ }
+ }
+
+ fn bail_if_error(&self) -> Result<()> {
+ if let Some(err) = self.error() {
+ bail!("{err}");
+ };
+ Ok(())
+ }
+
+ fn run_in_context<T>(&mut self, f: impl FnOnce(*mut c_context) -> T) -> Result<T> {
+ self.clean_err();
+ let o = f(self.0);
+ self.bail_if_error()?;
+ self.clean_err();
+ Ok(o)
+ }
+}
+impl Drop for NixContext {
+ fn drop(&mut self) {
+ unsafe {
+ nix_raw::c_context_free(self.0);
+ }
+ }
+}
+struct GlobalState {
+ store: Store,
+ state: EvalState,
+}
+impl GlobalState {
+ fn new() -> Result<Self> {
+ let mut ctx = NixContext::new();
+ let store = ctx
+ .run_in_context(|c| unsafe { nix_raw::store_open(c, c"daemon".as_ptr(), null_mut()) })
+ .map(Store)?;
+
+ let builder = ctx.run_in_context(|c| unsafe { eval_state_builder_new(c, store.0) })?;
+ ctx.run_in_context(|c| {
+ unsafe {
+ nix_raw::eval_state_builder_set_eval_setting(
+ c,
+ builder,
+ c"lazy-trees".as_ptr(),
+ c"true".as_ptr(),
+ )
+ }
+ // eval_s
+ })?;
+ let state = ctx
+ .run_in_context(|c| unsafe { eval_state_build(c, builder) })
+ .map(EvalState)?;
+
+ Ok(Self { store, state })
+ }
+}
+
+struct ThreadState {
+ ctx: NixContext,
+}
+impl ThreadState {
+ fn new() -> Result<Self> {
+ let ctx = NixContext::new();
+
+ Ok(Self { ctx })
+ }
+}
+
+static GLOBAL_STATE: LazyLock<GlobalState> =
+ LazyLock::new(|| GlobalState::new().expect("global state init shouldn't fail"));
+
+thread_local! {
+ static THREAD_STATE: RefCell<ThreadState> = RefCell::new(ThreadState::new().expect("thread state init shouldn't fail"));
+}
+fn with_default_context<T>(
+ f: impl FnOnce(*mut c_context, *mut nix_raw::EvalState) -> T,
+) -> Result<T> {
+ let global = &GLOBAL_STATE.state;
+ let (ctx, state) = THREAD_STATE.with_borrow_mut(|w| (w.ctx.0, global.0));
+ let mut ctx = NixContext(ctx);
+ let v = ctx.run_in_context(|c| f(c, state));
+ // It is reused for thread
+ std::mem::forget(ctx);
+ v
+}
+
+fn set_setting(s: &CStr, v: &CStr) -> Result<()> {
+ with_default_context(|c, _| unsafe { setting_set(c, s.as_ptr(), v.as_ptr()) }).map(|_| ())
+}
+
+pub struct FetchSettings(*mut fetchers_settings);
+impl FetchSettings {
+ pub fn new() -> Self {
+ Self::try_new().expect("allocation should not fail")
+ }
+ fn try_new() -> Result<Self> {
+ with_default_context(|c, _| unsafe { fetchers_settings_new(c) }).map(Self)
+ }
+ pub fn set(&mut self, setting: &CStr, value: &CStr) {
+ unsafe {
+ set_fetcher_setting(self.0.cast(), setting.as_ptr(), value.as_ptr());
+ };
+ }
+}
+unsafe impl Send for FetchSettings {}
+unsafe impl Sync for FetchSettings {}
+impl Drop for FetchSettings {
+ fn drop(&mut self) {
+ unsafe { fetchers_settings_free(self.0) };
+ }
+}
+pub struct FlakeSettings(*mut flake_settings);
+impl FlakeSettings {
+ pub fn new() -> Result<Self> {
+ with_default_context(|c, _| unsafe { flake_settings_new(c) }).map(Self)
+ }
+}
+unsafe impl Send for FlakeSettings {}
+unsafe impl Sync for FlakeSettings {}
+impl Drop for FlakeSettings {
+ fn drop(&mut self) {
+ unsafe {
+ flake_settings_free(self.0);
+ }
+ }
+}
+
+struct FlakeReferenceParseFlags(*mut flake_reference_parse_flags);
+impl FlakeReferenceParseFlags {
+ fn new(settings: &mut FlakeSettings) -> Result<Self> {
+ with_default_context(|c, _| unsafe { flake_reference_parse_flags_new(c, settings.0) })
+ .map(Self)
+ }
+ fn set_base_dir(&mut self, dir: &str) -> Result<()> {
+ with_default_context(|c, _| {
+ unsafe {
+ flake_reference_parse_flags_set_base_directory(
+ c,
+ self.0,
+ dir.as_ptr().cast(),
+ dir.len(),
+ )
+ };
+ })
+ }
+}
+impl Drop for FlakeReferenceParseFlags {
+ fn drop(&mut self) {
+ unsafe {
+ flake_reference_parse_flags_free(self.0);
+ }
+ }
+}
+struct FlakeLockFlags(*mut flake_lock_flags);
+impl FlakeLockFlags {
+ fn new(settings: &mut FlakeSettings) -> Result<Self> {
+ with_default_context(|c, _| unsafe { flake_lock_flags_new(c, settings.0) }).map(Self)
+ }
+}
+impl Drop for FlakeLockFlags {
+ fn drop(&mut self) {
+ unsafe {
+ flake_lock_flags_free(self.0);
+ }
+ }
+}
+
+unsafe extern "C" fn copy_nix_str(start: *const c_char, n: c_uint, user_data: *mut c_void) {
+ let s = unsafe { std::slice::from_raw_parts(start.cast::<u8>(), n as usize) };
+ let s = std::str::from_utf8(s).expect("c string has invalid utf-8");
+ unsafe { *user_data.cast::<String>() = s.to_owned() };
+}
+
+struct Store(*mut nix_raw::Store);
+unsafe impl Send for Store {}
+unsafe impl Sync for Store {}
+
+struct EvalState(*mut nix_raw::EvalState);
+impl EvalState {
+ // TODO: store ownership
+ fn new_raw(store: *mut nix_raw::Store) -> Result<Self> {
+ let builder =
+ with_default_context(|c, _| unsafe { nix_raw::eval_state_builder_new(c, store) })?;
+
+ with_default_context(|c, _| unsafe { eval_state_build(c, builder) }).map(Self)
+
+ // with_default_context(|c| state_create(c))
+ }
+}
+unsafe impl Send for EvalState {}
+unsafe impl Sync for EvalState {}
+impl Drop for EvalState {
+ fn drop(&mut self) {
+ unsafe {
+ state_free(self.0);
+ }
+ }
+}
+
+pub struct FlakeReference(*mut nix_raw::flake_reference);
+impl FlakeReference {
+ pub fn new(s: &str, fetch: &FetchSettings) -> Result<(Self, String)> {
+ let mut flake_settings = FlakeSettings::new()?;
+ let mut parse_flags = FlakeReferenceParseFlags::new(&mut flake_settings)?;
+
+ // parse_flags.set_base_dir("/home/lach/build/fleet")?;
+
+ let mut out = null_mut();
+ let mut fragment = String::new();
+ // let fetch_settings = fetcher_settings;
+ with_default_context(|c, _| unsafe {
+ nix_raw::flake_reference_and_fragment_from_string(
+ c,
+ fetch.0,
+ flake_settings.0,
+ parse_flags.0,
+ s.as_ptr().cast(),
+ s.len(),
+ &mut out,
+ Some(copy_nix_str),
+ (&raw mut fragment).cast(),
+ )
+ })?;
+ assert!(!out.is_null());
+
+ Ok((Self(out), fragment))
+ }
+ pub fn lock(&mut self, fetch: &FetchSettings) -> Result<LockedFlake> {
+ let mut settings = FlakeSettings::new()?;
+ let lock_flags = FlakeLockFlags::new(&mut settings)?;
+ with_default_context(|c, es| unsafe {
+ flake_lock(c, fetch.0, settings.0, es, lock_flags.0, self.0)
+ })
+ .map(LockedFlake)
+ }
+}
+unsafe impl Send for FlakeReference {}
+unsafe impl Sync for FlakeReference {}
+
+pub struct LockedFlake(*mut nix_raw::locked_flake);
+impl LockedFlake {
+ pub fn get_attrs(&self, settings: &mut FlakeSettings) -> Result<Value> {
+ with_default_context(|c, es| unsafe {
+ locked_flake_get_output_attrs(c, settings.0, es, self.0)
+ })
+ .map(Value)
+ }
+}
+unsafe impl Send for LockedFlake {}
+unsafe impl Sync for LockedFlake {}
+impl Drop for LockedFlake {
+ fn drop(&mut self) {
+ unsafe {
+ locked_flake_free(self.0);
+ };
+ }
+}
+
+type FieldName = [u8; 32];
+fn init_field_name(v: &str) -> FieldName {
+ let mut f = [0; 32];
+ assert!(v.len() < 32, "max field name is 31 char");
+ assert!(
+ v.bytes().all(|v| v != 0),
+ "nul bytes are unsupported in field name"
+ );
+ f[0..v.len()].copy_from_slice(v.as_bytes());
+ f
+}
+
+pub struct RealisedString(*mut nix_raw::realised_string);
+impl fmt::Debug for RealisedString {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.as_str().fmt(f)
+ }
+}
+
+impl RealisedString {
+ fn as_str(&self) -> &str {
+ let len = unsafe { nix_raw::realised_string_get_buffer_size(self.0) };
+ let data: *const u8 = unsafe { nix_raw::realised_string_get_buffer_start(self.0) }.cast();
+ let data = unsafe { std::slice::from_raw_parts(data, len) };
+ std::str::from_utf8(data).expect("non-utf8 strings not supported")
+ }
+ fn path_count(&self) -> usize {
+ unsafe { nix_raw::realised_string_get_store_path_count(self.0) }
+ }
+ fn path(&self, i: usize) -> String {
+ assert!(i < self.path_count());
+ let path = unsafe { nix_raw::realised_string_get_store_path(self.0, i) };
+ let mut err_out = String::new();
+ unsafe { nix_raw::store_path_name(path, Some(copy_nix_str), (&raw mut err_out).cast()) };
+ err_out
+ }
+}
+
+unsafe impl Send for RealisedString {}
+impl Drop for RealisedString {
+ fn drop(&mut self) {
+ with_default_context(|c, _| unsafe { nix_raw::realised_string_free(self.0) })
+ .expect("string free should not fail")
+ }
+}
-#[derive(Clone)]
-pub struct NixSession(pub(crate) Arc<tokio::sync::Mutex<PooledConnection<NixSessionPoolInner>>>);
+pub struct Value(*mut nix_raw::value);
-struct NixBuildTask(Value, oneshot::Sender<Result<HashMap<String, PathBuf>>>);
+unsafe impl Send for Value {}
+unsafe impl Sync for Value {}
-#[derive(Clone)]
-pub struct NixBuildBatch {
- tx: mpsc::UnboundedSender<NixBuildTask>,
+pub trait AsFieldName {
+ fn as_field_name<T>(&self, v: impl FnOnce(FieldName) -> Result<T>) -> Result<T>;
+ fn to_field_name(&self) -> Result<String>;
+}
+impl AsFieldName for Value {
+ fn as_field_name<T>(&self, v: impl FnOnce(FieldName) -> Result<T>) -> Result<T> {
+ let f = self.to_string()?;
+ v(init_field_name(&f))
+ }
+ fn to_field_name(&self) -> Result<String> {
+ self.to_string()
+ }
+}
+impl<E> AsFieldName for E
+where
+ E: AsRef<str>,
+{
+ fn as_field_name<T>(&self, v: impl FnOnce(FieldName) -> Result<T>) -> Result<T> {
+ let f = self.as_ref();
+ v(init_field_name(f))
+ }
+ fn to_field_name(&self) -> Result<String> {
+ Ok(self.as_ref().to_owned())
+ }
}
-#[instrument(skip(session, values))]
-async fn build_multiple(name: String, session: NixSession, values: Vec<Value>) -> Result<()> {
- let system = session.0.lock().await.nix_system.clone();
- let builtins = Value::binding(session, "builtins").await?;
- let drv = nix_go!(builtins.derivation(Obj {
- system,
- name,
- builder: "/bin/sh",
- // we want nothing from this derivation, it is only used to perform multiple builds at once.
- args: vec!["-c", "echo > $out"],
- preferLocalBuild: true,
- allowSubstitutes: false,
- buildInputs: values,
- }));
- drv.build().await?;
- Ok(())
+struct AttrsBuilder(*mut nix_raw::BindingsBuilder);
+impl AttrsBuilder {
+ fn new(capacity: usize) -> Self {
+ with_default_context(|c, es| unsafe { nix_raw::make_bindings_builder(c, es, capacity) })
+ .map(Self)
+ .expect("alloc should not fail")
+ }
+ fn insert(&mut self, k: &impl AsFieldName, v: Value) {
+ k.as_field_name(|name| {
+ with_default_context(|c, _| unsafe {
+ nix_raw::bindings_builder_insert(c, self.0, name.as_ptr().cast(), v.0)
+ })
+ })
+ .expect("builder insert shouldn't fail");
+ }
}
+impl Drop for AttrsBuilder {
+ fn drop(&mut self) {
+ unsafe { nix_raw::bindings_builder_free(self.0) };
+ }
+}
+
+impl Value {
+ pub fn new_attrs(v: HashMap<&str, Value>) -> Result<Self> {
+ let out = Self::new_uninit()?;
+ let mut b = AttrsBuilder::new(v.len());
+ for (k, v) in v {
+ b.insert(&k, v);
+ }
+ with_default_context(|c, _| unsafe { nix_raw::make_attrs(c, out.0, b.0) })?;
+ Ok(out)
+ }
+ fn new_list<T: Into<Self>>(v: Vec<T>) -> Result<Self> {
+ todo!()
+ }
+ fn new_uninit() -> Result<Self> {
+ let out = with_default_context(|c, es| unsafe { alloc_value(c, es) })?;
+ Ok(Self(out))
+ }
+ fn new_str(v: &str) -> Result<Self> {
+ let s = CString::new(v).expect("string should not contain NULs");
+ let uninit = Self::new_uninit()?;
+ // String is copied, `s` is free to be dropped
+ with_default_context(|c, _| unsafe { init_string(c, uninit.0, s.as_ptr()) })?;
+ Ok(uninit)
+ }
+ fn new_int(i: i64) -> Result<Self> {
+ let uninit = Self::new_uninit()?;
+ with_default_context(|c, _| unsafe { init_int(c, uninit.0, i) })?;
+ Ok(uninit)
+ }
+ fn new_bool(v: bool) -> Result<Self> {
+ let uninit = Self::new_uninit()?;
+ with_default_context(|c, _| unsafe { init_bool(c, uninit.0, v) })?;
+ Ok(uninit)
+ }
+ fn force(&mut self, st: &mut EvalState) -> Result<()> {
+ with_default_context(|c, _| unsafe { value_force(c, st.0, self.0) })?;
+ Ok(())
+ }
+ pub fn type_of(&self) -> Result<NixType> {
+ let ty = with_default_context(|c, _| unsafe { nix_raw::get_type(c, self.0) })?;
+ Ok(NixType::from_int(ty))
+ }
+ pub fn to_string(&self) -> Result<String> {
+ Ok(self.to_realised_string()?.as_str().to_owned())
+ }
+ pub fn to_realised_string(&self) -> Result<RealisedString> {
+ with_default_context(|c, es| unsafe { nix_raw::string_realise(c, es, self.0, false) })
+ .map(RealisedString)
+
+ // let store_paths = unsafe { nix_raw::realised_string_get_store_path_count(str) };
+ // for i in 0..store_paths {
+ // let store_path = unsafe { nix_raw::realised_string_get_store_path(str, i) };
+ // nix_raw::store_path_name(store_path, callback, user_data);
+ // }
+ // dbg!(store_paths);
+ // todo!();
+ }
+
+ pub fn has_field(&self, field: &str) -> Result<bool> {
+ let f = init_field_name(field);
+ with_default_context(|c, es| unsafe {
+ nix_raw::has_attr_byname(c, self.0, es, f.as_ptr().cast())
+ })
+ }
+ // pub fn derivation_path(&self) {
+ // nix_raw::real
+ // }
+ pub fn list_fields(&self) -> Result<Vec<String>> {
+ if !matches!(self.type_of()?, NixType::Attrs) {
+ bail!("invalid type: expected attrs");
+ }
-impl NixBuildBatch {
- fn new(name: String, session: NixSession) -> Self {
- let (tx, mut rx) = mpsc::unbounded_channel::<NixBuildTask>();
+ let len = with_default_context(|c, _| unsafe { nix_raw::get_attrs_size(c, self.0) })?;
+ let mut out = Vec::with_capacity(len as usize);
- tokio::task::spawn(async move {
- let mut deps = vec![];
- let mut build_data = vec![];
- while let Some(task) = rx.recv().await {
- build_data.push(task.0.clone());
- deps.push(task);
+ for i in 0..len {
+ let name = with_default_context(|c, es| unsafe {
+ nix_raw::get_attr_name_byidx(c, self.0, es, i)
+ })?;
+ let c = unsafe { CStr::from_ptr(name) };
+ out.push(c.to_str().expect("nix field names are utf-8").to_owned());
+ }
+ Ok(out)
+ }
+ pub fn get_elem(&self, v: usize) -> Result<Self> {
+ if !matches!(self.type_of()?, NixType::List) {
+ bail!("invalid type: expected list");
+ }
+ let len =
+ with_default_context(|c, _| unsafe { nix_raw::get_list_size(c, self.0) })? as usize;
+ if v >= len {
+ bail!("oob list get: {v} >= {len}");
+ }
+
+ with_default_context(|c, es| unsafe { nix_raw::get_list_byidx(c, self.0, es, v as u32) })
+ .map(Self)
+ }
+ pub fn attrs_update(self, other: Value) -> Result<Self> {
+ let a_fields = self.list_fields()?;
+ let b_fields = other.list_fields()?;
+ match (a_fields.len(), b_fields.len()) {
+ (_, 0) => return Ok(self),
+ (0, _) => return Ok(other),
+ _ => {}
+ }
+ let mut out = HashMap::new();
+ for f in a_fields.iter() {
+ if b_fields.contains(f) {
+ break;
}
- if deps.is_empty() {
- return;
+ out.insert(f.as_str(), self.get_field(f)?);
+ }
+ if out.is_empty() {
+ // All fields from lhs are overriden by rhs
+ return Ok(other);
+ }
+ for f in b_fields.iter() {
+ out.insert(f.as_str(), other.get_field(f)?);
+ }
+ Self::new_attrs(out)
+ }
+ pub fn get_field(&self, name: impl AsFieldName) -> Result<Self> {
+ if !matches!(self.type_of()?, NixType::Attrs) {
+ bail!("invalid type: expected attrs");
+ }
+
+ name.as_field_name(|name| {
+ with_default_context(|c, es| unsafe {
+ nix_raw::get_attr_byname(c, self.0, es, name.as_ptr().cast())
+ })
+ .map(Self)
+ })
+ .with_context(|| format!("getting field {:?}", name.to_field_name()))
+ }
+ pub fn call(&self, v: Value) -> Result<Self> {
+ if !matches!(self.type_of()?, NixType::Function) {
+ // TODO: Functors
+ bail!("invalid type: expected function");
+ }
+
+ let out = Value::new_uninit()?;
+ with_default_context(|c, es| unsafe { nix_raw::value_call(c, es, self.0, v.0, out.0) })?;
+
+ Ok(out)
+ }
+ pub fn eval(v: &str) -> Result<Self> {
+ let s = CString::new(v).expect("expression shouldn't have internal NULs");
+ let out = Self::new_uninit()?;
+ with_default_context(|c, es| unsafe {
+ expr_eval_from_string(c, es, s.as_ptr(), c"/homeless-shelter".as_ptr(), out.0)
+ })?;
+ Ok(out)
+ }
+ pub async fn build(&self, output: &str) -> Result<PathBuf> {
+ if !self.is_derivation() {
+ bail!("expected derivation to build")
+ }
+ let output_name = self.get_field("outputName")?.to_string()?;
+ let v = if output_name != output {
+ let out = self.get_field(output)?;
+ if !out.is_derivation() {
+ bail!("unknown output: {output}");
}
- match build_multiple(name, session, build_data).await {
- Ok(_) => {
- for NixBuildTask(v, o) in deps {
- let _ = o.send(v.build().await);
- }
- }
- Err(e) => {
- for NixBuildTask(v, o) in deps {
- let s = v.to_string_weak().await;
- let s = match s {
- Ok(s) => s,
- Err(e) => {
- let _ = o.send(Err(e));
- continue;
- }
- };
- if PathBuf::from(s).exists() {
- let _ = o.send(v.build().await);
- } else {
- let _ = o.send(Err(e.clone()));
- }
- }
- }
- };
- });
- Self { tx }
+ out
+ } else {
+ self.clone()
+ };
+ // to_string here blocks until the path is built
+ let drv_path = tokio::task::spawn_blocking(move || v.get_field("outPath")?.to_string())
+ .await
+ .expect("should not fail")?;
+ Ok(PathBuf::from(drv_path))
}
- pub async fn submit(self, task: Value) -> Result<HashMap<String, PathBuf>> {
- let Self { tx: task_tx } = self;
- let (tx, rx) = oneshot::channel();
- let _ = task_tx.send(NixBuildTask(task, tx));
- drop(task_tx);
- rx.await.expect("shoudn't be cancelled here")
+ pub fn as_json<T: DeserializeOwned>(&self) -> Result<T> {
+ let to_json = Self::eval("builtins.toJSON")?;
+ let s = to_json.call(self.clone())?.to_string()?;
+ Ok(serde_json::from_str(&s)?)
+ }
+ pub fn serialized<T: Serialize>(v: &T) -> Result<Self> {
+ Self::eval(&nixlike::serialize(v)?)
+ }
+
+ // Convert to string/evaluate derivations/etc
+ fn to_string_weak(&self) -> Result<String> {
+ // TODO
+ self.to_string()
+ }
+
+ fn is_derivation(&self) -> bool {
+ if !matches!(self.type_of(), Ok(NixType::Attrs)) {
+ return false;
+ }
+ let Some(ty) = self.get_field("type").ok() else {
+ return false;
+ };
+ matches!(ty.to_string().as_deref(), Ok("derivation"))
}
}
-impl NixSession {
- fn ptr_eq(a: &Self, b: &Self) -> bool {
- Arc::ptr_eq(&a.0, &b.0)
+impl From<String> for Value {
+ fn from(value: String) -> Self {
+ Value::new_str(&value).expect("todo: TryFrom")
}
+}
+impl From<bool> for Value {
+ fn from(value: bool) -> Self {
+ Value::new_bool(value).expect("todo: TryFrom")
+ }
+}
+impl From<&str> for Value {
+ fn from(value: &str) -> Self {
+ Value::new_str(&value).expect("todo: TryFrom")
+ }
+}
+impl<T> From<Vec<T>> for Value
+where
+ T: Into<Value>,
+{
+ fn from(value: Vec<T>) -> Self {
+ Value::new_list(value).expect("todo: TryFrom")
+ }
+}
- pub fn new_build_batch(&self, name: String) -> NixBuildBatch {
- NixBuildBatch::new(name, self.clone())
+impl Clone for Value {
+ fn clone(&self) -> Self {
+ with_default_context(|c, _| unsafe { value_incref(c, self.0) })
+ .expect("value incref should not fail");
+ Self(self.0)
}
}
+impl Drop for Value {
+ fn drop(&mut self) {
+ with_default_context(|c, _| unsafe { value_decref(c, self.0) })
+ .expect("value drop should not fail");
+ }
+}
+
+pub fn init_libraries() {
+ unsafe { nix_raw::GC_allow_register_threads() };
-pub fn init_tokio() {
- let _ = pool::TOKIO_RUNTIME.set(tokio::runtime::Handle::current());
+ let mut ctx = NixContext::new();
+ ctx.run_in_context(|c| unsafe { nix_raw::libutil_init(c) })
+ .expect("util init should not fail");
+ ctx.run_in_context(|c| unsafe { nix_raw::libstore_init(c) })
+ .expect("store init should not fail");
+ ctx.run_in_context(|c| unsafe { nix_raw::libexpr_init(c) })
+ .expect("expr init should not fail");
+
+ nix_logging_cxx::apply_tracing_logger();
}
+
+#[test_log::test]
+fn test_native() -> Result<()> {
+ let mut fetch_settings = FetchSettings::new();
+ fetch_settings.set(c"warn-dirty", c"false");
+ //
+
+ let (mut r, _) = FlakeReference::new("/home/lach/build/fleet", &fetch_settings)?;
+ let locked = r.lock(&fetch_settings)?;
+ let attrs = locked.get_attrs(&mut FlakeSettings::new()?)?;
+
+ let builtins = Value::eval("builtins")?;
+ dbg!(builtins.type_of()?);
+
+ dbg!(attrs.type_of()?);
+ dbg!(attrs.list_fields()?);
+ dbg!(
+ attrs
+ .get_field("packages")?
+ .get_field("x86_64-linux")?
+ .get_field("fleet")?
+ .get_field("outPath")?
+ .to_string()
+ );
+
+ Ok(())
+}
+
+// struct NixBuildTask(Value, oneshot::Sender<Result<HashMap<String, PathBuf>>>);
+//
+// #[derive(Clone)]
+// pub struct NixBuildBatch {
+// tx: mpsc::UnboundedSender<NixBuildTask>,
+// }
+//
+// #[instrument(skip(values))]
+// async fn build_multiple(name: String, values: Vec<Value>) -> Result<()> {
+// let builtins = Value::eval("builtins")?;
+// let drv = nix_go!(builtins.derivation(Obj {
+// // FIXME: pass system from localSystem or fleet args
+// // system,
+// name,
+// builder: "/bin/sh",
+// // we want nothing from this derivation, it is only used to perform multiple builds at once.
+// args: vec!["-c", "echo > $out"],
+// preferLocalBuild: true,
+// allowSubstitutes: false,
+// buildInputs: values,
+// }));
+// drv.build()?;
+// Ok(())
+// }
+//
+// impl NixBuildBatch {
+// fn new(name: String) -> Self {
+// let (tx, mut rx) = mpsc::unbounded_channel::<NixBuildTask>();
+//
+// tokio::task::spawn(async move {
+// let mut deps = vec![];
+// let mut build_data = vec![];
+// while let Some(task) = rx.recv().await {
+// build_data.push(task.0.clone());
+// deps.push(task);
+// }
+// if deps.is_empty() {
+// return;
+// }
+// match build_multiple(name, build_data).await {
+// Ok(_) => {
+// for NixBuildTask(v, o) in deps {
+// let _ = o.send(v.build());
+// }
+// }
+// Err(e) => {
+// for NixBuildTask(v, o) in deps {
+// let s = v.to_string_weak();
+// let s = match s {
+// Ok(s) => s,
+// Err(e) => {
+// let _ = o.send(Err(e));
+// continue;
+// }
+// };
+// if PathBuf::from(s).exists() {
+// let _ = o.send(v.build());
+// } else {
+// let _ = o.send(Err(e.clone()));
+// }
+// }
+// }
+// };
+// });
+// Self { tx }
+// }
+// pub async fn submit(self, task: Value) -> Result<HashMap<String, PathBuf>> {
+// let Self { tx: task_tx } = self;
+// let (tx, rx) = oneshot::channel();
+// let _ = task_tx.send(NixBuildTask(task, tx));
+// drop(task_tx);
+// rx.await.expect("shoudn't be cancelled here")
+// }
+// }
crates/nix-eval/src/logging.ccdiffbeforeafterboth--- /dev/null
+++ b/crates/nix-eval/src/logging.cc
@@ -0,0 +1,75 @@
+#include "nix-eval/src/logging.rs"
+#include "logging.hh"
+#include <nix/util/logging.hh>
+
+using namespace nix;
+
+struct TracingLogger : Logger {
+ TracingLogger() {}
+
+ bool isVerbose() override { return true; }
+ // void addFields(nlohmann::json & json, const Fields & fields)
+ // {
+ // if (fields.empty())
+ // return;
+ // auto & arr = json["fields"] = nlohmann::json::array();
+ // for (auto & f : fields)
+ // if (f.type == Logger::Field::tInt)
+ // arr.push_back(f.i);
+ // else if (f.type == Logger::Field::tString)
+ // arr.push_back(f.s);
+ // else
+ // unreachable();
+ // }
+ void log(Verbosity lvl, std::string_view s) override {
+ rust::Str str(s.data(), s.size());
+ emit_log(lvl, str);
+ }
+ void logEI(const ErrorInfo &ei) override { emit_log(ei.level, ei.msg.str()); }
+
+ void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
+ const std::string &s, const Fields &fields,
+ ActivityId parent) override {
+ auto b = new_start_activity(act, lvl, type);
+ for (auto &f : fields) {
+ if (f.type == Logger::Field::tInt) {
+ b->add_int_field(f.i);
+ } else if (f.type == Logger::Field::tString) {
+ b->add_string_field(f.s);
+ } else {
+ unreachable();
+ }
+ }
+ b->emit(parent, s);
+ };
+
+ void stopActivity(ActivityId act) override { emit_stop(act); };
+
+ void result(ActivityId act, ResultType type, const Fields &fields) override {
+ auto b = new_start_activity(act, 0, type);
+ for (auto &f : fields) {
+ if (f.type == Logger::Field::tInt) {
+ b->add_int_field(f.i);
+ } else if (f.type == Logger::Field::tString) {
+ b->add_string_field(f.s);
+ } else {
+ unreachable();
+ }
+ }
+ b->emit_result(type);
+ };
+
+ void writeToStdout(std::string_view s) override {
+ printf("writeToStdout() called\n");
+ }
+ void warn(const std::string &msg) override { emit_warn(msg); }
+
+ virtual std::optional<char> ask(std::string_view s) {
+ printf("ask() called\n");
+ return {};
+ }
+};
+
+extern "C" {
+void apply_tracing_logger() { logger = std::make_unique<TracingLogger>(); }
+}
crates/nix-eval/src/logging.hhdiffbeforeafterboth--- /dev/null
+++ b/crates/nix-eval/src/logging.hh
@@ -0,0 +1,5 @@
+#pragma once
+
+extern "C" {
+void apply_tracing_logger();
+}
crates/nix-eval/src/logging.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/nix-eval/src/logging.rs
@@ -0,0 +1,602 @@
+use std::collections::HashMap;
+use std::fmt::Arguments;
+use std::sync::{LazyLock, Mutex};
+
+use tracing::{
+ Level, Metadata, Span, debug, debug_span, error, error_span, event, info, info_span, trace,
+ trace_span, warn, warn_span,
+};
+use tracing_indicatif::span_ext::IndicatifSpanExt as _;
+
+#[derive(Debug)]
+enum ActivityType {
+ Unknown = 0,
+ CopyPath = 100,
+ FileTransfer = 101,
+ Realise = 102,
+ CopyPaths = 103,
+ Builds = 104,
+ Build = 105,
+ OptimiseStore = 106,
+ VerifyPaths = 107,
+ Substitute = 108,
+ QueryPathInfo = 109,
+ PostBuildHook = 110,
+ BuildWaiting = 111,
+ FetchTree = 112,
+}
+
+fn strip_prefix_suffix<'s, 'p>(a: &'s str, pref: &'p str, suff: &'p str) -> Option<&'s str> {
+ a.strip_prefix(pref)?.strip_suffix(suff)
+}
+
+fn parse_path(path: &str) -> &str {
+ let path = strip_prefix_suffix(path, "\x1b[35;1m", "\x1b[0m").unwrap_or(path);
+ path
+}
+
+fn parse_drv(drv: &str) -> &str {
+ let drv = parse_path(drv);
+ if let Some(pkg) = drv.strip_prefix("/nix/store/") {
+ let mut it = pkg.splitn(2, '-');
+ it.next();
+ if let Some(pkg) = it.next() {
+ return pkg;
+ }
+ }
+ drv
+}
+fn parse_host(host: &str) -> &str {
+ if host.is_empty() || host == "local" {
+ return "local";
+ }
+ // https/ssh is the default
+ host.strip_prefix("https://").unwrap_or(host)
+}
+
+impl ActivityType {
+ fn name(&self) -> &'static str {
+ match self {
+ ActivityType::Unknown => "nix",
+ ActivityType::CopyPath => "nix::copy-path",
+ ActivityType::FileTransfer => "nix::file-transfer",
+ ActivityType::Realise => "nix::realise",
+ ActivityType::CopyPaths => "nix::copy-paths",
+ ActivityType::Builds => "nix::builds",
+ ActivityType::Build => "nix::build",
+ ActivityType::OptimiseStore => "nix::optimise-store",
+ ActivityType::VerifyPaths => "nix::verify-paths",
+ ActivityType::Substitute => "nix::substitute",
+ ActivityType::QueryPathInfo => "nix::query-path-info",
+ ActivityType::PostBuildHook => "nix::post-build-hook",
+ ActivityType::BuildWaiting => "nix::build-waiting",
+ ActivityType::FetchTree => "nix::fetch-tree",
+ }
+ }
+ fn format(
+ &self,
+ values: &[FieldValue],
+ s: &str,
+ into: impl FnOnce(Arguments<'_>) -> Span,
+ ) -> Span {
+ use FieldValue::*;
+ match (self, values) {
+ (ActivityType::QueryPathInfo, [Str(drv), Str(host)]) => {
+ let drv = parse_drv(drv);
+ let host = parse_host(host);
+ debug_span!(target: "nix::query-path-info", "querying", drv, host)
+ }
+ (ActivityType::Substitute, [Str(drv), Str(host)]) => {
+ let drv = parse_drv(drv);
+ let host = parse_host(host);
+ debug_span!(target: "nix::substitute", "substituting", drv, host)
+ }
+ (ActivityType::CopyPath, [Str(drv), Str(from), Str(to)]) => {
+ let drv = parse_drv(drv);
+ let from = parse_host(from);
+ let to = parse_host(to);
+ debug_span!(target: "nix::copy-path", "copying", drv, from, to)
+ }
+ (ActivityType::Build, [Str(drv), Str(host), Int(_), Int(_)]) => {
+ let drv = parse_drv(drv);
+ let host = parse_host(host);
+ info_span!(target: "nix::build", "building", drv, host)
+ }
+ (ActivityType::FileTransfer, [Str(file)]) => {
+ info_span!(target: "nix::file-transfer", "downloading", file)
+ }
+ (ActivityType::Realise, []) => {
+ debug_span!(target: "nix::realise", "realising")
+ }
+ (ActivityType::CopyPaths, []) => {
+ debug_span!(target: "nix::copy-paths", "copying paths")
+ }
+ (ActivityType::Unknown, [])
+ if s.starts_with("copying \"") && s.ends_with("\" to the store") =>
+ {
+ let tree = s
+ .trim_start_matches("copying \"")
+ .trim_end_matches("\" to the store");
+ debug_span!(target: "nix::trees", "copying", tree)
+ }
+ (ActivityType::Unknown, [])
+ if s.starts_with("copying '") && s.ends_with("' to the store") =>
+ {
+ let tree = s
+ .trim_start_matches("copying '")
+ .trim_end_matches("' to the store");
+ debug_span!(target: "nix::trees", "copying", tree)
+ }
+ (ActivityType::Unknown, []) if s.starts_with("hashing '") && s.ends_with("'") => {
+ let tree = s.trim_start_matches("hashing '").trim_end_matches("'");
+ debug_span!(target: "nix::trees", "hashing", tree)
+ }
+ (ActivityType::Unknown, []) if s.starts_with("connecting to '") && s.ends_with("'") => {
+ let host = s
+ .trim_start_matches("connecting to '")
+ .trim_end_matches("'");
+ debug_span!(target: "nix::remote", "connecting", host)
+ }
+ (ActivityType::Unknown, [])
+ if s.starts_with("copying outputs from '") && s.ends_with("'") =>
+ {
+ let host = s
+ .trim_start_matches("copying outputs from '")
+ .trim_end_matches("'");
+ debug_span!(target: "nix::remote", "copying outputs", host)
+ }
+ (ActivityType::Unknown, [])
+ if s.starts_with("copying dependencies to '") && s.ends_with("'") =>
+ {
+ let host = s
+ .trim_start_matches("copying dependencies to '")
+ .trim_end_matches("'");
+ debug_span!(target: "nix::remote", "copying dependencies", host)
+ }
+ (ActivityType::Unknown, [])
+ if s.starts_with("waiting for the upload lock to '") && s.ends_with("'") =>
+ {
+ let host = s
+ .trim_start_matches("waiting for the upload lock to '")
+ .trim_end_matches("'");
+ debug_span!(target: "nix::remote", "waiting for upload lock", host)
+ }
+ (ActivityType::BuildWaiting, [])
+ if s.starts_with("waiting for a machine to build '") && s.ends_with("'") =>
+ {
+ let drv = parse_drv(
+ s.trim_start_matches("waiting for a machine to build '")
+ .trim_end_matches("'"),
+ );
+ debug_span!(target: "nix::build-waiting", "waiting for available builder", drv)
+ }
+ (ActivityType::Unknown, []) if s == "querying info about missing paths" => {
+ debug_span!(target: "nix::remote", "querying")
+ }
+ _ => into(format_args!("{}({values:?})", self.name())),
+ }
+ }
+ fn from_int(v: u32) -> Self {
+ match v {
+ 0 => Self::Unknown,
+ 100 => Self::CopyPath,
+ 101 => Self::FileTransfer,
+ 102 => Self::Realise,
+ 103 => Self::CopyPaths,
+ 104 => Self::Builds,
+ 105 => Self::Build,
+ 106 => Self::OptimiseStore,
+ 107 => Self::VerifyPaths,
+ 108 => Self::Substitute,
+ 109 => Self::QueryPathInfo,
+ 110 => Self::PostBuildHook,
+ 111 => Self::BuildWaiting,
+ 112 => Self::FetchTree,
+ _ => {
+ warn!("unknown nix action: {v}");
+ Self::Unknown
+ }
+ }
+ }
+}
+
+#[derive(Debug)]
+enum ResultType {
+ FileLinked = 100,
+ BuildLogLine = 101,
+ UntrustedPath = 102,
+ CorruptedPath = 103,
+ SetPhase = 104,
+ Progress = 105,
+ SetExpected = 106,
+ PostBuildLogLine = 107,
+ FetchStatus = 108,
+
+ Unknown = 999,
+}
+impl ResultType {
+ fn from_int(v: u32) -> Self {
+ match v {
+ 100 => Self::FileLinked,
+ 101 => Self::BuildLogLine,
+ 102 => Self::UntrustedPath,
+ 103 => Self::CorruptedPath,
+ 104 => Self::SetPhase,
+ 105 => Self::Progress,
+ 106 => Self::SetExpected,
+ 107 => Self::PostBuildLogLine,
+ 108 => Self::FetchStatus,
+
+ _ => {
+ warn!("unknown nix result: {v}");
+ Self::Unknown
+ }
+ }
+ }
+}
+#[derive(Clone, Copy)]
+enum Verbosity {
+ Error,
+ Warn,
+ Notice,
+ Info,
+ Talkative,
+ Chatty,
+ Debug,
+ Vomit,
+}
+impl Into<tracing::Level> for Verbosity {
+ fn into(self) -> tracing::Level {
+ match self {
+ Verbosity::Error => Level::ERROR,
+ Verbosity::Warn => Level::WARN,
+ Verbosity::Notice => Level::WARN,
+ Verbosity::Info => Level::INFO,
+ Verbosity::Talkative => Level::DEBUG,
+ Verbosity::Chatty => Level::DEBUG,
+ Verbosity::Debug => Level::DEBUG,
+ Verbosity::Vomit => Level::TRACE,
+ }
+ }
+}
+impl Verbosity {
+ fn from_int(u: u32) -> Self {
+ [
+ Self::Error,
+ Self::Warn,
+ Self::Notice,
+ Self::Info,
+ Self::Talkative,
+ Self::Chatty,
+ Self::Debug,
+ Self::Vomit,
+ ]
+ .get(u as usize)
+ .cloned()
+ .unwrap_or_else(|| {
+ warn!("unknown log level: {u}");
+ Verbosity::Vomit
+ })
+ }
+}
+
+#[derive(Hash, PartialEq, Eq, Clone, Copy)]
+enum MetadataKind {
+ Span,
+ Event,
+}
+// impl MetadataKind {
+// fn kind(&self) -> Kind {
+// match self {
+// MetadataKind::Span => Kind::SPAN,
+// MetadataKind::Event => Kind::EVENT,
+// }
+// }
+// }
+
+#[derive(Hash, PartialEq, Eq)]
+struct ForeignMetadataInfo {
+ target: &'static str,
+ level: Level,
+ kind: MetadataKind,
+ name: &'static str,
+ module: Option<&'static str>,
+ file: Option<&'static str>,
+ line: Option<u32>,
+ names: &'static [&'static str],
+}
+
+struct FakeCallsite;
+impl tracing::callsite::Callsite for FakeCallsite {
+ fn set_interest(&self, interest: tracing::subscriber::Interest) {
+ unreachable!()
+ }
+
+ fn metadata(&self) -> &Metadata<'_> {
+ unreachable!()
+ }
+}
+const FAKE_CALLSITE: FakeCallsite = FakeCallsite;
+
+#[cfg(false)]
+#[derive(Default)]
+struct ForeignSpanData {
+ interned: HashSet<&'static str>,
+ metadatas: HashMap<ForeignMetadataInfo, &'static Metadata<'static>>,
+}
+#[cfg(false)]
+impl ForeignSpanData {
+ fn intern(&mut self, s: &str) -> &'static str {
+ if let Some(v) = self.interned.get(s) {
+ return *v;
+ }
+ let leaked: Box<str> = s.into();
+ let leaked = Box::leak(leaked);
+ self.interned.insert(leaked);
+ return leaked;
+ }
+ fn alloc_metadata<'t>(
+ &'t mut self,
+ target: &'static str,
+ level: Level,
+ kind: MetadataKind,
+ name: &'static str,
+ module: Option<&'static str>,
+ file: Option<&'static str>,
+ line: Option<u32>,
+ names: &'static [&'static str],
+ ) -> &'static Metadata<'static> {
+ let info = ForeignMetadataInfo {
+ target,
+ level,
+ kind,
+ name,
+ module,
+ file,
+ line,
+ names,
+ };
+ if let Some(v) = self.metadatas.get(&info) {
+ return *v;
+ }
+ let fake = FakeCallsite;
+ let metadata = Box::leak::<'static>(Box::new(Metadata::new(
+ name,
+ target,
+ level,
+ file,
+ line,
+ module,
+ FieldSet::new(names, tracing::callsite::Identifier(&FAKE_CALLSITE)),
+ kind.kind(),
+ )));
+
+ let meta_raw = &raw const *metadata;
+ let fields_raw = &raw const *metadata.fields();
+
+ // SAFETY: FieldSet struct should be inside of metadata struct... Which we assume here, but do not test
+ // FIXME: Safety comment above might be invalidated at any time, this should actually be covered by unit test (or, better: runtime assertion... Somehow.)
+ let fields_offset = unsafe { fields_raw.cast::<u8>().offset_from(meta_raw.cast()) };
+ let field_set = unsafe {
+ ((&raw mut *metadata).cast::<()>())
+ .byte_offset(fields_offset)
+ .cast::<FieldSet>()
+ };
+ // FIXME: metadata borrow here invalidates our &mut borrow of 'static Metadata, and 'static FieldSet so this construction should be replaced with raw pointers or idk.
+ // Something should be better done inside of tracing crate itself, someting like interior mutability.
+ let callsite = Box::leak(Box::new(tracing::callsite::DefaultCallsite::new(metadata)));
+ unsafe { *field_set = FieldSet::new(names, tracing::callsite::Identifier(callsite)) };
+
+ tracing::callsite::register(&*callsite);
+
+ self.metadatas.insert(info, metadata);
+ return metadata;
+ }
+}
+
+#[cfg(false)]
+static FOREIGN_SPAN_DATA: LazyLock<Mutex<ForeignSpanData>> =
+ LazyLock::new(|| Mutex::new(ForeignSpanData::default()));
+static NIX_SPAN_MAPPING: LazyLock<Mutex<HashMap<u64, Span>>> =
+ LazyLock::new(|| Mutex::new(HashMap::new()));
+
+#[derive(Debug)]
+enum FieldValue {
+ Int(i32),
+ Str(String),
+}
+
+struct StartActivityBuilder {
+ activity_id: u64,
+ verbosity: Verbosity,
+ typ: ActivityType,
+ fields: Vec<FieldValue>,
+}
+impl StartActivityBuilder {
+ fn add_int_field(&mut self, i: i32) {
+ self.fields.push(FieldValue::Int(i));
+ }
+ fn add_string_field(&mut self, v: &str) {
+ self.fields.push(FieldValue::Str(v.to_owned()));
+ }
+ fn emit(&mut self, parent: u64, s: &str) {
+ let mut mapping = NIX_SPAN_MAPPING.lock().expect("not poisoned");
+
+ let parent = mapping.get(&parent);
+
+ // let meta = spans.alloc_metadata(
+ // self.typ.name(),
+ // self.verbosity.into(),
+ // MetadataKind::Span,
+ // "nix activity start",
+ // None,
+ // None,
+ // None,
+ // self.typ.fields(),
+ // );
+ //
+ // let mut fields = meta.fields().iter();
+ // let span = if let Some(parent) = parent {
+ // let s = Span::new(
+ // meta,
+ // &match meta.fields().len() {
+ // 1 => meta.fields().value_set(
+ // &<[_; 1]>::try_from([(
+ // &fields.next().expect("has field"),
+ // Some(&format_args!("Test") as &dyn tracing::Value),
+ // )])
+ // .expect("valid size"),
+ // ),
+ // _ => unreachable!(),
+ // },
+ // );
+ // s.follows_from(parent);
+ // s
+ // } else {
+ // Span::new_root(
+ // meta,
+ // &match meta.fields().len() {
+ // 1 => meta.fields().value_set(
+ // &<[_; 1]>::try_from([(
+ // &fields.next().expect("has field"),
+ // Some(&format_args!("Test") as &dyn tracing::Value),
+ // )])
+ // .expect("valid size"),
+ // ),
+ // _ => unreachable!(),
+ // },
+ // )
+ // };
+ //
+ // let id = span.id().expect("id created");
+
+ let span = {
+ let _in_parent = parent.map(|p| p.enter());
+ let level: Level = self.verbosity.into();
+ if level == Level::ERROR {
+ self.typ
+ .format(&self.fields, s, |v| error_span!("action", v))
+ } else if level == Level::WARN {
+ self.typ
+ .format(&self.fields, s, |v| warn_span!("action", v))
+ } else if level == Level::INFO {
+ self.typ
+ .format(&self.fields, s, |v| info_span!("action", v))
+ } else if level == Level::DEBUG {
+ self.typ
+ .format(&self.fields, s, |v| debug_span!("action", v))
+ } else {
+ self.typ
+ .format(&self.fields, s, |v| trace_span!("action", v))
+ }
+ };
+ if !s.trim().is_empty() {
+ span.pb_set_message(s);
+ let _e = span.enter();
+ let level: Level = self.verbosity.into();
+ if level == Level::ERROR {
+ error!(target: "nix", "{}", s)
+ } else if level == Level::WARN {
+ warn!(target: "nix", "{}", s)
+ } else if level == Level::INFO {
+ info!(target: "nix", "{}", s)
+ } else if level == Level::DEBUG {
+ debug!(target: "nix", "{}", s)
+ } else {
+ trace!(target: "nix", "{}", s)
+ }
+ } else {
+ span.pb_start();
+ }
+ mapping.insert(self.activity_id, span);
+ }
+ fn emit_result(&mut self, ty: u32) {
+ let mut mapping = NIX_SPAN_MAPPING.lock().expect("not poisoned");
+
+ let Some(parent) = mapping.get(&self.activity_id) else {
+ panic!("unexpected result for dead parent");
+ };
+
+ let _in_parent = parent.enter();
+ let res = ResultType::from_int(ty);
+
+ use FieldValue::*;
+ match (&res, self.fields.as_slice()) {
+ // ResultType::FileLinked => todo!(),
+ (ResultType::BuildLogLine, [Str(s)]) => {
+ info!("{s:?}");
+ }
+ // ResultType::UntrustedPath => todo!(),
+ // ResultType::CorruptedPath => todo!(),
+ // ResultType::SetPhase => todo!(),
+ (ResultType::SetExpected, [Int(act_ty), Int(_expected)]) => {
+ let _act_ty = ActivityType::from_int(*act_ty as u32);
+ }
+ (ResultType::SetPhase, [Str(phase)]) => {
+ // parent.pb_set_message(phase);
+ debug!(target: "nix::phase", phase)
+ }
+ (ResultType::Progress, [Int(done), Int(expected), Int(_), Int(_)]) => {
+ parent.pb_set_length(*expected as u64);
+ parent.pb_set_position(*done as u64);
+ }
+ _ => warn!("unknown progress report: {:?}({:?})", &res, &self.fields),
+ }
+ }
+}
+fn new_start_activity(activity_id: u64, lvl: u32, typ: u32) -> Box<StartActivityBuilder> {
+ Box::new(StartActivityBuilder {
+ activity_id,
+ verbosity: Verbosity::from_int(lvl),
+ typ: ActivityType::from_int(typ),
+ fields: vec![],
+ })
+}
+
+fn emit_warn(v: &str) {
+ warn!(target: "nix::eval", "{v}")
+}
+fn emit_stop(v: u64) {
+ let mut mapping = NIX_SPAN_MAPPING.lock().expect("not poisoned");
+ mapping.remove(&v);
+}
+fn emit_log(lvl: u32, v: &str) {
+ let verbosity = Verbosity::from_int(lvl);
+ let level: Level = verbosity.into();
+ if level == Level::ERROR {
+ error!(target: "nix", "{v}")
+ } else if level == Level::WARN {
+ warn!(target: "nix", "{v}")
+ } else if level == Level::INFO {
+ info!(target: "nix", "{v}")
+ } else if level == Level::DEBUG {
+ debug!(target: "nix", "{v}")
+ } else {
+ trace!(target: "nix", "{v}")
+ }
+}
+
+// fn start_activity(act: u64, lvl: u32, act_ty: u32, s: &str, parent: u32) {
+// tracing::Span::new(meta, values)
+// }
+
+#[cxx::bridge]
+pub mod nix_logging_cxx {
+ extern "Rust" {
+ type StartActivityBuilder;
+ fn new_start_activity(activity_id: u64, lvl: u32, typ: u32) -> Box<StartActivityBuilder>;
+ fn add_int_field(&mut self, i: i32);
+ fn add_string_field(&mut self, v: &str);
+ fn emit(&mut self, parent: u64, s: &str);
+ fn emit_result(&mut self, ty: u32);
+
+ fn emit_warn(v: &str);
+ fn emit_stop(id: u64);
+ fn emit_log(lvl: u32, v: &str);
+ }
+ unsafe extern "C++" {
+ include!("nix-eval/src/logging.hh");
+
+ fn apply_tracing_logger();
+ }
+}
crates/nix-eval/src/macros.rsdiffbeforeafterboth--- a/crates/nix-eval/src/macros.rs
+++ b/crates/nix-eval/src/macros.rs
@@ -1,171 +1,26 @@
-use serde::Serialize;
-
-use crate::{NixSession, Value};
-
-#[derive(Clone)]
-pub struct NixExprBuilder {
- pub(crate) out: String,
- used_fields: Vec<Value>,
-}
-pub trait AttrSetValue {
- fn to_builder(self) -> NixExprBuilder;
-}
-trait Primitive {}
-
-macro_rules! impl_primitive {
- ($($t:ty),+) => {
- $(
- impl Primitive for $t {}
- )+
- };
-}
-impl_primitive!(String, bool, &'static str);
-
-impl<T> AttrSetValue for T
-where
- // Limited by Primitive trait to avoid orphan rules violation with Vec<T: AttrSetValue>
- T: Serialize + Primitive,
-{
- fn to_builder(self) -> NixExprBuilder {
- let serialized = nixlike::serialize(self).expect("invalid value for apply");
- NixExprBuilder {
- out: serialized.trim_end().to_owned(),
- used_fields: Vec::new(),
- }
- }
-}
-impl AttrSetValue for Value {
- fn to_builder(self) -> NixExprBuilder {
- NixExprBuilder {
- out: format!("sess_field_{}", self.session_field_id()),
- used_fields: vec![self],
- }
- }
-}
-impl<T> AttrSetValue for Vec<T>
-where
- T: AttrSetValue,
-{
- fn to_builder(self) -> NixExprBuilder {
- let mut builder = NixExprBuilder::list();
- for v in self {
- builder.list_value(NixExprBuilder::attrset_value(v));
- }
- builder.list_end();
- builder
- }
-}
-
-impl NixExprBuilder {
- pub fn object() -> Self {
- NixExprBuilder {
- out: "{ ".to_owned(),
- used_fields: Vec::new(),
- }
- }
- pub fn list() -> Self {
- NixExprBuilder {
- out: "[".to_owned(),
- used_fields: Vec::new(),
- }
- }
- pub fn string(s: &str) -> Self {
- NixExprBuilder {
- out: nixlike::serialize(s)
- .expect("no problems with serializing_string")
- .trim_end()
- .to_owned(),
- used_fields: Vec::new(),
- }
- }
- pub fn attrset_value(v: impl AttrSetValue) -> Self {
- v.to_builder()
- }
- pub fn serialized(v: impl Serialize) -> Self {
- let serialized = nixlike::serialize(v).expect("invalid value for apply");
- Self {
- out: serialized.trim_end().to_owned(),
- used_fields: Vec::new(),
- }
- }
- pub fn value(f: Value) -> Self {
- Self {
- out: format!("sess_field_{}", f.session_field_id()),
- used_fields: vec![f],
- }
- }
- pub fn end_obj(&mut self) {
- self.out.push('}');
- }
- pub fn obj_key(&mut self, name: Self, value: Self) {
- self.out.push_str(r#""${"#);
- self.extend(name);
- self.out.push_str(r#"}" = "#);
- self.extend(value);
- self.out.push_str("; ");
- }
- pub fn list_value(&mut self, value: Self) {
- self.extend(value);
- self.out.push(' ');
- }
- pub fn list_end(&mut self) {
- self.out.push(']');
- }
-
- pub fn extend(&mut self, e: Self) {
- self.out.push_str(&e.out);
- self.used_fields.extend(e.used_fields);
- }
-
- #[allow(dead_code)]
- pub fn session(&self) -> NixSession {
- let mut session = None;
- for ele in &self.used_fields {
- if session.is_none() {
- session = Some(ele.session());
- continue;
- }
- let session = session.as_ref().expect("checked");
- let ele_sess = ele.session();
- assert!(
- NixSession::ptr_eq(session, &ele_sess),
- "can't mix fields from different session"
- );
- }
- session.expect("expr without fields used")
- }
- #[allow(dead_code)]
- pub fn index_attr(&mut self, s: &str) {
- let escaped = nixlike::serialize(s).expect("string");
- self.out.push('.');
- self.out.push_str(escaped.trim_end());
- }
-}
-
#[macro_export]
macro_rules! nix_expr_inner {
//(@munch_object FIXME: value should be arbitrary nix_expr_inner input... Time to write proc-macro?
(@obj($o:ident) $field:ident$(, $($tt:tt)*)?) => {{
- $o.obj_key(
- NixExprBuilder::string(stringify!($field)),
- NixExprBuilder::attrset_value($field),
+ $o.insert(
+ stringify!($field),
+ $crate::Value::from($field),
);
$(nix_expr_inner!(@obj($o) $($tt)*);)?
}};
(@obj($o:ident) $field:ident: $v:expr$(, $($tt:tt)*)?) => {{
- $o.obj_key(
- NixExprBuilder::string(stringify!($field)),
- NixExprBuilder::attrset_value($v),
+ $o.insert(
+ stringify!($field),
+ $crate::Value::from($v),
);
$(nix_expr_inner!(@obj($o) $($tt)*);)?
}};
(@obj($o:ident)) => {{}};
(Obj { $($tt:tt)* }) => {{
- use $crate::{macros::NixExprBuilder, nix_expr_inner};
- let mut out = NixExprBuilder::object();
+ use $crate::{nix_expr_inner};
+ let mut out = std::collections::hash_map::HashMap::new();
nix_expr_inner!(@obj(out) $($tt)*);
- out.end_obj();
- out
+ Value::new_attrs(out)?
}};
(@field($o:ident) . $var:ident $($tt:tt)*) => {{
$o.index_attr(stringify!($var));
@@ -185,10 +40,10 @@
};
(@field($o:ident)) => {};
($field:ident $($tt:tt)*) => {{
- use $crate::{macros::NixExprBuilder, nix_expr_inner};
+ use $crate::{nix_expr_inner};
// might be used if indexed
#[allow(unused_mut)]
- let mut out = NixExprBuilder::value($field.clone());
+ let mut out = $field.clone();
nix_expr_inner!(@field(out) $($tt)*);
out
}};
@@ -197,8 +52,7 @@
NixExprBuilder::string($v)
}};
({$v:expr}) => {{
- use $crate::macros::NixExprBuilder;
- NixExprBuilder::serialized(&$v)
+ $crate::Value::serialized(&$v)?
}}
}
#[macro_export]
@@ -212,40 +66,25 @@
#[macro_export]
macro_rules! nix_go {
- (@o($o:ident) . $var:ident $($tt:tt)*) => {{
- $o.push(Index::attr(stringify!($var)));
- nix_go!(@o($o) $($tt)*);
- }};
- (@o($o:ident) [{ $v:expr }] $($tt:tt)*) => {{
- $o.push(Index::attr(&$v));
- nix_go!(@o($o) $($tt)*);
+ (@o($o:expr) . $var:ident $($tt:tt)*) => {{
+ nix_go!(@o($o.get_field(stringify!($var))?) $($tt)*)
}};
- (@o($o:ident) [ $($var:tt)+ ] $($tt:tt)*) => {{
- $o.push(Index::Expr($crate::nix_expr_inner!($($var)+)));
- nix_go!(@o($o) $($tt)*);
+ (@o($o:expr) [ $v:expr ] $($tt:tt)*) => {{
+ nix_go!(@o($o.get_field($v)?) $($tt)*)
}};
- (@o($o:ident) ($($var:tt)*) $($tt:tt)*) => {
- $o.push(Index::ExprApply($crate::nix_expr_inner!($($var)+)));
- nix_go!(@o($o) $($tt)*);
- };
- (@o($o:ident) | $($var:tt)*) => {
- $o.push(Index::Pipe($crate::nix_expr_inner!($($var)+)));
- };
- (@o($o:ident) + $($var:tt)*) => {
- $o.push(Index::Merge($crate::nix_expr_inner!($($var)+)));
+ (@o($o:expr) ($($var:tt)*) $($tt:tt)*) => {
+ nix_go!(@o($o.call($crate::nix_expr_inner!($($var)+))?) $($tt)*)
};
- (@o($o:ident)) => {};
+ (@o($o:expr)) => {$o};
($field:ident $($tt:tt)+) => {{
- use $crate::{nix_go, Index};
- let field = $field.clone();
- let mut out = vec![];
- nix_go!(@o(out) $($tt)*);
- field.select(out).await?
+ use $crate::nix_go;
+ let out = $field.clone();
+ nix_go!(@o(out) $($tt)*)
}}
}
#[macro_export]
macro_rules! nix_go_json {
($($tt:tt)*) => {{
- $crate::nix_go!($($tt)*).as_json().await?
+ $crate::nix_go!($($tt)*).as_json()?
}};
}
crates/nix-eval/src/pool.rsdiffbeforeafterboth--- a/crates/nix-eval/src/pool.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use std::{
- ffi::OsString,
- sync::{Arc, OnceLock},
-};
-
-use r2d2::Pool;
-
-use crate::{Error, NixSession, Result, session::NixSessionInner};
-
-pub struct NixSessionPool(Pool<NixSessionPoolInner>);
-impl NixSessionPool {
- pub async fn new(
- flake: OsString,
- nix_args: Vec<OsString>,
- nix_system: String,
- fail_fast: bool,
- ) -> Result<Self> {
- let inner = tokio::task::block_in_place(|| {
- r2d2::Builder::<NixSessionPoolInner>::new()
- .min_idle(Some(0))
- .build(NixSessionPoolInner {
- flake,
- nix_args,
- nix_system,
- fail_fast,
- })
- })?;
- Ok(Self(inner))
- }
- pub async fn get(&self) -> Result<NixSession> {
- let v = tokio::task::block_in_place(|| self.0.get())?;
- Ok(NixSession(Arc::new(tokio::sync::Mutex::new(v))))
- }
-}
-
-pub(crate) struct NixSessionPoolInner {
- flake: OsString,
- nix_args: Vec<OsString>,
- fail_fast: bool,
- pub(crate) nix_system: String,
-}
-
-impl r2d2::ManageConnection for NixSessionPoolInner {
- type Connection = NixSessionInner;
- type Error = Error;
- fn connect(&self) -> std::result::Result<Self::Connection, Self::Error> {
- let _v = TOKIO_RUNTIME
- .get()
- .expect("missed tokio runtime init!")
- .enter();
- futures::executor::block_on(NixSessionInner::new(
- self.flake.as_os_str(),
- self.nix_args.iter().map(OsString::as_os_str),
- self.nix_system.clone(),
- self.fail_fast,
- ))
- }
-
- fn is_valid(&self, conn: &mut Self::Connection) -> std::result::Result<(), Self::Error> {
- let _v = TOKIO_RUNTIME
- .get()
- .expect("missed tokio runtime init!")
- .enter();
- let res = futures::executor::block_on(conn.execute_expression_number("2 + 2"))?;
- if res != 4 {
- // just in case, should fail much earlier
- return Err(Error::SessionInit("misbehaving session"));
- };
- Ok(())
- }
-
- fn has_broken(&self, _conn: &mut Self::Connection) -> bool {
- false
- }
-}
-pub static TOKIO_RUNTIME: OnceLock<tokio::runtime::Handle> = OnceLock::new();
crates/nix-eval/src/session.rsdiffbeforeafterboth--- a/crates/nix-eval/src/session.rs
+++ /dev/null
@@ -1,450 +0,0 @@
-use std::{ffi::OsStr, num::ParseIntError, process::Stdio, sync::Arc};
-
-use better_command::{ClonableHandler, Handler, NixHandler, NoopHandler};
-use futures::StreamExt;
-use itertools::Itertools as _;
-use serde::{Deserialize, de::DeserializeOwned};
-use thiserror::Error;
-use tokio::{
- io::AsyncWriteExt,
- process::{ChildStderr, ChildStdin, ChildStdout, Command},
- select,
- sync::{Mutex, mpsc, oneshot},
-};
-use tokio_util::codec::{FramedRead, LinesCodec};
-use tracing::{Level, debug, error, warn};
-
-#[derive(Error, Debug, Clone)]
-pub enum Error {
- #[error("failed to create nix repl session: {0}")]
- SessionInit(&'static str),
- #[error("unexpected end of output, nix crashed?")]
- MissingDelimiter,
-
- #[error("expression did'nt produce any output")]
- ExpectedOutput,
- #[error("expression produced output, which is unexpected")]
- UnexpectedOutput,
-
- #[error("unexpected expression output type")]
- InvalidType,
-
- #[error("failed to build attr {attribute}:\n{error}")]
- BuildFailed { attribute: String, error: String },
-
- #[error("output: {0}")]
- Json(Arc<serde_json::Error>),
- // int outputs are too specific, and should not be used,
- // thus error is ok to be not informative.
- #[error("int output: {0}")]
- Int(ParseIntError),
- #[error("pool: {0}")]
- Pool(Arc<r2d2::Error>),
- #[error("io: {0}")]
- Io(Arc<std::io::Error>),
-
- // TODO: Should be done by wrapper/in different type.
- #[error("at {0}: {1}")]
- InContext(String, Box<Self>),
-
- #[error("error: {0}")]
- NixError(String),
-}
-impl From<r2d2::Error> for Error {
- fn from(value: r2d2::Error) -> Self {
- Self::Pool(Arc::new(value))
- }
-}
-impl From<std::io::Error> for Error {
- fn from(value: std::io::Error) -> Self {
- Self::Io(Arc::new(value))
- }
-}
-impl From<serde_json::Error> for Error {
- fn from(value: serde_json::Error) -> Self {
- Self::Json(Arc::new(value))
- }
-}
-impl Error {
- pub(crate) fn context(self, context: String) -> Self {
- Self::InContext(context, Box::new(self))
- }
-}
-pub type Result<T, E = Error> = std::result::Result<T, E>;
-
-enum OutputLine {
- Out(String),
- Err(String),
-}
-struct OutputHandler {
- rx: mpsc::Receiver<OutputLine>,
- _cancel_handle: oneshot::Receiver<()>,
-}
-impl OutputHandler {
- fn new(out: ChildStdout, err: ChildStderr) -> Self {
- let mut out = FramedRead::new(out, LinesCodec::new());
- let mut err = FramedRead::new(err, LinesCodec::new());
- let (tx, rx) = mpsc::channel(20);
- let (mut cancelled, _cancel_handle) = oneshot::channel();
- tokio::spawn(async move {
- loop {
- select! {
- // We should receive errors earlier than synchronization
- biased;
- e = err.next() => {
- let Some(Ok(e)) = e else {
- if e.is_some() {
- error!("bad repl stderr: {e:?}");
- }
- continue;
- };
- let _ = tx.send(OutputLine::Err(e)).await;
- }
- o = out.next() => {
- let Some(Ok(o)) = o else {
- if o.is_some() {
- error!("bad repl stdout: {o:?}");
- }
- continue;
- };
- let _ = tx.send(OutputLine::Out(o)).await;
- }
- // Reader doesn't care about stdout, as this is cancelled.
- // Error still might be useful, to process leftover span closures?
- _ = cancelled.closed() => {
- break;
- }
- }
- }
- });
- Self { rx, _cancel_handle }
- }
- async fn next(&mut self) -> Option<OutputLine> {
- self.rx.recv().await
- }
-}
-
-#[must_use]
-struct ErrorCollector<'i, H> {
- collected: Vec<String>,
- inner: &'i mut H,
-}
-impl<'i, H> ErrorCollector<'i, H> {
- fn new(inner: &'i mut H) -> Self {
- Self {
- collected: vec![],
- inner,
- }
- }
-}
-impl<H> ErrorCollector<'_, H> {
- fn handle_line_inner(&mut self, msg: &str) -> bool {
- let Some(msg) = msg.strip_prefix("@nix ") else {
- return false;
- };
- #[derive(Deserialize)]
- struct ErrorAction {
- action: String,
- level: u32,
- msg: String,
- }
- let Ok(act) = serde_json::from_str::<ErrorAction>(msg) else {
- return false;
- };
- if act.action != "msg" || act.level != 0 {
- return false;
- }
- self.collected.push(act.msg);
- true
- }
- fn finish(self) -> Result<()> {
- // fn dedent(s: String) -> String {
- // s.split('\n').filter(|s| !s.trim().is_empty()).map(|v| v.)
- // }
- if !self.collected.is_empty() {
- return Err(Error::NixError(
- self.collected
- .iter()
- .map(|v| {
- if let Some(f) = v.strip_prefix("\u{1b}[31;1merror:\u{1b}[0m ") {
- let v = unindent::unindent(f.trim_start());
- v.trim().to_owned()
- } else {
- v.to_owned()
- }
- })
- .join("\n")
- .to_string(),
- ));
- }
- Ok(())
- }
- fn flush(self) {
- for line in self.collected {
- warn!("{line}");
- }
- }
-}
-impl<H: Handler> Handler for ErrorCollector<'_, H> {
- fn handle_line(&mut self, e: &str) {
- if self.handle_line_inner(e) {
- return;
- }
- self.inner.handle_line(e)
- }
-}
-
-pub struct NixSessionInner {
- full_delimiter: String,
- nix_handler: ClonableHandler<NixHandler>,
- out: OutputHandler,
- stdin: ChildStdin,
- string_wrapping: (String, String),
- number_wrapping: (String, String),
-
- executing_command: Arc<Mutex<()>>,
-
- next_id: u32,
- pub(crate) free_list: Vec<u32>,
-
- pub nix_system: String,
-}
-
-/// Discover inter-message repl delimiter
-const REPL_DELIMITER: &str = "\"FLEET_MAGIC_REPL_DELIMITER\"";
-/// Discover formatting around strings
-const TRAIN_STRING: &str = "\"TRAIN_STRING\"";
-/// Discover formatting around numbers
-const TRAIN_NUMBER: &str = "13141516";
-// Other types of formatting are not discovered, because they are not used, JSON serialization is used instead
-// Techically, number training is also not required, because numbers can be converted to string too...
-// Eh, I'll remove it later.
-
-impl NixSessionInner {
- pub(crate) async fn new(
- flake: &OsStr,
- extra_args: impl IntoIterator<Item = &OsStr>,
- nix_system: String,
- fail_fast: bool,
- ) -> Result<Self> {
- let mut cmd = Command::new("nix");
- cmd.arg("repl")
- .args(["--option", "pure-eval", "true"])
- .arg(flake)
- .arg("--log-format")
- .arg("internal-json");
- if !fail_fast {
- cmd.arg("--keep-going");
- }
- for arg in extra_args {
- cmd.arg(arg);
- }
- cmd.stdin(Stdio::piped());
- cmd.stdout(Stdio::piped());
- cmd.stderr(Stdio::piped());
- let cmd = cmd.spawn()?;
- let stdout = cmd.stdout.unwrap();
- let stderr = cmd.stderr.unwrap();
- let mut out = OutputHandler::new(stdout, stderr);
- let mut stdin = cmd.stdin.unwrap();
- // Standard repl hello doesn't work with internal-json logger
- stdin.write_all(REPL_DELIMITER.as_bytes()).await?;
- stdin.write_all(b"\n").await?;
- stdin.flush().await?;
- let nix_handler = NixHandler::default();
- let mut full_delimiter = None;
- let mut errors = vec![];
- while let Some(line) = out.next().await {
- let line = match line {
- OutputLine::Out(o) => o,
- OutputLine::Err(_e) => {
- // Handle startup errors, but skip repl hello?
- errors.push(_e);
- continue;
- }
- };
- if line.contains(REPL_DELIMITER) {
- debug!("discovered repl delimiter with added colors: {line}");
- full_delimiter = Some(line.to_owned());
- break;
- }
- }
- let Some(full_delimiter) = full_delimiter else {
- for e in errors {
- error!("{e}");
- }
- return Err(Error::SessionInit("failed to discover delimiter"));
- };
- let mut res = Self {
- full_delimiter,
- nix_handler: ClonableHandler::new(nix_handler),
- out,
- stdin,
- string_wrapping: Default::default(),
- number_wrapping: Default::default(),
-
- executing_command: Arc::new(Mutex::new(())),
-
- next_id: 0,
- free_list: vec![],
-
- nix_system,
- };
- res.train().await?;
- Ok(res)
- }
- async fn train(&mut self) -> Result<()> {
- {
- let full_string = self
- .execute_expression_raw(TRAIN_STRING, &mut NoopHandler)
- .await?;
- let string_offset = full_string.find(TRAIN_STRING).expect("contained");
- let string_prefix = &full_string[..string_offset];
- let string_suffix = &full_string[string_offset + TRAIN_STRING.len()..];
- self.string_wrapping = (string_prefix.to_owned(), string_suffix.to_owned());
- }
- {
- let full_number = self
- .execute_expression_raw(TRAIN_NUMBER, &mut NoopHandler)
- .await?;
- let number_offset = full_number.find(TRAIN_NUMBER).expect("contained");
- let number_prefix = &full_number[..number_offset];
- let number_suffix = &full_number[number_offset + TRAIN_NUMBER.len()..];
- self.number_wrapping = (number_prefix.to_owned(), number_suffix.to_owned());
- }
- Ok(())
- }
- async fn send_command(&mut self, cmd: impl AsRef<[u8]>) -> Result<()> {
- if tracing::enabled!(Level::DEBUG) && cmd.as_ref() != REPL_DELIMITER.as_bytes() {
- let cmd_str = String::from_utf8_lossy(cmd.as_ref());
- tracing::debug!("{cmd_str}");
- };
- self.stdin.write_all(cmd.as_ref()).await?;
- self.stdin.write_all(b"\n").await?;
- Ok(())
- }
- async fn read_until_delimiter(&mut self, err_handler: &mut dyn Handler) -> Result<String> {
- let mut out = String::new();
- while let Some(line) = self.out.next().await {
- let line = match line {
- OutputLine::Out(out) => out,
- OutputLine::Err(err) => {
- err_handler.handle_line(&err);
- continue;
- }
- };
- if line == self.full_delimiter {
- return Ok(out);
- }
- if !out.is_empty() {
- out.push('\n');
- }
- out.push_str(&line);
- }
- Err(Error::MissingDelimiter)
- }
- pub(crate) async fn execute_expression_number(
- &mut self,
- expr: impl AsRef<[u8]>,
- ) -> Result<u64> {
- let num = self.number_wrapping.clone();
- let n = self.execute_expression_wrapping(expr, &num).await?;
- n.parse::<u64>().map_err(Error::Int)
- }
- async fn execute_expression_string(&mut self, expr: impl AsRef<[u8]>) -> Result<String> {
- // builtins.toJSON escapes some thing in incorrect way, e.g escaped "$" in "\${" is being outputed as "\$",
- // while this escape should be removed as it is intended for nix itself, not for json output.
- //
- // This regex only allows \$ in the beginning of the string, it is easier to implement correctly.
- // TODO: Add peg parser for nix-produced JSON?..
- let regex = regex::Regex::new(r#"(?<prefix>[: {,\[]\\")\\\$"#).expect("fixup json");
-
- let num = self.string_wrapping.clone();
- let n = self.execute_expression_wrapping(expr, &num).await?;
- let n = regex.replace_all(&n, "$prefix$$");
- let str: String = serde_json::from_str(&n)?;
- Ok(str)
- }
- pub(crate) async fn execute_expression_to_json<V: DeserializeOwned>(
- &mut self,
- expr: impl AsRef<[u8]>,
- ) -> Result<V> {
- let mut fexpr = b"builtins.toJSON (".to_vec();
- fexpr.extend_from_slice(expr.as_ref());
- fexpr.push(b')');
-
- Ok(serde_json::from_str(
- &self.execute_expression_string(fexpr).await?,
- )?)
- }
- async fn execute_expression_wrapping(
- &mut self,
- expr: impl AsRef<[u8]>,
- wrapping: &(String, String),
- ) -> Result<String> {
- let mut nix_handler = self.nix_handler.clone();
- let mut collected = ErrorCollector::new(&mut nix_handler);
- let res = self.execute_expression_raw(expr, &mut collected).await?;
- if res.is_empty() {
- collected.finish()?;
- return Err(Error::ExpectedOutput);
- } else {
- collected.flush()
- };
- let Some(res) = res.strip_prefix(&wrapping.0) else {
- return Err(Error::InvalidType);
- };
- let Some(res) = res.strip_suffix(&wrapping.1) else {
- return Err(Error::InvalidType);
- };
- Ok(res.to_owned())
- }
- async fn execute_expression_empty(&mut self, expr: impl AsRef<[u8]>) -> Result<()> {
- let mut nix_handler = self.nix_handler.clone();
- let mut collected = ErrorCollector::new(&mut nix_handler);
- let v = self.execute_expression_raw(expr, &mut collected).await?;
- collected.finish()?;
- if !v.is_empty() {
- return Err(Error::UnexpectedOutput);
- }
- Ok(())
- }
- pub(crate) async fn execute_expression_raw(
- &mut self,
- expr: impl AsRef<[u8]>,
- err_handler: &mut dyn Handler,
- ) -> Result<String> {
- // Prevent two commands from being executed in parallel, messing with each other.
- let _lock = self.executing_command.clone();
- let _guard = _lock.lock().await;
-
- self.send_command(expr).await?;
- // It will be echoed
- self.send_command(REPL_DELIMITER).await?;
- self.read_until_delimiter(err_handler).await
- }
- pub(crate) async fn execute_assign(&mut self, expr: impl AsRef<str>) -> Result<u32> {
- let id = self.allocate_id();
- self.execute_expression_empty(format!("sess_field_{id} = {}", expr.as_ref()))
- .await?;
- Ok(id)
- }
-
- /// Id should be immediately used
- fn allocate_id(&mut self) -> u32 {
- if let Some(free) = self.free_list.pop() {
- free
- } else {
- let v = self.next_id;
- self.next_id += 1;
- v
- }
- }
- // Nix has no way to deallocate variable, yet GC will correct everything not reachable.
- // async fn free_id(&mut self, id: u32) -> Result<()> {
- // self.execute_expression_empty(format!("sess_field_{id} = null"))
- // .await?;
- // self.free_list.push(id);
- // Ok(())
- // }
-}
crates/nix-eval/src/value.rsdiffbeforeafterboth--- a/crates/nix-eval/src/value.rs
+++ b/crates/nix-eval/src/value.rs
@@ -3,302 +3,4 @@
use better_command::NixHandler;
use serde::{Serialize, de::DeserializeOwned};
-use crate::{Error, NixBuildBatch, NixSession, Result, macros::NixExprBuilder, nix_go};
-
-#[derive(Clone)]
-pub enum Index {
- Var(String),
- String(String),
- #[allow(dead_code)]
- Apply(String),
- #[allow(dead_code)]
- Expr(NixExprBuilder),
- ExprApply(NixExprBuilder),
- Pipe(NixExprBuilder),
- Merge(NixExprBuilder),
-}
-impl Index {
- pub fn var(v: impl AsRef<str>) -> Self {
- let v = v.as_ref();
- assert!(
- !(v.contains('.') | v.contains(' ')),
- "bad variable name: {v}"
- );
- Self::Var(v.to_owned())
- }
- pub fn attr(v: impl AsRef<str>) -> Self {
- Self::String(v.as_ref().to_owned())
- }
- #[allow(dead_code)]
- pub fn apply(v: impl Serialize) -> Self {
- let serialized = nixlike::serialize(v).expect("invalid value for apply");
- Self::Apply(serialized.trim_end().to_owned())
- }
-}
-impl fmt::Display for Index {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Index::Var(v) => {
- write!(f, "{v}")
- }
- Index::String(k) => {
- let v = nixlike::format_identifier(k.as_str());
- write!(f, ".{v}")
- }
- Index::Apply(_) => {
- write!(f, "<apply>(...)")
- }
- Index::Expr(e) => {
- write!(f, "[{}]", e.out)
- }
- Index::ExprApply(_) => {
- write!(f, "<apply>(...)")
- }
- Index::Pipe(e) => {
- write!(f, "<map>({})", e.out)
- }
- Index::Merge(e) => {
- write!(f, "//({})", e.out)
- }
- }
- }
-}
-impl fmt::Debug for Index {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{self}")
- }
-}
-struct PathDisplay<'i>(&'i [Index]);
-impl fmt::Display for PathDisplay<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if !matches!(self.0.first(), Some(Index::Var(_))) {
- write!(f, "<unknown>")?;
- }
- for i in self.0 {
- write!(f, "{i}")?;
- }
- Ok(())
- }
-}
-struct ValueInner {
- full_path: Vec<Index>,
- session: NixSession,
- value: u32,
-}
-#[derive(Clone)]
-pub struct Value(Arc<ValueInner>);
-impl Value {
- async fn new(session: NixSession, query: &str) -> Result<Self> {
- let vid = session.0.lock().await.execute_assign(query).await?;
- Ok(Self(Arc::new(ValueInner {
- full_path: vec![],
- session,
- value: vid,
- })))
- }
- /// Get a top-level binding.
- ///
- /// In flake repl session, every output is exposed as top-level binding.
- pub async fn binding(session: NixSession, query: &str) -> Result<Self> {
- // TODO: Verify that query is a valid variable name
- let vid = session.0.lock().await.execute_assign(query).await?;
- Ok(Self(Arc::new(ValueInner {
- full_path: vec![Index::Var(query.to_owned())],
- session,
- value: vid,
- })))
- }
- pub async fn select(&self, name: impl IntoIterator<Item = Index>) -> Result<Self> {
- let mut used_fields = Vec::new();
- let name = name.into_iter();
-
- let mut full_path = self.0.full_path.clone();
- let mut query = self.sess_field_name();
- for v in name {
- full_path.push(v.clone());
- match v {
- Index::Var(_) => panic!("var item may only be first"),
- Index::String(s) => {
- let escaped =
- nixlike::serialize(s).expect("strings are always serialized successfully");
- query.push('.');
- query.push_str(escaped.trim());
- }
- Index::Apply(a) => {
- // In cases like `a {}.b` first `{}.b` will be evaluated, so `a {}` should be encased in `()`
- query = format!("({query} {a})");
- }
- Index::Expr(e) => {
- let index = Value::new(self.0.session.clone(), &e.out).await?;
- used_fields.push(index.clone());
- query.push('.');
- let index = format!("${{sess_field_{}}}", index.0.value);
- query.push_str(&index);
- }
- Index::ExprApply(e) => {
- let index = Value::new(self.0.session.clone(), &e.out).await?;
- used_fields.push(index.clone());
- query.push(' ');
- let index = format!("sess_field_{}", index.0.value);
- query.push_str(&index);
- query = format!("({query})");
- }
- Index::Pipe(v) => {
- let index = Value::new(self.0.session.clone(), &v.out).await?;
- used_fields.push(index.clone());
- let index = format!("sess_field_{}", index.0.value);
- query = format!("({index} {query})");
- }
- Index::Merge(v) => {
- let index = Value::new(self.0.session.clone(), &v.out).await?;
- used_fields.push(index.clone());
- let index = format!("sess_field_{}", index.0.value);
- query = format!("({query} // {index})");
- }
- }
- }
-
- let vid = self
- .0
- .session
- .0
- .lock()
- .await
- .execute_assign(&query)
- .await
- .map_err(|e| e.context(self.attribute()))?;
- Ok(Self(Arc::new(ValueInner {
- full_path,
- session: self.0.session.clone(),
- value: vid,
- })))
- }
- pub async fn as_json<V: DeserializeOwned>(&self) -> Result<V> {
- let query = self.sess_field_name();
- self.0
- .session
- .0
- .lock()
- .await
- .execute_expression_to_json(&query)
- .await
- .map_err(|e| e.context(self.attribute()))
- }
- #[allow(dead_code)]
- pub async fn has_field(&self, name: &str) -> Result<bool> {
- let key = nixlike::escape_string(name);
- let query = format!("{} ? {key}", self.sess_field_name());
- self.0
- .session
- .0
- .lock()
- .await
- .execute_expression_to_json(&query)
- .await
- .map_err(|e| e.context(self.attribute()))
- }
- pub async fn list_fields(&self) -> Result<Vec<String>> {
- let query = format!("builtins.attrNames {}", self.sess_field_name());
- self.0
- .session
- .0
- .lock()
- .await
- .execute_expression_to_json(&query)
- .await
- .map_err(|e| e.context(self.attribute()))
- }
- pub async fn type_of(&self) -> Result<String> {
- let query = format!("builtins.typeOf {}", self.sess_field_name());
- self.0
- .session
- .0
- .lock()
- .await
- .execute_expression_to_json(&query)
- .await
- .map_err(|e| e.context(self.attribute()))
- }
- #[allow(dead_code)]
- pub async fn import(&self) -> Result<Self> {
- let import = Self::new(self.0.session.clone(), "import").await?;
- Ok(nix_go!(self | import))
- }
- fn sess_field_name(&self) -> String {
- format!("sess_field_{}", self.0.value)
- }
- pub async fn build_maybe_batch(
- &self,
- batch: Option<NixBuildBatch>,
- ) -> Result<HashMap<String, PathBuf>> {
- if let Some(batch) = batch {
- batch.submit(self.clone()).await
- } else {
- self.build().await
- }
- }
- pub async fn build(&self) -> Result<HashMap<String, PathBuf>> {
- let query = format!(":b {}", self.sess_field_name());
- let vid = self
- .0
- .session
- .0
- .lock()
- .await
- .execute_expression_raw(&query, &mut NixHandler::default())
- .await?;
- if vid.is_empty() {
- return Err(Error::BuildFailed {
- attribute: self.attribute(),
- error: "build produced no output".to_owned(),
- });
- }
- let Some(vid) = vid.strip_prefix("This derivation produced the following outputs:\n")
- else {
- return Err(Error::BuildFailed {
- attribute: self.attribute(),
- error: format!("failed to parse output: {vid}"),
- });
- };
- let outputs = vid
- .split('\n')
- .filter(|v| !v.is_empty())
- .map(|v| v.split_once(" -> ").expect("unexpected build output"))
- .map(|(a, b)| (a.trim_start().to_owned(), PathBuf::from(b)))
- .collect();
- Ok(outputs)
- }
- /// Weakly convert string-like types (derivation/path/string) to string
- pub async fn to_string_weak(&self) -> Result<String> {
- let query = format!("\"${{{}}}\"", self.sess_field_name());
- let vid: String = self
- .0
- .session
- .0
- .lock()
- .await
- .execute_expression_to_json(&query)
- .await?;
- Ok(vid)
- }
-
- fn attribute(&self) -> String {
- PathDisplay(&self.0.full_path).to_string()
- }
-
- pub(crate) fn session(&self) -> NixSession {
- self.0.session.clone()
- }
-
- pub(crate) fn session_field_id(&self) -> u32 {
- self.0.value
- }
-}
-impl Drop for ValueInner {
- fn drop(&mut self) {
- if let Ok(mut lock) = self.session.0.try_lock() {
- lock.free_list.push(self.value)
- }
- // Leaked
- }
-}
+use crate::{Result, Value, nix_go};
crates/nixlike/Cargo.tomldiffbeforeafterboth--- a/crates/nixlike/Cargo.toml
+++ b/crates/nixlike/Cargo.toml
@@ -10,7 +10,7 @@
alejandra = { git = "https://github.com/kamadorueda/alejandra" }
linked-hash-map = "0.5.6"
peg = "0.8.5"
-ron = "0.10.1"
+ron = "0.11.0"
serde = "1.0.219"
serde-transcode = "1.1.1"
serde_json = "1.0.140"
crates/nixlike/src/lib.rsdiffbeforeafterboth--- a/crates/nixlike/src/lib.rs
+++ b/crates/nixlike/src/lib.rs
@@ -5,6 +5,7 @@
//! expressions and expect it to work, only basic primitives are supported, and there is no
//! variables/recursive records, interpolation, e.t.c.
+use alejandra::config::Indentation;
use linked_hash_map::LinkedHashMap;
use peg::str::LineCol;
use se_impl::MySerialize;
@@ -196,7 +197,13 @@
assert_eq!(serialize("Hello\nworld").unwrap(), "\"Hello\\nworld\"\n");
}
pub fn format_nix(value: &String) -> String {
- let (_, out) = alejandra::format::in_memory("".to_owned(), value.to_owned());
+ let (_, out) = alejandra::format::in_memory(
+ "".to_owned(),
+ value.to_owned(),
+ alejandra::config::Config {
+ indentation: Indentation::TwoSpaces,
+ },
+ );
out
}
crates/nixlike/src/to_string.rsdiffbeforeafterboth--- a/crates/nixlike/src/to_string.rs
+++ b/crates/nixlike/src/to_string.rs
@@ -1,3 +1,5 @@
+use alejandra::config::Indentation;
+
use crate::Value;
pub fn write_identifier(k: &str, out: &mut String) {
@@ -98,6 +100,12 @@
pub fn write_nix(value: &Value) -> String {
let mut out = String::new();
write_nix_buf(value, &mut out);
- let (_, out) = alejandra::format::in_memory("".to_owned(), out);
+ let (_, out) = alejandra::format::in_memory(
+ "".to_owned(),
+ out,
+ alejandra::config::Config {
+ indentation: Indentation::TwoSpaces,
+ },
+ );
out
}
flake.lockdiffbeforeafterboth--- a/flake.lock
+++ b/flake.lock
@@ -15,6 +15,22 @@
"type": "github"
}
},
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1696426674,
+ "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
@@ -35,8 +51,117 @@
"type": "github"
}
},
+ "flake-parts_2": {
+ "inputs": {
+ "nixpkgs-lib": [
+ "nix",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1748821116,
+ "narHash": "sha256-F82+gS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE=",
+ "rev": "49f0870db23e8c1ca0b5259734a02cd9e1e371a1",
+ "revCount": 377,
+ "type": "tarball",
+ "url": "https://api.flakehub.com/f/pinned/hercules-ci/flake-parts/0.1.377%2Brev-49f0870db23e8c1ca0b5259734a02cd9e1e371a1/01972f28-554a-73f8-91f4-d488cc502f08/source.tar.gz"
+ },
+ "original": {
+ "type": "tarball",
+ "url": "https://flakehub.com/f/hercules-ci/flake-parts/0.1"
+ }
+ },
+ "git-hooks-nix": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "gitignore": [
+ "nix"
+ ],
+ "nixpkgs": [
+ "nix",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1747372754,
+ "narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=",
+ "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46",
+ "revCount": 1026,
+ "type": "tarball",
+ "url": "https://api.flakehub.com/f/pinned/cachix/git-hooks.nix/0.1.1026%2Brev-80479b6ec16fefd9c1db3ea13aeb038c60530f46/0196d79a-1b35-7b8e-a021-c894fb62163d/source.tar.gz"
+ },
+ "original": {
+ "type": "tarball",
+ "url": "https://flakehub.com/f/cachix/git-hooks.nix/0.1.941"
+ }
+ },
+ "nix": {
+ "inputs": {
+ "flake-parts": "flake-parts_2",
+ "git-hooks-nix": "git-hooks-nix",
+ "nixpkgs": "nixpkgs",
+ "nixpkgs-23-11": "nixpkgs-23-11",
+ "nixpkgs-regression": "nixpkgs-regression"
+ },
+ "locked": {
+ "lastModified": 1756860322,
+ "narHash": "sha256-mT01CpWVdqSm79L270dSkjdYbdc37r+Hq9vk4GTp7Ao=",
+ "path": "/home/lach/build/nix-src",
+ "type": "path"
+ },
+ "original": {
+ "path": "/home/lach/build/nix-src",
+ "type": "path"
+ }
+ },
"nixpkgs": {
"locked": {
+ "lastModified": 1755922037,
+ "narHash": "sha256-wY1+2JPH0ZZC4BQefoZw/k+3+DowFyfOxv17CN/idKs=",
+ "rev": "b1b3291469652d5a2edb0becc4ef0246fff97a7c",
+ "revCount": 808723,
+ "type": "tarball",
+ "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2505.808723%2Brev-b1b3291469652d5a2edb0becc4ef0246fff97a7c/0198daf7-011a-7703-95d7-57146e794342/source.tar.gz"
+ },
+ "original": {
+ "type": "tarball",
+ "url": "https://flakehub.com/f/NixOS/nixpkgs/0.2505"
+ }
+ },
+ "nixpkgs-23-11": {
+ "locked": {
+ "lastModified": 1717159533,
+ "narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
+ "type": "github"
+ }
+ },
+ "nixpkgs-regression": {
+ "locked": {
+ "lastModified": 1643052045,
+ "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
"lastModified": 1753320130,
"narHash": "sha256-KCuv6iYQ0XTVAEJvDLIsk99CJm7fuqIE0/KknyeYPtM=",
"owner": "nixos",
@@ -55,7 +180,8 @@
"inputs": {
"crane": "crane",
"flake-parts": "flake-parts",
- "nixpkgs": "nixpkgs",
+ "nix": "nix",
+ "nixpkgs": "nixpkgs_2",
"rust-overlay": "rust-overlay",
"shelly": "shelly",
"treefmt-nix": "treefmt-nix"
flake.nixdiffbeforeafterboth--- a/flake.nix
+++ b/flake.nix
@@ -17,6 +17,9 @@
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
+ # DeterminateSystem's nix fork is controversial, but I don't mind it,
+ # and it has lazy-trees support which is useful for fleet.
+ nix.url = "/home/lach/build/nix-src";
};
outputs =
inputs:
@@ -41,6 +44,11 @@
fleetModules.tf = ./modules/extras/tf.nix;
+ testObj = {
+ v = "Hello";
+ };
+ testString = "hello";
+
# To be used with https://github.com/NixOS/nix/pull/8892
schemas =
let
@@ -80,6 +88,7 @@
system,
pkgs,
self,
+ inputs',
...
}:
let
@@ -108,7 +117,7 @@
packages = lib.mkIf deployerSystem (
let
packages = pkgs.callPackages ./pkgs {
- inherit craneLib;
+ inherit craneLib inputs';
};
in
packages // { default = packages.fleet; }
@@ -120,6 +129,7 @@
nixpkgsCraneLib = inputs.crane.mkLib pkgs;
packages = pkgs.callPackages ./pkgs {
craneLib = nixpkgsCraneLib;
+ inherit inputs;
};
prefixAttrs =
prefix: attrs:
@@ -150,13 +160,16 @@
cargo-fuzz
cargo-watch
cargo-outdated
+ gdb
pkg-config
openssl
bacon
nil
rustPlatform.bindgenHook
- # nixVersions.nix_2_22
+ inputs'.nix.packages.nix-expr-c
+ inputs'.nix.packages.nix-flake-c
+ inputs'.nix.packages.nix-fetchers-c
];
environment.PROTOC = "${pkgs.protobuf}/bin/protoc";
};
lib/flakePart.nixdiffbeforeafterboth--- a/lib/flakePart.nix
+++ b/lib/flakePart.nix
@@ -65,7 +65,7 @@
normalEval = bootstrapNixpkgs.lib.evalModules {
modules = (import ../modules/module-list.nix) ++ [
module
- {
+ ({inputs', ...}: {
config = {
data = if isPath data then import data else data;
nixpkgs.buildUsing = mkOptionDefault bootstrapNixpkgs;
@@ -74,6 +74,7 @@
inherit
(import ../pkgs {
inherit (prev) callPackage;
+ inherit inputs';
craneLib = crane.mkLib prev;
})
fleet-install-secrets
@@ -82,7 +83,7 @@
})
];
};
- }
+ })
];
specialArgs = {
inherit inputs self;
modules/nixos/online.nixdiffbeforeafterboth--- a/modules/nixos/online.nix
+++ b/modules/nixos/online.nix
@@ -80,7 +80,8 @@
'';
supportsDryActivation = true;
};
- } // config.system.onlineActivationScripts;
+ }
+ // config.system.onlineActivationScripts;
config.systemd.services = mkIf config.networking.networkmanager.enable {
# If machine is managed by fleet, we should not restart NetworkManager during activation,
modules/secrets-data.nixdiffbeforeafterboth--- a/modules/secrets-data.nix
+++ b/modules/secrets-data.nix
@@ -105,7 +105,7 @@
description = "Age-compatible key";
};
};
- config = {};
+ config = { };
};
in
{
pkgs/default.nixdiffbeforeafterboth--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -1,9 +1,10 @@
{
callPackage,
craneLib,
+ inputs',
}:
{
- fleet = callPackage ./fleet.nix { inherit craneLib; };
+ fleet = callPackage ./fleet.nix { inherit craneLib inputs'; };
fleet-install-secrets = callPackage ./fleet-install-secrets.nix { inherit craneLib; };
fleet-generator-helper = callPackage ./fleet-generator-helper.nix { inherit craneLib; };
}
pkgs/fleet.nixdiffbeforeafterboth--- a/pkgs/fleet.nix
+++ b/pkgs/fleet.nix
@@ -1,16 +1,35 @@
{
+ lib,
craneLib,
installShellFiles,
+ inputs',
+ pkg-config,
+ rustPlatform,
}:
craneLib.buildPackage rec {
pname = "fleet";
-
- src = craneLib.cleanCargoSource (craneLib.path ../.);
+ src = lib.cleanSourceWith {
+ src = ../.;
+ filter =
+ path: type:
+ (lib.hasSuffix "\.cc" path)
+ || (lib.hasSuffix "\.hh" path)
+ || (craneLib.filterCargoSources path type);
+ };
strictDeps = true;
cargoExtraArgs = "--locked -p ${pname}";
- nativeBuildInputs = [ installShellFiles ];
+ buildInputs = [
+ inputs'.nix.packages.nix-expr-c
+ inputs'.nix.packages.nix-flake-c
+ inputs'.nix.packages.nix-fetchers-c
+ ];
+ nativeBuildInputs = [
+ installShellFiles
+ pkg-config
+ rustPlatform.bindgenHook
+ ];
postInstall = ''
for shell in bash fish zsh; do
treefmt.nixdiffbeforeafterboth--- a/treefmt.nix
+++ b/treefmt.nix
@@ -9,4 +9,5 @@
programs.shfmt.enable = true;
programs.rustfmt.enable = true;
programs.taplo.enable = true;
+ programs.clang-format.enable = true;
}