timetracker/scripts/apk_retention.sh
2026-08-03 21:51:48 +02:00

175 lines
6.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# apk_retention.sh — Build-Historie-Bereinigung für Timetrack APKs
#
# Retention-Strategie:
# Aktuelle Minor-Version → letzte 15 APKs nach Build-Nummer behalten
# Ältere Minor-Versionen → je eine APK behalten (höchste Build-Nummer = Release-Marker)
#
# Verwendung:
# bash scripts/apk_retention.sh [--dir build/apk] [--keep 15] [--dry-run]
#
# Dateiname-Format: timetrack-{VERSION}+{BUILD}.apk
# Beispiele: timetrack-0.1.0+1.apk timetrack-0.2.0+3.apk
set -euo pipefail
# ── Defaults ──────────────────────────────────────────────────────────────────
APK_DIR="build/apk"
KEEP=15
DRY_RUN=false
# ── Argument-Parsing ──────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
--dir) APK_DIR="$2"; shift 2 ;;
--keep) KEEP="$2"; shift 2 ;;
--dry-run) DRY_RUN=true; shift ;;
*) echo "Unbekanntes Argument: $1" >&2; exit 1 ;;
esac
done
# ── Verzeichnis prüfen ────────────────────────────────────────────────────────
if [[ ! -d "$APK_DIR" ]]; then
echo "apk_retention: Verzeichnis '$APK_DIR' nicht gefunden — übersprungen."
exit 0
fi
# ── Hilfsfunktionen ───────────────────────────────────────────────────────────
# Extrahiert "MAJOR.MINOR" aus "timetrack-0.1.0+5.apk" → "0.1"
minor_version() {
local name="$1"
# Dateiname: timetrack-0.1.0+5.apk → Extrahiere "0.1.0" → nehme "0.1"
local ver
ver=$(echo "$name" | grep -oP '(?<=timetrack-)\d+\.\d+\.\d+(?=\+)') || true
if [[ -z "$ver" ]]; then echo ""; return; fi
# Nur Major.Minor
echo "$ver" | cut -d. -f1-2
}
# Extrahiert Build-Nummer aus "timetrack-0.1.0+5.apk" → 5
build_number() {
local name="$1"
echo "$name" | grep -oP '(?<=\+)\d+(?=\.apk)' || echo "0"
}
# Löscht eine APK + zugehörige .sha256-Datei
delete_apk() {
local path="$1"
local sha="${path}.sha256"
if [[ "$DRY_RUN" == "true" ]]; then
echo " [DRY-RUN] würde löschen: $(basename "$path")"
[[ -f "$sha" ]] && echo " [DRY-RUN] würde löschen: $(basename "$sha")"
else
rm -f "$path"
[[ -f "$sha" ]] && rm -f "$sha"
echo " gelöscht: $(basename "$path")"
fi
}
# ── APKs einlesen (ohne Symlinks) ─────────────────────────────────────────────
declare -a all_apks=()
while IFS= read -r -d '' f; do
name=$(basename "$f")
# Nur versionierte APKs — kein "timetrack.apk"-Symlink
if [[ "$name" == timetrack-*.apk ]]; then
all_apks+=("$name")
fi
done < <(find "$APK_DIR" -maxdepth 1 -name "timetrack-*.apk" -not -type l -print0 2>/dev/null)
total=${#all_apks[@]}
if [[ $total -eq 0 ]]; then
echo "apk_retention: Keine APKs gefunden in '$APK_DIR' — nichts zu tun."
exit 0
fi
echo "apk_retention: $total APKs gefunden in '$APK_DIR'"
echo "apk_retention: Retention: letzte $KEEP in aktueller Minor-Version, je 1 pro älterer"
echo ""
# ── Minor-Versionen ermitteln ─────────────────────────────────────────────────
declare -A minor_map # minor → kommaseparierte Liste von Dateinamen
for name in "${all_apks[@]}"; do
mv=$(minor_version "$name")
if [[ -z "$mv" ]]; then continue; fi
if [[ -v minor_map["$mv"] ]]; then
minor_map["$mv"]="${minor_map[$mv]} $name"
else
minor_map["$mv"]="$name"
fi
done
# Sortierte Liste der Minor-Versionen (höchste = aktuell)
declare -a sorted_minors=()
while IFS= read -r mv; do
sorted_minors+=("$mv")
done < <(printf '%s\n' "${!minor_map[@]}" | sort -t. -k1,1n -k2,2n)
current_minor="${sorted_minors[-1]}"
echo " Aktuelle Minor-Version : $current_minor"
echo " Ältere Minor-Versionen : ${sorted_minors[*]::${#sorted_minors[@]}-1}"
echo ""
# ── Bereinigung: Ältere Minor-Versionen ──────────────────────────────────────
for mv in "${sorted_minors[@]}"; do
[[ "$mv" == "$current_minor" ]] && continue
# Alle APKs dieser Minor-Version nach Build-Nummer sortieren
declare -a mv_apks=()
for name in ${minor_map[$mv]}; do
mv_apks+=("$name")
done
# Sortieren nach Build-Nummer (numerisch)
declare -a sorted_mv=()
while IFS= read -r line; do
sorted_mv+=("$line")
done < <(printf '%s\n' "${mv_apks[@]}" | sort -t+ -k2,2n)
keeper="${sorted_mv[-1]}"
echo " Minor $mv: behalte $keeper (Release-Marker)"
for name in "${sorted_mv[@]}"; do
[[ "$name" == "$keeper" ]] && continue
delete_apk "$APK_DIR/$name"
done
unset mv_apks sorted_mv
done
# ── Bereinigung: Aktuelle Minor-Version ──────────────────────────────────────
declare -a cur_apks=()
for name in ${minor_map[$current_minor]}; do
cur_apks+=("$name")
done
# Sortieren nach Build-Nummer (numerisch)
declare -a sorted_cur=()
while IFS= read -r line; do
sorted_cur+=("$line")
done < <(printf '%s\n' "${cur_apks[@]}" | sort -t+ -k2,2n)
cur_count=${#sorted_cur[@]}
echo " Aktuelle Minor $current_minor: $cur_count APKs, behalte letzte $KEEP"
if [[ $cur_count -gt $KEEP ]]; then
to_delete=$(( cur_count - KEEP ))
for (( i=0; i<to_delete; i++ )); do
delete_apk "$APK_DIR/${sorted_cur[$i]}"
done
fi
# ── Zusammenfassung ───────────────────────────────────────────────────────────
echo ""
echo "apk_retention: Verbleibende APKs:"
find "$APK_DIR" -maxdepth 1 -name "timetrack-*.apk" -not -type l \
| sort | while read -r f; do
echo " $(basename "$f")"
done
echo ""
if [[ "$DRY_RUN" == "true" ]]; then
echo "apk_retention: [DRY-RUN] Keine Dateien wurden gelöscht."
else
echo "apk_retention: Bereinigung abgeschlossen."
fi