2023-10-02 06:26:11 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
2023-03-13 15:45:17 +00:00
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.home.mutableFile;
|
|
|
|
|
2024-07-10 11:24:29 +00:00
|
|
|
runtimeInputs = lib.makeBinPath (with pkgs; [
|
2024-07-29 08:02:52 +00:00
|
|
|
coreutils archiver curl git gopass
|
2024-07-10 11:24:29 +00:00
|
|
|
]);
|
|
|
|
|
2023-12-23 10:41:51 +00:00
|
|
|
# An attribute set to be used to get the fetching script.
|
2024-07-10 11:24:29 +00:00
|
|
|
fetchScript = _: value:
|
2024-01-01 05:56:10 +00:00
|
|
|
let
|
|
|
|
url = lib.escapeShellArg value.url;
|
|
|
|
path = lib.escapeShellArg value.path;
|
|
|
|
extraArgs = lib.escapeShellArgs value.extraArgs;
|
2023-12-23 10:41:51 +00:00
|
|
|
in
|
2024-01-01 05:56:10 +00:00
|
|
|
{
|
|
|
|
git = ''
|
|
|
|
[ -d ${path} ] || git clone ${extraArgs} ${url} ${path}
|
|
|
|
'';
|
|
|
|
fetch = ''
|
|
|
|
[ -e ${path} ] || curl ${extraArgs} ${url} --output ${path}"
|
|
|
|
'';
|
|
|
|
archive =
|
|
|
|
let
|
|
|
|
extractScript =
|
|
|
|
if (value.extractPath == null) then
|
|
|
|
''arc unarchive "/tmp/$filename" ${path}''
|
|
|
|
else
|
|
|
|
''arc extract "/tmp/$filename" ${lib.escapeShellArg value.extractPath} ${path}'';
|
|
|
|
in
|
|
|
|
''
|
|
|
|
[ -e ${path} ] || {
|
|
|
|
filename=$(curl ${extraArgs} --output-dir /tmp --silent --show-error --write-out '%{filename_effective}' --remote-name --remote-header-name --location ${url})
|
|
|
|
${extractScript}
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
gopass = ''
|
|
|
|
[ -e ${path} ] || gopass clone ${extraArgs} ${url} --path ${path} ${extraArgs}
|
|
|
|
'';
|
2024-02-13 04:26:14 +00:00
|
|
|
custom = ''
|
2024-02-28 10:45:37 +00:00
|
|
|
[ -e ${path} ] || ${extraArgs}
|
2024-02-13 04:26:14 +00:00
|
|
|
'';
|
2024-01-01 05:56:10 +00:00
|
|
|
};
|
2023-12-23 10:41:51 +00:00
|
|
|
|
2024-07-29 08:02:52 +00:00
|
|
|
# The file submodule. Take note the values here are sanitized to only
|
|
|
|
# represent relative paths starting with the given base directory as the root
|
|
|
|
# (such as the home directory).
|
|
|
|
#
|
|
|
|
# Playing with absolute paths is basically like playing with fire, some use
|
|
|
|
# cases are nice for it, some are bad especially that this is only used for
|
|
|
|
# home-manager where it is expected to be limited to its associated home
|
|
|
|
# directory. But that's for the user to know how their user interact with the
|
|
|
|
# rest of the system.
|
2023-03-19 09:16:25 +00:00
|
|
|
fileType = baseDir: { name, config, options, ... }: {
|
2023-03-13 15:45:17 +00:00
|
|
|
options = {
|
|
|
|
url = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
2023-07-27 03:13:39 +00:00
|
|
|
description = ''
|
2023-03-13 15:45:17 +00:00
|
|
|
The URL of the file to be fetched.
|
|
|
|
'';
|
|
|
|
example = "https://github.com/foo-dogsquared/dotfiles.git";
|
|
|
|
};
|
|
|
|
|
|
|
|
path = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
2023-07-27 03:13:39 +00:00
|
|
|
description = ''
|
2023-03-13 15:45:17 +00:00
|
|
|
The path of the mutable file. By default, it will be relative to the
|
|
|
|
home directory.
|
|
|
|
'';
|
|
|
|
example = lib.literalExpression "\${config.xdg.userDirs.documents}/top-secret";
|
2023-03-19 09:16:25 +00:00
|
|
|
default = name;
|
2023-03-17 14:39:52 +00:00
|
|
|
apply = p:
|
2023-03-19 09:16:25 +00:00
|
|
|
if lib.hasPrefix "/" p then p else "${baseDir}/${p}";
|
2023-03-13 15:45:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
extractPath = lib.mkOption {
|
|
|
|
type = with lib.types; nullOr str;
|
2023-07-27 03:13:39 +00:00
|
|
|
description = ''
|
2023-03-13 15:45:17 +00:00
|
|
|
The path within the archive to be extracted. This is only used if the
|
|
|
|
type is `archive`. If the value is `null` then it will extract the
|
|
|
|
whole archive into the directory.
|
|
|
|
'';
|
|
|
|
default = null;
|
|
|
|
example = "path/inside/of/the/archive";
|
|
|
|
};
|
|
|
|
|
|
|
|
type = lib.mkOption {
|
2023-07-22 03:05:31 +00:00
|
|
|
type = lib.types.enum [ "git" "fetch" "archive" "gopass" "custom" ];
|
2023-07-27 03:13:39 +00:00
|
|
|
description = ''
|
2023-03-13 15:45:17 +00:00
|
|
|
Type that configures the behavior for fetching the URL.
|
|
|
|
|
|
|
|
This accept only certain keywords.
|
|
|
|
|
2023-09-18 06:39:34 +00:00
|
|
|
- For `fetch`, the file will be fetched with {command}`curl`.
|
|
|
|
- For `git`, it will be fetched with {command}`git clone`.
|
|
|
|
- For `archive`, the file will be fetched with {command}`curl` and
|
|
|
|
extracted before putting the file.
|
|
|
|
- For `gopass`, the file will be cloned with {command}`gopass`.
|
2023-07-22 03:05:31 +00:00
|
|
|
- For `custom`, the file will be passed with a user-given command.
|
|
|
|
The `extraArgs` option is now assumed to be a list of a command and
|
2024-02-13 04:26:14 +00:00
|
|
|
its arguments. To make executing commands possible with custom
|
|
|
|
scripts, the URL and the path is stored in shell variables `$url` and
|
|
|
|
`$path` respectively.
|
2023-03-13 15:45:17 +00:00
|
|
|
|
|
|
|
The default type is `fetch`.
|
|
|
|
'';
|
|
|
|
default = "fetch";
|
|
|
|
example = "git";
|
|
|
|
};
|
2023-03-17 14:39:03 +00:00
|
|
|
|
|
|
|
extraArgs = lib.mkOption {
|
|
|
|
type = with lib.types; listOf str;
|
2023-07-27 03:13:39 +00:00
|
|
|
description = ''
|
2023-03-17 14:39:03 +00:00
|
|
|
A list of extra arguments to be included with the fetch command. Take
|
|
|
|
note of the commands used for each type as documented from
|
2023-07-27 03:13:39 +00:00
|
|
|
{option}`config.home.mutableFile.<name>.type`.
|
2023-03-17 14:39:03 +00:00
|
|
|
'';
|
2023-06-08 13:19:17 +00:00
|
|
|
default = [ ];
|
2023-03-17 14:39:03 +00:00
|
|
|
example = [ "--depth" "1" ];
|
|
|
|
};
|
2023-09-18 06:36:30 +00:00
|
|
|
|
|
|
|
postScript = lib.mkOption {
|
2023-12-23 10:44:06 +00:00
|
|
|
type = lib.types.lines;
|
2023-09-18 06:36:30 +00:00
|
|
|
description = ''
|
|
|
|
A shell script fragment to be executed after the download.
|
|
|
|
'';
|
|
|
|
default = "";
|
|
|
|
example = lib.literalExpression ''
|
2024-01-20 09:21:54 +00:00
|
|
|
''${config.xdg.configHome}/emacs/bin/doom install --no-config --no-fonts --install --force
|
2023-09-18 06:36:30 +00:00
|
|
|
'';
|
|
|
|
};
|
2023-03-13 15:45:17 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
in
|
|
|
|
{
|
|
|
|
options.home.mutableFile = lib.mkOption {
|
2023-03-19 09:16:25 +00:00
|
|
|
type = with lib.types; attrsOf (submodule (fileType config.home.homeDirectory));
|
2023-07-27 03:13:39 +00:00
|
|
|
description = ''
|
2023-03-13 15:45:17 +00:00
|
|
|
An attribute set of mutable files and directories to be declaratively put
|
|
|
|
into the home directory. Take note this is not exactly pure (or
|
|
|
|
idempotent) as it will only do its fetching when the designated file is
|
|
|
|
missing.
|
|
|
|
'';
|
|
|
|
default = { };
|
2023-12-20 14:07:09 +00:00
|
|
|
example = {
|
|
|
|
"library/dotfiles" = {
|
|
|
|
url = "https://github.com/foo-dogsquared/dotfiles.git";
|
|
|
|
type = "git";
|
|
|
|
};
|
2023-03-13 15:45:17 +00:00
|
|
|
|
2023-12-20 14:07:09 +00:00
|
|
|
"library/projects/keys" = {
|
|
|
|
url = "https://example.com/file.zip";
|
|
|
|
type = "archive";
|
|
|
|
};
|
|
|
|
};
|
2023-03-13 15:45:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
config = lib.mkIf (cfg != { }) {
|
2023-03-20 16:17:03 +00:00
|
|
|
systemd.user.services.fetch-mutable-files = {
|
2023-03-13 15:45:17 +00:00
|
|
|
Unit = {
|
2023-03-19 09:16:25 +00:00
|
|
|
Description = "Fetch mutable home-manager-managed files";
|
|
|
|
After = [ "default.target" "network-online.target" ];
|
|
|
|
Wants = [ "network-online.target" ];
|
2023-03-13 15:45:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Service = {
|
|
|
|
# We'll assume this service will have lots of things to download so it
|
|
|
|
# is best to make the temp directory to only last with the service.
|
|
|
|
PrivateUsers = true;
|
|
|
|
PrivateTmp = true;
|
|
|
|
|
|
|
|
Type = "oneshot";
|
|
|
|
RemainAfterExit = true;
|
2024-01-20 09:21:27 +00:00
|
|
|
|
2023-03-13 15:45:17 +00:00
|
|
|
ExecStart =
|
|
|
|
let
|
|
|
|
mutableFilesCmds = lib.mapAttrsToList
|
2024-02-28 10:45:37 +00:00
|
|
|
(path: value: let
|
|
|
|
url = lib.escapeShellArg value.url;
|
|
|
|
path = lib.escapeShellArg value.path;
|
|
|
|
in
|
|
|
|
''
|
|
|
|
(
|
2024-03-15 09:29:11 +00:00
|
|
|
URL=${url}
|
|
|
|
FILEPATH=${path}
|
|
|
|
DIRNAME=$(dirname ${path})
|
2024-02-28 10:45:37 +00:00
|
|
|
mkdir -p "$DIRNAME"
|
|
|
|
${(fetchScript path value).${value.type}}
|
|
|
|
)
|
|
|
|
'')
|
2023-03-13 15:45:17 +00:00
|
|
|
cfg;
|
2024-07-29 08:02:52 +00:00
|
|
|
|
|
|
|
shellScript = pkgs.writeShellScriptBin "fetch-mutable-files" ''
|
|
|
|
export PATH=${runtimeInputs}''${PATH:-:$PATH}
|
|
|
|
${lib.concatStringsSep "\n" mutableFilesCmds}
|
|
|
|
'';
|
2023-06-08 13:19:17 +00:00
|
|
|
in
|
2024-07-29 08:02:52 +00:00
|
|
|
lib.getExe shellScript;
|
2024-01-20 09:21:27 +00:00
|
|
|
|
|
|
|
ExecStartPost =
|
|
|
|
let
|
|
|
|
mutableFilesCmds = lib.mapAttrsToList
|
|
|
|
(path: value: value.postScript)
|
|
|
|
cfg;
|
2024-07-29 08:02:52 +00:00
|
|
|
|
|
|
|
shellScript = pkgs.writeShellScriptBin "fetch-mutable-files-post-script" ''
|
|
|
|
export PATH=${runtimeInputs}''${PATH:-:$PATH}
|
2024-07-10 11:24:29 +00:00
|
|
|
${lib.concatStringsSep "\n" mutableFilesCmds}
|
|
|
|
'';
|
2024-07-29 08:02:52 +00:00
|
|
|
in
|
|
|
|
lib.getExe shellScript;
|
2023-03-13 15:45:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Install.WantedBy = [ "default.target" ];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|