Working spike out
This commit is contained in:
parent
fe966932ed
commit
fddc0dd302
393
Cargo.lock
generated
393
Cargo.lock
generated
|
@ -37,19 +37,6 @@ dependencies = [
|
|||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"xz2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.4.1"
|
||||
|
@ -235,6 +222,22 @@ version = "3.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"iovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.2.1"
|
||||
|
@ -334,22 +337,6 @@ dependencies = [
|
|||
"cache-padded",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.11"
|
||||
|
@ -448,21 +435,6 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.1"
|
||||
|
@ -473,6 +445,18 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.24"
|
||||
|
@ -583,6 +567,12 @@ version = "0.26.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.4"
|
||||
|
@ -601,7 +591,7 @@ version = "0.3.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.2.1",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
|
@ -618,24 +608,31 @@ dependencies = [
|
|||
name = "harmonic"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"async-tar",
|
||||
"async-trait",
|
||||
"atty",
|
||||
"bytes 1.2.1",
|
||||
"clap",
|
||||
"color-eyre",
|
||||
"crossterm",
|
||||
"eyre",
|
||||
"futures",
|
||||
"futures 0.3.24",
|
||||
"glob",
|
||||
"nix",
|
||||
"owo-colors",
|
||||
"reqwest",
|
||||
"tar",
|
||||
"target-lexicon",
|
||||
"tempdir",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
"valuable",
|
||||
"walkdir",
|
||||
"xz2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -665,7 +662,7 @@ version = "0.2.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.2.1",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
@ -676,7 +673,7 @@ version = "0.4.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.2.1",
|
||||
"http",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
@ -699,7 +696,7 @@ version = "0.14.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.2.1",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
|
@ -718,16 +715,16 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
name = "hyper-rustls"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -766,6 +763,15 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iovec"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.5.0"
|
||||
|
@ -894,21 +900,15 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.10"
|
||||
name = "nix"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
|
||||
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -936,51 +936,6 @@ version = "1.14.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.0"
|
||||
|
@ -1125,6 +1080,43 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
dependencies = [
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.3.1",
|
||||
"rdrand",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
|
@ -1174,7 +1166,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"bytes 1.2.1",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
|
@ -1182,35 +1174,73 @@ dependencies = [
|
|||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"hyper-rustls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
|
@ -1218,13 +1248,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.20"
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1234,26 +1263,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.7.0"
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1349,6 +1365,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
@ -1376,6 +1398,17 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.4"
|
||||
|
@ -1383,17 +1416,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
name = "tempdir"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"rand",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1462,7 +1491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "89797afd69d206ccd11fb0ea560a44bbb87731d020670e79416d442919257d42"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"bytes 1.2.1",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
|
@ -1476,6 +1505,17 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-io"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
|
||||
dependencies = [
|
||||
"bytes 0.4.12",
|
||||
"futures 0.1.31",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.8.0"
|
||||
|
@ -1488,13 +1528,14 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.0"
|
||||
name = "tokio-rustls"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1503,7 +1544,7 @@ version = "0.7.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.2.1",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
|
@ -1631,6 +1672,12 @@ dependencies = [
|
|||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.2"
|
||||
|
@ -1683,12 +1730,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
@ -1701,6 +1742,17 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
|
@ -1793,6 +1845,25 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wepoll-ffi"
|
||||
version = "0.1.2"
|
||||
|
@ -1900,5 +1971,7 @@ version = "0.1.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
|
||||
dependencies = [
|
||||
"futures 0.1.31",
|
||||
"lzma-sys",
|
||||
"tokio-io",
|
||||
]
|
||||
|
|
|
@ -29,3 +29,5 @@ glob = "0.3.0"
|
|||
xz2 = { version = "0.1.7", features = ["static", "tokio"] }
|
||||
bytes = "1.2.1"
|
||||
tar = "0.4.38"
|
||||
nix = { version = "0.25.0", features = ["user", "fs"], default-features = false }
|
||||
walkdir = "2.3.2"
|
||||
|
|
|
@ -109,6 +109,7 @@
|
|||
]);
|
||||
|
||||
doCheck = true;
|
||||
RUSTFLAGS = "--cfg tracing_unstable";
|
||||
|
||||
override = { preBuild ? "", ... }: {
|
||||
preBuild = preBuild + ''
|
||||
|
|
37
src/cli/arg/channel_value.rs
Normal file
37
src/cli/arg/channel_value.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use reqwest::Url;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ChannelValue(pub String, pub Url);
|
||||
|
||||
impl clap::builder::ValueParserFactory for ChannelValue {
|
||||
type Parser = ChannelValueParser;
|
||||
fn value_parser() -> Self::Parser {
|
||||
ChannelValueParser
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ChannelValueParser;
|
||||
impl clap::builder::TypedValueParser for ChannelValueParser {
|
||||
type Value = ChannelValue;
|
||||
|
||||
fn parse_ref(
|
||||
&self,
|
||||
_cmd: &clap::Command,
|
||||
_arg: Option<&clap::Arg>,
|
||||
value: &std::ffi::OsStr,
|
||||
) -> Result<Self::Value, clap::Error> {
|
||||
let buf = value.to_str().ok_or_else(|| {
|
||||
clap::Error::raw(clap::ErrorKind::InvalidValue, "Should be all UTF-8")
|
||||
})?;
|
||||
let (name, url) = buf.split_once('=').ok_or_else(|| {
|
||||
clap::Error::raw(
|
||||
clap::ErrorKind::InvalidValue,
|
||||
"Should be formatted `name=url`",
|
||||
)
|
||||
})?;
|
||||
let name = name.to_owned();
|
||||
let url = url.parse().unwrap();
|
||||
Ok(ChannelValue(name, url))
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilte
|
|||
use valuable::Valuable;
|
||||
|
||||
#[derive(clap::Args, Debug, Valuable)]
|
||||
pub(crate) struct Instrumentation {
|
||||
pub struct Instrumentation {
|
||||
/// Enable debug logs, -vv for trace
|
||||
#[clap(
|
||||
short = 'v',
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
mod instrumentation;
|
||||
pub(crate) use instrumentation::Instrumentation;
|
||||
pub use instrumentation::Instrumentation;
|
||||
mod channel_value;
|
||||
pub use channel_value::ChannelValue;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
pub(crate) mod arg;
|
||||
pub(crate) mod subcommand;
|
||||
|
||||
use crate::interaction;
|
||||
use clap::Parser;
|
||||
use crate::{cli::arg::ChannelValue, interaction};
|
||||
use clap::{ArgAction, Parser};
|
||||
use harmonic::Harmonic;
|
||||
use reqwest::Url;
|
||||
use std::process::ExitCode;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -15,10 +14,16 @@ pub(crate) trait CommandExecute {
|
|||
#[derive(Debug, Parser)]
|
||||
#[clap(version)]
|
||||
pub(crate) struct HarmonicCli {
|
||||
#[clap(long, action(ArgAction::SetTrue), default_value = "false")]
|
||||
pub(crate) dry_run: bool,
|
||||
#[clap(flatten)]
|
||||
pub(crate) instrumentation: arg::Instrumentation,
|
||||
#[clap(long, default_value = "https://nixos.org/channels/nixpkgs-unstable")]
|
||||
pub(crate) channels: Vec<Url>,
|
||||
#[clap(
|
||||
long,
|
||||
value_parser,
|
||||
default_value = "nixpkgs=https://nixos.org/channels/nixpkgs-unstable"
|
||||
)]
|
||||
pub(crate) channels: Vec<arg::ChannelValue>,
|
||||
#[clap(long)]
|
||||
pub(crate) no_modify_profile: bool,
|
||||
#[clap(long, default_value = "32")]
|
||||
|
@ -30,12 +35,14 @@ pub(crate) struct HarmonicCli {
|
|||
#[async_trait::async_trait]
|
||||
impl CommandExecute for HarmonicCli {
|
||||
#[tracing::instrument(skip_all, fields(
|
||||
channels = %self.channels.iter().map(ToString::to_string).collect::<Vec<_>>().join(", "),
|
||||
channels = %self.channels.iter().map(|ChannelValue(name, url)| format!("{name} {url}")).collect::<Vec<_>>().join(", "),
|
||||
daemon_user_count = %self.daemon_user_count,
|
||||
no_modify_profile = %self.no_modify_profile,
|
||||
dry_run = %self.dry_run,
|
||||
))]
|
||||
async fn execute(self) -> eyre::Result<ExitCode> {
|
||||
let Self {
|
||||
dry_run,
|
||||
instrumentation: _,
|
||||
daemon_user_count,
|
||||
channels,
|
||||
|
@ -50,8 +57,13 @@ impl CommandExecute for HarmonicCli {
|
|||
|
||||
let mut harmonic = Harmonic::default();
|
||||
|
||||
harmonic.dry_run(dry_run);
|
||||
harmonic.daemon_user_count(daemon_user_count);
|
||||
harmonic.channels(channels);
|
||||
harmonic.channels(
|
||||
channels
|
||||
.into_iter()
|
||||
.map(|ChannelValue(name, url)| (name, url)),
|
||||
);
|
||||
harmonic.modify_profile(!no_modify_profile);
|
||||
|
||||
if !interaction::confirm("Are you ready to continue?").await? {
|
||||
|
@ -67,6 +79,8 @@ impl CommandExecute for HarmonicCli {
|
|||
harmonic.setup_default_profile().await?;
|
||||
harmonic.place_nix_configuration().await?;
|
||||
|
||||
harmonic.configure_nix_daemon_service().await?;
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
}
|
||||
|
|
76
src/error.rs
76
src/error.rs
|
@ -1,45 +1,29 @@
|
|||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum HarmonicError {
|
||||
#[error("Downloading Nix")]
|
||||
DownloadingNix(#[from] reqwest::Error),
|
||||
#[error("Unpacking Nix")]
|
||||
UnpackingNix(std::io::Error),
|
||||
#[error("Running `groupadd`")]
|
||||
GroupAddSpawn(std::io::Error),
|
||||
#[error("`groupadd` returned failure")]
|
||||
GroupAddFailure(std::process::ExitStatus),
|
||||
#[error("Running `useradd`")]
|
||||
UserAddSpawn(std::io::Error),
|
||||
#[error("`useradd` returned failure")]
|
||||
UserAddFailure(std::process::ExitStatus),
|
||||
#[error("Creating directory")]
|
||||
CreateDirectory(std::io::Error),
|
||||
#[error("Placing channel configuration")]
|
||||
PlaceChannelConfiguration(std::io::Error),
|
||||
#[error("Opening file `{0}`")]
|
||||
OpeningFile(std::path::PathBuf, std::io::Error),
|
||||
#[error("Writing to file `{0}`")]
|
||||
WritingFile(std::path::PathBuf, std::io::Error),
|
||||
#[error("Getting tempdir")]
|
||||
GettingTempDir(std::io::Error),
|
||||
#[error("Installing fetched Nix into the new store")]
|
||||
InstallNixIntoStore(std::io::Error),
|
||||
#[error("Installing fetched nss-cacert into the new store")]
|
||||
InstallNssCacertIntoStore(std::io::Error),
|
||||
#[error("Updating the Nix channel")]
|
||||
UpdatingNixChannel(std::io::Error),
|
||||
#[error("Globbing pattern error")]
|
||||
GlobPatternError(glob::PatternError),
|
||||
#[error("Could not find nss-cacert")]
|
||||
#[error("Reqest error")]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
#[error("Unarchiving error")]
|
||||
Unarchive(std::io::Error),
|
||||
#[error("Getting temporary directory")]
|
||||
TempDir(std::io::Error),
|
||||
#[error("Glob pattern error")]
|
||||
GlobPatternError(#[from] glob::PatternError),
|
||||
#[error("Glob globbing error")]
|
||||
GlobGlobError(#[from] glob::GlobError),
|
||||
#[error("Symlinking from `{0}` to `{1}`")]
|
||||
Symlink(std::path::PathBuf, std::path::PathBuf, std::io::Error),
|
||||
#[error("Renaming from `{0}` to `{1}`")]
|
||||
Rename(std::path::PathBuf, std::path::PathBuf, std::io::Error),
|
||||
#[error("Unarchived Nix store did not appear to include a `nss-cacert` location")]
|
||||
NoNssCacert,
|
||||
#[error("Creating /etc/nix/nix.conf")]
|
||||
CreatingNixConf(std::io::Error),
|
||||
#[error("No supported init syustem found")]
|
||||
#[error("No supported init system found")]
|
||||
InitNotSupported,
|
||||
#[error("Linking `{0}` to `{1}`")]
|
||||
Linking(std::path::PathBuf, std::path::PathBuf, std::io::Error),
|
||||
#[error("Running `systemd-tmpfiles`")]
|
||||
SystemdTmpfiles(std::io::Error),
|
||||
#[error("Creating directory `{0}`")]
|
||||
CreateDirectory(std::path::PathBuf, std::io::Error),
|
||||
#[error("Walking directory `{0}`")]
|
||||
WalkDirectory(std::path::PathBuf, walkdir::Error),
|
||||
#[error("Setting permissions `{0}`")]
|
||||
SetPermissions(std::path::PathBuf, std::io::Error),
|
||||
#[error("Command `{0}` failed to execute")]
|
||||
CommandFailedExec(String, std::io::Error),
|
||||
// TODO(@Hoverbear): This should capture the stdout.
|
||||
|
@ -47,4 +31,20 @@ pub enum HarmonicError {
|
|||
CommandFailedStatus(String),
|
||||
#[error("Join error")]
|
||||
JoinError(#[from] tokio::task::JoinError),
|
||||
#[error("Opening file `{0}` for writing")]
|
||||
OpenFile(std::path::PathBuf, std::io::Error),
|
||||
#[error("Opening file `{0}` for writing")]
|
||||
WriteFile(std::path::PathBuf, std::io::Error),
|
||||
#[error("Seeking file `{0}` for writing")]
|
||||
SeekFile(std::path::PathBuf, std::io::Error),
|
||||
#[error("Changing ownership of `{0}`")]
|
||||
Chown(std::path::PathBuf, nix::errno::Errno),
|
||||
#[error("Getting uid for user `{0}`")]
|
||||
UserId(String, nix::errno::Errno),
|
||||
#[error("Getting user `{0}`")]
|
||||
NoUser(String),
|
||||
#[error("Getting gid for group `{0}`")]
|
||||
GroupId(String, nix::errno::Errno),
|
||||
#[error("Getting group `{0}`")]
|
||||
NoGroup(String),
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ pub(crate) async fn confirm(question: impl AsRef<str>) -> eyre::Result<bool> {
|
|||
"\
|
||||
{question}\n\
|
||||
\n\
|
||||
{are_you_sure} ({yes}/{no})\
|
||||
{are_you_sure} ({yes}/{no}): \
|
||||
",
|
||||
question = question.as_ref(),
|
||||
are_you_sure = "Are you sure?".bright_white().bold(),
|
||||
|
|
607
src/lib.rs
607
src/lib.rs
|
@ -1,6 +1,8 @@
|
|||
mod error;
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
fs::Permissions,
|
||||
io::SeekFrom,
|
||||
os::unix::fs::PermissionsExt,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitStatus,
|
||||
|
@ -16,9 +18,9 @@ pub use nixos::NixOs;
|
|||
use bytes::Buf;
|
||||
use glob::glob;
|
||||
use reqwest::Url;
|
||||
use tempdir::TempDir;
|
||||
use tokio::{
|
||||
fs::{create_dir, create_dir_all, set_permissions, symlink, OpenOptions},
|
||||
io::AsyncWriteExt,
|
||||
io::{AsyncSeekExt, AsyncWriteExt},
|
||||
process::Command,
|
||||
task::spawn_blocking,
|
||||
};
|
||||
|
@ -26,8 +28,9 @@ use tokio::{
|
|||
// This uses a Rust builder pattern
|
||||
#[derive(Debug)]
|
||||
pub struct Harmonic {
|
||||
dry_run: bool,
|
||||
daemon_user_count: usize,
|
||||
channels: Vec<Url>,
|
||||
channels: Vec<(String, Url)>,
|
||||
modify_profile: bool,
|
||||
nix_build_group_name: String,
|
||||
nix_build_group_id: usize,
|
||||
|
@ -36,12 +39,16 @@ pub struct Harmonic {
|
|||
}
|
||||
|
||||
impl Harmonic {
|
||||
pub fn dry_run(&mut self, dry_run: bool) -> &mut Self {
|
||||
self.dry_run = dry_run;
|
||||
self
|
||||
}
|
||||
pub fn daemon_user_count(&mut self, count: usize) -> &mut Self {
|
||||
self.daemon_user_count = count;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn channels(&mut self, channels: impl IntoIterator<Item = Url>) -> &mut Self {
|
||||
pub fn channels(&mut self, channels: impl IntoIterator<Item = (String, Url)>) -> &mut Self {
|
||||
self.channels = channels.into_iter().collect();
|
||||
self
|
||||
}
|
||||
|
@ -53,61 +60,64 @@ impl Harmonic {
|
|||
}
|
||||
|
||||
impl Harmonic {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn fetch_nix(&self) -> Result<(), HarmonicError> {
|
||||
// TODO(@hoverbear): architecture specific download
|
||||
// TODO(@hoverbear): hash check
|
||||
let res = reqwest::get(
|
||||
// TODO(@hoverbear): custom url
|
||||
let tempdir = TempDir::new("nix").map_err(HarmonicError::TempDir)?;
|
||||
fetch_url_and_unpack_xz(
|
||||
"https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-x86_64-linux.tar.xz",
|
||||
tempdir.path(),
|
||||
self.dry_run,
|
||||
)
|
||||
.await
|
||||
.map_err(HarmonicError::DownloadingNix)?;
|
||||
let bytes = res.bytes().await.map_err(HarmonicError::DownloadingNix)?;
|
||||
// TODO(@Hoverbear): Pick directory
|
||||
let handle: Result<(), HarmonicError> = spawn_blocking(|| {
|
||||
let decoder = xz2::read::XzDecoder::new(bytes.reader());
|
||||
let mut archive = tar::Archive::new(decoder);
|
||||
let destination = "/nix/install";
|
||||
archive
|
||||
.unpack(destination)
|
||||
.map_err(HarmonicError::UnpackingNix)?;
|
||||
tracing::debug!(%destination, "Downloaded & extracted Nix");
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
handle?;
|
||||
let found_nix_path = if !self.dry_run {
|
||||
// TODO(@Hoverbear): I would like to make this less awful
|
||||
let found_nix_paths = glob::glob(&format!("{}/nix-*", tempdir.path().display()))?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
assert_eq!(
|
||||
found_nix_paths.len(),
|
||||
1,
|
||||
"Did not expect to find multiple nix paths, please report this"
|
||||
);
|
||||
found_nix_paths.into_iter().next().unwrap()
|
||||
} else {
|
||||
PathBuf::from("/nix/nix-*")
|
||||
};
|
||||
rename(found_nix_path.join("store"), "/nix/store", self.dry_run).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn create_group(&self) -> Result<(), HarmonicError> {
|
||||
let status = Command::new("groupadd")
|
||||
.arg("-g")
|
||||
.arg(self.nix_build_group_id.to_string())
|
||||
.arg("--system")
|
||||
.arg(&self.nix_build_group_name)
|
||||
.status()
|
||||
.await
|
||||
.map_err(HarmonicError::GroupAddSpawn)?;
|
||||
if !status.success() {
|
||||
Err(HarmonicError::GroupAddFailure(status))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
execute_command(
|
||||
Command::new("groupadd")
|
||||
.arg("-g")
|
||||
.arg(self.nix_build_group_id.to_string())
|
||||
.arg("--system")
|
||||
.arg(&self.nix_build_group_name),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn create_users(&self) -> Result<(), HarmonicError> {
|
||||
for index in 1..=self.daemon_user_count {
|
||||
let user_name = format!("{}{index}", self.nix_build_user_prefix);
|
||||
let user_id = self.nix_build_user_id_base + index;
|
||||
let status = Command::new("useradd")
|
||||
.args([
|
||||
execute_command(
|
||||
Command::new("useradd").args([
|
||||
"--home-dir",
|
||||
"/var/empty",
|
||||
"--comment",
|
||||
&format!("\"Nix build user {user_id}\""),
|
||||
"--gid",
|
||||
&self.nix_build_group_id.to_string(),
|
||||
&self.nix_build_group_name.to_string(),
|
||||
"--groups",
|
||||
&self.nix_build_group_name.to_string(),
|
||||
"--no-user-group",
|
||||
|
@ -119,24 +129,33 @@ impl Harmonic {
|
|||
"--password",
|
||||
"\"!\"",
|
||||
&user_name.to_string(),
|
||||
])
|
||||
.status()
|
||||
.await
|
||||
.map_err(HarmonicError::UserAddSpawn)?;
|
||||
if !status.success() {
|
||||
return Err(HarmonicError::UserAddFailure(status));
|
||||
}
|
||||
]),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn create_directories(&self) -> Result<(), HarmonicError> {
|
||||
let permissions = Permissions::from_mode(0o755);
|
||||
let paths = [
|
||||
create_directory("/nix", self.dry_run).await?;
|
||||
set_permissions(
|
||||
"/nix",
|
||||
None,
|
||||
Some("root".to_string()),
|
||||
Some(self.nix_build_group_name.clone()),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let permissions = Permissions::from_mode(0o0755);
|
||||
let paths = [
|
||||
"/nix/var",
|
||||
"/nix/var/log",
|
||||
"/nix/var/log/nix",
|
||||
"/nix/var/log/nix/drvs",
|
||||
"/nix/var/nix",
|
||||
"/nix/var/nix/db",
|
||||
"/nix/var/nix/gcroots",
|
||||
"/nix/var/nix/gcroots/per-user",
|
||||
|
@ -146,35 +165,54 @@ impl Harmonic {
|
|||
"/nix/var/nix/userpool",
|
||||
"/nix/var/nix/daemon-socket",
|
||||
];
|
||||
|
||||
for path in paths {
|
||||
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
|
||||
create_dir_with_permissions(path, permissions.clone())
|
||||
.await
|
||||
.map_err(HarmonicError::CreateDirectory)?;
|
||||
create_directory(path, self.dry_run).await?;
|
||||
set_permissions(path, Some(permissions.clone()), None, None, self.dry_run).await?;
|
||||
}
|
||||
create_directory("/nix/store", self.dry_run).await?;
|
||||
set_permissions(
|
||||
"/nix/store",
|
||||
Some(Permissions::from_mode(0o1775)),
|
||||
None,
|
||||
Some(self.nix_build_group_name.clone()),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
create_directory("/etc/nix", self.dry_run).await?;
|
||||
set_permissions(
|
||||
"/etc/nix",
|
||||
Some(Permissions::from_mode(0o0555)),
|
||||
None,
|
||||
None,
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn place_channel_configuration(&self) -> Result<(), HarmonicError> {
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open("/root/.nix-channels") // TODO(@hoverbear): We should figure out the actual root dir
|
||||
.await
|
||||
.map_err(HarmonicError::PlaceChannelConfiguration)?;
|
||||
|
||||
let buf = self
|
||||
.channels
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.map(|(name, url)| format!("{} {}", url, name))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
file.write_all(buf.as_bytes())
|
||||
.await
|
||||
.map_err(HarmonicError::PlaceChannelConfiguration)?;
|
||||
Ok(())
|
||||
|
||||
create_file_if_not_exists("/root/.nix-channels", buf, self.dry_run).await?;
|
||||
set_permissions(
|
||||
"/root/.nix-channels",
|
||||
Some(Permissions::from_mode(0o0664)),
|
||||
None,
|
||||
None,
|
||||
self.dry_run,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn configure_shell_profile(&self) -> Result<(), HarmonicError> {
|
||||
const PROFILE_TARGETS: &[&str] = &[
|
||||
"/etc/bashrc",
|
||||
|
@ -191,86 +229,105 @@ impl Harmonic {
|
|||
# Nix\n\
|
||||
if [ -e '{PROFILE_NIX_FILE}' ]; then\n\
|
||||
. '{PROFILE_NIX_FILE}'\n\
|
||||
fi\n
|
||||
fi\n\
|
||||
# End Nix\n
|
||||
\n",
|
||||
);
|
||||
if path.exists() {
|
||||
// TODO(@Hoverbear): Backup
|
||||
// TODO(@Hoverbear): See if the line already exists, skip setting it
|
||||
tracing::trace!("TODO");
|
||||
} else if let Some(parent) = path.parent() {
|
||||
create_dir_all(parent).await.unwrap()
|
||||
}
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.read(true)
|
||||
.write(true)
|
||||
.truncate(false)
|
||||
.open(profile_target)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::OpeningFile(path.to_owned(), e))?;
|
||||
file.write_all(buf.as_bytes())
|
||||
.await
|
||||
.map_err(|e| HarmonicError::WritingFile(path.to_owned(), e))?;
|
||||
create_or_append_file(path, buf, self.dry_run).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn setup_default_profile(&self) -> Result<(), HarmonicError> {
|
||||
Command::new("/nix/install/bin/nix-env")
|
||||
.arg("-i")
|
||||
.arg("/nix/install")
|
||||
.status()
|
||||
.await
|
||||
.map_err(HarmonicError::InstallNixIntoStore)?;
|
||||
// Find an `nss-cacert` package, add it too.
|
||||
let mut found_nss_ca_cert = None;
|
||||
for entry in
|
||||
glob("/nix/install/store/*-nss-cacert").map_err(HarmonicError::GlobPatternError)?
|
||||
{
|
||||
match entry {
|
||||
Ok(path) => {
|
||||
// TODO(@Hoverbear): Should probably ensure is unique
|
||||
found_nss_ca_cert = Some(path);
|
||||
break;
|
||||
}
|
||||
Err(_) => continue, /* Ignore it */
|
||||
};
|
||||
}
|
||||
if let Some(nss_ca_cert) = found_nss_ca_cert {
|
||||
let status = Command::new("/nix/install/bin/nix-env")
|
||||
.arg("-i")
|
||||
.arg(&nss_ca_cert)
|
||||
.status()
|
||||
.await
|
||||
.map_err(HarmonicError::InstallNssCacertIntoStore)?;
|
||||
if !status.success() {
|
||||
// TODO(@Hoverbear): report
|
||||
// Find an `nix` package
|
||||
let nix_pkg_glob = "/nix/store/*-nix-*";
|
||||
let found_nix_pkg = if !self.dry_run {
|
||||
let mut found_pkg = None;
|
||||
for entry in glob(nix_pkg_glob).map_err(HarmonicError::GlobPatternError)? {
|
||||
match entry {
|
||||
Ok(path) => {
|
||||
// TODO(@Hoverbear): Should probably ensure is unique
|
||||
found_pkg = Some(path);
|
||||
break;
|
||||
}
|
||||
Err(_) => continue, /* Ignore it */
|
||||
};
|
||||
}
|
||||
std::env::set_var("NIX_SSL_CERT_FILE", &nss_ca_cert);
|
||||
found_pkg
|
||||
} else {
|
||||
// This is a mock for dry running.
|
||||
Some(PathBuf::from(nix_pkg_glob))
|
||||
};
|
||||
let nix_pkg = if let Some(nix_pkg) = found_nix_pkg {
|
||||
nix_pkg
|
||||
} else {
|
||||
return Err(HarmonicError::NoNssCacert);
|
||||
}
|
||||
};
|
||||
|
||||
execute_command(
|
||||
Command::new(nix_pkg.join("bin/nix-env"))
|
||||
.arg("-i")
|
||||
.arg(&nix_pkg),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Find an `nss-cacert` package, add it too.
|
||||
let nss_ca_cert_pkg_glob = "/nix/store/*-nss-cacert-*";
|
||||
let found_nss_ca_cert_pkg = if !self.dry_run {
|
||||
let mut found_pkg = None;
|
||||
for entry in glob(nss_ca_cert_pkg_glob).map_err(HarmonicError::GlobPatternError)? {
|
||||
match entry {
|
||||
Ok(path) => {
|
||||
// TODO(@Hoverbear): Should probably ensure is unique
|
||||
found_pkg = Some(path);
|
||||
break;
|
||||
}
|
||||
Err(_) => continue, /* Ignore it */
|
||||
};
|
||||
}
|
||||
found_pkg
|
||||
} else {
|
||||
// This is a mock for dry running.
|
||||
Some(PathBuf::from(nss_ca_cert_pkg_glob))
|
||||
};
|
||||
|
||||
if let Some(nss_ca_cert_pkg) = found_nss_ca_cert_pkg {
|
||||
execute_command(
|
||||
Command::new(nix_pkg.join("bin/nix-env"))
|
||||
.arg("-i")
|
||||
.arg(&nss_ca_cert_pkg),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
set_env(
|
||||
"NIX_SSL_CERT_FILE",
|
||||
"/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt",
|
||||
self.dry_run,
|
||||
);
|
||||
nss_ca_cert_pkg
|
||||
} else {
|
||||
return Err(HarmonicError::NoNssCacert);
|
||||
};
|
||||
if !self.channels.is_empty() {
|
||||
status_failure_as_error(
|
||||
Command::new("/nix/install/bin/nix-channel")
|
||||
execute_command(
|
||||
Command::new(nix_pkg.join("bin/nix-channel"))
|
||||
.arg("--update")
|
||||
.arg("nixpkgs"),
|
||||
.arg("nixpkgs")
|
||||
.env(
|
||||
"NIX_SSL_CERT_FILE",
|
||||
"/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt",
|
||||
),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn place_nix_configuration(&self) -> Result<(), HarmonicError> {
|
||||
let mut nix_conf = OpenOptions::new()
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open("/etc/nix/nix.conf")
|
||||
.await
|
||||
.map_err(HarmonicError::CreatingNixConf)?;
|
||||
let buf = format!(
|
||||
"\
|
||||
{extra_conf}\n\
|
||||
|
@ -279,14 +336,10 @@ impl Harmonic {
|
|||
extra_conf = "", // TODO(@Hoverbear): populate me
|
||||
build_group_name = self.nix_build_group_name,
|
||||
);
|
||||
nix_conf
|
||||
.write_all(buf.as_bytes())
|
||||
.await
|
||||
.map_err(HarmonicError::CreatingNixConf)?;
|
||||
|
||||
Ok(())
|
||||
create_file_if_not_exists("/etc/nix/nix.conf", buf, self.dry_run).await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn configure_nix_daemon_service(&self) -> Result<(), HarmonicError> {
|
||||
if Path::new("/run/systemd/system").exists() {
|
||||
const SERVICE_SRC: &str =
|
||||
|
@ -299,30 +352,38 @@ impl Harmonic {
|
|||
"/nix/var/nix/profiles/default//lib/tmpfiles.d/nix-daemon.conf";
|
||||
const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf";
|
||||
|
||||
symlink(TMPFILES_SRC, TMPFILES_DEST).await.map_err(|e| {
|
||||
HarmonicError::Linking(PathBuf::from(TMPFILES_SRC), PathBuf::from(TMPFILES_DEST), e)
|
||||
})?;
|
||||
status_failure_as_error(
|
||||
symlink(TMPFILES_SRC, TMPFILES_DEST, self.dry_run).await?;
|
||||
execute_command(
|
||||
Command::new("systemd-tmpfiles")
|
||||
.arg("--create")
|
||||
.arg("--prefix=/nix/var/nix"),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
execute_command(
|
||||
Command::new("systemctl").arg("link").arg(SERVICE_SRC),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
execute_command(
|
||||
Command::new("systemctl").arg("enable").arg(SOCKET_SRC),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
status_failure_as_error(Command::new("systemctl").arg("link").arg(SERVICE_SRC)).await?;
|
||||
status_failure_as_error(Command::new("systemctl").arg("enable").arg(SOCKET_SRC))
|
||||
.await?;
|
||||
// TODO(@Hoverbear): Handle proxy vars
|
||||
status_failure_as_error(Command::new("systemctl").arg("daemon-reload")).await?;
|
||||
status_failure_as_error(
|
||||
execute_command(Command::new("systemctl").arg("daemon-reload"), self.dry_run).await?;
|
||||
execute_command(
|
||||
Command::new("systemctl")
|
||||
.arg("start")
|
||||
.arg("nix-daemon.socket"),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
status_failure_as_error(
|
||||
execute_command(
|
||||
Command::new("systemctl")
|
||||
.arg("restart")
|
||||
.arg("nix-daemon.service"),
|
||||
self.dry_run,
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
|
@ -335,9 +396,13 @@ impl Harmonic {
|
|||
impl Default for Harmonic {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
channels: vec!["https://nixos.org/channels/nixpkgs-unstable"
|
||||
.parse::<Url>()
|
||||
.unwrap()],
|
||||
dry_run: true,
|
||||
channels: vec![(
|
||||
"nixpkgs".to_string(),
|
||||
"https://nixos.org/channels/nixpkgs-unstable"
|
||||
.parse::<Url>()
|
||||
.unwrap(),
|
||||
)],
|
||||
daemon_user_count: 32,
|
||||
modify_profile: true,
|
||||
nix_build_group_name: String::from("nixbld"),
|
||||
|
@ -348,24 +413,256 @@ impl Default for Harmonic {
|
|||
}
|
||||
}
|
||||
|
||||
async fn create_dir_with_permissions(
|
||||
#[tracing::instrument(skip_all, fields(
|
||||
path = %path.as_ref().display(),
|
||||
permissions = tracing::field::valuable(&permissions.clone().map(|v| format!("{:#o}", v.mode()))),
|
||||
owner = tracing::field::valuable(&owner),
|
||||
group = tracing::field::valuable(&group),
|
||||
))]
|
||||
async fn set_permissions(
|
||||
path: impl AsRef<Path>,
|
||||
permissions: Permissions,
|
||||
) -> Result<(), std::io::Error> {
|
||||
let path = path.as_ref();
|
||||
create_dir(path).await?;
|
||||
set_permissions(path, permissions).await?;
|
||||
permissions: Option<Permissions>,
|
||||
owner: Option<String>,
|
||||
group: Option<String>,
|
||||
dry_run: bool,
|
||||
) -> Result<(), HarmonicError> {
|
||||
use nix::unistd::{chown, Group, User};
|
||||
use walkdir::WalkDir;
|
||||
if !dry_run {
|
||||
let path = path.as_ref();
|
||||
let uid = if let Some(owner) = owner {
|
||||
let uid = User::from_name(owner.as_str())
|
||||
.map_err(|e| HarmonicError::UserId(owner.clone(), e))?
|
||||
.ok_or(HarmonicError::NoUser(owner))?
|
||||
.uid;
|
||||
Some(uid)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let gid = if let Some(group) = group {
|
||||
let gid = Group::from_name(group.as_str())
|
||||
.map_err(|e| HarmonicError::GroupId(group.clone(), e))?
|
||||
.ok_or(HarmonicError::NoGroup(group))?
|
||||
.gid;
|
||||
Some(gid)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
for child in WalkDir::new(path) {
|
||||
let entry = child.map_err(|e| HarmonicError::WalkDirectory(path.to_owned(), e))?;
|
||||
if let Some(ref perms) = permissions {
|
||||
tokio::fs::set_permissions(path, perms.clone())
|
||||
.await
|
||||
.map_err(|e| HarmonicError::SetPermissions(path.to_owned(), e))?;
|
||||
}
|
||||
chown(entry.path(), uid, gid)
|
||||
.map_err(|e| HarmonicError::Chown(entry.path().to_owned(), e))?;
|
||||
}
|
||||
} else {
|
||||
tracing::info!("Dry run: Would recursively set permissions/ownership");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn status_failure_as_error(command: &mut Command) -> Result<ExitStatus, HarmonicError> {
|
||||
let command_str = format!("{:?}", command.as_std());
|
||||
let status = command
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CommandFailedExec(command_str.clone(), e))?;
|
||||
match status.success() {
|
||||
true => Ok(status),
|
||||
false => Err(HarmonicError::CommandFailedStatus(command_str)),
|
||||
#[tracing::instrument(skip_all, fields(
|
||||
path = %path.as_ref().display(),
|
||||
))]
|
||||
async fn create_directory(path: impl AsRef<Path>, dry_run: bool) -> Result<(), HarmonicError> {
|
||||
use tokio::fs::create_dir;
|
||||
if !dry_run {
|
||||
let path = path.as_ref();
|
||||
create_dir(path)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CreateDirectory(path.to_owned(), e))?;
|
||||
} else {
|
||||
tracing::info!("Dry run: Would create directory");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(command = %format!("{:?}", command.as_std())))]
|
||||
async fn execute_command(
|
||||
command: &mut Command,
|
||||
dry_run: bool,
|
||||
) -> Result<ExitStatus, HarmonicError> {
|
||||
if !dry_run {
|
||||
let command_str = format!("{:?}", command.as_std());
|
||||
let status = command
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CommandFailedExec(command_str.clone(), e))?;
|
||||
match status.success() {
|
||||
true => Ok(status),
|
||||
false => Err(HarmonicError::CommandFailedStatus(command_str)),
|
||||
}
|
||||
} else {
|
||||
tracing::info!("Dry run: Would execute");
|
||||
// You cannot conjure "good" exit status in Rust without breaking the rules
|
||||
// So... we conjure one from `true`
|
||||
Command::new("true")
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CommandFailedExec(String::from("true"), e))
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(
|
||||
path = %path.as_ref().display(),
|
||||
buf = %format!("```{}```", buf.as_ref()),
|
||||
))]
|
||||
async fn create_or_append_file(
|
||||
path: impl AsRef<Path>,
|
||||
buf: impl AsRef<str>,
|
||||
dry_run: bool,
|
||||
) -> Result<(), HarmonicError> {
|
||||
use tokio::fs::{create_dir_all, OpenOptions};
|
||||
let path = path.as_ref();
|
||||
let buf = buf.as_ref();
|
||||
if !dry_run {
|
||||
if let Some(parent) = path.parent() {
|
||||
create_dir_all(parent)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CreateDirectory(parent.to_owned(), e))?;
|
||||
}
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(&path)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::OpenFile(path.to_owned(), e))?;
|
||||
|
||||
file.seek(SeekFrom::End(0))
|
||||
.await
|
||||
.map_err(|e| HarmonicError::SeekFile(path.to_owned(), e))?;
|
||||
file.write_all(buf.as_bytes())
|
||||
.await
|
||||
.map_err(|e| HarmonicError::WriteFile(path.to_owned(), e))?;
|
||||
} else {
|
||||
tracing::info!("Dry run: Would create or append");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(
|
||||
path = %path.as_ref().display(),
|
||||
buf = %format!("`{}`", buf.as_ref()),
|
||||
))]
|
||||
async fn create_file_if_not_exists(
|
||||
path: impl AsRef<Path>,
|
||||
buf: impl AsRef<str>,
|
||||
dry_run: bool,
|
||||
) -> Result<(), HarmonicError> {
|
||||
use tokio::fs::{create_dir_all, OpenOptions};
|
||||
let path = path.as_ref();
|
||||
let buf = buf.as_ref();
|
||||
if !dry_run {
|
||||
if let Some(parent) = path.parent() {
|
||||
create_dir_all(parent)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CreateDirectory(parent.to_owned(), e))?;
|
||||
}
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(&path)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::OpenFile(path.to_owned(), e))?;
|
||||
|
||||
file.write_all(buf.as_bytes())
|
||||
.await
|
||||
.map_err(|e| HarmonicError::WriteFile(path.to_owned(), e))?;
|
||||
} else {
|
||||
tracing::info!("Dry run: Would create (or error if exists)");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(
|
||||
src = %src.as_ref().display(),
|
||||
dest = %dest.as_ref().display(),
|
||||
))]
|
||||
async fn symlink(
|
||||
src: impl AsRef<Path>,
|
||||
dest: impl AsRef<Path>,
|
||||
dry_run: bool,
|
||||
) -> Result<(), HarmonicError> {
|
||||
let src = src.as_ref();
|
||||
let dest = dest.as_ref();
|
||||
if !dry_run {
|
||||
tokio::fs::symlink(src, dest)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::Symlink(src.to_owned(), dest.to_owned(), e))?;
|
||||
} else {
|
||||
tracing::info!("Dry run: Would symlink",);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(
|
||||
src = %src.as_ref().display(),
|
||||
dest = %dest.as_ref().display(),
|
||||
))]
|
||||
async fn rename(
|
||||
src: impl AsRef<Path>,
|
||||
dest: impl AsRef<Path>,
|
||||
dry_run: bool,
|
||||
) -> Result<(), HarmonicError> {
|
||||
let src = src.as_ref();
|
||||
let dest = dest.as_ref();
|
||||
if !dry_run {
|
||||
tokio::fs::rename(src, dest)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::Rename(src.to_owned(), dest.to_owned(), e))?;
|
||||
} else {
|
||||
tracing::info!("Dry run: Would rename",);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(
|
||||
url = %url.as_ref(),
|
||||
dest = %dest.as_ref().display(),
|
||||
))]
|
||||
async fn fetch_url_and_unpack_xz(
|
||||
url: impl AsRef<str>,
|
||||
dest: impl AsRef<Path>,
|
||||
dry_run: bool,
|
||||
) -> Result<(), HarmonicError> {
|
||||
let url = url.as_ref();
|
||||
let dest = dest.as_ref().to_owned();
|
||||
if !dry_run {
|
||||
let res = reqwest::get(url).await.map_err(HarmonicError::Reqwest)?;
|
||||
let bytes = res.bytes().await.map_err(HarmonicError::Reqwest)?;
|
||||
// TODO(@Hoverbear): Pick directory
|
||||
let handle: Result<(), HarmonicError> = spawn_blocking(move || {
|
||||
let decoder = xz2::read::XzDecoder::new(bytes.reader());
|
||||
let mut archive = tar::Archive::new(decoder);
|
||||
archive.unpack(&dest).map_err(HarmonicError::Unarchive)?;
|
||||
tracing::debug!(dest = %dest.display(), "Downloaded & extracted Nix");
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
handle?;
|
||||
} else {
|
||||
tracing::info!("Dry run: Would fetch and unpack xz tarball");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(
|
||||
k = %k.as_ref().to_string_lossy(),
|
||||
v = %v.as_ref().to_string_lossy(),
|
||||
))]
|
||||
fn set_env(k: impl AsRef<OsStr>, v: impl AsRef<OsStr>, dry_run: bool) {
|
||||
if !dry_run {
|
||||
std::env::set_var(k.as_ref(), v.as_ref());
|
||||
} else {
|
||||
tracing::info!("Dry run: Would set env");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue