From 2f4eb20a64371bb036f3568de9ded8adeca14df7 Mon Sep 17 00:00:00 2001 From: Gabriel Arazas <foodogsquared@foodogsquared.one> Date: Mon, 13 Mar 2023 23:45:17 +0800 Subject: [PATCH] modules/mutable-files: init module --- modules/home-manager/files/mutable-files.nix | 133 +++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 modules/home-manager/files/mutable-files.nix diff --git a/modules/home-manager/files/mutable-files.nix b/modules/home-manager/files/mutable-files.nix new file mode 100644 index 00000000..57650da7 --- /dev/null +++ b/modules/home-manager/files/mutable-files.nix @@ -0,0 +1,133 @@ +{ config, options, lib, pkgs, ... }: + +let + cfg = config.home.mutableFile; + homeDir = config.home.homeDirectory; + + fileSubmodule = { name, config, options, ... }: { + options = { + url = lib.mkOption { + type = lib.types.str; + description = lib.mkDoc '' + The URL of the file to be fetched. + ''; + example = "https://github.com/foo-dogsquared/dotfiles.git"; + }; + + path = lib.mkOption { + type = lib.types.str; + description = lib.mkDoc '' + The path of the mutable file. By default, it will be relative to the + home directory. + ''; + default = "${homeDir}/${name}"; + example = lib.literalExpression "\${config.xdg.userDirs.documents}/top-secret"; + }; + + extractPath = lib.mkOption { + type = with lib.types; nullOr str; + description = lib.mkDoc '' + 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 { + type = lib.types.enum [ "git" "fetch" "archive" ]; + description = lib.mkDoc '' + Type that configures the behavior for fetching the URL. + + This accept only certain keywords. + + - For `fetch`, the file will be fetched with `curl`. + - For `git`, it will be fetched with `git clone`. + - For `archive`, the file will be extracted before putting the file. + + The default type is `fetch`. + ''; + default = "fetch"; + example = "git"; + }; + }; + }; +in +{ + options.home.mutableFile = lib.mkOption { + type = with lib.types; attrsOf (submodule fileSubmodule); + description = lib.mkDoc '' + 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 = { }; + example = lib.literalExpression '' + { + "library/dotfiles" = { + url = "https://github.com/foo-dogsquared/dotfiles.git"; + type = "git"; + }; + + "library/projects/keys" = { + url = "https://example.com/file.zip"; + type = "archive"; + }; + } + ''; + }; + + config = lib.mkIf (cfg != { }) { + systemd.user.services.put-mutable-files = { + Unit = { + Description = "Download mutable home-manager-managed files"; + After = [ "default.target" ]; + }; + + 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; + ExecStart = + let + mutableFilesCmds = lib.mapAttrsToList + (path: value: + let + url = lib.escapeShellArg value.url; + path = lib.escapeShellArg value.path; + in + '' + ${lib.optionalString (value.type == "git") "[ -d ${path} ] || git clone ${url} ${path}"} + ${lib.optionalString (value.type == "fetch") "[ -d ${path} ] || curl ${url} --output ${path}"} + ${lib.optionalString (value.type == "archive") '' + [ -d ${path} ] || { + filename=$(curl --output-dir /tmp --silent --show-error --write-out '%{filename_effective}' --remote-name --remote-header-name --location ${url}) + ${if (value.extractPath != null) then + ''arc extract "/tmp/$filename" ${lib.escapeShellArg value.extractPath} ${path}'' + else + ''arc unarchive "/tmp/$filename" ${path}'' + } + } + ''} + '') + cfg; + + script = pkgs.writeShellApplication { + name = "put-mutable-files"; + runtimeInputs = with pkgs; [ archiver curl git ]; + text = lib.concatStringsSep "\n" mutableFilesCmds; + }; + in + "${script}/bin/put-mutable-files"; + }; + + Install.WantedBy = [ "default.target" ]; + }; + }; +}