diff options
| author | Tim Keller <tjk@tjkeller.xyz> | 2025-12-27 22:05:00 -0600 |
|---|---|---|
| committer | Tim Keller <tjk@tjkeller.xyz> | 2025-12-27 22:05:00 -0600 |
| commit | f813117ee176205721f48dd5e8d7b21f1c411702 (patch) | |
| tree | 41e901344c7a51c8e5dc7a4410e5638958797ee8 /hm-reposync.nix | |
| download | hm-reposync-f813117ee176205721f48dd5e8d7b21f1c411702.tar.xz hm-reposync-f813117ee176205721f48dd5e8d7b21f1c411702.zip | |
initial commit
Diffstat (limited to 'hm-reposync.nix')
| -rw-r--r-- | hm-reposync.nix | 206 |
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]; + }; +} |
