arbys/default.nix

234 lines
6.1 KiB
Nix

{
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";
after = [ "local-fs.target" ];
wantedBy = [
"sysinit-reactivation.target"
"multi-user.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";
};
};
};
}