oh-my-devpod 0.14.5 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,733 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ production_libexec="/usr/local/libexec/oh-my-devpod"
5
+ production_controller="${production_libexec}/brew-provisioner"
6
+ resolved_self="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)/$(basename "${BASH_SOURCE[0]}")"
7
+ if command -v readlink >/dev/null 2>&1; then
8
+ resolved_self="$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || printf '%s' "${resolved_self}")"
9
+ fi
10
+
11
+ if [[ "${resolved_self}" == "${production_controller}" ]]; then
12
+ test_mode=0
13
+ prefix="/home/linuxbrew/.linuxbrew"
14
+ state_dir="/var/lib/oh-my-devpod/linuxbrew"
15
+ libexec_dir="${production_libexec}"
16
+ sudoers_file="/etc/sudoers.d/oh-my-devpod-brew"
17
+ service_user="omd-brew"
18
+ manager_group="omd-brew"
19
+ global_brew="/usr/local/bin/brew"
20
+ profile_file="/etc/profile.d/oh-my-devpod-brew.sh"
21
+ else
22
+ test_mode="${OHMYDEVPOD_SHARED_BREW_TEST_MODE:-0}"
23
+ prefix="${OHMYDEVPOD_SHARED_BREW_PREFIX:-/home/linuxbrew/.linuxbrew}"
24
+ state_dir="${OHMYDEVPOD_SHARED_BREW_STATE_DIR:-/var/lib/oh-my-devpod/linuxbrew}"
25
+ libexec_dir="${OHMYDEVPOD_SHARED_BREW_LIBEXEC_DIR:-${production_libexec}}"
26
+ sudoers_file="${OHMYDEVPOD_SHARED_BREW_SUDOERS_FILE:-/etc/sudoers.d/oh-my-devpod-brew}"
27
+ service_user="${OHMYDEVPOD_SHARED_BREW_SERVICE_USER:-omd-brew}"
28
+ manager_group="${OHMYDEVPOD_SHARED_BREW_MANAGER_GROUP:-omd-brew}"
29
+ global_brew="${OHMYDEVPOD_SHARED_BREW_GLOBAL_BIN:-${libexec_dir}/global-bin/brew}"
30
+ profile_file="${OHMYDEVPOD_SHARED_BREW_PROFILE_FILE:-${state_dir}/profile.d/oh-my-devpod-brew.sh}"
31
+ fi
32
+
33
+ manifest="${state_dir}/manifest"
34
+ inventory_dir="${state_dir}/inventory"
35
+ lock_dir="${state_dir}/locks"
36
+ lock_file="${lock_dir}/mutation.lock"
37
+ members_dir="${state_dir}/members"
38
+ service_home="${state_dir}/service-home"
39
+ stable_gateway="${libexec_dir}/brew-gateway"
40
+
41
+ fail() {
42
+ printf 'error: shared Linuxbrew provisioner: %s\n' "$*" >&2
43
+ exit 1
44
+ }
45
+
46
+ valid_account_name() {
47
+ [[ "$1" =~ ^[a-z_][a-z0-9_-]*[$]?$ ]]
48
+ }
49
+
50
+ valid_formula_name() {
51
+ [[ "$1" =~ ^[a-z0-9][a-z0-9@+_.-]*$ ]]
52
+ }
53
+
54
+ require_root() {
55
+ [[ "${test_mode}" == "1" || "$(id -u)" -eq 0 ]] || fail "must run as root"
56
+ }
57
+
58
+ stat_uid() {
59
+ if stat -c '%u' "$1" >/dev/null 2>&1; then
60
+ stat -c '%u' "$1"
61
+ else
62
+ stat -f '%u' "$1"
63
+ fi
64
+ }
65
+
66
+ stat_gid() {
67
+ if stat -c '%g' "$1" >/dev/null 2>&1; then
68
+ stat -c '%g' "$1"
69
+ else
70
+ stat -f '%g' "$1"
71
+ fi
72
+ }
73
+
74
+ stat_mode() {
75
+ if stat -c '%a' "$1" >/dev/null 2>&1; then
76
+ stat -c '%a' "$1"
77
+ else
78
+ stat -f '%Lp' "$1"
79
+ fi
80
+ }
81
+
82
+ path_has_no_symlink_components() {
83
+ local path="$1" current="/" part
84
+ local -a parts
85
+ [[ "${path}" == /* ]] || return 1
86
+ IFS='/' read -r -a parts <<< "${path#/}"
87
+ for part in "${parts[@]}"; do
88
+ [[ -n "${part}" ]] || continue
89
+ if [[ "${current}" == "/" ]]; then
90
+ current="/${part}"
91
+ else
92
+ current="${current}/${part}"
93
+ fi
94
+ [[ ! -L "${current}" ]] || return 1
95
+ done
96
+ }
97
+
98
+ ensure_safe_parent() {
99
+ local path="$1" parent
100
+ parent="$(dirname "${path}")"
101
+ path_has_no_symlink_components "${parent}" || fail "unsafe symlink in path: ${parent}"
102
+ mkdir -p "${parent}"
103
+ path_has_no_symlink_components "${parent}" || fail "unsafe symlink in path: ${parent}"
104
+ }
105
+
106
+ manifest_value() {
107
+ local key="$1"
108
+ sed -n "s/^${key}=//p" "${manifest}" | head -n 1
109
+ }
110
+
111
+ new_installation_id() {
112
+ if [[ -r /proc/sys/kernel/random/uuid ]]; then
113
+ tr -d '[:space:]' < /proc/sys/kernel/random/uuid
114
+ elif command -v uuidgen >/dev/null 2>&1; then
115
+ uuidgen | tr '[:upper:]' '[:lower:]'
116
+ else
117
+ printf '%08x-%04x-%04x-%04x-%012x\n' "$(date +%s)" "$$" "${RANDOM}" "${RANDOM}" "${RANDOM}"
118
+ fi
119
+ }
120
+
121
+ account_uid() {
122
+ if [[ "${test_mode}" == "1" ]]; then
123
+ printf '%s\n' "${OHMYDEVPOD_SHARED_BREW_TEST_SERVICE_UID:-$(id -u)}"
124
+ else
125
+ id -u "$1"
126
+ fi
127
+ }
128
+
129
+ group_gid() {
130
+ if [[ "${test_mode}" == "1" ]]; then
131
+ printf '%s\n' "${OHMYDEVPOD_SHARED_BREW_TEST_SERVICE_GID:-$(id -g)}"
132
+ else
133
+ getent group "$1" | awk -F: '{print $3}'
134
+ fi
135
+ }
136
+
137
+ passwd_home() {
138
+ local user="$1"
139
+ if [[ "${test_mode}" == "1" ]]; then
140
+ if [[ "${user}" == "${service_user}" ]]; then
141
+ printf '%s\n' "${service_home}"
142
+ else
143
+ printf '%s\n' "${OHMYDEVPOD_SHARED_BREW_TEST_TARGET_HOME:-${HOME}}"
144
+ fi
145
+ else
146
+ getent passwd "${user}" | awk -F: '{print $6}'
147
+ fi
148
+ }
149
+
150
+ invoking_user() {
151
+ local user
152
+ if [[ "${test_mode}" == "1" ]]; then
153
+ user="${OHMYDEVPOD_SHARED_BREW_TEST_TARGET_USER:-$(id -un)}"
154
+ else
155
+ user="${SUDO_USER:-}"
156
+ [[ -n "${user}" && "${user}" != "root" ]] || fail "cannot determine the non-root user to enroll; run through sudo from that account"
157
+ fi
158
+ valid_account_name "${user}" || fail "invalid invoking user"
159
+ printf '%s\n' "${user}"
160
+ }
161
+
162
+ run_as_account() {
163
+ local user="$1" home="$2"
164
+ shift 2
165
+ if [[ "${test_mode}" == "1" || "$(id -un)" == "${user}" ]]; then
166
+ env -i HOME="${home}" USER="${user}" PATH="/usr/local/bin:/usr/bin:/bin:${prefix}/bin" OHMYDEVPOD_SHARED_BREW_PREFIX="${prefix}" OMD_TEST_BREW_LOG="${OMD_TEST_BREW_LOG:-/dev/null}" "$@"
167
+ elif command -v runuser >/dev/null 2>&1; then
168
+ runuser -u "${user}" -- env -i HOME="${home}" USER="${user}" PATH="/usr/local/bin:/usr/bin:/bin:${prefix}/bin" "$@"
169
+ else
170
+ sudo -n -H -u "${user}" -- env -i HOME="${home}" USER="${user}" PATH="/usr/local/bin:/usr/bin:/bin:${prefix}/bin" "$@"
171
+ fi
172
+ }
173
+
174
+ ensure_accounts() {
175
+ local nologin
176
+ if [[ "${test_mode}" == "1" ]]; then
177
+ return 0
178
+ fi
179
+ valid_account_name "${service_user}" || fail "invalid service user"
180
+ valid_account_name "${manager_group}" || fail "invalid manager group"
181
+ if [[ -e "${manifest}" || -L "${manifest}" ]]; then
182
+ getent group "${manager_group}" >/dev/null || fail "shared manager group is missing"
183
+ id -u "${service_user}" >/dev/null 2>&1 || fail "shared service account is missing"
184
+ [[ "$(id -g "${service_user}")" == "$(group_gid "${manager_group}")" ]] || fail "service account primary group mismatch"
185
+ return 0
186
+ fi
187
+ if ! getent group "${manager_group}" >/dev/null; then
188
+ groupadd --system "${manager_group}"
189
+ fi
190
+ if ! id -u "${service_user}" >/dev/null 2>&1; then
191
+ ensure_safe_parent "${service_home}"
192
+ nologin="$(command -v nologin || printf '/usr/sbin/nologin')"
193
+ useradd --system --gid "${manager_group}" --home-dir "${service_home}" --create-home --shell "${nologin}" "${service_user}"
194
+ fi
195
+ [[ "$(id -g "${service_user}")" == "$(group_gid "${manager_group}")" ]] || fail "service account primary group mismatch"
196
+ }
197
+
198
+ prepare_state_layout() {
199
+ local service_uid service_gid
200
+ ensure_safe_parent "${state_dir}"
201
+ ensure_safe_parent "${prefix}"
202
+ path_has_no_symlink_components "${libexec_dir}" || fail "unsafe libexec path"
203
+ [[ ! -e "${state_dir}" || -d "${state_dir}" ]] || fail "shared state path is not a directory"
204
+ [[ ! -L "${state_dir}" ]] || fail "shared state path is a symlink"
205
+ mkdir -p "${state_dir}" "${inventory_dir}" "${lock_dir}" "${members_dir}" "${service_home}" "${libexec_dir}/bin"
206
+ path_has_no_symlink_components "${state_dir}" || fail "unsafe shared state path"
207
+ [[ ! -L "${inventory_dir}" && ! -L "${lock_dir}" && ! -L "${members_dir}" && ! -L "${service_home}" ]] || fail "unsafe shared state subdirectory"
208
+ [[ ! -L "${lock_file}" ]] || fail "shared mutation lock is a symlink"
209
+ : > "${lock_file}"
210
+ service_uid="$(account_uid "${service_user}")"
211
+ service_gid="$(group_gid "${manager_group}")"
212
+ if [[ "${test_mode}" != "1" ]]; then
213
+ chown root:root "${state_dir}" "${lock_dir}" "${members_dir}"
214
+ chmod 0755 "${state_dir}" "${lock_dir}" "${members_dir}"
215
+ chown "${service_uid}:${service_gid}" "${inventory_dir}" "${service_home}" "${lock_file}"
216
+ chmod 0755 "${inventory_dir}"
217
+ chmod 0750 "${service_home}"
218
+ chmod 0600 "${lock_file}"
219
+ fi
220
+ }
221
+
222
+ validate_policy_targets() {
223
+ local expected_link existing_target
224
+ expected_link="${libexec_dir}/bin/brew"
225
+ if [[ -e "${global_brew}" || -L "${global_brew}" ]]; then
226
+ [[ -L "${global_brew}" ]] || fail "refusing to replace existing ${global_brew}"
227
+ existing_target="$(readlink "${global_brew}")"
228
+ [[ "${existing_target}" == "${expected_link}" ]] || fail "refusing to replace external Brew command at ${global_brew}"
229
+ fi
230
+ if [[ -e "${profile_file}" || -L "${profile_file}" ]]; then
231
+ [[ -f "${profile_file}" && ! -L "${profile_file}" ]] || fail "refusing unsafe shell profile activation path"
232
+ head -n 1 "${profile_file}" | grep -Fqx '# Generated by oh-my-devpod. Shared Linuxbrew gateway.' || fail "refusing to replace existing shell profile activation"
233
+ fi
234
+ if [[ -e "${sudoers_file}" || -L "${sudoers_file}" ]]; then
235
+ [[ -f "${sudoers_file}" && ! -L "${sudoers_file}" ]] || fail "refusing unsafe sudoers policy path"
236
+ head -n 1 "${sudoers_file}" | grep -Fqx '# Generated by oh-my-devpod. Shared Linuxbrew policy.' || fail "refusing to replace existing sudoers policy"
237
+ fi
238
+ }
239
+
240
+ install_policy() {
241
+ local link temporary sudoers_tmp global_tmp profile_tmp existing_target
242
+ [[ -f "${stable_gateway}" && ! -L "${stable_gateway}" ]] || fail "fixed Brew gateway is missing"
243
+ [[ "${test_mode}" == "1" || "$(stat_uid "${stable_gateway}")" == "0" ]] || fail "fixed Brew gateway is not root-owned"
244
+ [[ "${test_mode}" == "1" || "$(stat_gid "${stable_gateway}")" == "0" ]] || fail "fixed Brew gateway group is not root"
245
+ link="${libexec_dir}/bin/brew"
246
+ temporary="${libexec_dir}/bin/.brew.$$"
247
+ ln -s ../brew-gateway "${temporary}"
248
+ mv -f "${temporary}" "${link}"
249
+
250
+ ensure_safe_parent "${global_brew}"
251
+ if [[ -e "${global_brew}" || -L "${global_brew}" ]]; then
252
+ [[ -L "${global_brew}" ]] || fail "refusing to replace existing ${global_brew}"
253
+ existing_target="$(readlink "${global_brew}")"
254
+ [[ "${existing_target}" == "${link}" ]] || fail "refusing to replace external Brew command at ${global_brew}"
255
+ else
256
+ global_tmp="$(dirname "${global_brew}")/.brew.$$"
257
+ ln -s "${link}" "${global_tmp}"
258
+ mv -f "${global_tmp}" "${global_brew}"
259
+ fi
260
+
261
+ ensure_safe_parent "${profile_file}"
262
+ profile_tmp="${profile_file}.tmp.$$"
263
+ {
264
+ printf '%s\n' '# Generated by oh-my-devpod. Shared Linuxbrew gateway.'
265
+ printf 'case ":$PATH:" in *":%s:"*) ;; *) PATH="%s:$PATH" ;; esac\n' "${libexec_dir}/bin" "${libexec_dir}/bin"
266
+ printf '%s\n' 'export PATH'
267
+ } > "${profile_tmp}"
268
+ chmod 0644 "${profile_tmp}"
269
+ if [[ "${test_mode}" != "1" ]]; then chown root:root "${profile_tmp}"; fi
270
+ mv -f "${profile_tmp}" "${profile_file}"
271
+
272
+ ensure_safe_parent "${sudoers_file}"
273
+ sudoers_tmp="${sudoers_file}.tmp.$$"
274
+ {
275
+ printf '%s\n' '# Generated by oh-my-devpod. Shared Linuxbrew policy.'
276
+ printf '%%%s ALL=(%s) NOPASSWD: %s --service *\n' "${manager_group}" "${service_user}" "${stable_gateway}"
277
+ printf '%%%s ALL=(root) NOPASSWD: %s source-profile upstream\n' "${manager_group}" "${libexec_dir}/brew-provisioner"
278
+ printf '%%%s ALL=(root) NOPASSWD: %s source-profile cn\n' "${manager_group}" "${libexec_dir}/brew-provisioner"
279
+ } > "${sudoers_tmp}"
280
+ chmod 0440 "${sudoers_tmp}"
281
+ if [[ "${test_mode}" != "1" ]]; then
282
+ chown root:root "${sudoers_tmp}"
283
+ command -v visudo >/dev/null 2>&1 || fail "visudo is required"
284
+ visudo -cf "${sudoers_tmp}" >/dev/null || fail "generated sudoers policy is invalid"
285
+ fi
286
+ mv -f "${sudoers_tmp}" "${sudoers_file}"
287
+ }
288
+
289
+ install_dependencies() {
290
+ [[ "${test_mode}" == "1" ]] && return 0
291
+ command -v apt-get >/dev/null 2>&1 || fail "Ubuntu apt-get is required"
292
+ apt-get update
293
+ apt-get install -y build-essential procps curl file git ca-certificates util-linux
294
+ }
295
+
296
+ brew_remote_for_profile() {
297
+ case "$1" in
298
+ upstream) printf '%s\n' 'https://github.com/Homebrew/brew.git' ;;
299
+ cn) printf '%s\n' 'https://mirrors.ustc.edu.cn/brew.git' ;;
300
+ *) return 1 ;;
301
+ esac
302
+ }
303
+
304
+ initialize_prefix() {
305
+ local mirror_profile="$1" remote test_brew
306
+ [[ ! -e "${prefix}" && ! -L "${prefix}" ]] || fail "refusing to initialize over existing path: ${prefix}"
307
+ mkdir -p "${prefix}"
308
+ if [[ "${test_mode}" == "1" ]]; then
309
+ test_brew="${OHMYDEVPOD_SHARED_BREW_TEST_REAL_BIN:-}"
310
+ [[ -x "${test_brew}" && ! -L "${test_brew}" ]] || fail "test mode requires a fake Brew backend"
311
+ mkdir -p "${prefix}/bin" "${prefix}/Cellar"
312
+ install -m 0755 "${test_brew}" "${prefix}/bin/brew"
313
+ return 0
314
+ fi
315
+
316
+ install_dependencies
317
+ chown "${service_user}:${manager_group}" "${prefix}"
318
+ remote="$(brew_remote_for_profile "${mirror_profile}")" || fail "invalid mirror profile"
319
+ run_as_account "${service_user}" "${service_home}" git clone --depth 1 "${remote}" "${prefix}"
320
+ [[ -x "${prefix}/bin/brew" ]] || fail "Linuxbrew installation did not create ${prefix}/bin/brew"
321
+ }
322
+
323
+ user_for_uid() {
324
+ local uid="$1"
325
+ if [[ "${test_mode}" == "1" ]]; then
326
+ printf '%s\n' "${OHMYDEVPOD_SHARED_BREW_TEST_LEGACY_OWNER:-$(id -un)}"
327
+ else
328
+ getent passwd "${uid}" | awk -F: '{print $1}'
329
+ fi
330
+ }
331
+
332
+ legacy_home_for_user() {
333
+ local user="$1"
334
+ if [[ "${test_mode}" == "1" && -n "${OHMYDEVPOD_SHARED_BREW_TEST_LEGACY_HOME:-}" ]]; then
335
+ printf '%s\n' "${OHMYDEVPOD_SHARED_BREW_TEST_LEGACY_HOME}"
336
+ else
337
+ passwd_home "${user}"
338
+ fi
339
+ }
340
+
341
+ validate_legacy_prefix() {
342
+ local owner_uid owner_gid owner owner_home marker marker_uid reported
343
+ [[ -d "${prefix}" && ! -L "${prefix}" && -x "${prefix}/bin/brew" ]] || fail "unmanaged-prefix-conflict: prefix is missing or unsafe"
344
+ path_has_no_symlink_components "${prefix}" || fail "unmanaged-prefix-conflict: prefix path contains a symlink"
345
+ owner_uid="$(stat_uid "${prefix}")"
346
+ owner_gid="$(stat_gid "${prefix}")"
347
+ owner="$(user_for_uid "${owner_uid}")"
348
+ [[ -n "${owner}" && "${owner}" != "root" ]] || fail "unmanaged-prefix-conflict: cannot resolve a non-root legacy owner"
349
+ owner_home="$(legacy_home_for_user "${owner}")"
350
+ [[ "${owner_home}" == /* && -d "${owner_home}" && ! -L "${owner_home}" ]] || fail "unmanaged-prefix-conflict: legacy owner home is unsafe"
351
+ [[ "$(cd "${owner_home}" 2>/dev/null && pwd -P)" == "${owner_home}" ]] || fail "unmanaged-prefix-conflict: legacy owner home resolves through a symlink"
352
+ marker="${owner_home}/.local/state/oh-my-devpod/managed/linuxbrew"
353
+ [[ -f "${marker}" && ! -L "${marker}" ]] || fail "unmanaged-prefix-conflict: legacy OMD marker is missing"
354
+ marker_uid="$(stat_uid "${marker}")"
355
+ [[ "${test_mode}" == "1" || "${marker_uid}" == "${owner_uid}" ]] || fail "unmanaged-prefix-conflict: legacy marker owner mismatch"
356
+ grep -qx 'managed_by=oh-my-devpod' "${marker}" || fail "unmanaged-prefix-conflict: invalid legacy marker"
357
+ grep -qx 'component=linuxbrew' "${marker}" || fail "unmanaged-prefix-conflict: invalid legacy component"
358
+ grep -qx 'kind=directory' "${marker}" || fail "unmanaged-prefix-conflict: invalid legacy marker kind"
359
+ grep -Fqx "artifact=${prefix}" "${marker}" || fail "unmanaged-prefix-conflict: legacy marker does not match the prefix"
360
+ if [[ -e "${prefix}/etc/homebrew/brew.env" || -L "${prefix}/etc/homebrew/brew.env" ]]; then
361
+ source_profile_brew_env_is_managed "${prefix}/etc/homebrew/brew.env" || fail "unmanaged-prefix-conflict: legacy Homebrew source configuration was modified"
362
+ fi
363
+ reported="$(run_as_account "${owner}" "${owner_home}" "${prefix}/bin/brew" --prefix)"
364
+ [[ "${reported}" == "${prefix}" ]] || fail "unmanaged-prefix-conflict: Brew reports another prefix"
365
+ printf '%s\t%s\t%s\t%s\n' "${owner}" "${owner_uid}" "${owner_gid}" "${owner_home}"
366
+ }
367
+
368
+ write_inventory_record() {
369
+ local formula="$1" installation_id="$2" state="$3" provenance="$4" destination="$5" temporary
370
+ valid_formula_name "${formula}" || return 0
371
+ temporary="${destination}.tmp.$$"
372
+ {
373
+ printf 'managed_by=oh-my-devpod\n'
374
+ printf 'component=%s\n' "${formula}"
375
+ printf 'kind=brew-formula\n'
376
+ printf 'artifact=%s\n' "${formula}"
377
+ printf 'brew_prefix=%s\n' "${prefix}"
378
+ printf 'installation_id=%s\n' "${installation_id}"
379
+ printf 'state=%s\n' "${state}"
380
+ printf 'provenance=%s\n' "${provenance}"
381
+ } > "${temporary}"
382
+ mv -f "${temporary}" "${destination}"
383
+ }
384
+
385
+ legacy_formula_is_managed() {
386
+ local owner_home="$1" formula="$2" marker
387
+ marker="${owner_home}/.local/state/oh-my-devpod/managed/${formula}"
388
+ [[ -f "${marker}" && ! -L "${marker}" ]] || return 1
389
+ grep -qx 'managed_by=oh-my-devpod' "${marker}" || return 1
390
+ grep -qx 'kind=brew-formula' "${marker}" || return 1
391
+ grep -Fqx "artifact=${formula}" "${marker}" || return 1
392
+ grep -Fqx "brew_prefix=${prefix}" "${marker}" || return 1
393
+ }
394
+
395
+ stage_legacy_inventory() {
396
+ local owner_home="$1" installation_id="$2" staging="$3" formula state provenance
397
+ mkdir -p "${staging}"
398
+ [[ -d "${prefix}/Cellar" ]] || return 0
399
+ while IFS= read -r formula; do
400
+ [[ -n "${formula}" ]] || continue
401
+ state=external
402
+ provenance=legacy-preexisting
403
+ if legacy_formula_is_managed "${owner_home}" "${formula}"; then
404
+ state=managed
405
+ provenance=legacy-marker
406
+ fi
407
+ write_inventory_record "${formula}" "${installation_id}" "${state}" "${provenance}" "${staging}/${formula}"
408
+ done < <(find "${prefix}/Cellar" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | LC_ALL=C sort -u)
409
+ }
410
+
411
+ write_manifest() {
412
+ local mirror_profile="$1" installation_id="$2" service_uid="$3" service_gid="$4" destination="$5"
413
+ {
414
+ printf 'schema_version=1\n'
415
+ printf 'managed_by=oh-my-devpod\n'
416
+ printf 'mode=shared-service-account\n'
417
+ printf 'prefix=%s\n' "${prefix}"
418
+ printf 'service_user=%s\n' "${service_user}"
419
+ printf 'service_uid=%s\n' "${service_uid}"
420
+ printf 'manager_group=%s\n' "${manager_group}"
421
+ printf 'service_gid=%s\n' "${service_gid}"
422
+ printf 'mirror_profile=%s\n' "${mirror_profile}"
423
+ printf 'installation_id=%s\n' "${installation_id}"
424
+ } > "${destination}"
425
+ chmod 0644 "${destination}"
426
+ }
427
+
428
+ validate_existing_manifest() {
429
+ local expected_uid expected_gid
430
+ [[ -f "${manifest}" && ! -L "${manifest}" ]] || fail "shared manifest is missing or unsafe"
431
+ path_has_no_symlink_components "${state_dir}" || fail "shared state path contains a symlink"
432
+ [[ "$(manifest_value schema_version || true)" == "1" ]] || fail "unsupported shared manifest schema"
433
+ [[ "$(manifest_value managed_by || true)" == "oh-my-devpod" ]] || fail "shared manifest ownership mismatch"
434
+ [[ "$(manifest_value mode || true)" == "shared-service-account" ]] || fail "shared manifest mode mismatch"
435
+ [[ "$(manifest_value prefix || true)" == "${prefix}" ]] || fail "shared manifest prefix mismatch"
436
+ [[ "$(manifest_value service_user || true)" == "${service_user}" ]] || fail "shared manifest service user mismatch"
437
+ [[ "$(manifest_value manager_group || true)" == "${manager_group}" ]] || fail "shared manifest manager group mismatch"
438
+ expected_uid="$(account_uid "${service_user}")"
439
+ expected_gid="$(group_gid "${manager_group}")"
440
+ [[ "$(manifest_value service_uid || true)" == "${expected_uid}" ]] || fail "shared manifest service UID mismatch"
441
+ [[ "$(manifest_value service_gid || true)" == "${expected_gid}" ]] || fail "shared manifest service GID mismatch"
442
+ [[ "$(manifest_value installation_id || true)" =~ ^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$ ]] || fail "shared manifest installation ID is invalid"
443
+ [[ -x "${prefix}/bin/brew" && ! -L "${prefix}" ]] || fail "shared Brew backend is missing or unsafe"
444
+ path_has_no_symlink_components "${prefix}" || fail "shared Brew prefix contains a symlink"
445
+ [[ "${test_mode}" == "1" || "$(stat_uid "${prefix}")" == "${expected_uid}" ]] || fail "shared Brew prefix owner mismatch"
446
+ [[ "${test_mode}" == "1" || "$(stat_gid "${prefix}")" == "${expected_gid}" ]] || fail "shared Brew prefix group mismatch"
447
+ if [[ "${test_mode}" != "1" ]]; then
448
+ [[ "$(stat_uid "${manifest}")" == "0" && "$(stat_gid "${manifest}")" == "0" ]] || fail "shared manifest is not root-owned"
449
+ (( (8#$(stat_mode "${manifest}") & 8#022) == 0 )) || fail "shared manifest is writable by non-root users"
450
+ [[ "$(stat_uid "${state_dir}")" == "0" ]] || fail "shared state directory is not root-owned"
451
+ [[ "$(stat_uid "${libexec_dir}/brew-provisioner")" == "0" && "$(stat_uid "${stable_gateway}")" == "0" ]] || fail "shared controllers are not root-owned"
452
+ fi
453
+ }
454
+
455
+ initialize_shared_state() {
456
+ local mirror_profile="$1" installation_id service_uid service_gid manifest_tmp created_prefix=0
457
+ local legacy_info="" legacy_owner="" legacy_uid="" legacy_gid="" legacy_home="" inventory_staging=""
458
+ installation_id="$(new_installation_id)"
459
+ service_uid="$(account_uid "${service_user}")"
460
+ service_gid="$(group_gid "${manager_group}")"
461
+ manifest_tmp="${state_dir}/.manifest.$$"
462
+ inventory_staging="${state_dir}/.inventory.$$"
463
+ [[ -z "$(find "${inventory_dir}" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)" ]] || fail "shared inventory exists without a valid manifest"
464
+
465
+ if [[ -e "${prefix}" || -L "${prefix}" ]]; then
466
+ legacy_info="$(validate_legacy_prefix)"
467
+ IFS=$'\t' read -r legacy_owner legacy_uid legacy_gid legacy_home <<< "${legacy_info}"
468
+ stage_legacy_inventory "${legacy_home}" "${installation_id}" "${inventory_staging}"
469
+ else
470
+ created_prefix=1
471
+ if ! initialize_prefix "${mirror_profile}"; then
472
+ rm -rf "${prefix}"
473
+ fail "could not initialize the shared Brew prefix"
474
+ fi
475
+ mkdir -p "${inventory_staging}"
476
+ fi
477
+ write_manifest "${mirror_profile}" "${installation_id}" "${service_uid}" "${service_gid}" "${manifest_tmp}"
478
+
479
+ if [[ "${test_mode}" != "1" ]]; then
480
+ if ! chown -R "${service_uid}:${service_gid}" "${prefix}"; then
481
+ [[ "${created_prefix}" != "1" ]] || rm -rf "${prefix}"
482
+ rm -rf "${inventory_staging}" "${manifest_tmp}"
483
+ fail "could not transfer the Brew prefix to the service account"
484
+ fi
485
+ fi
486
+ if [[ "${mirror_profile}" == "cn" ]]; then
487
+ mkdir -p "${prefix}/etc/homebrew"
488
+ source_profile_brew_env > "${state_dir}/.brew-env-new.$$"
489
+ if [[ "${test_mode}" == "1" ]]; then
490
+ install -m 0644 "${state_dir}/.brew-env-new.$$" "${prefix}/etc/homebrew/brew.env"
491
+ else
492
+ install -o "${service_uid}" -g "${service_gid}" -m 0644 "${state_dir}/.brew-env-new.$$" "${prefix}/etc/homebrew/brew.env"
493
+ fi
494
+ rm -f "${state_dir}/.brew-env-new.$$"
495
+ elif source_profile_brew_env_is_managed "${prefix}/etc/homebrew/brew.env"; then
496
+ rm -f "${prefix}/etc/homebrew/brew.env"
497
+ fi
498
+ if ! cp -R "${inventory_staging}/." "${inventory_dir}/" 2>/dev/null; then
499
+ if [[ "${test_mode}" != "1" && -n "${legacy_uid}" ]]; then
500
+ chown -R "${legacy_uid}:${legacy_gid}" "${prefix}" || true
501
+ elif [[ "${created_prefix}" == "1" ]]; then
502
+ rm -rf "${prefix}"
503
+ fi
504
+ rm -rf "${inventory_staging}" "${manifest_tmp}"
505
+ fail "could not publish the shared Brew inventory"
506
+ fi
507
+ rm -rf "${inventory_staging}"
508
+ if [[ "${test_mode}" != "1" ]]; then
509
+ chown -R "${service_uid}:${service_gid}" "${inventory_dir}"
510
+ fi
511
+ if ! mv -f "${manifest_tmp}" "${manifest}"; then
512
+ if [[ "${test_mode}" != "1" && -n "${legacy_uid}" ]]; then
513
+ chown -R "${legacy_uid}:${legacy_gid}" "${prefix}" || true
514
+ elif [[ "${created_prefix}" == "1" ]]; then
515
+ rm -rf "${prefix}"
516
+ fi
517
+ fail "could not publish the shared Brew manifest"
518
+ fi
519
+ if [[ "${test_mode}" != "1" ]]; then
520
+ chown root:root "${manifest}"
521
+ fi
522
+ if [[ -n "${legacy_owner}" ]]; then
523
+ printf '%s\n' "${legacy_owner}" > "${state_dir}/.legacy-owner"
524
+ fi
525
+ }
526
+
527
+ enroll_user() {
528
+ local target_user="$1" target_home uid record temporary
529
+ valid_account_name "${target_user}" || fail "invalid target user"
530
+ if [[ "${test_mode}" == "1" ]]; then
531
+ uid="${OHMYDEVPOD_SHARED_BREW_TEST_TARGET_UID:-$(id -u)}"
532
+ else
533
+ uid="$(id -u "${target_user}")" || fail "target user does not exist"
534
+ fi
535
+ target_home="$(passwd_home "${target_user}")"
536
+ [[ "${target_home}" == /* && -d "${target_home}" && ! -L "${target_home}" ]] || fail "target home is unsafe"
537
+ if [[ "${test_mode}" != "1" ]]; then
538
+ usermod -a -G "${manager_group}" "${target_user}"
539
+ fi
540
+ record="${members_dir}/${target_user}"
541
+ [[ ! -L "${record}" ]] || fail "unsafe member record"
542
+ temporary="${record}.tmp.$$"
543
+ {
544
+ printf 'managed_by=oh-my-devpod\n'
545
+ printf 'user=%s\n' "${target_user}"
546
+ printf 'uid=%s\n' "${uid}"
547
+ printf 'home=%s\n' "${target_home}"
548
+ printf 'enrolled_at=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
549
+ } > "${temporary}"
550
+ chmod 0644 "${temporary}"
551
+ mv -f "${temporary}" "${record}"
552
+ if [[ "${test_mode}" != "1" ]]; then
553
+ chown root:root "${record}"
554
+ fi
555
+ }
556
+
557
+ remote_url() {
558
+ local repository="$1"
559
+ run_as_account "${service_user}" "${service_home}" git -C "${repository}" remote get-url origin 2>/dev/null
560
+ }
561
+
562
+ set_remote_url() {
563
+ local repository="$1" target="$2"
564
+ if remote_url "${repository}" >/dev/null 2>&1; then
565
+ run_as_account "${service_user}" "${service_home}" git -C "${repository}" remote set-url origin "${target}"
566
+ else
567
+ run_as_account "${service_user}" "${service_home}" git -C "${repository}" remote add origin "${target}"
568
+ fi
569
+ }
570
+
571
+ restore_remote_url() {
572
+ local repository="$1" present="$2" value="$3"
573
+ if [[ "${present}" == "1" ]]; then
574
+ set_remote_url "${repository}" "${value}"
575
+ elif remote_url "${repository}" >/dev/null 2>&1; then
576
+ run_as_account "${service_user}" "${service_home}" git -C "${repository}" remote remove origin
577
+ fi
578
+ }
579
+
580
+ source_profile_brew_env() {
581
+ cat <<'EOF'
582
+ # Generated by oh-my-devpod. Shared host-scoped Homebrew source configuration.
583
+ HOMEBREW_BREW_GIT_REMOTE=https://mirrors.ustc.edu.cn/brew.git
584
+ HOMEBREW_CORE_GIT_REMOTE=https://mirrors.ustc.edu.cn/homebrew-core.git
585
+ HOMEBREW_API_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles/api
586
+ HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles
587
+ EOF
588
+ }
589
+
590
+ source_profile_brew_env_is_managed() {
591
+ local path="$1" actual shared legacy
592
+ [[ -f "${path}" && ! -L "${path}" ]] || return 1
593
+ actual="$(cat "${path}")"
594
+ shared="$(source_profile_brew_env)"
595
+ legacy="${shared/#\# Generated by oh-my-devpod. Shared host-scoped Homebrew source configuration./\# Generated by oh-my-devpod. Managed native source configuration.}"
596
+ [[ "${actual}" == "${shared}" || "${actual}" == "${legacy}" ]]
597
+ }
598
+
599
+ set_source_profile_locked() {
600
+ local profile="$1" brew_remote core_remote core_dir env_path env_backup env_existed=0
601
+ local brew_old="" brew_present=0 core_old="" core_present=0 manifest_tmp service_uid service_gid apply_failed=0
602
+ case "${profile}" in
603
+ upstream)
604
+ brew_remote='https://github.com/Homebrew/brew.git'
605
+ core_remote='https://github.com/Homebrew/homebrew-core.git'
606
+ ;;
607
+ cn)
608
+ brew_remote='https://mirrors.ustc.edu.cn/brew.git'
609
+ core_remote='https://mirrors.ustc.edu.cn/homebrew-core.git'
610
+ ;;
611
+ *)
612
+ fail "invalid source profile"
613
+ ;;
614
+ esac
615
+ validate_existing_manifest
616
+ command -v git >/dev/null 2>&1 || fail "git is required to change the shared Brew source"
617
+ [[ -d "${prefix}/.git" ]] || fail "shared Brew repository metadata is missing"
618
+ core_dir="${prefix}/Library/Taps/homebrew/homebrew-core"
619
+ env_path="${prefix}/etc/homebrew/brew.env"
620
+ env_backup="${state_dir}/.brew-env.$$"
621
+ manifest_tmp="${state_dir}/.manifest.$$"
622
+ if brew_old="$(remote_url "${prefix}")"; then brew_present=1; fi
623
+ if [[ -d "${core_dir}/.git" ]] && core_old="$(remote_url "${core_dir}")"; then core_present=1; fi
624
+ if [[ -e "${env_path}" || -L "${env_path}" ]]; then
625
+ source_profile_brew_env_is_managed "${env_path}" || fail "refusing to overwrite modified shared Homebrew source configuration"
626
+ cp -p "${env_path}" "${env_backup}"
627
+ env_existed=1
628
+ fi
629
+
630
+ if ! set_remote_url "${prefix}" "${brew_remote}"; then
631
+ fail "could not update the shared Brew remote"
632
+ fi
633
+ if [[ -d "${core_dir}/.git" ]] && ! set_remote_url "${core_dir}" "${core_remote}"; then
634
+ restore_remote_url "${prefix}" "${brew_present}" "${brew_old}" || true
635
+ fail "could not update the shared Homebrew/core remote"
636
+ fi
637
+
638
+ service_uid="$(account_uid "${service_user}")"
639
+ service_gid="$(group_gid "${manager_group}")"
640
+ mkdir -p "$(dirname "${env_path}")" || apply_failed=1
641
+ if [[ "${apply_failed}" == "0" && "${profile}" == "cn" ]]; then
642
+ source_profile_brew_env > "${state_dir}/.brew-env-new.$$" || apply_failed=1
643
+ if [[ "${apply_failed}" == "0" ]]; then
644
+ install -o "${service_uid}" -g "${service_gid}" -m 0644 "${state_dir}/.brew-env-new.$$" "${env_path}" || apply_failed=1
645
+ fi
646
+ rm -f "${state_dir}/.brew-env-new.$$"
647
+ elif [[ "${apply_failed}" == "0" ]]; then
648
+ rm -f "${env_path}" || apply_failed=1
649
+ fi
650
+ if [[ "${apply_failed}" == "0" ]]; then
651
+ awk -v profile="${profile}" 'BEGIN { changed=0 } /^mirror_profile=/ { print "mirror_profile=" profile; changed=1; next } { print } END { if (!changed) print "mirror_profile=" profile }' "${manifest}" > "${manifest_tmp}" || apply_failed=1
652
+ fi
653
+ if [[ "${apply_failed}" == "0" ]]; then
654
+ chmod 0644 "${manifest_tmp}" || apply_failed=1
655
+ fi
656
+ if [[ "${apply_failed}" == "0" ]]; then
657
+ mv -f "${manifest_tmp}" "${manifest}" || apply_failed=1
658
+ fi
659
+ if [[ "${apply_failed}" != "0" ]]; then
660
+ restore_remote_url "${prefix}" "${brew_present}" "${brew_old}" || true
661
+ [[ ! -d "${core_dir}/.git" ]] || restore_remote_url "${core_dir}" "${core_present}" "${core_old}" || true
662
+ if [[ "${env_existed}" == "1" ]]; then
663
+ cp -p "${env_backup}" "${env_path}" || true
664
+ else
665
+ rm -f "${env_path}"
666
+ fi
667
+ rm -f "${manifest_tmp}" "${state_dir}/.brew-env-new.$$"
668
+ fail "could not publish the shared Brew source profile"
669
+ fi
670
+ [[ "${test_mode}" == "1" ]] || chown root:root "${manifest}"
671
+ rm -f "${env_backup}"
672
+ }
673
+
674
+ set_source_profile() {
675
+ local profile="$1"
676
+ require_root
677
+ command -v flock >/dev/null 2>&1 || fail "flock is required"
678
+ [[ -f "${lock_file}" && ! -L "${lock_file}" ]] || fail "shared mutation lock is missing or unsafe"
679
+ exec 9<> "${lock_file}"
680
+ flock -x 9
681
+ set_source_profile_locked "${profile}"
682
+ }
683
+
684
+ ensure_shared_brew() {
685
+ local mirror_profile="$1" target_user legacy_owner="" existing_claim=0
686
+ require_root
687
+ case "${mirror_profile}" in upstream|cn) ;; *) fail "invalid mirror profile" ;; esac
688
+ target_user="$(invoking_user)"
689
+ if [[ -e "${manifest}" || -L "${manifest}" ]]; then
690
+ existing_claim=1
691
+ elif [[ -e "${prefix}" || -L "${prefix}" ]]; then
692
+ validate_legacy_prefix >/dev/null
693
+ fi
694
+ ensure_accounts
695
+ if [[ "${existing_claim}" == "1" ]]; then
696
+ validate_existing_manifest
697
+ fi
698
+ prepare_state_layout
699
+ validate_policy_targets
700
+ command -v flock >/dev/null 2>&1 || fail "flock is required"
701
+ exec 9<> "${lock_file}"
702
+ flock -x 9
703
+
704
+ if [[ -e "${manifest}" || -L "${manifest}" ]]; then
705
+ validate_existing_manifest
706
+ else
707
+ initialize_shared_state "${mirror_profile}"
708
+ fi
709
+
710
+ install_policy
711
+ enroll_user "${target_user}"
712
+ if [[ -f "${state_dir}/.legacy-owner" ]]; then
713
+ legacy_owner="$(tr -d '[:space:]' < "${state_dir}/.legacy-owner")"
714
+ if [[ -n "${legacy_owner}" && "${legacy_owner}" != "${target_user}" ]]; then
715
+ enroll_user "${legacy_owner}"
716
+ fi
717
+ rm -f "${state_dir}/.legacy-owner"
718
+ fi
719
+ }
720
+
721
+ case "${1:-}" in
722
+ ensure)
723
+ [[ "$#" -eq 2 ]] || fail "usage: ensure PROFILE"
724
+ ensure_shared_brew "$2"
725
+ ;;
726
+ source-profile)
727
+ [[ "$#" -eq 2 ]] || fail "usage: source-profile PROFILE"
728
+ set_source_profile "$2"
729
+ ;;
730
+ *)
731
+ fail "unsupported operation"
732
+ ;;
733
+ esac