diff --git a/Cargo.lock b/Cargo.lock index eef46d8..d5fd1ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index dd24d30..d0ea161 100644 --- a/Cargo.toml +++ b/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"] } diff --git a/configuration.nix b/configuration.nix deleted file mode 100644 index f34f2e5..0000000 --- a/configuration.nix +++ /dev/null @@ -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"; -} diff --git a/default.nix b/default.nix index b7a3bef..13e49d4 100644 --- a/default.nix +++ b/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; }; }) diff --git a/flake.nix b/flake.nix index 8e0dfb6..a577619 100644 --- a/flake.nix +++ b/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 // { diff --git a/modules/dynamic-overrides.nix b/modules/dynamic-overrides.nix deleted file mode 100644 index 1952e96..0000000 --- a/modules/dynamic-overrides.nix +++ /dev/null @@ -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"; - } -] diff --git a/modules/dynamicism/default.nix b/modules/dynamicism/default.nix deleted file mode 100644 index 8e4764c..0000000 --- a/modules/dynamicism/default.nix +++ /dev/null @@ -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 - ''; - }; -} diff --git a/modules/dynamicism/lib.nix b/modules/dynamicism/lib.nix deleted file mode 100644 index a988ae4..0000000 --- a/modules/dynamicism/lib.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ - lib ? import , -}: 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; -}) diff --git a/modules/dynamicism/submodule.nix b/modules/dynamicism/submodule.nix deleted file mode 100644 index 1b9dac9..0000000 --- a/modules/dynamicism/submodule.nix +++ /dev/null @@ -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"; - }; - }; -} diff --git a/modules/tests-common.nix b/modules/tests-common.nix deleted file mode 100644 index 322771e..0000000 --- a/modules/tests-common.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ pkgs, ... }: -{ - nix.package = pkgs.lixPackageSets.latest.lix; -} diff --git a/modules/tests-main.py b/modules/tests-main.py deleted file mode 100644 index dbd33a6..0000000 --- a/modules/tests-main.py +++ /dev/null @@ -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 {{ 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=}") diff --git a/modules/tests.nix b/modules/tests.nix deleted file mode 100644 index 917c7bd..0000000 --- a/modules/tests.nix +++ /dev/null @@ -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; - }; -}) diff --git a/package.nix b/package.nix index 2cb8cee..68878aa 100644 --- a/package.nix +++ b/package.nix @@ -81,6 +81,6 @@ in stdenv.mkDerivation (self: { }); meta = { - mainProgram = "dynix"; + mainProgram = "PKGNAME"; }; })) diff --git a/shell.nix b/shell.nix index c73c766..9e803f7 100644 --- a/shell.nix +++ b/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 - ]; }) diff --git a/src/args.rs b/src/args.rs index cc2fa07..8de2c39 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use clap::ColorChoice; use crate::prelude::*; @@ -49,46 +47,26 @@ impl FromStr for NixOsOption { } #[derive(Debug, Clone, PartialEq, clap::Parser)] -pub struct AppendCmd { - #[arg(required = true)] - pub name: Arc, - #[arg(required = true)] - pub value: Arc, -} - -#[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, + #[arg(long)] + pub file: Box, - #[command(subcommand)] - pub subcommand: Subcommand, + #[arg(required = true)] + pub name: Box, + #[arg(required = true)] + pub value: Box, + ///// Flakeref to a base configuration to modify. + //#[arg(group = "config", long, default_value("."))] + //#[arg(long, default_value(Some(".")))] + //flake: Option>>, + // + //#[arg(group = "config", long)] + //expr: Option, } -///// Flakeref to a base configuration to modify. -//#[arg(group = "config", long, default_value("."))] -//#[arg(long, default_value(Some(".")))] -//flake: Option>>, -// -//#[arg(group = "config", long)] -//expr: Option, //impl Parser { // fn eval_cmd(&self) { diff --git a/src/color.rs b/src/color.rs index 1e420f6..19d90c7 100644 --- a/src/color.rs +++ b/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 = 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 = OnceLock::new(); +pub static CLI_ENABLE_COLOR: OnceLock = 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 = 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() })); diff --git a/src/lib.rs b/src/lib.rs index 1956803..d939afa 100644 --- a/src/lib.rs +++ b/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, delta_args: DeltaCmd) -> Result<(), BoxDynError> { - todo!(); -} - -#[tracing::instrument(level = "debug")] -pub fn do_append(args: Arc, 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, - pub value: Box, -} - -pub fn expr_for_configuration(source_file: &Path) -> OsString { - [ - OsStr::new("import { configuration = "), - source_file.as_os_str(), - OsStr::new("; }"), - ] - .into_iter() - .collect() + pub value: Box, } pub fn get_where(option_name: &str, configuration_nix: &Path) -> Result, BoxDynError> { - let expr = expr_for_configuration(configuration_nix); + let expr: OsString = [ + // foo + OsStr::new("import { 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 Result { +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 { 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, + last_line_def: SourceLine, new_prio: i64, new_value: Arc, ) -> Result { - 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, +) -> 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(()) } diff --git a/src/main.rs b/src/main.rs index ef00265..f9bfa8d 100644 --- a/src/main.rs +++ b/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> { - 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> { 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 } } diff --git a/src/source.rs b/src/source.rs index c0099a1..7a482db 100644 --- a/src/source.rs +++ b/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, -) -> 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 { 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 { - 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, +} + #[derive(Debug, Clone)] pub struct SourceFile { path: Arc, @@ -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, options: OpenOptions) -> Result { - 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 = 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::(); + 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) -> 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 = 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 = 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::(); + 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 { Arc::clone(&self.path) } diff --git a/tests/basic/configuration-package.nix b/tests/basic/configuration-package.nix deleted file mode 100644 index 4b50af6..0000000 --- a/tests/basic/configuration-package.nix +++ /dev/null @@ -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" -'' diff --git a/tests/basic/configuration.nix b/tests/basic/configuration.nix deleted file mode 100644 index 06470e3..0000000 --- a/tests/basic/configuration.nix +++ /dev/null @@ -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 - ]; -} diff --git a/tests/basic/default.nix b/tests/basic/default.nix deleted file mode 100644 index 7396103..0000000 --- a/tests/basic/default.nix +++ /dev/null @@ -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 { }, -}: pkgs.testers.runNixOSTest ./test.nix diff --git a/tests/basic/test-script.py b/tests/basic/test-script.py deleted file mode 100644 index f507503..0000000 --- a/tests/basic/test-script.py +++ /dev/null @@ -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 {{ }}; - 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=}" diff --git a/tests/basic/test.nix b/tests/basic/test.nix deleted file mode 100644 index 61110ad..0000000 --- a/tests/basic/test.nix +++ /dev/null @@ -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; -} diff --git a/tests/default.nix b/tests/default.nix deleted file mode 100644 index bc4beef..0000000 --- a/tests/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ - pkgs ? import { }, - lib ? pkgs.lib, -}: lib.makeScope lib.callPackageWith (self: let - inherit (pkgs.testers) runNixOSTest; -in { - basic = runNixOSTest ./basic/test.nix; -})