summaryrefslogtreecommitdiffstats
path: root/lib/populate.jq
blob: fa13795b685775ed59cc9cc333ed82902f9dcb0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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_cmd) -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_cmd) \($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