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.
- package/package.json +1 -1
- package/runtime/VERSION +1 -1
- package/runtime/bin/omd +0 -0
- package/runtime/build/omd-brew-gateway.sh +326 -0
- package/runtime/build/omd-brew-provisioner.sh +733 -0
- package/runtime/install/bootstrap.sh +42 -2
- package/runtime/install/update.sh +22 -1
- package/runtime/modules/core/linuxbrew.sh +36 -1
- package/runtime/modules/lib/common.sh +50 -8
- package/runtime/modules/lib/shared-linuxbrew.sh +242 -0
- package/runtime/modules/lib/source-config.sh +8 -3
- package/runtime/modules/tools/zsh-config.sh +8 -0
|
@@ -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
|