|
7 | 7 | # ============================================================================== |
8 | 8 | # Opt-in debugging, driven by a comma-separated dev_mode variable: |
9 | 9 | # |
10 | | -# motd set up MOTD/SSH in a kept container so it can be inspected |
11 | | -# keep never delete the container when a build fails |
| 10 | +# net log every engine fetch: HTTP status, duration, URL |
| 11 | +# timing show how long each step took, and the slowest ones at the end |
12 | 12 | # trace bash set -x tracing |
13 | | -# pause pause after each msg_info step |
| 13 | +# pause wait for a keypress after each step |
| 14 | +# keep never delete the container when a build fails |
14 | 15 | # breakpoint open a shell on error instead of cleaning up |
| 16 | +# motd set up MOTD/SSH early so a kept container can be inspected |
15 | 17 | # logs persist all logs to /var/log/community-scripts/ |
16 | | -# dryrun print commands instead of running them |
17 | 18 | # |
18 | | -# Parsing and the interactive picker live here; the flags themselves are read at |
19 | | -# the point of use, in the backends and the messaging helpers. |
| 19 | +# Any of them also prints the resolved context up front: where the engine came |
| 20 | +# from, which roots and URLs are in play, and what the host looks like. |
| 21 | +# |
| 22 | +# Parsing, the context block and the timing bookkeeping live here. The flags |
| 23 | +# themselves are read at the point of use, in the backends and the messaging |
| 24 | +# helpers -- except net, which build.func reads straight from $dev_mode, |
| 25 | +# because the fetches worth seeing happen before this file is even loaded. |
20 | 26 | # ============================================================================== |
21 | 27 |
|
22 | 28 | [[ -n "${_DEV_MODE_FUNC_LOADED:-}" ]] && return |
23 | 29 | _DEV_MODE_FUNC_LOADED=1 |
24 | 30 |
|
| 31 | +_DEV_MODE_FLAGS=(net timing trace pause keep breakpoint motd logs) |
| 32 | + |
25 | 33 | # ------------------------------------------------------------------------------ |
26 | 34 | # parse_dev_mode() |
27 | 35 | # |
28 | | -# - Parses comma-separated dev_mode variable (e.g., "motd,keep,trace") |
29 | | -# - Sets global flags for each mode: |
30 | | -# * DEV_MODE_MOTD: Setup SSH/MOTD before installation |
31 | | -# * DEV_MODE_KEEP: Never delete container on failure |
32 | | -# * DEV_MODE_TRACE: Enable bash set -x tracing |
33 | | -# * DEV_MODE_PAUSE: Pause after each msg_info step |
34 | | -# * DEV_MODE_BREAKPOINT: Open shell on error instead of cleanup |
35 | | -# * DEV_MODE_LOGS: Persist all logs to /var/log/community-scripts/ |
36 | | -# * DEV_MODE_DRYRUN: Show commands without executing |
37 | | -# - Call this early in script execution |
| 36 | +# - Parses the comma-separated dev_mode variable (e.g. "net,timing") |
| 37 | +# - Exports one DEV_MODE_<FLAG> per known flag, true or false |
| 38 | +# - Names the known flags when one is unrecognised: a typo would otherwise be |
| 39 | +# indistinguishable from a mode that simply does nothing |
38 | 40 | # ------------------------------------------------------------------------------ |
39 | 41 | parse_dev_mode() { |
40 | | - local mode |
41 | | - # Initialize all flags to false |
42 | | - export DEV_MODE_MOTD=false |
43 | | - export DEV_MODE_KEEP=false |
44 | | - export DEV_MODE_TRACE=false |
45 | | - export DEV_MODE_PAUSE=false |
46 | | - export DEV_MODE_BREAKPOINT=false |
47 | | - export DEV_MODE_LOGS=false |
48 | | - export DEV_MODE_DRYRUN=false |
49 | | - |
50 | | - # Parse comma-separated modes |
51 | | - if [[ -n "${dev_mode:-}" ]]; then |
52 | | - IFS=',' read -ra MODES <<<"$dev_mode" |
53 | | - for mode in "${MODES[@]}"; do |
54 | | - mode="$(echo "$mode" | xargs)" # Trim whitespace |
55 | | - case "$mode" in |
56 | | - motd) export DEV_MODE_MOTD=true ;; |
57 | | - keep) export DEV_MODE_KEEP=true ;; |
58 | | - trace) export DEV_MODE_TRACE=true ;; |
59 | | - pause) export DEV_MODE_PAUSE=true ;; |
60 | | - breakpoint) export DEV_MODE_BREAKPOINT=true ;; |
61 | | - logs) export DEV_MODE_LOGS=true ;; |
62 | | - dryrun) export DEV_MODE_DRYRUN=true ;; |
63 | | - *) |
64 | | - if declare -f msg_warn >/dev/null 2>&1; then |
65 | | - msg_warn "Unknown dev_mode: '$mode' (ignored)" |
66 | | - else |
67 | | - echo "[WARN] Unknown dev_mode: '$mode' (ignored)" >&2 |
68 | | - fi |
69 | | - ;; |
70 | | - esac |
71 | | - done |
72 | | - |
73 | | - # Show active dev modes |
74 | | - local active_modes=() |
75 | | - [[ $DEV_MODE_MOTD == true ]] && active_modes+=("motd") |
76 | | - [[ $DEV_MODE_KEEP == true ]] && active_modes+=("keep") |
77 | | - [[ $DEV_MODE_TRACE == true ]] && active_modes+=("trace") |
78 | | - [[ $DEV_MODE_PAUSE == true ]] && active_modes+=("pause") |
79 | | - [[ $DEV_MODE_BREAKPOINT == true ]] && active_modes+=("breakpoint") |
80 | | - [[ $DEV_MODE_LOGS == true ]] && active_modes+=("logs") |
81 | | - [[ $DEV_MODE_DRYRUN == true ]] && active_modes+=("dryrun") |
82 | | - |
83 | | - if [[ ${#active_modes[@]} -gt 0 ]]; then |
84 | | - if declare -f msg_custom >/dev/null 2>&1; then |
85 | | - msg_custom "🔧" "${YWB}" "Dev modes active: ${active_modes[*]}" |
| 42 | + local flag mode |
| 43 | + for flag in "${_DEV_MODE_FLAGS[@]}"; do |
| 44 | + export "DEV_MODE_${flag^^}=false" |
| 45 | + done |
| 46 | + |
| 47 | + [[ -z "${dev_mode:-}" ]] && return 0 |
| 48 | + |
| 49 | + local active=() |
| 50 | + IFS=',' read -ra _dev_modes <<<"$dev_mode" |
| 51 | + for mode in "${_dev_modes[@]}"; do |
| 52 | + mode="${mode//[[:space:]]/}" |
| 53 | + [[ -z "$mode" ]] && continue |
| 54 | + if [[ " ${_DEV_MODE_FLAGS[*]} " == *" $mode "* ]]; then |
| 55 | + export "DEV_MODE_${mode^^}=true" |
| 56 | + active+=("$mode") |
| 57 | + else |
| 58 | + if declare -f msg_warn >/dev/null 2>&1; then |
| 59 | + msg_warn "Unknown dev_mode '$mode' — known: ${_DEV_MODE_FLAGS[*]}" |
86 | 60 | else |
87 | | - echo "[DEV] Active modes: ${active_modes[*]}" >&2 |
| 61 | + echo "[WARN] Unknown dev_mode '$mode' — known: ${_DEV_MODE_FLAGS[*]}" >&2 |
88 | 62 | fi |
89 | 63 | fi |
| 64 | + done |
| 65 | + |
| 66 | + ((${#active[@]})) || return 0 |
| 67 | + if declare -f msg_custom >/dev/null 2>&1; then |
| 68 | + msg_custom "🔧" "${YWB:-}" "Dev mode: ${active[*]}" |
| 69 | + else |
| 70 | + echo "[DEV] ${active[*]}" >&2 |
90 | 71 | fi |
| 72 | + dev_mode_context |
91 | 73 | } |
92 | 74 |
|
93 | | -dev_mode_menu() { |
94 | | - local motd=OFF keep=OFF trace=OFF pause=OFF breakpoint=OFF logs=OFF dryrun=OFF verbose=OFF |
95 | | - |
96 | | - IFS=',' read -r -a _modes <<<"$dev_mode" |
97 | | - for m in "${_modes[@]}"; do |
98 | | - case "$m" in |
99 | | - motd) motd=ON ;; |
100 | | - keep) keep=ON ;; |
101 | | - trace) trace=ON ;; |
102 | | - pause) pause=ON ;; |
103 | | - breakpoint) breakpoint=ON ;; |
104 | | - logs) logs=ON ;; |
105 | | - dryrun) dryrun=ON ;; |
| 75 | +# ------------------------------------------------------------------------------ |
| 76 | +# dev_mode_context() |
| 77 | +# |
| 78 | +# - Prints where the engine and the scripts were resolved from, and the host |
| 79 | +# facts every other flag's output has to be read against |
| 80 | +# ------------------------------------------------------------------------------ |
| 81 | +dev_mode_context() { |
| 82 | + local engine="network (${COMMUNITY_SCRIPTS_CORE_URL:-unset})" |
| 83 | + if [[ -n "${COMMUNITY_SCRIPTS_CORE_DIR:-}" ]]; then |
| 84 | + case "$COMMUNITY_SCRIPTS_CORE_DIR" in |
| 85 | + */cs-engine.*) engine="prefetched → ${COMMUNITY_SCRIPTS_CORE_DIR}" ;; |
| 86 | + *) engine="local checkout → ${COMMUNITY_SCRIPTS_CORE_DIR}" ;; |
106 | 87 | esac |
| 88 | + fi |
| 89 | + |
| 90 | + { |
| 91 | + echo " ── dev context ─────────────────────────────────────────────" |
| 92 | + printf ' %-14s %s\n' "engine" "$engine" |
| 93 | + printf ' %-14s %s\n' "engine url" "${COMMUNITY_SCRIPTS_CORE_URL:-unset}" |
| 94 | + printf ' %-14s %s\n' "scripts root" "${COMMUNITY_SCRIPTS_ROOT:-none (remote)}" |
| 95 | + printf ' %-14s %s\n' "scripts url" "${COMMUNITY_SCRIPTS_URL:-unset}" |
| 96 | + printf ' %-14s %s\n' "app" "${APP:-?} (${NSAPP:-?}, type ${APP_TYPE:-?})" |
| 97 | + printf ' %-14s %s\n' "platform" "$(declare -f detect_lxc_platform >/dev/null 2>&1 && detect_lxc_platform || echo unknown)" |
| 98 | + printf ' %-14s %s\n' "arch" "$(dpkg --print-architecture 2>/dev/null || uname -m)" |
| 99 | + if command -v pveversion >/dev/null 2>&1; then |
| 100 | + printf ' %-14s %s\n' "pve" "$(declare -f _pve_version >/dev/null 2>&1 && _pve_version || pveversion)" |
| 101 | + fi |
| 102 | + printf ' %-14s %s\n' "bash" "${BASH_VERSION%%(*}" |
| 103 | + printf ' %-14s %s\n' "curl" "$(curl --version 2>/dev/null | head -1 | awk '{print $2}' || echo absent)" |
| 104 | + printf ' %-14s %s\n' "session" "${SESSION_ID:-none}" |
| 105 | + echo " ────────────────────────────────────────────────────────────" |
| 106 | + } >&2 |
| 107 | +} |
| 108 | + |
| 109 | +# ------------------------------------------------------------------------------ |
| 110 | +# _dev_step_start() / _dev_step_end() |
| 111 | +# |
| 112 | +# - Called from msg_info and msg_ok; no-ops unless dev_mode=timing |
| 113 | +# - _dev_step_end returns a suffix for the msg_ok line, so the elapsed time |
| 114 | +# lands on the step it belongs to instead of in a separate stream |
| 115 | +# - State goes in a file: msg_ok is often reached inside $( ), where a variable |
| 116 | +# assignment would be discarded on return |
| 117 | +# ------------------------------------------------------------------------------ |
| 118 | +_dev_timing_file() { |
| 119 | + local base |
| 120 | + for base in /dev/shm "${TMPDIR:-/tmp}" /tmp; do |
| 121 | + [[ -d "$base" && -w "$base" ]] && { |
| 122 | + printf '%s/cs-timing.%s' "$base" "$$" |
| 123 | + return 0 |
| 124 | + } |
107 | 125 | done |
| 126 | + return 1 |
| 127 | +} |
| 128 | + |
| 129 | +_dev_step_key() { |
| 130 | + printf '%s' "$1" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9]/_/g' |
| 131 | +} |
| 132 | + |
| 133 | +_dev_step_start() { |
| 134 | + [[ "${DEV_MODE_TIMING:-false}" == "true" ]] || return 0 |
| 135 | + local f |
| 136 | + f="$(_dev_timing_file)" || return 0 |
| 137 | + printf 'S %s %s\n' "${EPOCHREALTIME/./}" "$(_dev_step_key "$1")" >>"$f" |
| 138 | +} |
| 139 | + |
| 140 | +_dev_step_end() { |
| 141 | + [[ "${DEV_MODE_TIMING:-false}" == "true" ]] || return 0 |
| 142 | + local f key start ms |
| 143 | + f="$(_dev_timing_file)" || return 0 |
| 144 | + key="$(_dev_step_key "$1")" |
| 145 | + start="$(awk -v k="$key" '$1=="S" && $3==k {t=$2} END {print t}' "$f" 2>/dev/null)" |
| 146 | + [[ -z "$start" ]] && return 0 |
| 147 | + ms=$(((${EPOCHREALTIME/./} - start) / 1000)) |
| 148 | + printf 'E %s %s\n' "$ms" "$key" >>"$f" |
| 149 | + printf ' %s(%s ms)%s' "${DGN:-}" "$ms" "${CL:-}" |
| 150 | +} |
| 151 | + |
| 152 | +# ------------------------------------------------------------------------------ |
| 153 | +# dev_mode_timing_summary() |
| 154 | +# |
| 155 | +# - Prints the slowest steps of the run; called from the exit path |
| 156 | +# ------------------------------------------------------------------------------ |
| 157 | +dev_mode_timing_summary() { |
| 158 | + [[ "${DEV_MODE_TIMING:-false}" == "true" ]] || return 0 |
| 159 | + local f |
| 160 | + f="$(_dev_timing_file)" || return 0 |
| 161 | + [[ -s "$f" ]] || return 0 |
| 162 | + { |
| 163 | + echo " ── slowest steps ───────────────────────────────────────────" |
| 164 | + awk '$1=="E" {printf " %8s ms %s\n", $2, $3}' "$f" | sort -rn | head -10 |
| 165 | + echo " ────────────────────────────────────────────────────────────" |
| 166 | + } >&2 |
| 167 | + rm -f "$f" |
| 168 | +} |
108 | 169 |
|
109 | | - [[ "$var_verbose" == "yes" ]] && verbose=ON |
| 170 | +# ------------------------------------------------------------------------------ |
| 171 | +# dev_mode_menu() |
| 172 | +# |
| 173 | +# - Whiptail picker for the same flags, plus verbose |
| 174 | +# ------------------------------------------------------------------------------ |
| 175 | +dev_mode_menu() { |
| 176 | + local flag state items=() selection |
| 177 | + local -A labels=( |
| 178 | + [net]="Log every engine fetch" |
| 179 | + [timing]="Time each step" |
| 180 | + [trace]="Bash command tracing" |
| 181 | + [pause]="Step-by-step execution" |
| 182 | + [keep]="Preserve container on failure" |
| 183 | + [breakpoint]="Interactive shell on error" |
| 184 | + [motd]="Early SSH/MOTD setup" |
| 185 | + [logs]="Persistent logging" |
| 186 | + ) |
| 187 | + for flag in "${_DEV_MODE_FLAGS[@]}"; do |
| 188 | + state=OFF |
| 189 | + [[ ",${dev_mode:-}," == *",${flag},"* ]] && state=ON |
| 190 | + items+=("$flag" "${labels[$flag]}" "$state") |
| 191 | + done |
| 192 | + items+=("verbose" "Verbose logging" "$([[ "${var_verbose:-no}" == "yes" ]] && echo ON || echo OFF)") |
110 | 193 |
|
111 | | - local selection |
112 | 194 | selection=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ |
113 | 195 | --title "DEV MODE" \ |
114 | | - --checklist "Choose one or more Options" 16 51 10 \ |
115 | | - "motd" "Early SSH/MOTD Setup" "$motd" \ |
116 | | - "keep" "Preserve Container on Failure" "$keep" \ |
117 | | - "trace" "Bash Command Tracing" "$trace" \ |
118 | | - "pause" "Step-by-Step Execution" "$pause" \ |
119 | | - "breakpoint" "Interactive Shell on Error" "$breakpoint" \ |
120 | | - "logs" "Persistent Logging" "$logs" \ |
121 | | - "dryrun" "Simulation Mode" "$dryrun" \ |
122 | | - "verbose" "Verbose logging" "$verbose" \ |
| 196 | + --checklist "Choose one or more options" 17 56 10 \ |
| 197 | + "${items[@]}" \ |
123 | 198 | 3>&1 1>&2 2>&3) || exit_script |
124 | 199 |
|
125 | 200 | dev_mode="" |
126 | 201 | var_verbose="no" |
127 | | - local modes_out=() |
128 | | - |
| 202 | + local picked=() tag |
129 | 203 | for tag in $selection; do |
130 | 204 | tag="${tag%\"}" |
131 | 205 | tag="${tag#\"}" |
132 | 206 | if [[ "$tag" == "verbose" ]]; then |
133 | 207 | var_verbose="yes" |
134 | 208 | else |
135 | | - modes_out+=("$tag") |
| 209 | + picked+=("$tag") |
136 | 210 | fi |
137 | 211 | done |
138 | | - |
139 | | - dev_mode=$( |
| 212 | + dev_mode="$( |
140 | 213 | IFS=, |
141 | | - echo "${modes_out[*]}" |
142 | | - ) |
143 | | - unset DEV_MODE_MOTD DEV_MODE_KEEP DEV_MODE_TRACE DEV_MODE_PAUSE DEV_MODE_BREAKPOINT DEV_MODE_LOGS DEV_MODE_DRYRUN |
| 214 | + echo "${picked[*]}" |
| 215 | + )" |
| 216 | + export dev_mode |
| 217 | + |
144 | 218 | parse_dev_mode |
145 | 219 | if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then |
146 | 220 | mkdir -p /var/log/community-scripts |
|
0 commit comments