From d0d2d550883b18656195331e549effc8de6e7016 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 31 Dec 2023 13:48:08 +0000 Subject: [PATCH] refactor: temporarly break cross, but greatly cleanup data --- --- a/Cargo.lock +++ b/Cargo.lock @@ -117,9 +117,9 @@ [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -170,9 +170,9 @@ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", @@ -190,37 +190,37 @@ [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9" [[package]] name = "arc-swap" @@ -242,13 +242,13 @@ [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] @@ -387,9 +387,9 @@ [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -482,9 +482,9 @@ [[package]] name = "clap" -version = "4.4.7" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" dependencies = [ "clap_builder", "clap_derive", @@ -492,9 +492,9 @@ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" dependencies = [ "anstream", "anstyle", @@ -514,7 +514,7 @@ "heck", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] @@ -544,9 +544,9 @@ [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cookie-factory" @@ -556,9 +556,9 @@ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "countme" @@ -568,9 +568,9 @@ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -615,7 +615,7 @@ checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.1", + "hashbrown 0.14.3", "lock_api", "once_cell", "parking_lot_core", @@ -633,9 +633,9 @@ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", "serde", @@ -691,7 +691,7 @@ dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] @@ -708,9 +708,9 @@ [[package]] name = "env_logger" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" dependencies = [ "humantime", "is-terminal", @@ -727,12 +727,12 @@ [[package]] name = "errno" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -752,7 +752,7 @@ [[package]] name = "fleet" -version = "0.1.0" +version = "0.2.0" dependencies = [ "abort-on-drop", "age", @@ -827,7 +827,7 @@ "intl-memoizer", "intl_pluralrules", "rustc-hash", - "self_cell", + "self_cell 0.10.3", "smallvec", "unic-langid", ] @@ -858,9 +858,9 @@ [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -873,9 +873,9 @@ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -883,15 +883,15 @@ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -900,38 +900,38 @@ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -968,9 +968,9 @@ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", @@ -989,9 +989,9 @@ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hashbrown" @@ -1001,9 +1001,9 @@ [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -1028,9 +1028,9 @@ [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] @@ -1077,7 +1077,7 @@ "serde", "serde_derive", "thiserror", - "toml 0.8.0", + "toml 0.8.8", "unic-langid", ] @@ -1119,35 +1119,35 @@ "proc-macro2", "quote", "strsim", - "syn 2.0.38", + "syn 2.0.43", "unic-langid", ] [[package]] name = "i18n-embed-impl" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a4d5bff745c9a6e1459c490059281b353a4ab0a4e1e58b3eeeaef71f97d07b" +checksum = "81093c4701672f59416582fe3145676126fd23ba5db910acad0793c1108aaa58" dependencies = [ "find-crate", "i18n-config", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1161,12 +1161,12 @@ [[package]] name = "indexmap" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.1", + "hashbrown 0.14.3", ] [[package]] @@ -1229,13 +1229,13 @@ [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi 0.3.3", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1255,15 +1255,15 @@ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -1279,15 +1279,15 @@ [[package]] name = "libc" -version = "0.2.149" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libmimalloc-sys" @@ -1307,7 +1307,7 @@ dependencies = [ "bitflags 2.4.1", "libc", - "redox_syscall 0.4.1", + "redox_syscall", ] [[package]] @@ -1318,15 +1318,15 @@ [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1355,9 +1355,9 @@ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memoffset" @@ -1394,9 +1394,9 @@ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -1514,9 +1514,9 @@ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -1535,9 +1535,9 @@ [[package]] name = "openssh" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfe68c42d6ee6bd9de175b7a5d9bb86aa99d4e2fa7cf2f2a44e97f60b6d2759" +checksum = "8274f2bf1fc3785406a3ff07c92c15590c00e84efb883da77b671562ca9a6115" dependencies = [ "dirs", "libc", @@ -1595,13 +1595,13 @@ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall", "smallvec", "windows-targets 0.48.5", ] @@ -1659,7 +1659,7 @@ dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] @@ -1721,9 +1721,9 @@ [[package]] name = "portable-atomic" -version = "1.4.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "powerfmt" @@ -1763,9 +1763,9 @@ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] @@ -1849,7 +1849,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", ] [[package]] @@ -1859,15 +1859,6 @@ checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ "rand_core 0.5.1", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", ] [[package]] @@ -1885,7 +1876,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", "libredox", "thiserror", ] @@ -2015,7 +2006,7 @@ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.38", + "syn 2.0.43", "walkdir", ] @@ -2043,22 +2034,22 @@ [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "salsa20" @@ -2116,9 +2107,18 @@ [[package]] name = "self_cell" -version = "0.10.2" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.3", +] + +[[package]] +name = "self_cell" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" +checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" [[package]] name = "serde" @@ -2146,7 +2146,7 @@ dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] @@ -2162,9 +2162,9 @@ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -2182,9 +2182,9 @@ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -2231,9 +2231,9 @@ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "smol_str" @@ -2246,9 +2246,9 @@ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", @@ -2305,9 +2305,9 @@ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2", "quote", @@ -2342,22 +2342,22 @@ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.4.1", + "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] @@ -2380,22 +2380,22 @@ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] @@ -2410,9 +2410,9 @@ [[package]] name = "time" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" dependencies = [ "deranged", "powerfmt", @@ -2429,27 +2429,27 @@ [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" dependencies = [ "time-core", ] [[package]] name = "tinystr" -version = "0.7.2" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8faa444297615a4e020acb64146b0603c9c395c03a97c17fd9028816d3b4d63e" +checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" dependencies = [ "displaydoc", ] [[package]] name = "tokio" -version = "1.33.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -2466,13 +2466,13 @@ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] @@ -2510,9 +2510,9 @@ [[package]] name = "toml" -version = "0.8.0" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ "serde", "serde_spanned", @@ -2522,18 +2522,18 @@ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ "indexmap", "serde", @@ -2561,7 +2561,7 @@ dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] [[package]] @@ -2588,20 +2588,20 @@ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -2632,18 +2632,18 @@ [[package]] name = "unic-langid" -version = "0.9.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +checksum = "238722e6d794ed130f91f4ea33e01fcff4f188d92337a21297892521c72df516" dependencies = [ "unic-langid-impl", ] [[package]] name = "unic-langid-impl" -version = "0.9.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" +checksum = "4bd55a2063fdea4ef1f8633243a7b0524cbeef1905ae04c31a1c9b9775c55bc6" dependencies = [ "serde", "tinystr", @@ -2772,9 +2772,9 @@ [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2782,24 +2782,24 @@ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2807,22 +2807,22 @@ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "winapi" @@ -2856,12 +2856,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] @@ -2883,6 +2883,15 @@ ] [[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] name = "windows-targets" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2913,6 +2922,21 @@ ] [[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2925,6 +2949,12 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2937,6 +2967,12 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2949,6 +2985,12 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2961,6 +3003,12 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2973,6 +3021,12 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2985,6 +3039,12 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2997,10 +3057,16 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] name = "winnow" -version = "0.5.15" +version = "0.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" dependencies = [ "memchr", ] @@ -3024,9 +3090,9 @@ [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -3039,5 +3105,5 @@ dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.43", ] --- a/cmds/fleet/Cargo.toml +++ b/cmds/fleet/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fleet" description = "NixOS configuration management" -version = "0.1.0" +version = "0.2.0" authors = ["Yaroslav Bolyukin "] edition = "2021" --- a/cmds/fleet/src/better_nix_eval.rs +++ b/cmds/fleet/src/better_nix_eval.rs @@ -9,7 +9,7 @@ use std::sync::{Arc, OnceLock}; use anyhow::{anyhow, bail, ensure, Context, Result}; -use better_command::{ClonableHandler, NixHandler, Handler, NoopHandler}; +use better_command::{ClonableHandler, Handler, NixHandler, NoopHandler}; use futures::StreamExt; use itertools::Itertools; use r2d2::{Pool, PooledConnection}; @@ -299,8 +299,11 @@ let mut fexpr = b"builtins.toJSON (".to_vec(); fexpr.extend_from_slice(expr.as_ref()); fexpr.push(b')'); - let v = self.execute_expression_string(fexpr).await?; - Ok(serde_json::from_str(&v)?) + let v = self + .execute_expression_string(fexpr) + .await + .context("string expression")?; + serde_json::from_str(&v).context("json parse") } async fn execute_expression_wrapping( &mut self, @@ -450,15 +453,26 @@ #[macro_export] macro_rules! nix_expr_inner { - (Obj { $($ident:ident: $($val:tt)+),* $(,)? }) => {{ - use $crate::better_nix_eval::NixExprBuilder; + //(@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::field($field), + ); + nix_expr_inner!(@obj($o) $($tt)*); + }}; + (@obj($o:ident) $field:ident: $v:block, $($tt:tt)*) => {{ + $o.obj_key( + NixExprBuilder::string(stringify!($field)), + NixExprBuilder::serialized(&$v), + ); + nix_expr_inner!(@obj($o) $($tt)*); + }}; + (@obj($o:ident)) => {{}}; + (Obj { $($tt:tt)* }) => {{ + use $crate::{better_nix_eval::NixExprBuilder, nix_expr_inner}; let mut out = NixExprBuilder::object(); - $( - out.obj_key( - NixExprBuilder::string(stringify!($ident)), - $crate::nix_expr_inner!($($val)+), - ); - )* + nix_expr_inner!(@obj(out) $($tt)*); out.end_obj(); out }}; @@ -522,6 +536,9 @@ $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)) => {}; ($field:ident $($tt:tt)+) => {{ use $crate::{nix_go, better_nix_eval::Index}; @@ -545,6 +562,7 @@ Apply(String), Expr(NixExprBuilder), ExprApply(NixExprBuilder), + Pipe(NixExprBuilder), } impl Index { pub fn var(v: impl AsRef) -> Self { @@ -582,6 +600,9 @@ Index::ExprApply(e) => { write!(f, "({})", e.out) } + Index::Pipe(e) => { + write!(f, "({})", e.out) + } } } } @@ -604,9 +625,9 @@ session: NixSession, value: Option, } -fn context(full_path: Option<&[Index]>, query: &str) -> String { +fn context(op: &str, full_path: Option<&[Index]>, query: &str) -> String { if let Some(full_path) = &full_path { - format!("full path: {}", PathDisplay(full_path)) + format!("on {op}, full path: {}", PathDisplay(full_path)) } else { format!("query: {query:?}") } @@ -628,7 +649,7 @@ .await .execute_assign(query) .await - .with_context(|| context(None, query))?; + .with_context(|| context("new root", None, query))?; Ok(Self(Arc::new(FieldInner { full_path: None, session, @@ -686,6 +707,12 @@ query.push_str(&index); query = format!("({query})"); } + Index::Pipe(v) => { + let index = Field::new(self.0.session.clone(), &v.out).await?; + used_fields.push(index.clone()); + let index = format!("sess_field_{}", index.0.value.expect("value")); + query = format!("({index} {query})"); + } } } @@ -720,7 +747,7 @@ .await .execute_expression_to_json(&query) .await - .with_context(|| context(self.0.full_path.as_deref(), &query)) + .with_context(|| context("as_json", self.0.full_path.as_deref(), &query)) } pub async fn has_field(&self, name: &str) -> Result { let id = self.0.value.expect("can't list root fields"); @@ -733,7 +760,7 @@ .await .execute_expression_to_json(&query) .await - .with_context(|| context(self.0.full_path.as_deref(), &query)) + .with_context(|| context("has_field", self.0.full_path.as_deref(), &query)) } pub async fn list_fields(&self) -> Result> { let id = self.0.value.expect("can't list root fields"); @@ -745,7 +772,7 @@ .await .execute_expression_to_json(&query) .await - .with_context(|| context(self.0.full_path.as_deref(), &query)) + .with_context(|| context("list field", self.0.full_path.as_deref(), &query)) } pub async fn type_of(&self) -> Result { let id = self.0.value.expect("can't list root fields"); @@ -757,7 +784,11 @@ .await .execute_expression_to_json(&query) .await - .with_context(|| context(self.0.full_path.as_deref(), &query)) + .with_context(|| context("type_of", self.0.full_path.as_deref(), &query)) + } + pub async fn import(&self) -> Result { + let import = Self::new(self.0.session.clone(), "import").await?; + Ok(nix_go!(self | import)) } pub async fn build(&self) -> Result> { let id = self.0.value.expect("can't use build on not-value"); @@ -773,7 +804,7 @@ ensure!( !vid.is_empty(), "build failed: {}", - context(self.0.full_path.as_deref(), &query), + context("build", self.0.full_path.as_deref(), &query), ); let Some(vid) = vid.strip_prefix("This derivation produced the following outputs:\n") else { --- a/cmds/fleet/src/cmds/build_systems.rs +++ b/cmds/fleet/src/cmds/build_systems.rs @@ -53,7 +53,7 @@ fn build_attr(&self) -> String { match self { PackageAction::SdImage => "sdImage".to_owned(), - PackageAction::InstallationCd => "installationCd".to_owned(), + PackageAction::InstallationCd => "isoImage".to_owned(), } } } @@ -178,7 +178,7 @@ if !build.disable_rollback { let _span = info_span!("preparing").entered(); info!("preparing for rollback"); - let generation = get_current_generation(&host).await?; + let generation = get_current_generation(host).await?; info!( "rollback target would be {} {}", generation.id, generation.datetime @@ -234,7 +234,7 @@ let mut switch_script = built.clone(); switch_script.push("bin"); switch_script.push("switch-to-configuration"); - let mut cmd = host.cmd(switch_script).await?; + let mut cmd = host.cmd(switch_script).in_current_span().await?; cmd.arg(action.name()); if let Err(e) = cmd.sudo().run().in_current_span().await { error!("failed to activate: {e}"); @@ -285,11 +285,9 @@ info!("building"); let host = config.host(&host).await?; let action = Action::from(self.subcommand.clone()); - let fleet_field = &config.fleet_field; + let fleet_config = &config.config_field; let drv = nix_go!( - fleet_field.buildSystems(Obj { - localSystem: { config.local_system.clone() } - })[{ action.build_attr() }][{ &host.name }] + fleet_config.hosts[{ &host.name }].nixosSystem.config.system.build[{ action.build_attr() }] ); let outputs = drv.build().await.map_err(|e| { if action.build_attr() == "sdImage" { --- a/cmds/fleet/src/cmds/info.rs +++ b/cmds/fleet/src/cmds/info.rs @@ -37,9 +37,9 @@ InfoCmd::ListHosts { ref tagged } => { 'host: for host in config.list_hosts().await? { if !tagged.is_empty() { - let fleet_field = &config.fleet_field; + let config = &config.config_unchecked_field; let tags: Vec = - nix_go_json!(fleet_field.configuredSystems[{ host.name }].config.tags); + nix_go_json!(config.hosts[{ host.name }].nixosSystem.config.tags); for tag in tagged { if !tags.contains(tag) { continue 'host; --- a/cmds/fleet/src/cmds/secrets/mod.rs +++ b/cmds/fleet/src/cmds/secrets/mod.rs @@ -10,14 +10,15 @@ use futures::StreamExt; use itertools::Itertools; use owo_colors::OwoColorize; +use serde::Deserialize; use std::{ - collections::HashSet, + collections::{BTreeSet, HashSet}, io::{self, Cursor, Read}, path::PathBuf, }; use tabled::{Table, Tabled}; use tokio::fs::read_to_string; -use tracing::{info, info_span, warn}; +use tracing::{error, info, info_span, warn, Instrument}; #[derive(Parser)] pub enum Secret { @@ -92,77 +93,182 @@ List {}, } -async fn generate_shared( +#[tracing::instrument(skip(config, secret, field, prefer_identities))] +async fn update_owner_set( + secret_name: &str, config: &Config, - display_name: &str, - secret: Field, + mut secret: FleetSharedSecret, + field: Field, + updated_set: &[String], + prefer_identities: &[String], ) -> Result { - Ok(if secret.has_field("generateImpure").await? { - let config_field = &config.config_unchecked_field; - let generate = nix_go!(secret.generateImpure); - let owners: Vec = nix_go_json!(secret.expectedOwners); + let original_set = secret.owners.clone(); - let on: String = nix_go_json!(generate.on); - let call_package = nix_go!( - config_field.buildableSystems(Obj { - localSystem: { config.local_system.clone() } - })[{ on }] - .config - .nixpkgs - .resolvedPkgs - .callPackage - ); + let set = original_set.iter().collect::>(); + let expected_set = updated_set.iter().collect::>(); - let host = config.host(&on).await?; + if set == expected_set { + info!("no need to update owner list, it is already correct"); + return Ok(secret); + } - let generator = nix_go!(call_package(generate.generator)(Obj {})); - let generator = generator.build().await?; - let generator = generator - .get("out") - .ok_or_else(|| anyhow!("missing generateImpure out"))?; - let generator = host.remote_derivation(generator).await?; + let should_regenerate = if set.difference(&expected_set).next().is_some() { + // TODO: Remove this warning for revokable secrets. + warn!("host was removed from secret owners, but until this host rebuild, the secret will still be stored on it."); + nix_go_json!(field.regenerateOnOwnerRemoved) + } else if expected_set.difference(&set).next().is_some() { + nix_go_json!(field.regenerateOnOwnerAdded) + } else { + false + }; + + if should_regenerate { + info!("secret is owner-dependent, will regenerate"); + let generated = generate_shared(config, secret_name, field, updated_set.to_vec()).await?; + Ok(generated) + } else { + let identity_holder = if !prefer_identities.is_empty() { + prefer_identities + .iter() + .find(|i| original_set.iter().any(|s| s == *i)) + } else { + secret.owners.first() + }; + let Some(identity_holder) = identity_holder else { + bail!("no available holder found"); + }; - let mut recipients = String::new(); - for owner in &owners { - let key = config.key(owner).await?; - recipients.push_str(&format!("-r \"{key}\" ")); + if let Some(data) = secret.secret.secret { + let host = config.host(identity_holder).await?; + let encrypted = host.reencrypt(data, updated_set.to_vec()).await?; + secret.secret.secret = Some(encrypted); } - recipients.push_str("-e"); - let out = host.mktemp_dir().await?; + secret.owners = updated_set.to_vec(); + Ok(secret) + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +enum GeneratorKind { + Impure, +} + +async fn generate_impure( + config: &Config, + _display_name: &str, + secret: Field, + default_generator: Field, + owners: &[String], +) -> Result { + let config_field = &config.config_unchecked_field; + let generator = nix_go!(secret.generator); + + let on: String = nix_go_json!(default_generator.impureOn); + let call_package = nix_go!( + config_field.buildableSystems(Obj { + localSystem: { config.local_system.clone() }, + })[{ on }] + .config + .nixpkgs + .resolvedPkgs + .callPackage + ); + + let host = config.host(&on).await?; + + let generator = nix_go!(call_package(generator)(Obj {})); + let generator = generator.build().await?; + let generator = generator + .get("out") + .ok_or_else(|| anyhow!("missing generateImpure out"))?; + let generator = host.remote_derivation(generator).await?; + + let mut recipients = String::new(); + for owner in owners { + let key = config.key(owner).await?; + recipients.push_str(&format!("-r \"{key}\" ")); + } + recipients.push_str("-e"); - let mut gen = host.cmd(generator).await?; - gen.env("rageArgs", recipients).env("out", &out); - gen.run().await?; + let out = host.mktemp_dir().await?; - { - let marker = host.read_file_text(format!("{out}/marker")).await?; - ensure!(marker == "SUCCESS", "generation not succeeded"); - } + let mut gen = host.cmd(generator).await?; + gen.env("rageArgs", recipients).env("out", &out); + gen.run().await.context("impure generator")?; - let public = host.read_file_text(format!("{out}/public")).await.ok(); - let secret = host.read_file_bin(format!("{out}/secret")).await.ok(); - if let Some(secret) = &secret { - ensure!( - age::Decryptor::new(Cursor::new(&secret)).is_ok(), - "builder produced non-encrypted value as secret, this is highly insecure" - ); + { + let marker = host.read_file_text(format!("{out}/marker")).await?; + ensure!(marker == "SUCCESS", "generation not succeeded"); + } + + let public = host.read_file_text(format!("{out}/public")).await.ok(); + let secret = host.read_file_bin(format!("{out}/secret")).await.ok(); + if let Some(secret) = &secret { + ensure!( + age::Decryptor::new(Cursor::new(&secret)).is_ok(), + "builder produced non-encrypted value as secret, this is highly insecure, and not allowed." + ); + } + + let created_at = host.read_file_value(format!("{out}/created_at")).await?; + let expires_at = host.read_file_value(format!("{out}/expires_at")).await.ok(); + + Ok(FleetSecret { + created_at, + expires_at, + public, + secret: secret.map(SecretData), + }) +} +async fn generate( + config: &Config, + display_name: &str, + secret: Field, + owners: &[String], +) -> Result { + 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" { + bail!("secret has no generator defined, can't automatically generate it."); } + if gen_ty != "lambda" { + bail!("generator should be lambda, got {gen_ty}"); + } + } + let default_pkgs = &config.default_pkgs; + let default_call_package = nix_go!(default_pkgs.callPackage); + // Generators provide additional information in passthru, to access + // passthru we should call generator, but information about where this generator is supposed to build + // is located in passthru... Thus evaluating generator on host. + // + // Maybe it is also possible to do some magic with __functor? + // + // I don't want to make modules always responsible for additional secret data anyway, + // so it should be in derivation, and not in the secret data itself. + let default_generator = nix_go!(default_call_package(generator)(Obj {})); - let created_at = host.read_file_value(format!("{out}/created_at")).await?; - let expires_at = host.read_file_value(format!("{out}/expires_at")).await.ok(); + let kind: GeneratorKind = nix_go_json!(default_generator.generatorKind); - FleetSharedSecret { - owners, - secret: FleetSecret { - created_at, - expires_at, - public, - secret: secret.map(SecretData), - }, + match kind { + GeneratorKind::Impure => { + generate_impure(config, display_name, secret, default_generator, owners).await } - } else { - bail!("no generator defined for {display_name}") + } +} +async fn generate_shared( + config: &Config, + display_name: &str, + secret: Field, + expected_owners: Vec, +) -> Result { + // let owners: Vec = nix_go_json!(secret.expectedOwners); + Ok(FleetSharedSecret { + secret: generate(config, display_name, secret, &expected_owners).await?, + owners: expected_owners, }) } @@ -270,9 +376,7 @@ machines = shared.owners; } - let recipients = config - .recipients(&machines.iter().map(String::as_str).collect_vec()) - .await?; + let recipients = config.recipients(machines.clone()).await?; let secret = { let mut input = vec![]; @@ -362,7 +466,7 @@ remove_machines, prefer_identities, } => { - let mut secret = config.shared_secret(&name)?; + let secret = config.shared_secret(&name)?; if secret.secret.secret.is_none() { bail!("no secret"); } @@ -378,61 +482,84 @@ if target_machines.is_empty() { info!("no machines left for secret, removing it"); config.remove_shared(&name); - return Ok(()); - } - - if target_machines == initial_machines { - warn!("secret owners are already correct"); return Ok(()); } - let identity_holder = if !prefer_identities.is_empty() { - prefer_identities - .iter() - .find(|i| initial_machines.iter().any(|s| s == *i)) - } else { - secret.owners.first() - }; - let Some(identity_holder) = identity_holder else { - bail!("no available holder found"); - }; - let target_recipients = futures::stream::iter(&target_machines) - .then(|m| async { config.key(m).await }) - .collect::>() - .await; - let target_recipients = - target_recipients.into_iter().collect::>>()?; + let config_field = &config.config_unchecked_field; + let config_field = nix_go!(config_field.configUnchecked); + let field = nix_go!(config_field.sharedSecrets[{ name }]); - if let Some(data) = secret.secret.secret { - let host = config.host(&identity_holder).await?; - let encrypted = host.reencrypt(data, target_recipients).await?; - secret.secret.secret = Some(encrypted); - } - - secret.owners = target_machines; - config.replace_shared(name, secret); + let updated = update_owner_set( + &name, + config, + secret, + field, + &target_machines, + &prefer_identities, + ) + .await?; + config.replace_shared(name, updated); } Secret::Regenerate { prefer_identities } => { + info!("checking for secrets to regenerate"); { + let _span = info_span!("shared").entered(); let expected_shared_set = config .list_configured_shared() .await? .into_iter() .collect::>(); let shared_set = config.list_shared().into_iter().collect::>(); - for removed in expected_shared_set.difference(&shared_set) { - info!("generating secret: {removed}"); + for missing in expected_shared_set.difference(&shared_set) { let config_field = &config.config_unchecked_field; let config_field = nix_go!(config_field.configUnchecked); - let secret = nix_go!(config_field.sharedSecrets[{ removed }]); - let shared = generate_shared(config, removed, secret).await?; - config.replace_shared(removed.to_string(), shared) + let secret = nix_go!(config_field.sharedSecrets[{ missing }]); + let expected_owners: Option> = + nix_go_json!(secret.expectedOwners); + let Some(expected_owners) = expected_owners else { + // TODO: Might still need to regenerate + continue; + }; + info!("generating secret: {missing}"); + let shared = generate_shared(config, missing, secret, expected_owners) + .in_current_span() + .await?; + config.replace_shared(missing.to_string(), shared) + } + } + for host in config.list_hosts().await? { + let _span = info_span!("host", host = host.name).entered(); + let expected_set = host + .list_configured_secrets() + .in_current_span() + .await? + .into_iter() + .collect::>(); + let stored_set = config + .list_secrets(&host.name) + .into_iter() + .collect::>(); + for missing in expected_set.difference(&stored_set) { + info!("generating secret: {missing}"); + let secret = host.secret_field(missing).in_current_span().await?; + let generated = + match generate(config, missing, secret, &[host.name.clone()]) + .in_current_span() + .await + { + Ok(v) => v, + Err(e) => { + error!("{e}"); + continue; + } + }; + config.insert_secret(&host.name, missing.to_string(), generated) } } let mut to_remove = Vec::new(); for name in &config.list_shared() { info!("updating secret: {name}"); - let mut data = config.shared_secret(name)?; + let data = config.shared_secret(name)?; let config_field = &config.config_unchecked_field; let config_field = nix_go!(config_field.configUnchecked); let expected_owners: Vec = @@ -442,55 +569,20 @@ to_remove.push(name.to_string()); continue; } - let set = data.owners.iter().collect::>(); - let expected_set = expected_owners.iter().collect::>(); - let should_remove = set.difference(&expected_set).next().is_some(); - if set == expected_set { - info!("secret data is ok"); - continue; - } let secret = nix_go!(config_field.sharedSecrets[{ name }]); - let owner_dependent: bool = nix_go_json!(secret.ownerDependent); - let regenerate_on_remove: bool = nix_go_json!(secret.regenerateOnOwnerRemoved); - #[allow(clippy::nonminimal_bool)] - if !owner_dependent && !(should_remove && regenerate_on_remove) { - warn!("reencrypting secret '{name}' for new owner set"); - // TODO: force regeneration - if should_remove { - warn!("secret will not be regenerated for removed machines, and until host rebuild, they will still possess the ability to decode secret"); - } - - let identity_holder = if !prefer_identities.is_empty() { - prefer_identities - .iter() - .find(|i| data.owners.iter().any(|s| s == *i)) - } else { - data.owners.first() - }; - let Some(identity_holder) = identity_holder else { - bail!("no available holder found"); - }; - - let target_recipients = futures::stream::iter(&expected_owners) - .then(|m| async { config.key(m).await }) - .collect::>() - .await; - let target_recipients = - target_recipients.into_iter().collect::>>()?; - - if let Some(secret) = data.secret.secret { - let host = config.host(identity_holder).await?; - let encrypted = host.reencrypt(secret, target_recipients).await?; - - data.secret.secret = Some(encrypted); - } - data.owners = expected_owners; - config.replace_shared(name.to_owned(), data); - } else { - let shared = generate_shared(config, name, secret).await?; - config.replace_shared(name.to_owned(), shared) - } + config.replace_shared( + name.to_owned(), + update_owner_set( + &name, + config, + data, + secret, + &expected_owners, + &prefer_identities, + ) + .await?, + ); } for k in to_remove { config.remove_shared(&k); --- a/cmds/fleet/src/command.rs +++ b/cmds/fleet/src/command.rs @@ -1,5 +1,3 @@ -use std::thread::sleep; -use std::time::Duration; use std::{ffi::OsStr, pin, process::Stdio, sync::Arc, task::Poll}; use anyhow::{anyhow, Result}; @@ -9,7 +7,7 @@ use openssh::{OverSsh, OwningCommand, Session}; use tokio::{io::AsyncRead, process::Command, select}; use tokio_util::codec::{BytesCodec, FramedRead, LinesCodec}; -use tracing::{info, debug}; +use tracing::debug; fn escape_bash(input: &str, out: &mut String) { const TO_ESCAPE: &str = "$ !\"#&'()*,;<>?[\\]^`{|}"; @@ -162,6 +160,10 @@ self } pub fn sudo(mut self) -> Self { + // TODO: Multiple escalation strategies. + // Maybe escalation should be moved to ConfigHost, to also support cases + // when there is no sudo on remote machine, but instead we can reconnect + // as root using ssh? if std::env::var_os("NO_SUDO").is_some() { let mut out = Self::new("su"); out.ssh_session = self.ssh_session.take(); @@ -267,7 +269,7 @@ ) -> Result>> { cmd.stderr(Stdio::piped()); cmd.stdout(Stdio::piped()); - debug!("running command {cmd:?} on local"); + debug!("running command {str:?} on local"); let mut child = cmd.spawn()?; let mut stderr = child.stderr.take().unwrap(); let stdout = child.stdout.take().unwrap(); @@ -328,7 +330,7 @@ err_handler: &mut dyn Handler, mut out_handler: Option<&mut dyn Handler>, ) -> Result>> { - debug!("running command {cmd:?} over ssh"); + debug!("running command {str:?} over ssh"); cmd.stderr(openssh::Stdio::piped()); cmd.stdout(openssh::Stdio::piped()); let mut child = cmd.spawn().await?; --- a/cmds/fleet/src/host.rs +++ b/cmds/fleet/src/host.rs @@ -14,6 +14,7 @@ use openssh::SessionBuilder; use serde::de::DeserializeOwned; use tempfile::NamedTempFile; +use tracing::instrument; use crate::{ better_nix_eval::{Field, NixSessionPool}, @@ -28,12 +29,13 @@ pub opts: FleetOpts, pub data: Mutex, pub nix_args: Vec, - /// fleetConfigurations.. - pub fleet_field: Field, - /// fleet_config.configUnchecked + /// fleet_config.config pub config_field: Field, - /// fleet_config.unchecked + /// fleet_config.unchecked.config pub config_unchecked_field: Field, + + /// import nixpkgs {system = local}; + pub default_pkgs: Field, } #[derive(Clone)] @@ -48,9 +50,12 @@ } pub struct ConfigHost { + config: Config, pub name: String, pub local: bool, pub session: OnceLock>, + + pub nixos_config: Field, } impl ConfigHost { async fn open_session(&self) -> Result> { @@ -64,7 +69,7 @@ let session = session .connect(&self.name) .await - .map_err(|e| anyhow!("ssh error: {e}"))?; + .map_err(|e| anyhow!("ssh error while connecting to {}: {e}", self.name))?; let session = Arc::new(session); self.session.set(session.clone()).expect("TOCTOU happened"); Ok(session) @@ -119,7 +124,8 @@ let mut cmd = self.cmd("fleet-install-secrets").await?; cmd.arg("reencrypt").eqarg("--secret", data.encode_z85()); for target in targets { - cmd.eqarg("--targets", target); + let key = self.config.key(&target).await?; + cmd.eqarg("--targets", key); } let encoded = cmd .sudo() @@ -139,7 +145,7 @@ .arg("--substitute-on-destination") .comparg("--to", format!("ssh-ng://{}", self.name)) .arg(path); - nix.run_nix().await?; + nix.run_nix().await.context("nix copy")?; Ok(path.to_owned()) } pub async fn systemctl_stop(&self, name: &str) -> Result<()> { @@ -161,6 +167,25 @@ } cmd.run().await } + + pub async fn list_configured_secrets(&self) -> Result> { + let nixos = &self.nixos_config; + let secrets = nix_go!(nixos.secrets); + let mut out = Vec::new(); + for name in secrets.list_fields().await? { + let secret = nix_go!(secrets[{ name }]); + let is_shared: bool = nix_go_json!(secret.shared); + if is_shared { + continue; + } + out.push(name); + } + Ok(out) + } + pub async fn secret_field(&self, name: &str) -> Result { + let nixos = &self.nixos_config; + Ok(nix_go!(nixos.secrets[{ name }])) + } } impl Config { @@ -178,28 +203,28 @@ } pub async fn host(&self, name: &str) -> Result { + let config = &self.config_unchecked_field; + let nixos_config = nix_go!(config.configuredSystems[{ name }].config); Ok(ConfigHost { + config: self.clone(), name: name.to_owned(), local: self.is_local(name), session: OnceLock::new(), + nixos_config, }) } pub async fn list_hosts(&self) -> Result> { - let fleet_field = &self.fleet_field; - let names = nix_go!(fleet_field.configuredHosts).list_fields().await?; + let config = &self.config_unchecked_field; + let names = nix_go!(config.hosts).list_fields().await?; let mut out = vec![]; for name in names { - out.push(ConfigHost { - local: self.is_local(&name), - name, - session: OnceLock::new(), - }) + out.push(self.host(&name).await?); } Ok(out) } pub async fn system_config(&self, host: &str) -> Result { - let fleet_field = &self.fleet_field; - Ok(nix_go!(fleet_field.configuredSystems[{ host }].config)) + let fleet_field = &self.config_unchecked_field; + Ok(nix_go!(fleet_field.hosts[{ host }].nixosSystem.config)) } pub(super) fn data(&self) -> MutexGuard { @@ -233,6 +258,14 @@ data.shared_secrets.remove(secret); } + pub fn list_secrets(&self, host: &str) -> Vec { + let data = self.data(); + let Some(secrets) = data.host_secrets.get(host) else { + return Vec::new(); + }; + secrets.keys().cloned().collect() + } + pub fn has_secret(&self, host: &str, secret: &str) -> bool { let data = self.data(); let Some(host_secrets) = data.host_secrets.get(host) else { @@ -319,18 +352,27 @@ let pool = NixSessionPool::new(directory.as_os_str().to_owned(), nix_args.clone()).await?; let root_field = pool.get().await?; + let builtins_field = Field::field(root_field.clone(), "builtins").await?; if self.local_system == "detect" { - let builtins_field = Field::field(root_field.clone(), "builtins").await?; self.local_system = nix_go_json!(builtins_field.currentSystem); } let local_system = self.local_system.clone(); let fleet_root = Field::field(root_field, "fleetConfigurations").await?; - let fleet_field = nix_go!(fleet_root.default); - let config_field = nix_go!(fleet_field.configUnchecked); - let config_unchecked_field = nix_go!(fleet_field.unchecked); + let config_field = nix_go!(fleet_field.config); + let config_unchecked_field = nix_go!(fleet_field.unchecked.config); + + let import = nix_go!(builtins_field.import); + let overlays = nix_go!(fleet_field.overlays); + let nixpkgs = nix_go!(fleet_field.nixpkgs | import); + + let default_pkgs = nix_go!(nixpkgs(Obj { + overlays, + system: { self.local_system.clone() }, + })); + let mut fleet_data_path = directory.clone(); fleet_data_path.push("fleet.nix"); let bytes = std::fs::read_to_string(fleet_data_path)?; @@ -342,9 +384,9 @@ data, local_system, nix_args, - fleet_field, config_field, config_unchecked_field, + default_pkgs, }))) } } --- a/cmds/fleet/src/keys.rs +++ b/cmds/fleet/src/keys.rs @@ -43,9 +43,9 @@ age::ssh::Recipient::from_str(&key).map_err(|e| anyhow!("parse recipient error: {:?}", e)) } - pub async fn recipients(&self, hosts: &[&str]) -> Result> { + pub async fn recipients(&self, hosts: Vec) -> Result> { futures::stream::iter(hosts.iter()) - .then(|m| self.recipient(m)) + .then(|m| self.recipient(m.as_ref())) .try_collect::>() .await } --- a/cmds/fleet/src/main.rs +++ b/cmds/fleet/src/main.rs @@ -12,6 +12,8 @@ mod fleetdata; use std::ffi::OsString; +use std::io::{stderr, stdout, Write}; +use std::process::exit; use std::time::Duration; use anyhow::{bail, Result}; @@ -24,7 +26,7 @@ use host::{Config, FleetOpts}; use human_repr::HumanCount; use indicatif::{ProgressState, ProgressStyle}; -use tracing::info; +use tracing::{error, info}; use tracing::{info_span, Instrument}; use tracing_indicatif::IndicatifLayer; use tracing_subscriber::{prelude::*, EnvFilter}; @@ -81,7 +83,7 @@ } #[derive(Parser)] -#[clap(version = "1.0", author)] +#[clap(version, author)] struct RootOpts { #[clap(flatten)] fleet_opts: FleetOpts, @@ -136,13 +138,13 @@ ), ); - let filter = EnvFilter::from_default_env(); + let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); tracing_subscriber::registry() .with( tracing_subscriber::fmt::layer() .without_time() - .with_target(false) + .with_target(true) .with_writer(indicatif_layer.get_stderr_writer()) .with_filter(filter), // .withou, ) @@ -151,8 +153,15 @@ } #[tokio::main] -async fn main() -> Result<()> { +async fn main() { setup_logging(); + if let Err(e) = main_real().await { + error!("{e:#}"); + exit(1); + } +} + +async fn main_real() -> Result<()> { let _ = better_nix_eval::TOKIO_RUNTIME.set(tokio::runtime::Handle::current()); let nix_args = std::env::var_os("NIX_ARGS") --- a/flake.lock +++ b/flake.lock @@ -38,11 +38,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1703705939, - "narHash": "sha256-9s2Ep3NyRDj9HUgfv2TQUwQEanRUAmeXkvKIr/o1XbY=", + "lastModified": 1703974965, + "narHash": "sha256-dvZjLuAcLnv25bqStTL2ZICC5YSs8aynF5amRM+I6UM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "1ada32da4ba24d7310653c9ac54888bee463f455", + "rev": "9f434bd436e2bb5615827469ed651e30c26daada", "type": "github" }, "original": { @@ -67,11 +67,11 @@ ] }, "locked": { - "lastModified": 1703643208, - "narHash": "sha256-UL4KO8JxnD5rOycwHqBAf84lExF1/VnYMDC7b/wpPDU=", + "lastModified": 1703902408, + "narHash": "sha256-qXdWvu+tlgNjeoz8yQMRKSom6QyRROfgpmeOhwbujqw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "ce117f3e0de8262be8cd324ee6357775228687cf", + "rev": "319f57cd2c34348c55970a4bf2b35afe82088681", "type": "github" }, "original": { --- a/flake.nix +++ b/flake.nix @@ -29,7 +29,7 @@ llvmPkgs = pkgs.buildPackages.llvmPackages_11; rust = (pkgs.rustChannelOf { - date = "2023-12-26"; + date = "2023-12-29"; channel = "nightly"; }) .default --- a/lib/default.nix +++ b/lib/default.nix @@ -1,18 +1,31 @@ {flake-utils}: { fleetConfiguration = { + # TODO: Provide by fleet, instead of requesting user to provide it. + # This is not good that user needs to provide it, as it becomes a flake data, and fleet arbitrarily rewriting it + # always dirnets the flake. Instead, fleetConfiguration should return function, parameters of which should be filled + # by fleet itself, which is possible since fleet moving to nix repl execution. data, nixpkgs, + overlays ? [], hosts, - ... - } @ allConfig: let + modules, + globalModules ? [], + }: let hostNames = nixpkgs.lib.attrNames hosts; - config = builtins.removeAttrs allConfig ["nixpkgs" "data"]; fleetLib = import ./fleetLib.nix { inherit nixpkgs hostNames; }; in let root = nixpkgs.lib.evalModules { - modules = (import ../modules/fleet/_modules.nix) ++ [config data]; + modules = + (import ../modules/fleet/_modules.nix) + ++ [ + data + ({...}: { + inherit globalModules hosts; + }) + ] + ++ modules; specialArgs = { inherit nixpkgs fleetLib; }; @@ -25,84 +38,20 @@ withData = { root, data, - }: rec { + }: { configuredHosts = root.config.hosts; - configuredUncheckedHosts = root.config.hosts; - configuredSystems = configuredSystemsWithExtraModules []; - configuredSystemsWithExtraModules = extraModules: - nixpkgs.lib.listToAttrs ( - map - ( - name: { - inherit name; - value = nixpkgs.lib.nixosSystem { - system = configuredHosts.${name}.system; - modules = configuredHosts.${name}.modules ++ extraModules; - specialArgs = { - inherit fleetLib; - fleet = fleetLib.hostsToAttrs (host: configuredSystems.${host}.config); - }; - }; - } - ) - (builtins.attrNames root.config.hosts) - ); - buildableSystems = {localSystem}: let - buildConfigurationModule = {config, ...}: { - # Equivalent to nixpkgs.localSystem - # nixpkgs.system = localSystem; - nixpkgs.buildPlatform.system = localSystem; - }; - in - configuredSystemsWithExtraModules [ - buildConfigurationModule - ]; - buildSystems = {localSystem}: let - buildConfigurationModule = {config, ...}: { - # Equivalent to nixpkgs.localSystem - # nixpkgs.system = localSystem; - nixpkgs.buildPlatform.system = localSystem; - }; - in { - toplevel = builtins.mapAttrs (_name: value: value.config.system.build.toplevel) (configuredSystemsWithExtraModules [ - buildConfigurationModule - ({...}: { - buildTarget = "toplevel"; - }) - ]); - sdImage = builtins.mapAttrs (_name: value: value.config.system.build.sdImage) (configuredSystemsWithExtraModules [ - buildConfigurationModule - #(nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64-installer.nix") - ({...}: { - buildTarget = "sd-image"; - }) - ]); - installationCd = builtins.mapAttrs (_name: value: value.config.system.build.isoImage) (configuredSystemsWithExtraModules [ - buildConfigurationModule - (nixpkgs + "/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix") - ({lib, ...}: { - buildTarget = "installation-cd"; - # Needed for https://github.com/NixOS/nixpkgs/issues/58959 - boot.supportedFilesystems = lib.mkForce ["btrfs" "reiserfs" "vfat" "f2fs" "xfs" "ntfs" "cifs"]; - }) - ]); - }; - configUnchecked = root.config; + config = root.config; }; defaultData = withData { inherit data; root = checkedRoot; }; uncheckedData = withData {inherit data root;}; - in rec { - inherit (defaultData) configuredHosts configuredSystems buildSystems configUnchecked buildableSystems; + in { + inherit nixpkgs overlays; + inherit (defaultData) configuredHosts configuredSystems config buildableSystems; unchecked = { - inherit (uncheckedData) configuredHosts configuredSystems buildSystems configUnchecked buildableSystems; - }; - injectData = data: let - injectedData = withData data; - in { - inherit (injectedData) configuredHosts configuredSystems buildSystems configUnchecked; + inherit (uncheckedData) configuredHosts configuredSystems config buildableSystems; }; }; } --- a/modules/fleet/meta.nix +++ b/modules/fleet/meta.nix @@ -1,49 +1,82 @@ -{ lib, fleetLib, config, ... }: with lib; -let - host = with types; { - options = { - modules = mkOption { - type = listOf (mkOptionType { - name = "submodule"; - inherit (submodule { }) check; - merge = lib.options.mergeOneOption; - description = "Nixos modules"; - }); - description = "List of nixos modules"; - default = [ ]; - }; - system = mkOption { - type = str; - description = "Type of system"; +{ + lib, + fleetLib, + config, + nixpkgs, + ... +}: +with lib; let + hostModule = with types; + {...} @ hostConfig: { + options = { + modules = mkOption { + type = listOf (mkOptionType { + name = "submodule"; + inherit (submodule {}) check; + merge = lib.options.mergeOneOption; + description = "Nixos modules"; + }); + description = "List of nixos modules"; + default = []; + }; + system = mkOption { + type = str; + description = "Type of system"; + }; + encryptionKey = mkOption { + type = str; + description = "Encryption key"; + }; + nixosSystem = mkOption { + type = unspecified; + description = "Nixos configuration"; + }; }; - encryptionKey = mkOption { - type = str; - description = "Encryption key"; + config.nixosSystem = nixpkgs.lib.nixosSystem { + inherit (hostConfig.config) system modules; + specialArgs = { + inherit fleetLib; + fleet = fleetLib.hostsToAttrs (host: config.hosts.${host}.nixosSystem.config); + }; }; }; + overlayType = mkOptionType { + name = "nixpkgs-overlay"; + description = "nixpkgs overlay"; + check = lib.isFunction; + merge = lib.mergeOneOption; }; -in -{ +in { options = with types; { hosts = mkOption { - type = attrsOf (submodule host); - default = { }; + type = attrsOf (submodule hostModule); + default = {}; description = "Configurations of individual hosts"; }; globalModules = mkOption { type = listOf (mkOptionType { name = "submodule"; - inherit (submodule { }) check; + inherit (submodule {}) check; merge = lib.options.mergeOneOption; description = "Nixos modules"; }); description = "Modules, which should be added to every system"; - default = [ ]; + default = []; }; + overlays = mkOption { + default = []; + type = listOf overlayType; + }; }; config = { hosts = fleetLib.hostsToAttrs (host: { - modules = config.globalModules; + modules = + config.globalModules + ++ [ + ({...}: { + nixpkgs.overlays = config.overlays; + }) + ]; }); globalModules = import ../../nixos/modules/module-list.nix; }; --- a/modules/fleet/secrets.nix +++ b/modules/fleet/secrets.nix @@ -1,69 +1,48 @@ { lib, fleetLib, config, ... }: with lib; with fleetLib; let - sharedSecret = with types; { + sharedSecret = with types; ({config, ...}: { options = { expectedOwners = mkOption { - type = listOf str; + type = nullOr (listOf str); description = '' - List of hosts to encrypt secret for + List of hosts to encrypt secret for. null if managed by user (= via owners field from fleet.nix) Secrets would be decrypted and stored to /run/secrets/$\{name} on owners ''; - default = [ ]; }; - ownerDependent = mkOption { + # TODO: Aren't those options may be just desugared to data/expectedData? + regenerateOnOwnerAdded = mkOption { type = bool; - description = "Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted"; + description = '' + Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted. + + You want to have this option set to true, when this secret contains some reference to its owners, i.e x509 SANs. + ''; }; - generateImpure = mkOption { - type = unspecified; + regenerateOnOwnerRemoved = mkOption { + default = config.regenerateOnOwnerAdded; + type = bool; + description = '' + Should this secret be removed on owner removal, or it may be just reencrypted + + Most probably its value should be equal to regenerateOnOwnerAdded, override only if you know what are you doing. + Contrary to regenerateOnOwnerAdded, you may want to set this option to false, when host permissions are revoked + in some other way than by this secret ownership, I.e by firewall/etc. + ''; }; generator = mkOption { - type = nullOr (submodule { - packages = mkOption { - type = attrsOf package; - description = '' - Derivation to execute for shared secret generation (key = system). - This derivation should produce directory, with exactly two files: - - publicData - - encryptedSecretData - - If null - secret value may only be created manually. - ''; - }; - expectedData = mkOption { - type = types.unspecified; - description = "Data expected to be used for secret generation, if doesn't match specified - secret should be regenerated"; - }; - dependencies = mkOption { - type = listOf str; - description = '' - List of secrets, on which this secret depends. - - During generation, generator command will be ran on host, which already has specified secrets generated. - ''; - default = []; - }; - data = mkOption { - type = types.unspecified; - description = "Data used for secret generation. Imported from fleet.nix"; - default = null; - internal = true; - }; - }); - default = null; - }; - expireIn = mkOption { - type = nullOr int; - description = "Time in hours, in which this secret should be regenerated"; + type = nullOr unspecified; + description = "Derivation to evaluate for secret generation"; default = null; }; createdAt = mkOption { type = nullOr str; + description = "When this secret was (re)generated"; default = null; }; expiresAt = mkOption { type = nullOr str; + description = "On which date this secret will expire, someone should regenerate this secret before it expires."; default = null; }; @@ -78,6 +57,9 @@ ''; default = [ ]; }; + # TODO: Make secret generator generate arbitrary number of secret/public parts? + # Make it generate a folder, where all files except suffixed by .enc are public, and the rest are secret? + # How should modules refer to those files then? public = mkOption { type = nullOr str; description = "Secret public data. Imported from fleet.nix"; @@ -90,7 +72,7 @@ internal = true; }; }; - }; + }); hostSecret = with types; { options = { createdAt = mkOption { @@ -132,7 +114,7 @@ config = { assertions = mapAttrsToList (name: secret: { - assertion = builtins.sort (a: b: a < b) secret.owners == builtins.sort (a: b: a < b) secret.expectedOwners; + assertion = secret.expectedOwners == null || builtins.sort (a: b: a < b) secret.owners == builtins.sort (a: b: a < b) secret.expectedOwners; message = "Shared secret ${name} is expected to be encrypted for ${builtins.toJSON secret.expectedOwners}, but it is encrypted for ${builtins.toJSON secret.owners}. Run fleet secrets regenerate to fix"; }) config.sharedSecrets; @@ -141,6 +123,7 @@ let cleanupSecret = (secretName: v: { inherit (v) public secret; + shared = true; }); in [ --- a/nixos/secrets.nix +++ b/nixos/secrets.nix @@ -5,7 +5,7 @@ let sysConfig = config; secretType = types.submodule ({ config, ... }: { - config = let secretName = config._module.args.name; in rec { + config = let secretName = config._module.args.name; in { stableSecretPath = mkOptionDefault "/run/secrets/secret-stable-${secretName}"; secretPath = mkOptionDefault "/run/secrets/secret-${config.secretHash}-${secretName}"; secretHash = mkOptionDefault (if config.secret != null then (builtins.hashString "sha1" config.secret) else throw "secret is not defined for secret ${secretName}"); @@ -14,63 +14,74 @@ publicPath = mkOptionDefault "/run/secrets/public-${config.publicHash}-${secretName}"; publicHash = mkOptionDefault (if config.public != null then (builtins.hashString "sha1" config.public) else throw "public is not defined for secret ${secretName}"); }; - options = { + options = with types; { + shared = mkOption { + description = "Is this secret owned by this machine, or propagated from shared secrets"; + default = false; + }; + + generator = mkOption { + type = nullOr unspecified; + description = "Derivation to evaluate for secret generation"; + default = null; + }; + public = mkOption { - type = types.nullOr types.str; + type = nullOr str; description = "Secret public data"; default = null; }; secret = mkOption { - type = types.nullOr types.str; + type = nullOr str; description = "Encrypted secret data"; default = null; }; mode = mkOption { - type = types.str; + type = str; description = "Secret mode"; default = "0440"; }; owner = mkOption { - type = types.str; + type = str; description = "Owner of the secret"; default = "root"; }; group = mkOption { - type = types.str; + type = str; description = "Group of the secret"; default = sysConfig.users.users.${config.owner}.group; }; secretHash = mkOption { - type = types.str; + type = str; description = "Hash of .secret field"; }; publicHash = mkOption { - type = types.str; + type = str; description = "Hash of .public field"; }; stableSecretPath = mkOption { - type = types.str; + type = str; description = '' Use this, if target process supports re-reading of secret from disk, and doesn't needs to be restarted when secret is updated in file ''; }; secretPath = mkOption { - type = types.str; + type = str; description = "Path to decrypted secret, suffixed with contents hash"; }; stablePublicPath = mkOption { - type = types.str; + type = str; description = '' Use this, if target process supports re-reading of secret from disk, and doesn't needs to be restarted when secret is updated in file ''; }; publicPath = mkOption { - type = types.str; + type = str; description = "Path to the public part of secret"; }; }; -- gitstuff