Compare commits
No commits in common. "447ae19b3c37047129590da8e9334257a19ca077" and "551e5a78519d7b860b6b401d10a34f66d136c846" have entirely different histories.
447ae19b3c
...
551e5a7851
25 changed files with 286 additions and 1379 deletions
427
Cargo.lock
generated
427
Cargo.lock
generated
|
|
@ -2,15 +2,6 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
|
|
@ -47,7 +38,7 @@ version = "1.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -58,7 +49,19 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
|||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "append-override"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"command-error",
|
||||
"fs-err",
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -149,45 +152,12 @@ version = "1.0.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||
|
||||
[[package]]
|
||||
name = "dynix"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"command-error",
|
||||
"fs-err",
|
||||
"itertools",
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tap",
|
||||
"tracing",
|
||||
"tracing-human-layer",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs-err"
|
||||
version = "3.2.2"
|
||||
|
|
@ -209,12 +179,6 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
|
|
@ -225,86 +189,24 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_ci"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
|
|
@ -323,15 +225,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
|
|
@ -344,39 +237,6 @@ version = "1.70.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "4.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
|
||||
dependencies = [
|
||||
"supports-color 2.1.0",
|
||||
"supports-color 3.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
|
|
@ -413,51 +273,6 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
|
|
@ -501,58 +316,18 @@ dependencies = [
|
|||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shell-words"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "smawk"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "supports-color"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89"
|
||||
dependencies = [
|
||||
"is-terminal",
|
||||
"is_ci",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "supports-color"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6"
|
||||
dependencies = [
|
||||
"is_ci",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
|
|
@ -564,43 +339,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
|
||||
dependencies = [
|
||||
"smawk",
|
||||
"terminal_size",
|
||||
"unicode-linebreak",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
|
|
@ -630,51 +368,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-human-layer"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b285fd79bba4659408f5d290b3f30fd69d428c630d8c00bb4ba255f2501d50e3"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"owo-colors",
|
||||
"parking_lot",
|
||||
"textwrap",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -683,18 +376,6 @@ version = "1.0.22"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-command"
|
||||
version = "1.0.1"
|
||||
|
|
@ -707,12 +388,6 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
|
|
@ -821,15 +496,6 @@ dependencies = [
|
|||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
|
|
@ -839,23 +505,6 @@ dependencies = [
|
|||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link 0.2.1",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
|
|
@ -865,54 +514,6 @@ dependencies = [
|
|||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.16"
|
||||
|
|
|
|||
11
Cargo.toml
11
Cargo.toml
|
|
@ -1,25 +1,20 @@
|
|||
[package]
|
||||
name = "dynix"
|
||||
name = "append-override"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "dynix"
|
||||
name = "append-override"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
name = "dynix"
|
||||
name = "append_override"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.54", features = ["color", "derive"] }
|
||||
command-error = "0.8.0"
|
||||
fs-err = "3.2.2"
|
||||
itertools = "0.14.0"
|
||||
libc = { version = "0.2.180", features = ["extra_traits"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
tap = "1.0.1"
|
||||
tracing = { version = "0.1.44", features = ["attributes"] }
|
||||
tracing-human-layer = "0.2.1"
|
||||
tracing-subscriber = { version = "0.3.22", default-features = false, features = ["std", "env-filter", "fmt", "ansi", "registry", "parking_lot"] }
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
{ pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./modules/dynamic-overrides.nix
|
||||
./modules/dynamicism
|
||||
"${modulesPath}/profiles/qemu-guest.nix"
|
||||
];
|
||||
|
||||
dynamicism.for.gotosocial.enable = true;
|
||||
|
||||
# Just an example system.
|
||||
users.mutableUsers = false;
|
||||
users.users.root = {
|
||||
password = "root";
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PermitRootLogin = "yes";
|
||||
};
|
||||
};
|
||||
|
||||
environment.shellAliases = {
|
||||
ls = "eza --long --header --group --group-directories-first --classify --binary";
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
eza
|
||||
fd
|
||||
ripgrep
|
||||
];
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
67
default.nix
67
default.nix
|
|
@ -6,70 +6,15 @@
|
|||
}: let
|
||||
inherit (qpkgs) lib;
|
||||
|
||||
dynix = qpkgs.callPackage ./package.nix { };
|
||||
PKGNAME = qpkgs.callPackage ./package.nix { };
|
||||
|
||||
byStdenv = lib.mapAttrs (stdenvName: stdenv: let
|
||||
withStdenv = dynix.override { inherit stdenv; };
|
||||
dynix' = withStdenv.overrideAttrs (prev: {
|
||||
withStdenv = PKGNAME.override { inherit stdenv; };
|
||||
PKGNAME' = withStdenv.overrideAttrs (prev: {
|
||||
pname = "${prev.pname}-${stdenvName}";
|
||||
});
|
||||
in dynix') qpkgs.validStdenvs;
|
||||
in PKGNAME') qpkgs.validStdenvs;
|
||||
|
||||
evalNixos = import (pkgs.path + "/nixos");
|
||||
|
||||
doChange = {
|
||||
option,
|
||||
value,
|
||||
}:
|
||||
assert lib.isList option;
|
||||
assert lib.all lib.isString option;
|
||||
let
|
||||
nixosBefore = evalNixos {
|
||||
configuration = ./configuration.nix;
|
||||
};
|
||||
dynamicBefore = nixosBefore.config.dynamicism.finalSettings;
|
||||
|
||||
nixosAfter = evalNixos {
|
||||
configuration = { ... }: {
|
||||
imports = [
|
||||
./configuration.nix
|
||||
(lib.setAttrByPath option (lib.mkOverride (-999) value))
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
withActivationScripts = evalNixos {
|
||||
configuration = ({ ... }: {
|
||||
imports = [ ./configuration.nix ];
|
||||
config.environment.systemPackages = [ nixosAfter.config.dynamicism.for.gotosocial.activate ];
|
||||
});
|
||||
};
|
||||
in {
|
||||
inherit nixosBefore nixosAfter withActivationScripts;
|
||||
};
|
||||
|
||||
in dynix.overrideAttrs (final: prev: let
|
||||
self = final.finalPackage;
|
||||
in lib.recursiveUpdate prev {
|
||||
passthru = {
|
||||
ts = let
|
||||
scope = pkgs.callPackage ./modules/tests.nix { };
|
||||
in scope.packages scope;
|
||||
|
||||
dync = self.nixos.config.dynamicism;
|
||||
dyno = self.nixos.options.dynamicism;
|
||||
gotosocial = self.nixos.options.dynamicism.for.valueMeta.attrs.gotosocial.configuration;
|
||||
|
||||
inherit byStdenv;
|
||||
nixos = evalNixos {
|
||||
configuration = ./configuration.nix;
|
||||
};
|
||||
c = self.nixos;
|
||||
nixos-vm = self.nixos.config.system.build.vm;
|
||||
doChange = builtins.seq self.nixos.config.dynamicism doChange;
|
||||
withVox = self.doChange {
|
||||
option = lib.splitString "." "services.gotosocial.settings.application-name";
|
||||
value = "Vox is an asshole";
|
||||
};
|
||||
};
|
||||
in PKGNAME.overrideAttrs (prev: lib.recursiveUpdate prev {
|
||||
passthru = { inherit byStdenv; };
|
||||
})
|
||||
|
|
|
|||
16
flake.nix
16
flake.nix
|
|
@ -21,21 +21,21 @@
|
|||
qpkgs = import qyriad-nur { inherit pkgs; };
|
||||
inherit (qpkgs) lib;
|
||||
|
||||
dynix = import ./default.nix { inherit pkgs qpkgs; };
|
||||
PKGNAME = import ./default.nix { inherit pkgs qpkgs; };
|
||||
extraVersions = lib.mapAttrs' (stdenvName: value: {
|
||||
name = "${stdenvName}-dynix";
|
||||
name = "${stdenvName}-PKGNAME";
|
||||
inherit value;
|
||||
}) dynix.byStdenv;
|
||||
}) PKGNAME.byStdenv;
|
||||
|
||||
devShell = import ./shell.nix { inherit pkgs qpkgs dynix; };
|
||||
devShell = import ./shell.nix { inherit pkgs qpkgs PKGNAME; };
|
||||
extraDevShells = lib.mapAttrs' (stdenvName: value: {
|
||||
name = "${stdenvName}-dynix";
|
||||
name = "${stdenvName}-PKGNAME";
|
||||
inherit value;
|
||||
}) dynix.byStdenv;
|
||||
}) PKGNAME.byStdenv;
|
||||
in {
|
||||
packages = extraVersions // {
|
||||
default = dynix;
|
||||
inherit dynix;
|
||||
default = PKGNAME;
|
||||
inherit PKGNAME;
|
||||
};
|
||||
|
||||
devShells = extraDevShells // {
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
# Managed by dynix.
|
||||
{ lib, ... }:
|
||||
|
||||
lib.mkMerge [
|
||||
{
|
||||
services.gotosocial = {
|
||||
enable = true;
|
||||
setupPostgresqlDB = true;
|
||||
settings = {
|
||||
application-name = "example!";
|
||||
host = "yuki.local";
|
||||
};
|
||||
};
|
||||
}
|
||||
{
|
||||
services.gotosocial.settings.application-name = lib.mkOverride 99 "removed herobrine";
|
||||
}
|
||||
{
|
||||
services.gotosocial.settings.application-name = lib.mkOverride 98 "reädded herobrine";
|
||||
}
|
||||
]
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
{ pkgs, lib, config, options, ... }:
|
||||
let
|
||||
inherit (lib.options)
|
||||
mkOption
|
||||
showOption
|
||||
;
|
||||
t = lib.types;
|
||||
|
||||
inherit (import ./lib.nix { inherit lib; })
|
||||
typeCheck
|
||||
convenientAttrPath
|
||||
concatFoldl
|
||||
recUpdateFoldl
|
||||
recUpdateFoldlAttrs
|
||||
;
|
||||
|
||||
evalNixos = import (pkgs.path + "/nixos");
|
||||
|
||||
opts = options.dynamicism;
|
||||
|
||||
subOpts = lib.mapAttrs (_: metaAttr: metaAttr.configuration.options) options.dynamicism.for.valueMeta.attrs;
|
||||
settingsFormat = pkgs.formats.yaml { };
|
||||
|
||||
finalSettingsFor = { ... }@submod: recUpdateFoldl (optPath:
|
||||
lib.setAttrByPath optPath (lib.getAttrFromPath optPath config)
|
||||
) submod.source-options;
|
||||
|
||||
ourAssertions = lib.concatAttrValues {
|
||||
unitsExist = subOpts
|
||||
|> lib.attrValues
|
||||
|> concatFoldl (submod: submod.systemd-services-updated.value
|
||||
|> lib.map (unit: {
|
||||
assertion = config.systemd.units.${unit}.enable or false;
|
||||
message = ''
|
||||
${showOption submod.systemd-services-updated.loc}' specified non-existent unit '${unit}'
|
||||
'';
|
||||
})
|
||||
|> lib.optionals submod.enable.value
|
||||
);
|
||||
|
||||
optsExist = concatFoldl (submod: lib.optionals submod.enable.value (lib.map (optPath: {
|
||||
assertion = lib.hasAttrByPath optPath options;
|
||||
message = "'${showOption submod.source-options.loc}' specified non-existent option '${showOption optPath}'";
|
||||
}) submod.source-options.value)) (lib.attrValues subOpts);
|
||||
};
|
||||
in
|
||||
{
|
||||
#
|
||||
# Interface.
|
||||
#
|
||||
options.dynamicism = {
|
||||
for = mkOption {
|
||||
type = t.attrsOf (t.submoduleWith {
|
||||
modules = [ ./submodule.nix ];
|
||||
shorthandOnlyDefinesConfig = false;
|
||||
specialArgs = { inherit pkgs; };
|
||||
});
|
||||
default = { };
|
||||
};
|
||||
|
||||
finalSettings = mkOption {
|
||||
type = t.attrsOf t.raw;
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
Attrset of each `source-options` tree to their actual values.
|
||||
'';
|
||||
};
|
||||
|
||||
doChange = mkOption {
|
||||
type = t.functionTo t.pathInStore;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
The function to call to Do The Thing.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# Assertions.
|
||||
config.assertions = ourAssertions;
|
||||
|
||||
#
|
||||
# Generic implementation.
|
||||
#
|
||||
config.dynamicism.doChange = {
|
||||
option,
|
||||
value,
|
||||
configuration ? builtins.getEnv "NIXOS_CONFIG",
|
||||
}: let
|
||||
loc = opts.doChange.loc ++ [ "(function argument)" "value" ];
|
||||
option' = typeCheck loc convenientAttrPath option;
|
||||
nixosAfter = evalNixos {
|
||||
configuration = { config, ... }: {
|
||||
imports = [
|
||||
configuration
|
||||
(lib.setAttrByPath option' (lib.mkOverride (-999) value))
|
||||
];
|
||||
|
||||
environment.systemPackages = [
|
||||
config.dynamicism.for.gotosocial.activate
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
allActivations = lib.mapAttrsToList (name: submod: submod.activate) config.dynamicism.for;
|
||||
allActivationScripts = pkgs.writeShellApplication {
|
||||
name = "dynamicism-activate";
|
||||
runtimeInputs = allActivations;
|
||||
text = nixosAfter.config.dynamicism.for
|
||||
|> lib.mapAttrsToList (name: submod: ''
|
||||
echo "Activating dynamicism for ${name}"
|
||||
${lib.getExe submod.activate}
|
||||
'')
|
||||
|> lib.concatStringsSep "\n";
|
||||
};
|
||||
in allActivationScripts;
|
||||
|
||||
config.dynamicism.finalSettings = lib.asserts.checkAssertWarn ourAssertions [ ] (
|
||||
recUpdateFoldlAttrs (name: { ... }@submod: finalSettingsFor submod) config.dynamicism.for
|
||||
);
|
||||
|
||||
# Implementations.
|
||||
config.dynamicism.for.gotosocial = let
|
||||
cfg = config.dynamicism.for.gotosocial;
|
||||
in {
|
||||
source-options = [
|
||||
"services.gotosocial.settings"
|
||||
];
|
||||
|
||||
configFile = settingsFormat.generate "gotosocial-override.yml" config.services.gotosocial.settings;
|
||||
|
||||
unitDropins."gotosocial.service" = pkgs.writeText "gotosocial-override.conf" ''
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=${lib.getExe' pkgs.gotosocial "gotosocial"} --config-path ${cfg.configFile} start
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
lib ? import <nixpkgs/lib>,
|
||||
}: let
|
||||
t = lib.types;
|
||||
in lib.fix (self: {
|
||||
/** Perform module-system type checking and resolving on a single option value. */
|
||||
typeCheck = loc: option: value:
|
||||
assert lib.isOptionType option;
|
||||
assert lib.isList loc;
|
||||
assert lib.all lib.isString loc;
|
||||
let
|
||||
merged = lib.modules.mergeDefinitions loc option [ {
|
||||
inherit value;
|
||||
file = "«inline»";
|
||||
} ];
|
||||
in merged.mergedValue;
|
||||
|
||||
/** Either a list of strings, or a dotted string that will be split. */
|
||||
convenientAttrPath = t.coercedTo t.str (lib.splitString ".") (t.listOf t.str);
|
||||
|
||||
concatFoldl = f: list: lib.foldl' (acc: value: acc ++ (f value)) [ ] list;
|
||||
recUpdateFoldl = f: list: lib.foldl' (acc: value: lib.recursiveUpdate acc (f value)) { } list;
|
||||
recUpdateFoldlAttrs = f: attrs: lib.foldlAttrs (acc: name: value: lib.recursiveUpdate acc (f name value)) { } attrs;
|
||||
})
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
{
|
||||
name,
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib.modules)
|
||||
mkIf
|
||||
;
|
||||
inherit (lib.options)
|
||||
mkOption
|
||||
mkEnableOption
|
||||
literalExpression
|
||||
;
|
||||
inherit (lib.types)
|
||||
mkOptionType
|
||||
;
|
||||
t = lib.types;
|
||||
|
||||
/** Either a list of strings, or a dotted string that will be split. */
|
||||
convenientAttrPath = t.coercedTo t.str (lib.splitString ".") (t.listOf t.str);
|
||||
|
||||
executablePathInStore = mkOptionType {
|
||||
name = "exepath";
|
||||
description = "executable path in the Nix store";
|
||||
descriptionClass = "noun";
|
||||
merge = lib.mergeEqualOption;
|
||||
functor = lib.defaultFunctor "exepath";
|
||||
check = x: if lib.isDerivation x then (
|
||||
x.meta.mainProgram or null != null
|
||||
) else (
|
||||
lib.pathInStore.check x
|
||||
);
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
enable = mkEnableOption "dynamicism for '${name}'";
|
||||
|
||||
source-options = mkOption {
|
||||
type = t.listOf convenientAttrPath;
|
||||
description = "A list of attrpaths of the NixOS option the dynamicism for '${name}' uses";
|
||||
example = literalExpression ''
|
||||
[ [ "services" "gotosocial" "settings" ] "services.nginx.settings" ]
|
||||
'';
|
||||
};
|
||||
|
||||
systemd-services-updated = mkOption {
|
||||
type = t.listOf t.str;
|
||||
description = ''
|
||||
A list of systemd unit names (including the suffix, e.g. `.service`) that need to be updated.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
[ "gotosocial.service" ]
|
||||
'';
|
||||
|
||||
default = lib.attrNames config.unitDropins;
|
||||
};
|
||||
|
||||
configFile = mkOption {
|
||||
type = t.pathInStore;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
unitDropins = mkOption {
|
||||
type = t.attrsOf t.pathInStore;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
activate = mkOption {
|
||||
type = executablePathInStore;
|
||||
internal = true;
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf config.enable {
|
||||
activate = pkgs.writeShellApplication {
|
||||
name = "dynamicism-for-${name}-activate";
|
||||
runtimeInputs = [ pkgs.systemd ];
|
||||
text = let
|
||||
doEdits = config.unitDropins
|
||||
|> lib.mapAttrsToList (service: dropin: ''
|
||||
cat "${dropin}" | systemctl edit "${service}" --runtime --stdin
|
||||
'');
|
||||
doReloads = config.unitDropins
|
||||
|> lib.mapAttrsToList (service: _: ''
|
||||
systemctl reload-or-restart "${service}"
|
||||
'');
|
||||
in [
|
||||
doEdits
|
||||
doReloads
|
||||
] |> lib.concatLists
|
||||
|> lib.concatStringsSep "\n";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
nix.package = pkgs.lixPackageSets.latest.lix;
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#import re
|
||||
from pathlib import Path
|
||||
from typing import cast, TYPE_CHECKING
|
||||
|
||||
from test_driver.machine import Machine
|
||||
from test_driver.errors import RequestedAssertionFailed
|
||||
|
||||
DEFAULT_NIX = "@DEFAULT_NIX@"
|
||||
CONFIGURATION_NIX = "@CONFIGURATION_NIX@"
|
||||
DYNAMICISM = "@DYNAMICISM@"
|
||||
|
||||
if TYPE_CHECKING:
|
||||
global machine
|
||||
machine = cast(Machine, ...)
|
||||
|
||||
def run_log(machine: Machine, *commands: str, timeout: int | None = 60) -> str:
|
||||
output = ""
|
||||
for command in commands:
|
||||
with machine.nested(f"must succeed: {command}"):
|
||||
(status, out) = machine.execute(f"{command} | tee /dev/stderr", timeout=timeout)
|
||||
if status != 0:
|
||||
machine.log(f"output: {out}")
|
||||
raise RequestedAssertionFailed(
|
||||
f"command `{command}` failed (exit code {status})",
|
||||
)
|
||||
output += out
|
||||
|
||||
return output
|
||||
|
||||
def get_config_file() -> Path:
|
||||
machine.wait_for_unit("gotosocial.service")
|
||||
gotosocial_pid = int(machine.get_unit_property("gotosocial.service", "MainPID"))
|
||||
print(f"{gotosocial_pid=}")
|
||||
|
||||
cmdline = machine.succeed(f"cat /proc/{gotosocial_pid}/cmdline")
|
||||
cmdline_args = cmdline.split("\0")
|
||||
|
||||
config_file_idx = cmdline_args.index("--config-path") + 1
|
||||
config_file = Path(cmdline_args[config_file_idx])
|
||||
|
||||
machine.log(f"copying from VM: {config_file=}")
|
||||
machine.copy_from_vm(config_file.as_posix())
|
||||
|
||||
return machine.out_dir / config_file.name
|
||||
|
||||
|
||||
machine.wait_for_unit("default.target")
|
||||
assert "lix" in run_log(machine, "nix --version").lower()
|
||||
|
||||
print(f"{CONFIGURATION_NIX=}")
|
||||
machine.succeed("mkdir -vp /etc/nixos")
|
||||
machine.copy_from_host(CONFIGURATION_NIX, "/etc/nixos")
|
||||
machine.copy_from_host(DYNAMICISM, "/etc/nixos")
|
||||
|
||||
run_log(machine, f"nix build --log-format multiline-with-logs --impure -E 'import <nixpkgs/nixos> {{ configuration = {CONFIGURATION_NIX}; }}'")
|
||||
run_log(machine, f"nixos-rebuild switch --file {CONFIGURATION_NIX} --verbose --print-build-logs")
|
||||
|
||||
config_file_local = get_config_file()
|
||||
machine.log(f"opening copied file: {config_file_local=}")
|
||||
with open(config_file_local, "r") as f:
|
||||
text = f.read()
|
||||
lines = text.splitlines()
|
||||
application_name = next(line for line in lines if line.startswith("application-name:"))
|
||||
assert "gotosocial-for-machine" in application_name, f"'gotosocial-for-machine' should be in {application_name=}"
|
||||
|
||||
print(f"{DEFAULT_NIX=}")
|
||||
|
||||
run_log(machine, "eza -lah --color=always --group-directories-first --tree /etc/")
|
||||
|
||||
#exec_start = machine.succeed("systemctl show gotosocial.service --property=ExecStart --value")
|
||||
#exec_start = machine.succeed("systemctl show gotosocial.service --property=ExecStart --value")
|
||||
|
||||
#service_text = machine.succeed("systemctl show gotosocial.service")
|
||||
#service_props = dict(line.split("=", maxsplit=1) for line in service_text.splitlines())
|
||||
#exec_start = service_props['ExecStart']
|
||||
#print(f"{exec_start=}")
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
testers,
|
||||
}:
|
||||
let
|
||||
inherit (testers) runNixOSTest;
|
||||
in lib.makeScope lib.callPackageWith (self: {
|
||||
main = runNixOSTest {
|
||||
name = "nixos-test-dynamicism-main";
|
||||
|
||||
defaults = { pkgs, ... }: {
|
||||
imports = [ ./dynamicism ];
|
||||
|
||||
nix = {
|
||||
package = pkgs.lixPackageSets.latest.lix;
|
||||
settings.experimental-features = [ "nix-command" ];
|
||||
nixPath = [ "nixpkgs=${pkgs.path}" ];
|
||||
};
|
||||
|
||||
environment.shellAliases = {
|
||||
ls = "eza --long --header --group --group-directories-first --classify --binary";
|
||||
};
|
||||
environment.systemPackages = with pkgs; [
|
||||
eza
|
||||
fd
|
||||
ripgrep
|
||||
];
|
||||
};
|
||||
|
||||
nodes.machine = { name, ... }: {
|
||||
#services.gotosocial = {
|
||||
# enable = true;
|
||||
# setupPostgresqlDB = true;
|
||||
# settings = {
|
||||
# application-name = "gotosocial-for-${name}";
|
||||
# host = "${name}.local";
|
||||
# };
|
||||
#};
|
||||
#
|
||||
#dynamicism.for.gotosocial.enable = true;
|
||||
};
|
||||
|
||||
# What's a little IFD between friends?
|
||||
testScript = pkgs.replaceVars ./tests-main.py {
|
||||
DEFAULT_NIX = ../default.nix;
|
||||
CONFIGURATION_NIX = ./tests-configuration.nix;
|
||||
DYNAMICISM = ./dynamicism;
|
||||
}
|
||||
|> builtins.readFile;
|
||||
};
|
||||
})
|
||||
|
|
@ -81,6 +81,6 @@ in stdenv.mkDerivation (self: {
|
|||
});
|
||||
|
||||
meta = {
|
||||
mainProgram = "dynix";
|
||||
mainProgram = "PKGNAME";
|
||||
};
|
||||
}))
|
||||
|
|
|
|||
14
shell.nix
14
shell.nix
|
|
@ -3,21 +3,15 @@
|
|||
qpkgs ? let
|
||||
src = fetchTarball "https://github.com/Qyriad/nur-packages/archive/main.tar.gz";
|
||||
in import src { inherit pkgs; },
|
||||
dynix ? import ./default.nix { inherit pkgs qpkgs; },
|
||||
PKGNAME ? import ./default.nix { inherit pkgs qpkgs; },
|
||||
}: let
|
||||
inherit (pkgs) lib;
|
||||
|
||||
mkDevShell = dynix: qpkgs.callPackage dynix.mkDevShell { };
|
||||
devShell = mkDevShell dynix;
|
||||
mkDevShell = PKGNAME: qpkgs.callPackage PKGNAME.mkDevShell { };
|
||||
devShell = mkDevShell PKGNAME;
|
||||
|
||||
byStdenv = lib.mapAttrs (lib.const mkDevShell) dynix.byStdenv;
|
||||
byStdenv = lib.mapAttrs (lib.const mkDevShell) PKGNAME.byStdenv;
|
||||
|
||||
in devShell.overrideAttrs (prev: lib.recursiveUpdate prev {
|
||||
passthru = { inherit byStdenv; };
|
||||
env.PYTHONPATH = [
|
||||
"${pkgs.python3Packages.beartype}/${pkgs.python3.sitePackages}"
|
||||
] |> lib.concatStringsSep ":";
|
||||
packages = prev.packages or [ ] ++ [
|
||||
pkgs.python3Packages.beartype
|
||||
];
|
||||
})
|
||||
|
|
|
|||
42
src/args.rs
42
src/args.rs
|
|
@ -1,5 +1,3 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use clap::ColorChoice;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
|
@ -49,39 +47,18 @@ impl FromStr for NixOsOption {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, clap::Parser)]
|
||||
pub struct AppendCmd {
|
||||
#[arg(required = true)]
|
||||
pub name: Arc<str>,
|
||||
#[arg(required = true)]
|
||||
pub value: Arc<str>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, clap::Parser)]
|
||||
pub struct DeltaCmd {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, clap::Subcommand)]
|
||||
#[command(flatten_help = true)]
|
||||
pub enum Subcommand {
|
||||
Append(AppendCmd),
|
||||
// TODO: rename
|
||||
Delta(DeltaCmd),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, clap::Parser)]
|
||||
#[command(version, about, author)]
|
||||
#[command(arg_required_else_help(true), args_override_self(true))]
|
||||
#[command(propagate_version = true)]
|
||||
pub struct Args {
|
||||
#[arg(long, global(true), default_value = "auto")]
|
||||
#[command(version, about, author, arg_required_else_help(true))]
|
||||
pub struct Parser {
|
||||
#[arg(long, default_value = "auto")]
|
||||
pub color: ColorChoice,
|
||||
|
||||
// FIXME: default to /etc/configuration.nix, or something?
|
||||
#[arg(long, global(true), default_value = "./configuration.nix")]
|
||||
pub file: Arc<OsStr>,
|
||||
#[arg(long)]
|
||||
pub file: Box<OsStr>,
|
||||
|
||||
#[command(subcommand)]
|
||||
pub subcommand: Subcommand,
|
||||
}
|
||||
#[arg(required = true)]
|
||||
pub name: Box<str>,
|
||||
#[arg(required = true)]
|
||||
pub value: Box<str>,
|
||||
///// Flakeref to a base configuration to modify.
|
||||
//#[arg(group = "config", long, default_value("."))]
|
||||
//#[arg(long, default_value(Some(".")))]
|
||||
|
|
@ -89,6 +66,7 @@ pub struct Args {
|
|||
//
|
||||
//#[arg(group = "config", long)]
|
||||
//expr: Option<String>,
|
||||
}
|
||||
|
||||
//impl Parser {
|
||||
// fn eval_cmd(&self) {
|
||||
|
|
|
|||
15
src/color.rs
15
src/color.rs
|
|
@ -6,14 +6,10 @@ use std::{
|
|||
#[allow(unused_imports)]
|
||||
use crate::prelude::*;
|
||||
|
||||
/// The actual, final value for whether color should be used, based on CLI and environment values.
|
||||
pub static SHOULD_COLOR: LazyLock<bool> = LazyLock::new(|| is_clicolor_forced() || is_color_reqd());
|
||||
|
||||
/// Initialized from the `--color` value from the CLI, along with `io::stdin().is_terminal()`.
|
||||
pub static _CLI_ENABLE_COLOR: OnceLock<bool> = OnceLock::new();
|
||||
pub static CLI_ENABLE_COLOR: OnceLock<bool> = OnceLock::new();
|
||||
|
||||
fn is_color_reqd() -> bool {
|
||||
_CLI_ENABLE_COLOR.get().copied().unwrap_or(false)
|
||||
CLI_ENABLE_COLOR.get().copied().unwrap_or(false)
|
||||
}
|
||||
|
||||
fn is_clicolor_forced() -> bool {
|
||||
|
|
@ -28,6 +24,8 @@ fn is_clicolor_forced() -> bool {
|
|||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub static SHOULD_COLOR: LazyLock<bool> = LazyLock::new(|| is_clicolor_forced() || is_color_reqd());
|
||||
|
||||
/// Silly wrapper around LazyLock<&'static str> to impl Display.
|
||||
pub(crate) struct _LazyLockDisplay(LazyLock<&'static str>);
|
||||
impl Display for _LazyLockDisplay {
|
||||
|
|
@ -49,5 +47,8 @@ pub(crate) const ANSI_CYAN: _LazyLockDisplay = _LazyLockDisplay(LazyLock::new(||
|
|||
}));
|
||||
|
||||
pub(crate) const ANSI_RESET: _LazyLockDisplay = _LazyLockDisplay(LazyLock::new(|| {
|
||||
SHOULD_COLOR.then_some("\x1b[0m").unwrap_or_default()
|
||||
SHOULD_COLOR
|
||||
// C'mon rustfmt, just format it to match ^.
|
||||
.then_some("\x1b[0m")
|
||||
.unwrap_or_default()
|
||||
}));
|
||||
|
|
|
|||
166
src/lib.rs
166
src/lib.rs
|
|
@ -1,4 +1,4 @@
|
|||
use std::{iter, sync::Arc};
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
pub(crate) mod prelude {
|
||||
#![allow(unused_imports)]
|
||||
|
|
@ -25,18 +25,14 @@ pub(crate) mod prelude {
|
|||
pub use fs_err::File;
|
||||
#[cfg(unix)]
|
||||
pub use fs_err::os::unix::fs::{FileExt, OpenOptionsExt};
|
||||
|
||||
pub use tap::{Pipe, Tap};
|
||||
|
||||
pub use tracing::{Level, debug, error, info, trace, warn};
|
||||
}
|
||||
|
||||
use prelude::*;
|
||||
|
||||
pub mod args;
|
||||
pub use args::{AppendCmd, Args, DeltaCmd};
|
||||
pub use args::Parser;
|
||||
mod color;
|
||||
pub use color::{_CLI_ENABLE_COLOR, SHOULD_COLOR};
|
||||
pub use color::{CLI_ENABLE_COLOR, SHOULD_COLOR};
|
||||
pub mod line;
|
||||
mod nixcmd;
|
||||
pub use line::Line;
|
||||
|
|
@ -49,67 +45,23 @@ use crate::source::SourceFile;
|
|||
|
||||
pub const ASCII_WHITESPACE: &[char] = &['\t', '\n', '\x0C', '\r', ' '];
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
pub fn do_delta(args: Arc<Args>, delta_args: DeltaCmd) -> Result<(), BoxDynError> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
pub fn do_append(args: Arc<Args>, append_args: AppendCmd) -> Result<(), BoxDynError> {
|
||||
let filepath = Path::new(&args.file);
|
||||
let filepath: PathBuf = if filepath.is_relative() && !filepath.starts_with("./") {
|
||||
iter::once(OsStr::new("./"))
|
||||
.chain(filepath.iter())
|
||||
.collect()
|
||||
} else {
|
||||
filepath.to_path_buf()
|
||||
};
|
||||
|
||||
// Get what file that thing is defined in.
|
||||
let def_path = get_where(&append_args.name, &filepath)?;
|
||||
|
||||
let mut opts = File::options();
|
||||
opts.read(true)
|
||||
.write(true)
|
||||
.create(false)
|
||||
.custom_flags(libc::O_CLOEXEC);
|
||||
let source_file = SourceFile::open_from(Arc::from(def_path), opts)?;
|
||||
|
||||
let pri = get_highest_prio(&append_args.name, source_file.clone())?;
|
||||
let new_pri = pri - 1;
|
||||
|
||||
let new_pri_line = get_next_prio_line(
|
||||
source_file.clone(),
|
||||
append_args.name.into(),
|
||||
new_pri,
|
||||
append_args.value.into(),
|
||||
)?;
|
||||
|
||||
eprintln!("new_pri_line={new_pri_line}");
|
||||
|
||||
write_next_prio(source_file, new_pri_line)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct DefinitionWithLocation {
|
||||
pub file: Box<Path>,
|
||||
pub value: Box<serde_json::Value>,
|
||||
}
|
||||
|
||||
pub fn expr_for_configuration(source_file: &Path) -> OsString {
|
||||
[
|
||||
OsStr::new("import <nixpkgs/nixos> { configuration = "),
|
||||
source_file.as_os_str(),
|
||||
OsStr::new("; }"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
pub value: Box<str>,
|
||||
}
|
||||
|
||||
pub fn get_where(option_name: &str, configuration_nix: &Path) -> Result<Box<Path>, BoxDynError> {
|
||||
let expr = expr_for_configuration(configuration_nix);
|
||||
let expr: OsString = [
|
||||
// foo
|
||||
OsStr::new("import <nixpkgs/nixos> { configuration = "),
|
||||
configuration_nix.as_os_str(),
|
||||
OsStr::new("; }"),
|
||||
]
|
||||
.into_iter()
|
||||
.map(ToOwned::to_owned)
|
||||
.collect();
|
||||
|
||||
let attrpath = format!("options.{}.definitionsWithLocations", option_name);
|
||||
|
||||
let output = nixcmd::NixEvalExpr { expr, attrpath }
|
||||
|
|
@ -123,10 +75,19 @@ pub fn get_where(option_name: &str, configuration_nix: &Path) -> Result<Box<Path
|
|||
Ok(Box::from(last_location.file))
|
||||
}
|
||||
|
||||
pub fn get_highest_prio(option_name: &str, source: SourceFile) -> Result<i64, BoxDynError> {
|
||||
pub fn get_highest_prio(
|
||||
option_name: &str,
|
||||
source: SourceFile,
|
||||
) -> Result<(i64, SourceLine), BoxDynError> {
|
||||
// Get the current highest priority.
|
||||
|
||||
let expr = expr_for_configuration(&source.path());
|
||||
let expr: OsString = [
|
||||
OsStr::new("import <nixpkgs/nixos> { configuration = "),
|
||||
source.path().as_os_str(),
|
||||
OsStr::new("; }"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
// Get the highest priority, and the file its defined in.
|
||||
let attrpath = format!("options.{}.highestPrio", option_name);
|
||||
|
|
@ -136,44 +97,93 @@ pub fn get_highest_prio(option_name: &str, source: SourceFile) -> Result<i64, Bo
|
|||
let stdout = output.stdout();
|
||||
let highest_prio = i64::from_str(stdout.trim())?;
|
||||
|
||||
Ok(highest_prio)
|
||||
let needle = format!("lib.mkOverride ({})", highest_prio);
|
||||
|
||||
let path = source.path();
|
||||
let lines = source.lines()?;
|
||||
let line = lines
|
||||
.iter()
|
||||
// We're more likely to find it at the end, so let's start there.
|
||||
.rev()
|
||||
.find(|&line| line.text.contains(&needle))
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"couldn't find override number {highest_prio} in {}",
|
||||
path.display(),
|
||||
)
|
||||
});
|
||||
|
||||
Ok((highest_prio, line.clone()))
|
||||
}
|
||||
|
||||
pub fn get_next_prio_line(
|
||||
source: SourceFile,
|
||||
option_name: Arc<str>,
|
||||
last_line_def: SourceLine,
|
||||
new_prio: i64,
|
||||
new_value: Arc<str>,
|
||||
) -> Result<SourceLine, BoxDynError> {
|
||||
let source_lines = source.lines()?;
|
||||
let last_line = source_lines.last();
|
||||
assert_eq!(last_line.map(SourceLine::text).as_deref(), Some("]"));
|
||||
let last_line = last_line.unwrap();
|
||||
if !last_line_def.text.ends_with(';') {
|
||||
todo!();
|
||||
}
|
||||
let next_line = source.line(last_line_def.line.next())?;
|
||||
if next_line.text.trim() != "}" {
|
||||
todo!();
|
||||
}
|
||||
|
||||
let (indentation, _rest) = last_line_def.text.split_at(
|
||||
last_line_def
|
||||
.text
|
||||
.find(|ch: char| !ch.is_ascii_whitespace())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
// FIXME: fix indentation
|
||||
let new_text = format!("{indentation}{option_name} = lib.mkOverride ({new_prio}) ({new_value});",);
|
||||
|
||||
let new_line = SourceLine {
|
||||
line: last_line.line,
|
||||
line: next_line.line.next(),
|
||||
path: source.path(),
|
||||
text: Arc::from(format!(
|
||||
" {option_name} = lib.mkOverride ({new_prio}) ({new_value});",
|
||||
)),
|
||||
text: Arc::from(new_text),
|
||||
};
|
||||
|
||||
Ok(new_line)
|
||||
}
|
||||
|
||||
pub fn write_next_prio(mut source: SourceFile, new_line: SourceLine) -> Result<(), BoxDynError> {
|
||||
pub fn write_next_prio(
|
||||
mut source: SourceFile,
|
||||
last_line_def: SourceLine,
|
||||
new_text: Arc<str>,
|
||||
) -> Result<(), BoxDynError> {
|
||||
//let lines = source.lines()?;
|
||||
|
||||
let open_brace_line = source.line(last_line_def.line.prev())?.text();
|
||||
let close_brace_line = source.line(last_line_def.line.next())?.text();
|
||||
|
||||
let new_mod_start = SourceLine {
|
||||
line: new_line.line.prev(),
|
||||
line: last_line_def.line.next(),
|
||||
path: source.path(),
|
||||
text: Arc::from(" {"),
|
||||
text: open_brace_line,
|
||||
};
|
||||
let new_line = SourceLine {
|
||||
line: new_mod_start.line.next(),
|
||||
path: source.path(),
|
||||
text: Arc::from(new_text),
|
||||
};
|
||||
let new_mod_end = SourceLine {
|
||||
line: new_line.line.next(),
|
||||
path: source.path(),
|
||||
text: Arc::from(" }"),
|
||||
text: close_brace_line,
|
||||
};
|
||||
|
||||
source.insert_lines(&[new_mod_start, new_line, new_mod_end])?;
|
||||
dbg!(&new_mod_start.text());
|
||||
|
||||
source.insert_lines(&[
|
||||
new_mod_start,
|
||||
new_line,
|
||||
new_mod_end,
|
||||
])?;
|
||||
|
||||
//source.insert_line(new_line.line, new_line.text())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
52
src/main.rs
52
src/main.rs
|
|
@ -1,16 +1,17 @@
|
|||
use std::io::{self, IsTerminal};
|
||||
use std::path::Path;
|
||||
use std::process::ExitCode;
|
||||
use std::{error::Error as StdError, sync::Arc};
|
||||
|
||||
use append_override::source::SourceFile;
|
||||
use clap::{ColorChoice, Parser as _};
|
||||
use tracing_human_layer::HumanLayer;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::{EnvFilter, layer::SubscriberExt};
|
||||
use fs_err::File;
|
||||
use fs_err::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
fn main_wrapped() -> Result<(), Box<dyn StdError + Send + Sync + 'static>> {
|
||||
let args = Arc::new(dynix::Args::parse());
|
||||
let args = append_override::Parser::parse();
|
||||
|
||||
let success = dynix::_CLI_ENABLE_COLOR.set(match args.color {
|
||||
let success = append_override::CLI_ENABLE_COLOR.set(match args.color {
|
||||
ColorChoice::Always => true,
|
||||
ColorChoice::Auto => io::stdin().is_terminal(),
|
||||
ColorChoice::Never => false,
|
||||
|
|
@ -19,20 +20,35 @@ fn main_wrapped() -> Result<(), Box<dyn StdError + Send + Sync + 'static>> {
|
|||
success.expect("logic error in CLI_ENABLE_COLOR");
|
||||
}
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(HumanLayer::new().with_color_output(*dynix::SHOULD_COLOR))
|
||||
.with(EnvFilter::from_default_env())
|
||||
.init();
|
||||
// FIXME: handle relative paths without leading ./
|
||||
let filepath = Path::new(&args.file);
|
||||
|
||||
tracing::debug!("Parsed command-line arguments: {args:?}");
|
||||
// Get what file that thing is defined in.
|
||||
let def_path = append_override::get_where(&args.name, filepath)?;
|
||||
let def_path = Arc::from(def_path);
|
||||
let mut opts = File::options();
|
||||
opts.read(true)
|
||||
.write(true)
|
||||
.create(false)
|
||||
.custom_flags(libc::O_CLOEXEC);
|
||||
let source_file = SourceFile::open_from(Arc::clone(&def_path), opts)?;
|
||||
|
||||
{
|
||||
use dynix::args::Subcommand::*;
|
||||
match &args.subcommand {
|
||||
Append(append_args) => dynix::do_append(args.clone(), append_args.clone())?,
|
||||
Delta(delta_args) => dynix::do_delta(args.clone(), delta_args.clone())?,
|
||||
};
|
||||
}
|
||||
let (pri, last_def_line) = append_override::get_highest_prio(&args.name, source_file.clone())?;
|
||||
let new_pri = pri - 1;
|
||||
|
||||
eprintln!("{last_def_line}");
|
||||
|
||||
let new_pri_line = append_override::get_next_prio_line(
|
||||
source_file.clone(),
|
||||
args.name.into(),
|
||||
last_def_line.clone(),
|
||||
new_pri,
|
||||
args.value.into(),
|
||||
)?;
|
||||
|
||||
eprintln!("new_pri_line={new_pri_line}");
|
||||
|
||||
append_override::write_next_prio(source_file, last_def_line, new_pri_line.text())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -41,7 +57,7 @@ fn main() -> ExitCode {
|
|||
match main_wrapped() {
|
||||
Ok(_) => ExitCode::SUCCESS,
|
||||
Err(e) => {
|
||||
eprintln!("dynix: error: {}", e);
|
||||
eprintln!("append-override: error: {}", e);
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
|
|
|
|||
172
src/source.rs
172
src/source.rs
|
|
@ -1,10 +1,10 @@
|
|||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
cell::{Cell, Ref, RefCell},
|
||||
hash::Hash,
|
||||
io::{BufRead, BufReader, BufWriter},
|
||||
ops::Deref,
|
||||
ptr,
|
||||
sync::{Arc, Mutex, OnceLock},
|
||||
iter, mem,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::{Arc, Mutex, MutexGuard, OnceLock, PoisonError, RwLock},
|
||||
};
|
||||
|
||||
use crate::Line;
|
||||
|
|
@ -13,33 +13,6 @@ use crate::color::{ANSI_CYAN, ANSI_GREEN, ANSI_MAGENTA, ANSI_RESET};
|
|||
use crate::prelude::*;
|
||||
|
||||
use fs_err::OpenOptions;
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn replace_file<'a>(
|
||||
path: &Path,
|
||||
contents: impl IntoIterator<Item = &'a [u8]>,
|
||||
) -> Result<(), IoError> {
|
||||
let tmp_path = path.with_added_extension(".tmp");
|
||||
let tmp_file = File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.custom_flags(libc::O_EXCL | libc::O_CLOEXEC)
|
||||
.open(&tmp_path)?;
|
||||
|
||||
let mut writer = BufWriter::new(tmp_file);
|
||||
for slice in contents {
|
||||
writer.write_all(slice)?;
|
||||
}
|
||||
|
||||
writer.flush()?;
|
||||
drop(writer);
|
||||
|
||||
// Rename the temporary file to the new file, which is atomic (TODO: I think).
|
||||
fs_err::rename(&tmp_path, &path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
pub struct SourceLine {
|
||||
|
|
@ -52,30 +25,6 @@ impl SourceLine {
|
|||
pub fn text(&self) -> Arc<str> {
|
||||
Arc::clone(&self.text)
|
||||
}
|
||||
|
||||
pub fn text_ref(&self) -> &str {
|
||||
&self.text
|
||||
}
|
||||
|
||||
pub fn text_bytes(&self) -> Arc<[u8]> {
|
||||
let len: usize = self.text.as_bytes().len();
|
||||
|
||||
// We need to consume an Arc, but we are &self.
|
||||
let text = Arc::clone(&self.text);
|
||||
let str_ptr: *const str = Arc::into_raw(text);
|
||||
let start: *const u8 = str_ptr.cast();
|
||||
let slice_ptr: *const [u8] = ptr::slice_from_raw_parts(start, len);
|
||||
|
||||
unsafe { Arc::<[u8]>::from_raw(slice_ptr) }
|
||||
}
|
||||
|
||||
pub fn text_bytes_ref(&self) -> &[u8] {
|
||||
self.text.as_bytes()
|
||||
}
|
||||
|
||||
pub fn path(&self) -> Arc<Path> {
|
||||
Arc::clone(&self.path)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SourceLine {
|
||||
|
|
@ -90,6 +39,11 @@ impl Display for SourceLine {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
pub struct SourcePath {
|
||||
path: Arc<Path>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SourceFile {
|
||||
path: Arc<Path>,
|
||||
|
|
@ -102,11 +56,6 @@ pub struct SourceFile {
|
|||
impl SourceFile {
|
||||
/// Panics if `path` is a directory path instead of a file path.
|
||||
pub fn open_from(path: Arc<Path>, options: OpenOptions) -> Result<Self, IoError> {
|
||||
trace!(
|
||||
"SourceFile::open_from(path={:?}, options={:?})",
|
||||
path,
|
||||
options.options(),
|
||||
);
|
||||
assert!(path.file_name().is_some());
|
||||
|
||||
let file = Arc::new(Mutex::new(options.open(&*path)?));
|
||||
|
|
@ -177,7 +126,6 @@ impl SourceFile {
|
|||
if new_lines.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let num_lines_before_new = new_lines.last().unwrap().line.prev().index() as usize;
|
||||
|
||||
debug_assert!(new_lines.is_sorted_by(|lhs, rhs| lhs.line.next() == rhs.line));
|
||||
|
||||
|
|
@ -185,12 +133,12 @@ impl SourceFile {
|
|||
let cur_lines = self.lines()?;
|
||||
let first_half = cur_lines
|
||||
.iter()
|
||||
.take(num_lines_before_new)
|
||||
.take(new_lines.last().unwrap().line.prev().index() as usize)
|
||||
.map(SourceLine::text);
|
||||
let middle = new_lines.iter().map(SourceLine::text);
|
||||
let second_half = cur_lines
|
||||
.iter()
|
||||
.skip(num_lines_before_new)
|
||||
.skip(new_lines.last().unwrap().line.prev().index() as usize)
|
||||
.map(SourceLine::text);
|
||||
|
||||
let final_lines: Vec<SourceLine> = first_half
|
||||
|
|
@ -210,11 +158,31 @@ impl SourceFile {
|
|||
|
||||
drop(cur_lines);
|
||||
|
||||
let data = final_lines
|
||||
.iter()
|
||||
.map(SourceLine::text_bytes_ref)
|
||||
.pipe(|iterator| Itertools::intersperse(iterator, b"\n"));
|
||||
replace_file(&path, data)?;
|
||||
// Write it to a file in the same directory.
|
||||
let new_name: OsString = [
|
||||
// foo
|
||||
path.file_name().unwrap(),
|
||||
OsStr::new(".tmp"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<OsString>();
|
||||
let tmp_path = path.with_file_name(&new_name);
|
||||
let tmp_file = File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.custom_flags(libc::O_EXCL | libc::O_CLOEXEC)
|
||||
.open(&tmp_path)?;
|
||||
|
||||
let mut writer = BufWriter::new(tmp_file);
|
||||
for line in final_lines.iter() {
|
||||
writer.write_all(line.text().as_bytes())?;
|
||||
writer.write_all(b"\n")?;
|
||||
}
|
||||
writer.flush()?;
|
||||
drop(writer);
|
||||
// Rename the temporary file to the new file, which is atomic (TODO: I think).
|
||||
fs_err::rename(&tmp_path, &path)?;
|
||||
|
||||
// Finally, update state.
|
||||
self.lines.get().unwrap().replace(final_lines);
|
||||
|
|
@ -222,6 +190,74 @@ impl SourceFile {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_line(&mut self, at: Line, text: Arc<str>) -> Result<(), IoError> {
|
||||
self.lines()?;
|
||||
let path = self.path();
|
||||
|
||||
//let lines = Arc::get_mut(&mut self.lines).unwrap().get_mut().unwrap();
|
||||
let lines_guard = self.lines.get().unwrap().borrow();
|
||||
let lines = &*lines_guard;
|
||||
let first_half = lines.iter().take(at.index() as usize).map(SourceLine::text);
|
||||
let second_half = lines.iter().skip(at.index() as usize).map(SourceLine::text);
|
||||
|
||||
let new_lines: Vec<SourceLine> = first_half
|
||||
.chain(iter::once(Arc::clone(&text)))
|
||||
.chain(second_half)
|
||||
.enumerate()
|
||||
.map(|(idx, text)| SourceLine {
|
||||
line: Line::from_index(idx as u64),
|
||||
text,
|
||||
path: Arc::clone(&path),
|
||||
})
|
||||
.collect();
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
assert_eq!(new_lines.len(), lines.len() + 1);
|
||||
let newly = new_lines.get(at.index() as usize);
|
||||
assert_eq!(newly.map(SourceLine::text), Some(text));
|
||||
|
||||
// Assert lines are continuous.
|
||||
let linenrs: Vec<Line> = new_lines
|
||||
.iter()
|
||||
.map(|source_line| source_line.line)
|
||||
.collect();
|
||||
assert!(linenrs.is_sorted());
|
||||
}
|
||||
|
||||
// Write it to a file in the same directory.
|
||||
let new_name: OsString = [
|
||||
// foo
|
||||
path.file_name().unwrap(),
|
||||
OsStr::new(".tmp"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<OsString>();
|
||||
let tmp_path = path.with_file_name(&new_name);
|
||||
let tmp_file = File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.custom_flags(libc::O_EXCL | libc::O_CLOEXEC)
|
||||
.open(&tmp_path)?;
|
||||
|
||||
let mut writer = BufWriter::new(tmp_file);
|
||||
for line in new_lines.iter() {
|
||||
writer.write_all(line.text().as_bytes())?;
|
||||
writer.write_all(b"\n")?;
|
||||
}
|
||||
writer.flush()?;
|
||||
drop(writer);
|
||||
// Rename the temporary file to the new file, which is atomic (TODO: I think).
|
||||
fs_err::rename(&tmp_path, &path)?;
|
||||
|
||||
drop(lines_guard);
|
||||
let mut lines_guard = self.lines.get().unwrap().borrow_mut();
|
||||
// Finally, update state.
|
||||
let _old_lines = mem::replace(&mut *lines_guard, new_lines);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn path(&self) -> Arc<Path> {
|
||||
Arc::clone(&self.path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
runCommand,
|
||||
}: runCommand "tests-basic-configuration-dot-nix" {
|
||||
} ''
|
||||
set -euo pipefail
|
||||
mkdir -vp "$out/share/nixos"
|
||||
cp -rv ${./configuration.nix} "$out/share/nixos/configuration.nix"
|
||||
cp -rv ${../../modules/dynamicism} "$out/share/nixos/dynamicism"
|
||||
''
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
{ pkgs, lib, config, modulesPath, ... }:
|
||||
let
|
||||
name = config.networking.hostName;
|
||||
nixosLibPath = (modulesPath + "/../lib");
|
||||
moduleList = import (modulesPath + "/module-list.nix");
|
||||
|
||||
optionalPath = p: lib.optional (builtins.pathExists p) p;
|
||||
in
|
||||
assert builtins.pathExists nixosLibPath;
|
||||
builtins.seq lib
|
||||
builtins.seq modulesPath
|
||||
builtins.seq moduleList
|
||||
{
|
||||
imports = moduleList ++ [
|
||||
(modulesPath + "/testing/test-instrumentation.nix")
|
||||
] ++ lib.concatLists [
|
||||
(optionalPath ./hardware-configuration.nix)
|
||||
(optionalPath ./dynamicism)
|
||||
(optionalPath ../../modules/dynamicism)
|
||||
];
|
||||
|
||||
system.switch.enable = true;
|
||||
documentation.enable = false;
|
||||
|
||||
networking.hostName = "machine";
|
||||
|
||||
boot.loader.grub = {
|
||||
enable = true;
|
||||
device = "/dev/vda";
|
||||
forceInstall = true;
|
||||
};
|
||||
|
||||
nix = {
|
||||
package = pkgs.lixPackageSets.latest.lix;
|
||||
nixPath = [ "nixpkgs=${pkgs.path}" ];
|
||||
|
||||
settings = {
|
||||
experimental-features = [ "nix-command" "pipe-operator" ];
|
||||
substituters = lib.mkForce [ ];
|
||||
hashed-mirrors = null;
|
||||
connect-timeout = 1;
|
||||
};
|
||||
};
|
||||
|
||||
services.gotosocial = {
|
||||
enable = true;
|
||||
setupPostgresqlDB = true;
|
||||
settings = {
|
||||
application-name = "gotosocial-for-${name}";
|
||||
host = "${name}.local";
|
||||
};
|
||||
};
|
||||
|
||||
dynamicism.for.gotosocial.enable = true;
|
||||
|
||||
environment.pathsToLink = [ "/share" ];
|
||||
environment.variables = {
|
||||
"NIXOS_CONFIG" = "/etc/nixos/configuration.nix";
|
||||
};
|
||||
|
||||
environment.shellAliases = {
|
||||
ls = "eza --long --header --group --group-directories-first --classify --binary";
|
||||
};
|
||||
environment.systemPackages = with pkgs; [
|
||||
eza
|
||||
fd
|
||||
ripgrep
|
||||
];
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
/**
|
||||
* Convenience shortcut for running this test from the command-line.
|
||||
* Normally this test is initialized from /tests/default.nix.
|
||||
*/
|
||||
{
|
||||
pkgs ? import <nixpkgs> { },
|
||||
}: pkgs.testers.runNixOSTest ./test.nix
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
from pathlib import Path
|
||||
import shlex
|
||||
import textwrap
|
||||
from typing import cast, TYPE_CHECKING
|
||||
|
||||
from beartype import beartype
|
||||
|
||||
from test_driver.machine import Machine
|
||||
from test_driver.errors import RequestedAssertionFailed
|
||||
|
||||
if TYPE_CHECKING:
|
||||
global machine
|
||||
machine = cast(Machine, ...)
|
||||
assert machine.shell is not None
|
||||
|
||||
ls = "eza -lah --color=always --group-directories-first"
|
||||
|
||||
@beartype
|
||||
def run_log(machine: Machine, *commands: str, timeout: int | None = 60) -> str:
|
||||
output = ""
|
||||
for command in commands:
|
||||
with machine.nested(f"must succeed: {command}"):
|
||||
(status, out) = machine.execute(f"{command} | tee /dev/stderr", timeout=timeout)
|
||||
if status != 0:
|
||||
machine.log(f"output: {out}")
|
||||
raise RequestedAssertionFailed(
|
||||
f"command `{command}` failed (exit code {status})",
|
||||
)
|
||||
output += out
|
||||
|
||||
return output
|
||||
|
||||
@beartype
|
||||
def get_config_file() -> str:
|
||||
machine.wait_for_unit("gotosocial.service")
|
||||
gotosocial_pid = int(machine.get_unit_property("gotosocial.service", "MainPID"))
|
||||
print(f"{gotosocial_pid=}")
|
||||
|
||||
cmdline = machine.succeed(f"cat /proc/{gotosocial_pid}/cmdline")
|
||||
cmdline_args = cmdline.split("\0")
|
||||
|
||||
config_file_idx = cmdline_args.index("--config-path") + 1
|
||||
config_file = Path(cmdline_args[config_file_idx])
|
||||
|
||||
machine.log(f"copying from VM: {config_file=}")
|
||||
machine.copy_from_vm(config_file.as_posix())
|
||||
|
||||
config_file_path = machine.out_dir / config_file.name
|
||||
with open(config_file_path, "r") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
machine.wait_for_unit("default.target")
|
||||
assert "lix" in machine.succeed("nix --version").lower()
|
||||
|
||||
run_log(machine, "nixos-generate-config")
|
||||
machine.succeed("mkdir -vp /etc/nixos")
|
||||
machine.succeed("cp -rv /run/current-system/sw/share/nixos/* /etc/nixos/")
|
||||
machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs -v --fallback >&2")
|
||||
|
||||
config_text = get_config_file()
|
||||
lines = config_text.splitlines()
|
||||
application_name = next((line for line in lines if line.startswith("application-name:")), None)
|
||||
assert application_name is not None, f"no 'application-name:' found in config file: {textwrap.indent(config_text, "")}"
|
||||
assert "gotosocial-for-machine" in application_name, f"'gotosocial-for-machine' should be in {application_name=}"
|
||||
|
||||
new_app_name = "yay!"
|
||||
expr = textwrap.dedent(f"""
|
||||
let
|
||||
nixos = import <nixpkgs/nixos> {{ }};
|
||||
in nixos.config.dynamicism.doChange {{
|
||||
option = "services.gotosocial.settings.application-name";
|
||||
value = "{new_app_name}";
|
||||
}}
|
||||
""").strip()
|
||||
machine.succeed(rf"""
|
||||
nix run --show-trace --log-format raw-with-logs --impure -E {shlex.quote(expr)}
|
||||
""".strip())
|
||||
|
||||
config_file_new = get_config_file()
|
||||
lines = config_file_new.splitlines()
|
||||
|
||||
application_name = next(line for line in lines if line.startswith("application-name:"))
|
||||
assert new_app_name in application_name, f"'{new_app_name}' should be in {application_name=}"
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
{
|
||||
name = "nixos-test-dynamicism-main";
|
||||
|
||||
defaults = { ... }: { };
|
||||
|
||||
#node.pkgsReadOnly = false;
|
||||
|
||||
extraPythonPackages = p: [
|
||||
p.beartype
|
||||
];
|
||||
|
||||
nodes.machine = { pkgs, config, ... }: {
|
||||
imports = [ ./configuration.nix ];
|
||||
|
||||
system.includeBuildDependencies = true;
|
||||
system.switch.enable = true;
|
||||
|
||||
virtualisation.additionalPaths = [ config.system.build.toplevel ];
|
||||
virtualisation = {
|
||||
memorySize = 4096;
|
||||
cores = 4;
|
||||
writableStore = true;
|
||||
mountHostNixStore = true;
|
||||
installBootLoader = true;
|
||||
};
|
||||
|
||||
environment.systemPackages = let
|
||||
configFileTree = pkgs.callPackage ./configuration-package.nix { };
|
||||
in [
|
||||
configFileTree
|
||||
];
|
||||
};
|
||||
|
||||
# What's a little IFD between friends?
|
||||
testScript = ./test-script.py
|
||||
|> builtins.readFile;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
pkgs ? import <nixpkgs> { },
|
||||
lib ? pkgs.lib,
|
||||
}: lib.makeScope lib.callPackageWith (self: let
|
||||
inherit (pkgs.testers) runNixOSTest;
|
||||
in {
|
||||
basic = runNixOSTest ./basic/test.nix;
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue