-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathupdate.sh
More file actions
179 lines (162 loc) · 6.84 KB
/
Copy pathupdate.sh
File metadata and controls
179 lines (162 loc) · 6.84 KB
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# License: MIT | https://raw.githubusercontent.com/community-scripts/core/main/LICENSE
# ==============================================================================
# MISC/UPDATE.SH - THE `update` HELPER
# ==============================================================================
#
# Pulled by /usr/bin/update (generated by write_update_entrypoint in core.func).
# Its job is to decide whether the installed app can still be updated BEFORE any
# app script is pulled, so a script that was removed upstream explains itself
# instead of failing with a raw 404.
#
# Flow:
# 1. Ask the website (/api/update-info?slug=<slug>) for this app's state.
# 2. active | disabled → pull and run ct/<name>.sh exactly as before. The
# in-script guards (runtime_script_status_guard, check_breaking_change_guard)
# then run as usual, so nothing about a normal update changes.
# 3. deleted | unknown → print the reason (deleted message, breaking-change
# summary, or a generic notice) and do NOT pull. Still offer to update any
# addons, which are independent scripts and keep working.
# 4. Website unreachable → fail open: attempt the normal update.
#
# Context is passed in by the entrypoint via the environment:
# SCRIPT_SLUG, UPDATE_SCRIPT_NAME, COMMUNITY_SCRIPTS_URL,
# COMMUNITY_SCRIPTS_WEBSITE_URL
# ==============================================================================
set -uo pipefail
SLUG="${SCRIPT_SLUG:-}"
NAME="${UPDATE_SCRIPT_NAME:-$SLUG}"
# A name that is not a slug means a damaged entrypoint; the slug still works.
[[ "$NAME" =~ ^[a-zA-Z0-9._-]+$ ]] || NAME="$SLUG"
# The retired Gitea mirror; GitHub serves the same repos.
_cs_github_base() {
local u="${1%/}"
case "$u" in
*//git.community-scripts.org/*)
u="${u#*//git.community-scripts.org/}"
u="${u/\/raw\/branch\//\/}"
u="${u/\/raw\/tag\//\/}"
u="${u/\/raw\/commit\//\/}"
printf 'https://raw.githubusercontent.com/%s' "$u"
;;
*) printf '%s' "$u" ;;
esac
}
BASE="$(_cs_github_base "${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main}")"
export COMMUNITY_SCRIPTS_URL="$BASE"
WEBSITE="${COMMUNITY_SCRIPTS_WEBSITE_URL:-https://community-scripts.org}"
# ── Minimal output helpers (this runs standalone, before core.func exists) ──────
if [[ -t 1 && "${TERM:-}" != "dumb" ]]; then
RD=$'\033[01;31m'; GN=$'\033[1;92m'; YW=$'\033[33m'; CL=$'\033[0m'
else
RD=""; GN=""; YW=""; CL=""
fi
msg_info() { echo -e " ${YW}➤${CL} $*"; }
msg_ok() { echo -e " ${GN}✔${CL} $*"; }
msg_err() { echo -e " ${RD}✘${CL} $*"; }
# Extract a top-level string field from the (flat) JSON body. Matches the sed
# approach already used by the shell-side guards in ui/menu.func — no jq needed.
json_str() {
printf '%s' "$1" | sed -n "s/.*\"$2\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" | head -1
}
script_exists() {
curl -fsSL --connect-timeout 5 --max-time 10 -o /dev/null "${BASE}/ct/${1}.sh" 2>/dev/null
}
# ct/ scripts get renamed; a container keeps whatever slug it was built with.
# Try the successors, but only accept one that actually exists.
# A successful update regenerates /usr/bin/update, so this self-heals.
resolve_script_name() {
local n="$1" c
script_exists "$n" && { printf '%s' "$n"; return 0; }
for c in "${n#alpine-}" "$(printf '%s' "$n" | sed -E 's/-v[0-9]+$//')"; do
[[ -n "$c" && "$c" != "$n" ]] || continue
script_exists "$c" && { printf '%s' "$c"; return 0; }
done
case "$n" in
pbs) script_exists proxmox-backup-server && { printf '%s' proxmox-backup-server; return 0; } ;;
esac
return 1
}
# Pull and run the app update script. An unresolvable name used to curl a 404 into
# bash -c "", which did nothing and still exited 0.
run_app_update() {
local resolved
resolved="$(resolve_script_name "$NAME")" || {
msg_err "No update script found for '${NAME}' (${BASE}/ct/${NAME}.sh)"
return 1
}
[[ "$resolved" != "$NAME" ]] && msg_info "Script was renamed: ${NAME} -> ${resolved}"
bash -c "$(curl -fsSL "${BASE}/ct/${resolved}.sh")"
}
# Offer to update addons installed on top of the app. Addons drop an
# /usr/local/bin/update_<name> script at install; they are independent of the app
# script, so they are worth offering even when the app itself is gone. Mirrors
# run_addon_updates() in ui/menu.func.
run_addon_updates() {
shopt -s nullglob
local addons=(/usr/local/bin/update_*)
shopt -u nullglob
((${#addons[@]} == 0)) && return 0
echo
msg_info "Detected installed addon update script(s):"
local a
for a in "${addons[@]}"; do echo " - ${a##*/update_}"; done
echo
local name ans
for a in "${addons[@]}"; do
name="${a##*/update_}"
printf 'Do you also want to update addon "%s"? (y/N) [60s]: ' "$name"
ans=""
if read -r -t 60 ans </dev/tty; then :; else echo; fi
case "${ans,,}" in
y | yes) bash "$a" || msg_err "Addon update for $name failed (rc=$?)" ;;
*) msg_info "Skipped addon: $name" ;;
esac
done
}
# ── No slug recorded: behave exactly like the legacy entrypoint. ───────────────
if [[ -z "$SLUG" ]]; then
run_app_update
exit $?
fi
# ── Ask the website what to do. ────────────────────────────────────────────────
info="$(curl -fsSL --connect-timeout 5 --max-time 8 "${WEBSITE}/api/update-info?slug=${SLUG}" 2>/dev/null)"
# Fail open: an unreachable website must never turn `update` into a hard error.
if [[ -z "$info" ]]; then
msg_info "Update service unreachable — continuing with a normal update."
run_app_update
exit $?
fi
state="$(json_str "$info" state)"
message="$(json_str "$info" message)"
case "$state" in
deleted)
# An explicit removal (is_deleted=true): the record still exists and says so.
msg_err "${NAME}: this script has been removed from community-scripts."
if [[ -n "$message" ]]; then
echo
echo -e " $message"
echo
else
echo " There is no update to install. Your container keeps working, but it"
echo " will no longer receive updates from the project."
fi
msg_info "More info: ${WEBSITE}/scripts/${SLUG}"
# The app is gone, but its addons are separate and may still update.
run_addon_updates
exit 0
;;
*)
# active | disabled | unknown | anything else -> update normally.
#
# "unknown" means no catalog record for this slug, NOT that the script was
# removed (removals are flagged is_deleted, which is the case above). Base-OS
# LXCs (alpine, debian, ubuntu, fedora, arch, ...) and any untracked script land
# here, so we must NOT refuse them - fail open to the normal update, exactly as
# the legacy entrypoint did. "disabled" also updates so the in-script status
# guard can surface the disable reason (one source of truth) and stop if needed.
run_app_update
exit $?
;;
esac