234 lines
6.1 KiB
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";
|
|
};
|
|
};
|
|
};
|
|
}
|