diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..ffd0f0d --- /dev/null +++ b/default.nix @@ -0,0 +1,230 @@ +{ + pkgs, + lib, + config, + ... +}: +let + inherit (lib) + mkEnableOption + mkOption + mkIf + ; + inherit (lib.types) + submodule + bool + nullOr + lines + str + path + int + attrsOf + either + ; + fileType = submodule ( + { options, ... }: + { + options = { + enable = mkEnableOption "creation of this file" // { + default = true; + example = false; + }; + + text = mkOption { + default = ""; + type = lines; + description = "Text of the file"; + }; + + source = mkOption { + type = nullOr (either path str); + default = null; + description = "Path of the source file or directory - be sure to quote paths you don't want nix to copy to the store."; + }; + + executable = mkOption { + type = bool; + default = false; + example = true; + description = '' + Whether to set the execute bit on the target file. + Only effective in combination with `.text` specified, else permissions will be of the source file. + ''; + }; + + uid = mkOption { + type = nullOr int; + default = null; + description = '' + Uid this file will belong to + ''; + }; + + gid = mkOption { + type = nullOr int; + default = null; + description = '' + Gid this file will belong to + ''; + }; + + clobber = mkOption { + default = config.environment.arbys.clobber; + defaultText = ''config.environment.arbys.clobber''; + type = bool; + description = '' + Whether to clobber file or throw error on conflict + ''; + }; + + }; + } + ); +in +{ + options.environment = { + arbys = { + clobber = mkOption { + type = bool; + default = false; + description = '' + Whether to clobber conflicted files by default + ''; + }; + enable = mkEnableOption "Arbitrary Symlink Manager"; + }; + files = mkOption { + description = "Files to link"; + type = attrsOf fileType; + default = { }; + }; + }; + config = + let + files = + let + enabled = lib.filterAttrs (_: v: v.enable) config.environment.files; + sourced = builtins.mapAttrs ( + n: v: + let + processed = + if (v.source == null) then + ( + if (v.text == "") then + throw "Must provide source or text for ${n}" + else + v + // { + source = pkgs.writeTextFile { + name = "${n}-source"; + text = v.text; + executable = v.executable ? false; + }; + } + ) + else + v; + filtered = builtins.removeAttrs processed [ + "text" + "enable" + "executable" + ]; + nullFiltered = lib.filterAttrs (_: v: v != null) filtered; + in + nullFiltered + // { + type = "symlink"; + } + ) enabled; + in + lib.foldlAttrs ( + acc: n: v: + acc ++ (lib.singleton ({ target = n; } // v)) + ) [ ] sourced; + manifest = pkgs.writeTextFile { + name = "manifest.json"; + text = ( + builtins.toJSON { + inherit files; + clobber_by_default = config.environment.arbys.clobber; + version = 1; + } + ); + checkPhase = '' + set -e + CUE_CACHE_DIR=$(pwd)/.cache + CUE_CONFIG_DIR=$(pwd)/.config + + ${lib.getExe pkgs.cue} vet -c ${./v1.cue} $target + ''; + }; + in + mkIf (config.environment.arbys.enable) { + systemd.targets.arbys = { + description = "Create Arbitrary Symlinks"; + requiredBy = [ "sysinit-reactivation.target" ]; + before = [ "sysinit-reactivation.target" ]; + requires = [ + "arbys-activate.service" + "arbys-copy.service" + "arbys-cleanup.service" + ]; + }; + + systemd.services = + let + manifestPath = "/var/lib/arbys"; + in + { + arbys-prep = { + description = "Ensure manifest dir exists"; + script = "mkdir -p ${manifestPath}"; + serviceConfig.Type = "oneshot"; + unitConfig.RefuseManualStart = true; + }; + + arbys-activate = { + description = "Link files from arbys manifest"; + serviceConfig.Type = "oneshot"; + requires = [ + "arbys-prep.service" + "arbys-copy.service" + ]; + after = [ "arbys-prep.service" ]; + script = + let + linker = lib.getExe (pkgs.smfh or pkgs.rustPlatform.buildRustPackage ./smfh.nix { }); + in + '' + new_manifest=${manifest} + + if [ ! -f ${manifestPath}/manifest.json ]; then + ${linker} activate $new_manifest + exit 0 + fi; + + ${linker} diff $new_manifest ${manifestPath}/manifest.json + ''; + }; + + arbys-copy = { + description = "Copy manifest into the state dir"; + serviceConfig.Type = "oneshot"; + after = [ "arbys-activate.service" ]; + script = "cp ${manifest} ${manifestPath}/manifest.json"; + }; + + arbys-cleanup = { + description = "Clean up manifest if disabled"; + serviceConfig.type = "oneshot"; + after = [ "arbys.target" ]; + unitConfig.RefuseManualStart = false; + script = + if (lib.count (builtins.attrNames config.environment.files) == 0) then + "rm ${manifestPath}/manifest.json" + else + "true"; + }; + }; + }; +} diff --git a/flake.nix b/flake.nix index 834db14..5da6a06 100644 --- a/flake.nix +++ b/flake.nix @@ -8,6 +8,7 @@ # }; outputs = + { self, # nixpkgs, @@ -25,236 +26,6 @@ # newPath = "https://git.atagen.co/atagen/arbys"; # modules = [ self.nixosModules.arbys ]; # }; - nixosModules.arbys = - { - pkgs, - lib, - config, - ... - }: - let - inherit (lib) - mkEnableOption - mkOption - mkIf - ; - inherit (lib.types) - submodule - bool - nullOr - lines - str - path - int - attrsOf - either - ; - fileType = submodule ( - { options, ... }: - { - options = { - enable = mkEnableOption "creation of this file" // { - default = true; - example = false; - }; - - text = mkOption { - default = ""; - type = lines; - description = "Text of the file"; - }; - - source = mkOption { - type = nullOr (either path str); - default = null; - description = "Path of the source file or directory - be sure to quote paths you don't want nix to copy to the store."; - }; - - executable = mkOption { - type = bool; - default = false; - example = true; - description = '' - Whether to set the execute bit on the target file. - Only effective in combination with `.text` specified, else permissions will be of the source file. - ''; - }; - - uid = mkOption { - type = nullOr int; - default = null; - description = '' - Uid this file will belong to - ''; - }; - - gid = mkOption { - type = nullOr int; - default = null; - description = '' - Gid this file will belong to - ''; - }; - - clobber = mkOption { - default = config.environment.arbys.clobber; - defaultText = ''config.environment.arbys.clobber''; - type = bool; - description = '' - Whether to clobber file or throw error on conflict - ''; - }; - - }; - } - ); - in - { - options.environment = { - arbys = { - clobber = mkOption { - type = bool; - default = false; - description = '' - Whether to clobber conflicted files by default - ''; - }; - enable = mkEnableOption "Arbitrary Symlink Manager"; - }; - files = mkOption { - description = "Files to link"; - type = attrsOf fileType; - default = { }; - }; - }; - config = - let - files = - let - enabled = lib.filterAttrs (_: v: v.enable) config.environment.files; - sourced = builtins.mapAttrs ( - n: v: - let - processed = - if (v.source == null) then - ( - if (v.text == "") then - throw "Must provide source or text for ${n}" - else - v - // { - source = pkgs.writeTextFile { - name = "${n}-source"; - text = v.text; - executable = v.executable ? false; - }; - } - ) - else - v; - filtered = builtins.removeAttrs processed [ - "text" - "enable" - "executable" - ]; - nullFiltered = lib.filterAttrs (_: v: v != null) filtered; - in - nullFiltered - // { - type = "symlink"; - } - ) enabled; - in - lib.foldlAttrs ( - acc: n: v: - acc ++ (lib.singleton ({ target = n; } // v)) - ) [ ] sourced; - manifest = pkgs.writeTextFile { - name = "manifest.json"; - text = ( - builtins.toJSON { - inherit files; - clobber_by_default = config.environment.arbys.clobber; - version = 1; - } - ); - checkPhase = '' - set -e - CUE_CACHE_DIR=$(pwd)/.cache - CUE_CONFIG_DIR=$(pwd)/.config - - ${lib.getExe pkgs.cue} vet -c ${./v1.cue} $target - ''; - }; - in - mkIf (config.environment.arbys.enable) { - systemd.targets.arbys = { - description = "Create Arbitrary Symlinks"; - requiredBy = [ "sysinit-reactivation.target" ]; - before = [ "sysinit-reactivation.target" ]; - requires = [ - "arbys-activate.service" - "arbys-copy.service" - "arbys-cleanup.service" - ]; - }; - - systemd.services = - let - manifestPath = "/var/lib/arbys"; - in - { - arbys-prep = { - description = "Ensure manifest dir exists"; - script = "mkdir -p ${manifestPath}"; - serviceConfig.Type = "oneshot"; - unitConfig.RefuseManualStart = true; - }; - - arbys-activate = { - description = "Link files from arbys manifest"; - serviceConfig.Type = "oneshot"; - requires = [ - "arbys-prep.service" - "arbys-copy.service" - ]; - after = [ "arbys-prep.service" ]; - script = - let - linker = lib.getExe pkgs.smfh; - in - '' - new_manifest=${manifest} - - if [ ! -f ${manifestPath}/manifest.json ]; then - ${linker} activate $new_manifest - exit 0 - fi; - - ${linker} diff $new_manifest ${manifestPath}/manifest.json - ''; - }; - - arbys-copy = { - description = "Copy manifest into the state dir"; - serviceConfig.Type = "oneshot"; - after = [ "arbys-activate.service" ]; - script = "cp ${manifest} ${manifestPath}/manifest.json"; - }; - - arbys-cleanup = { - description = "Clean up manifest if disabled"; - serviceConfig.type = "oneshot"; - after = [ "arbys.target" ]; - unitConfig.RefuseManualStart = false; - script = - if (lib.count (builtins.attrNames config.environment.files) == 0) then - "rm ${manifestPath}/manifest.json" - else - "true"; - }; - }; - }; - }; + nixosModules.arbys = import ./default.nix; }; } diff --git a/smfh.nix b/smfh.nix new file mode 100644 index 0000000..154cb49 --- /dev/null +++ b/smfh.nix @@ -0,0 +1,20 @@ +{ + rustPlatform, + fetchFromGitHub, +}: +let + src = fetchFromGitHub { + owner = "feel-co"; + repo = "smfh"; + rev = "39f5c06153f63100376bc607b1465850b6df77fd"; + hash = "sha256-/9Ww10kYopxfCNNnNDwENTubs7Wzqlw+O6PJAHNOYQw="; + }; + cargo = (builtins.fromTOML (builtins.readFile "${src}/Cargo.toml")); +in +rustPlatform.buildRustPackage { + inherit src; + inherit (cargo.package) version; + pname = cargo.package.name; + cargoLock.lockFile = "${src}/Cargo.lock"; + meta.mainProgram = "smfh"; +}