aboutsummaryrefslogtreecommitdiff
path: root/hm-reposync.nix
diff options
context:
space:
mode:
Diffstat (limited to 'hm-reposync.nix')
-rw-r--r--hm-reposync.nix206
1 files changed, 206 insertions, 0 deletions
diff --git a/hm-reposync.nix b/hm-reposync.nix
new file mode 100644
index 0000000..3a4bf50
--- /dev/null
+++ b/hm-reposync.nix
@@ -0,0 +1,206 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}: let
+ cfg = config.reposync;
+ stowType = lib.types.submodule (
+ {name, ...}: {
+ options = {
+ enable = lib.mkEnableOption "call stow for this target stow command";
+ targetPrefix = lib.mkOption {
+ type = lib.types.path;
+ default = config.home.homeDirectory;
+ description = "absolute prefix for the stow target";
+ };
+ target = lib.mkOption {
+ type = lib.types.str;
+ default = ".";
+ description = "path to stow target relative to targetPrefix (home directory by default)";
+ };
+ packages = lib.mkOption {
+ type = lib.types.str;
+ default = name;
+ defaultText = "<name>";
+ example = "vim zsh";
+ description = "packages to stow from repository. use '.' to automatically stow all";
+ };
+ };
+ config = {
+ enable = lib.mkDefault true;
+ };
+ }
+ );
+ outOfStoreGitRepositoryType = lib.types.submodule (
+ {
+ name,
+ config,
+ ...
+ }: {
+ options = {
+ enable = lib.mkEnableOption "whether to download this repository";
+ server = lib.mkOption {
+ type = lib.types.str;
+ default = "https://github.com/";
+ example = "git@github.com:";
+ description = "git server suffixed with the repo delimiter (e.g. '/' or ':')";
+ };
+ repository = lib.mkOption {
+ type = lib.types.str;
+ default = name;
+ defaultText = "<name>";
+ example = "nix-community/home-manager";
+ description = "git repository url";
+ };
+ # TODO test to add this remote feature
+ #remotes = lib.mkOption {
+ # type = lib.types.attrsOf lib.types.str;
+ # default = {};
+ # example = {
+ # fork = "https://github.com/tjkeller-xyz/home-manager.git";
+ # };
+ # description = "alternative remotes to the git repository";
+ #};
+ #defaultRemote = lib.mkOption {
+ # type = lib.types.nullOr lib.types.str;
+ # default = null;
+ # example = "fork";
+ # description = "default remote to use";
+ #};
+ #branch = lib.mkOption {
+ # type = lib.types.nullOr lib.types.str;
+ # default = null;
+ # example = "master";
+ # description = "pull from this branch. set to null for default branch";
+ #};
+ targetPrefix = lib.mkOption {
+ type = lib.types.path;
+ default = config.home.homeDirectory;
+ description = "absolute prefix for the git repository path";
+ };
+ target = lib.mkOption {
+ type = lib.types.str;
+ default = name;
+ defaultText = "<name>";
+ description = "path to git repository relative to targetPrefix (home directory by default)";
+ };
+ stow = lib.mkOption {
+ type = lib.types.attrsOf stowType;
+ default = {};
+ description = "stow packages from the repository";
+ };
+ extraCloneOptions = lib.mkOption {
+ type = lib.types.str;
+ default = "";
+ example = "--recurse-submodules";
+ description = "extra command flags to add to git clone";
+ };
+ extraPullOptions = lib.mkOption {
+ type = lib.types.str;
+ default = "";
+ example = "--recurse-submodules";
+ description = "extra command flags to add to git pull";
+ };
+ extraCommands = lib.mkOption {
+ type = lib.types.str;
+ default = "";
+ description = ''
+ extra commands to run each time repo is synced.
+
+ IMPORANT: all commands should be idempotent to prevent breaking
+ after repeated syncs.
+ like `home.activation` scripts, extra commands should respect the
+ DRY_RUN environment variable and use $VERBOSE_ARG in case the
+ verbose flag is set.
+ commands can be prefixed with `run` to automatically respect the
+ DRY_RUN function.
+ '';
+ };
+ generatedCalls = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ readOnly = true;
+ description = "generated calls for reposync.sh script";
+ };
+ };
+ config = let
+ # generate calls
+ callClone = url: target: flags: ''clonemissing "${url}" "${target}" "${flags}"'';
+ callPull = target: flags: ''pull "${target}" "${flags}"'';
+ callRemoteAdd = target: remote: url: ''remoteadd "${target}" "${remote}" "${url}"'';
+ callStowRepo = dir: target: packages: ''stowrepo "${dir}" "${target}" "${packages}"'';
+ # misc
+ stowToCalls = s: callStowRepo target (combineTarget s.targetPrefix s.target) s.packages;
+ combineTarget = prefix: target: prefix + "/" + target;
+ target = combineTarget config.targetPrefix config.target;
+ url = config.server + config.repository;
+ in {
+ enable = lib.mkDefault true;
+ generatedCalls = lib.lists.flatten [
+ (callClone url target config.extraCloneOptions)
+ (callPull target config.extraPullOptions)
+ #(lib.mapAttrsToList (name: url: callRemoteAdd name url) config.remotes)
+ (lib.mapAttrsToList (name: s: lib.mkIf s.enable (stowToCalls s)) config.stow)
+ config.extraCommands
+ ];
+ };
+ }
+ );
+in {
+ options.reposync.outOfStoreGitRepository = lib.mkOption {
+ type = lib.types.attrsOf outOfStoreGitRepositoryType;
+ default = {};
+ description = "imperative git repositories to be cloned";
+ };
+
+ config = let
+ repocfg = cfg.outOfStoreGitRepository;
+ lines = lineslist: lib.strings.concatStringsSep "\n" lineslist;
+ fname = name: ''_sync-${name}'';
+ wrapRepoCalls = name: r: ''
+ function ${fname name}() {
+ echo "Syncing repository '${name}'"
+ ${lines r.generatedCalls}
+ echo "Done"
+ }
+ '';
+ allRepoCallFuncs = lines (lib.mapAttrsToList (name: r: wrapRepoCalls name r) repocfg);
+ syncAllFunc = ''
+ function all() {
+ ${lines (lib.mapAttrsToList (name: r: fname name) repocfg)}
+ }
+ '';
+ argCases = ''
+ ${lines (lib.mapAttrsToList (name: r: "${name}) ${fname name} ;;") repocfg)}
+ -a|--all) all ;;
+ '';
+ src = pkgs.writeShellScriptBin "reposync" ''
+ export PATH="${pkgs.git}/bin:$PATH"
+ export PATH="${pkgs.stow}/bin:$PATH"
+ ${builtins.readFile ./reposync-functions.sh}
+ ${allRepoCallFuncs}
+ ${syncAllFunc}
+ for arg in "$@"; do
+ case "$arg" in
+ ${argCases}
+ esac
+ done
+ '';
+ hm-reposync = pkgs.stdenv.mkDerivation rec {
+ name = "hm-reposync";
+
+ inherit src;
+ dontUnpack = true;
+
+ buildInputs = with pkgs; [git stow];
+
+ installPhase = ''
+ mkdir -p $out/bin
+ cp $src/bin/reposync $out/bin
+ chmod +x $out/bin/reposync
+ '';
+ };
+ in {
+ home.packages = [hm-reposync];
+ };
+}