diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/populate.jq | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/lib/populate.jq b/lib/populate.jq new file mode 100644 index 0000000..23e16e8 --- /dev/null +++ b/lib/populate.jq @@ -0,0 +1,126 @@ +def default(value; f): if . == null then value else f end; +def default(value): default(value; .); + +($target_spec + | match("^(?:([^@]+)@)?([^:/]+)(?::([^/]*))?(/.*)?") + | { + user: .captures[0].string | default("root"), + host: .captures[1].string, + port: .captures[2].string | default(22; + if test("^[0-9]+$") then fromjson else + error(@json "bad target port: \(.)") + end), + path: .captures[3].string | default("/var/src"), + } +) + as $target | + +(($target.host == $origin_host) and ($target.user == $origin_user)) + as $is_local | + + +to_entries | +map(select(.value.type == "file")) as $file_sources | +map(select(.value.type == "git")) as $git_sources | +map(select(.value.type == "symlink")) as $symlink_sources | + +($file_sources + $symlink_sources) as $rsync_sources | + + +# Safeguard to prevent clobbering of misspelled targets. +# This script must run at target first. +def checktarget_script: + @sh "if ! test -f \($target.path)/.populate; then", + @sh " echo error: missing sentinel file: \($target.host):\($target.path)/.populate >&2", + @sh " exit 1", + @sh "fi"; + + +def git_script: + @sh "fetch_git(){(", + #@sh " export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt", + + @sh " dst_dir=\($target.path)/$1", + @sh " src_url=$2", + @sh " src_ref=$3", + + @sh " if ! test -e \"$dst_dir\"; then", + @sh " git clone \"$src_url\" \"$dst_dir\"", + @sh " fi", + + @sh " cd \"$dst_dir\"", + + @sh " if ! url=$(git config remote.origin.url); then", + @sh " git remote add origin \"$src_url\"", + @sh " elif test \"$url\" != \"$src_url\"; then", + @sh " git remote set-url origin \"$src_url\"", + @sh " fi", + + # TODO resolve src_ref to commit hash", + @sh " hash=$src_ref", + + @sh " if ! test \"$(git log --format=%H -1)\" = \"$hash\"; then", + @sh " git fetch origin", + @sh " git checkout \"$hash\" -- \"$dst_dir\"", + @sh " git checkout -f \"$hash\"", + @sh " fi", + + @sh " git clean -dxf", + @sh ")}", + ($git_sources[] | + @sh "fetch_git \(.key) \(.value.git.url) \(.value.git.ref)"); + + +def rsync_script: + @sh "srcdir=$(mktemp -dt populate.XXXXXXXX)", + @sh "chmod 0755 \"$srcdir\"", + @sh "trap cleanup EXIT", + @sh "cleanup() {", + @sh " (set +f; rm -f \"$srcdir\"/*)", + @sh " rmdir \"$srcdir\"", + @sh "}", + + ($symlink_sources[] | + @sh "ln -s \(.value.symlink.target) \"$srcdir\"/\(.key)"), + + @sh "proot \\", + ($file_sources[] | + @sh " -b \(.value.file.path):\"$srcdir\"/\(.key) \\"), + @sh " rsync \\", + @sh " -vFrlptD \\", + @sh " --delete \\", + @sh " -f \("P /*", ($rsync_sources[] | "R /\(.key)")) \\", + @sh " \"$srcdir\"/ \\", + (if $is_local then + @sh " \($target.path)" + else + # ControlPersist=no so we reuse existing control sockets but if we + # create a new one, then remove it immediately after we are done so + # this script does not hang. + @sh " -e \("ssh -o ControlPersist=no -p \($target.port)") \\", + @sh " \($target.user)@\($target.host):\($target.path)" + end); + + +def compile: + [ + @sh "set -euf", + .[] + ] + | map(select(. != null)) | join("\n"); + + +def ssh_target: + @sh "echo \(compile) \\", + @sh " | ssh \($target.user)@\($target.host) -p \($target.port) -T"; + + +[ + (if $use_force then null else checktarget_script end), + git_script +] +| (if $is_local then . else [ssh_target] end), +[ + rsync_script +] +| compile |