feat(deploy): brew-style time-based progress bar with runtime-cache
- new render_progress() draws ████████░░░░░░░░ 42% (1m23s / ~3m18s) bar - runtime_lookup/save persist step durations in tmp/.deploy-runtimes (gitignored — auto-learns from successful runs) - first run = spinner mode (no baseline yet), subsequent runs show real % - still shows live xcodebuild action (Compiling X.swift) as subtitle - format_duration helper: 45s / 1m23s readable output
This commit is contained in:
parent
f48df2a968
commit
32d270ccad
@ -93,9 +93,76 @@ run() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Runtime-Cache für Progress-Bar (lernt Dauer pro Step über Runs hinweg)
|
||||||
|
RUNTIME_CACHE="$SCRIPT_DIR/tmp/.deploy-runtimes"
|
||||||
|
mkdir -p "$(dirname "$RUNTIME_CACHE")"
|
||||||
|
runtime_lookup() {
|
||||||
|
local label="$1"
|
||||||
|
[[ -f "$RUNTIME_CACHE" ]] || return 1
|
||||||
|
grep -aE "^$(printf '%s' "$label" | sed 's/[][\.*^$/]/\\&/g')\|" "$RUNTIME_CACHE" 2>/dev/null \
|
||||||
|
| tail -1 | cut -d'|' -f2
|
||||||
|
}
|
||||||
|
runtime_save() {
|
||||||
|
local label="$1" duration="$2"
|
||||||
|
# Keep only last entry per label
|
||||||
|
if [[ -f "$RUNTIME_CACHE" ]]; then
|
||||||
|
grep -avE "^$(printf '%s' "$label" | sed 's/[][\.*^$/]/\\&/g')\|" "$RUNTIME_CACHE" > "$RUNTIME_CACHE.tmp" || true
|
||||||
|
mv "$RUNTIME_CACHE.tmp" "$RUNTIME_CACHE"
|
||||||
|
fi
|
||||||
|
echo "$label|$duration" >> "$RUNTIME_CACHE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Render brew-style progress bar: ████████░░░░░░░░ 42% (1m23s / ~3m18s)
|
||||||
|
render_progress() {
|
||||||
|
local elapsed="$1" expected="$2" label="$3" subtitle="$4"
|
||||||
|
local width=30 pct filled empty bar elapsed_h expected_h
|
||||||
|
if (( expected > 0 )); then
|
||||||
|
pct=$(( elapsed * 100 / expected ))
|
||||||
|
(( pct > 99 )) && pct=99 # never show 100% while still running
|
||||||
|
filled=$(( elapsed * width / expected ))
|
||||||
|
(( filled > width )) && filled=$width
|
||||||
|
else
|
||||||
|
pct=0; filled=0
|
||||||
|
fi
|
||||||
|
empty=$(( width - filled ))
|
||||||
|
bar=$(printf '%*s' "$filled" '' | tr ' ' '█')$(printf '%*s' "$empty" '' | tr ' ' '░')
|
||||||
|
elapsed_h=$(format_duration "$elapsed")
|
||||||
|
if (( expected > 0 )); then
|
||||||
|
expected_h=$(format_duration "$expected")
|
||||||
|
if [[ -n "$subtitle" ]]; then
|
||||||
|
printf '\r\033[K%s==>%s %s\n %s %s%3d%%%s (%s / ~%s) %s%s↳ %s%s\033[1A' \
|
||||||
|
"$BLUE" "$RESET" "$label" "$bar" "$YELLOW" "$pct" "$RESET" "$elapsed_h" "$expected_h" "$BLUE" "$BOLD" "$subtitle" "$RESET" >&2
|
||||||
|
else
|
||||||
|
printf '\r\033[K%s==>%s %s\n %s %s%3d%%%s (%s / ~%s)\033[1A' \
|
||||||
|
"$BLUE" "$RESET" "$label" "$bar" "$YELLOW" "$pct" "$RESET" "$elapsed_h" "$expected_h" >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# No baseline yet — spinner mode
|
||||||
|
local spin='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
|
||||||
|
local frame="${spin:RUN_QUIET_I%10:1}"
|
||||||
|
if [[ -n "$subtitle" ]]; then
|
||||||
|
printf '\r\033[K%s %s==>%s %s %s(%s)%s ↳ %s' \
|
||||||
|
"$frame" "$BLUE" "$RESET" "$label" "$YELLOW" "$elapsed_h" "$RESET" "$subtitle" >&2
|
||||||
|
else
|
||||||
|
printf '\r\033[K%s %s==>%s %s %s(%s)%s' \
|
||||||
|
"$frame" "$BLUE" "$RESET" "$label" "$YELLOW" "$elapsed_h" "$RESET" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
format_duration() {
|
||||||
|
local s="$1"
|
||||||
|
if (( s < 60 )); then
|
||||||
|
printf '%ds' "$s"
|
||||||
|
else
|
||||||
|
printf '%dm%02ds' $((s / 60)) $((s % 60))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# run_quiet "Label" <log-file> <cmd...>
|
# run_quiet "Label" <log-file> <cmd...>
|
||||||
# Runs cmd silently with a spinner + elapsed time. On error dumps last 40 log
|
# Runs cmd silently with a brew-style progress bar (time-based, learns durations
|
||||||
# lines and exits. With --verbose / non-TTY: streams full output normally.
|
# across runs). On error dumps last 40 log lines and exits. With --verbose / non-TTY:
|
||||||
|
# streams full output normally.
|
||||||
run_quiet() {
|
run_quiet() {
|
||||||
local label="$1"; shift
|
local label="$1"; shift
|
||||||
local logfile="$1"; shift
|
local logfile="$1"; shift
|
||||||
@ -109,40 +176,44 @@ run_quiet() {
|
|||||||
return ${PIPESTATUS[0]}
|
return ${PIPESTATUS[0]}
|
||||||
fi
|
fi
|
||||||
local start=$SECONDS
|
local start=$SECONDS
|
||||||
local spin='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
|
local expected pid elapsed subtitle
|
||||||
local i=0 pid elapsed frame subtitle
|
expected=$(runtime_lookup "$label" || echo 0)
|
||||||
|
expected=${expected:-0}
|
||||||
|
RUN_QUIET_I=0
|
||||||
( "$@" >"$logfile" 2>&1 ) &
|
( "$@" >"$logfile" 2>&1 ) &
|
||||||
pid=$!
|
pid=$!
|
||||||
|
# If we have a baseline, render 2-line bar (label + bar); else 1-line spinner.
|
||||||
|
if (( expected > 0 )); then
|
||||||
|
printf '\n\033[1A' >&2 # Reserve second line, then move up
|
||||||
|
fi
|
||||||
while kill -0 "$pid" 2>/dev/null; do
|
while kill -0 "$pid" 2>/dev/null; do
|
||||||
elapsed=$((SECONDS - start))
|
elapsed=$((SECONDS - start))
|
||||||
frame="${spin:i%10:1}"
|
RUN_QUIET_I=$((RUN_QUIET_I + 1))
|
||||||
i=$((i + 1))
|
|
||||||
# Pull latest meaningful build action from log (last 20 lines, filtered)
|
|
||||||
subtitle=""
|
subtitle=""
|
||||||
if [[ -f "$logfile" ]]; then
|
if [[ -f "$logfile" ]]; then
|
||||||
subtitle=$(tail -20 "$logfile" 2>/dev/null \
|
subtitle=$(tail -20 "$logfile" 2>/dev/null \
|
||||||
| grep -aE '^(Compiling|CompileSwift|CompileC|Linking|Ld|Touch|CodeSign|ProcessProductPackaging|ExtractAppIntentsMetadata|Validate|Archive|GenerateAssetSymbols|CopySwiftLibs|PhaseScriptExecution|> Task|BUILD|\[CP|\[Pods)' \
|
| grep -aE '^(Compiling|CompileSwift|CompileC|Linking|Ld|Touch|CodeSign|ProcessProductPackaging|ExtractAppIntentsMetadata|Validate|Archive|GenerateAssetSymbols|CopySwiftLibs|PhaseScriptExecution|> Task|BUILD|\[CP|\[Pods)' \
|
||||||
| tail -1 \
|
| tail -1 \
|
||||||
| sed -E 's|.*/||; s|\(.*||' \
|
| sed -E 's|.*/||; s|\(.*||' \
|
||||||
| cut -c1-60)
|
| cut -c1-50)
|
||||||
fi
|
|
||||||
if [[ -n "$subtitle" ]]; then
|
|
||||||
printf '\r\033[K%s %s==>%s %s %s(%ds)%s ↳ %s' \
|
|
||||||
"$frame" "$BLUE" "$RESET" "$label" "$YELLOW" "$elapsed" "$RESET" "$subtitle" >&2
|
|
||||||
else
|
|
||||||
printf '\r\033[K%s %s==>%s %s %s(%ds)%s' \
|
|
||||||
"$frame" "$BLUE" "$RESET" "$label" "$YELLOW" "$elapsed" "$RESET" >&2
|
|
||||||
fi
|
fi
|
||||||
|
render_progress "$elapsed" "$expected" "$label" "$subtitle"
|
||||||
sleep 0.2
|
sleep 0.2
|
||||||
done
|
done
|
||||||
wait "$pid"
|
wait "$pid"
|
||||||
local rc=$?
|
local rc=$?
|
||||||
elapsed=$((SECONDS - start))
|
elapsed=$((SECONDS - start))
|
||||||
printf '\r\033[K' >&2 # Clear spinner line
|
# Clear both lines if we used 2-line mode, else 1 line
|
||||||
if [[ $rc -eq 0 ]]; then
|
if (( expected > 0 )); then
|
||||||
ok "$label ${YELLOW}(${elapsed}s)${RESET}"
|
printf '\r\033[K\n\033[K\033[1A' >&2
|
||||||
else
|
else
|
||||||
error "$label fehlgeschlagen nach ${elapsed}s (exit $rc)"
|
printf '\r\033[K' >&2
|
||||||
|
fi
|
||||||
|
if [[ $rc -eq 0 ]]; then
|
||||||
|
ok "$label ${YELLOW}($(format_duration "$elapsed"))${RESET}"
|
||||||
|
runtime_save "$label" "$elapsed"
|
||||||
|
else
|
||||||
|
error "$label fehlgeschlagen nach $(format_duration "$elapsed") (exit $rc)"
|
||||||
echo "" >&2
|
echo "" >&2
|
||||||
echo "${BOLD}── Letzte Log-Zeilen (${logfile}) ──${RESET}" >&2
|
echo "${BOLD}── Letzte Log-Zeilen (${logfile}) ──${RESET}" >&2
|
||||||
tail -40 "$logfile" >&2
|
tail -40 "$logfile" >&2
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user