diff options
Diffstat (limited to 'pkgs/populate/default.nix')
-rw-r--r-- | pkgs/populate/default.nix | 148 |
1 files changed, 134 insertions, 14 deletions
diff --git a/pkgs/populate/default.nix b/pkgs/populate/default.nix index acb5a5f..f0eb7d1 100644 --- a/pkgs/populate/default.nix +++ b/pkgs/populate/default.nix @@ -1,20 +1,140 @@ -{ coreutils, findutils, git, gnused, jq, openssh, pass, rsync, runCommand, stdenv }: +with import ../../lib; +with shell; + +{ coreutils, dash, findutils, git, jq, openssh, rsync, writeDash }: let - PATH = stdenv.lib.makeBinPath [ - coreutils - findutils - git - gnused - jq - openssh - pass - rsync + check = { force, target }: let + sentinelFile = "${target.path}/.populate"; + in shell' target /* sh */ '' + ${optionalString force /* sh */ '' + mkdir -vp ${quote (dirOf sentinelFile)} + touch ${quote sentinelFile} + ''} + if ! test -f ${quote sentinelFile}; then + >&2 printf 'error: missing sentinel file: %s\n' ${quote ( + optionalString (!isLocalTarget target) "${target.host}:" + + sentinelFile + )} + exit 1 + fi + ''; + + pop.file = target: file: rsync' target (quote file.path); + + pop.git = target: git: shell' target /* sh */ '' + if ! test -e ${quote target.path}; then + git clone --recurse-submodules ${quote git.url} ${quote target.path} + fi + cd ${quote target.path} + if ! url=$(git config remote.origin.url); then + git remote add origin ${quote git.url} + elif test "$url" != ${quote git.url}; then + git remote set-url origin ${quote git.url} + fi + + # TODO resolve git_ref to commit hash + hash=${quote git.ref} + + if ! test "$(git log --format=%H -1)" = "$hash"; then + if ! git log -1 "$hash" >/dev/null 2>&1; then + git fetch origin + fi + git checkout "$hash" -- ${quote target.path} + git -c advice.detachedHead=false checkout -f "$hash" + git submodule update --init --recursive + fi + + git clean -dfx + ''; + + pop.pass = target: pass: let + passPrefix = "${pass.dir}/${pass.name}"; + in /* sh */ '' + umask 0077 + + tmp_dir=$(${coreutils}/bin/mktemp -dt populate-pass.XXXXXXXX) + trap cleanup EXIT + cleanup() { + rm -fR "$tmp_dir" + } + + ${findutils}/bin/find ${quote passPrefix} -type f | + while read -r gpg_path; do + + rel_name=''${gpg_path#${quote passPrefix}} + rel_name=''${rel_name%.gpg} + + pass_date=$( + ${git}/bin/git -C ${quote pass.dir} log -1 --format=%aI "$gpg_path" + ) + pass_name=${quote pass.name}/$rel_name + tmp_path=$tmp_dir/$rel_name + + ${coreutils}/bin/mkdir -p "$(${coreutils}/bin/dirname "$tmp_path")" + PASSWORD_STORE_DIR=${quote pass.dir} pass show "$pass_name" > "$tmp_path" + ${coreutils}/bin/touch -d "$pass_date" "$tmp_path" + done + + ${rsync' target /* sh */ "$tmp_dir"} + ''; + + pop.pipe = target: pipe: /* sh */ '' + ${quote pipe.command} | { + ${shell' target /* sh */ "cat > ${quote target.path}"} + } + ''; + + # TODO rm -fR instead of ln -f? + pop.symlink = target: symlink: shell' target /* sh */ '' + ln -fns ${quote symlink.target} ${quote target.path} + ''; + + populate = target: name: source: let + source' = source.${source.type}; + target' = target // { path = "${target.path}/${name}"; }; + in writeDash "populate.${target'.host}.${name}" '' + set -efu + ${pop.${source.type} target' source'} + ''; + + rsync' = target: sourcePath: /* sh */ '' + source_path=${sourcePath} + if test -d "$source_path"; then + source_path=$source_path/ + fi + ${rsync}/bin/rsync \ + -e ${quote (ssh' target)} \ + -vFrlptD \ + --delete-excluded \ + "$source_path" \ + ${quote ( + optionalString (!isLocalTarget target) + "${target.user}@${target.host}:" + + target.path + )} + ''; + + shell' = target: script: + if isLocalTarget target + then script + else /* sh */ '' + ${ssh' target} ${quote target.host} ${quote script} + ''; + + ssh' = target: concatMapStringsSep " " quote [ + "${openssh}/bin/ssh" + "-l" target.user + "-o" "ControlPersist=no" + "-p" target.port + "-T" ]; + in -runCommand "populate-2.2.0" {} '' - mkdir -p $out/bin - cp ${./populate.sh} $out/bin/populate - sed -i '1s,.*,&\nPATH=${PATH},' $out/bin/populate +{ force ? false, source, target }: writeDash "populate.${target.host}" '' + set -efu + ${check { inherit force target; }} + set -x + ${concatStringsSep "\n" (mapAttrsToList (populate target) source)} '' |