summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/populate.jq126
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