#!/data/data/com.termux/files/usr/bin/bash # HPOS — Startpunkt auf dem Telefon. # # Dieses Skript ist absichtlich das EINZIGE, was per adb aufs Telefon kommt. # Alles Weitere holt es sich per git. Der Grund: danach ist das Repo auf dem # Telefon eine Arbeitskopie, keine Kopie. Aktualisieren heißt dann schlicht # # cd ~/hpos && git pull # # statt jedes Mal wieder den PC anzuwerfen und Dateien zu schieben. # # Aufruf in Termux: # bash /sdcard/hpos-init.sh # bash /sdcard/hpos-init.sh https://beispiel/anderes-repo.git set -uo pipefail REPO="${1:-${HPOS_REPO:-@@REPO_URL@@}}" ZIEL="$HOME/hpos" if [ -t 1 ]; then C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m' C_HEAD=$'\033[1;36m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m' else C_OK=""; C_WARN=""; C_ERR=""; C_HEAD=""; C_DIM=""; C_OFF="" fi kopf() { printf '\n%s══ %s ══%s\n' "$C_HEAD" "$*" "$C_OFF"; } ok() { printf '%s ✓%s %s\n' "$C_OK" "$C_OFF" "$*"; } warn() { printf '%s !%s %s\n' "$C_WARN" "$C_OFF" "$*"; } fehl() { printf '%s ✗%s %s\n' "$C_ERR" "$C_OFF" "$*"; } hinweis() { printf '%s %s%s\n' "$C_DIM" "$*" "$C_OFF"; } case "$REPO" in @@*) fehl "Keine Repo-Adresse hinterlegt." hinweis "Aufruf mit Adresse: bash /sdcard/hpos-init.sh " exit 1 ;; esac # Benutzernamen aus der Adresse entfernen. Lesen geht anonym; steht ein Name in # der URL, fragt git trotzdem nach einem Passwort — auf einer Bildschirmtastatur # eine unnötige Quälerei. Zum Schreiben braucht man ihn ohnehin nur auf dem PC. REPO_ROH="$REPO" REPO=$(printf '%s' "$REPO" | sed -E 's#^(https?://)[^/@]+@#\1#') [ "$REPO" != "$REPO_ROH" ] && ANONYM=1 || ANONYM=0 kopf "HPOS — Ersteinrichtung auf dem Telefon" hinweis "Repo: $REPO" [ "$ANONYM" = 1 ] && hinweis "(Benutzername entfernt — gelesen wird anonym)" # ── Vollupgrade ────────────────────────────────────────────────────────────── # Muss VOR jedem anderen Paket laufen. Termux verträgt keine Teilupgrades: die # Pakete werden gegeneinander gebaut, und ein einzeln nachinstalliertes trifft # sonst auf veraltete Abhängigkeiten. Genau daran ist proot-distro gescheitert — # es trug die Shebang-Zeile python3.13, während eine andere Fassung installiert war. # # Überspringbar mit HPOS_OHNE_UPGRADE=1, etwa bei knappem Datenvolumen. kopf "Termux auf einen Stand bringen" if [ "${HPOS_OHNE_UPGRADE:-0}" = 1 ]; then warn "Übersprungen (HPOS_OHNE_UPGRADE=1)" hinweis "Bei Problemen mit proot-distro ist das die erste Stelle zum Nachsehen." else hinweis "Das dauert und lädt einiges. Es erspart aber genau die Fehler," hinweis "die sonst erst Stunden später als kaputtes proot-distro auffallen." export DEBIAN_FRONTEND=noninteractive # Beim Wechsel der Python-Version wirft dpkg dutzende Zeilen # "unable to delete old directory ... Directory not empty". Das sind # Warnungen, keine Fehler: in site-packages liegen noch .pyc-Dateien oder # selbst installierte Module. Sie herauszufiltern ist kein Verstecken — # sie verdecken sonst die eine Zeile, auf die es ankommt. rauschfilter() { grep -viE 'unable to delete old directory|^(Unpacking|Preparing|Selecting) '; } # --force-confold: vorhandene Konfiguration behalten, statt mitten im Lauf # eine Rückfrage zu stellen, die niemand beantwortet. AUSGABE=$(mktemp) pkg update -y >"$AUSGABE" 2>&1; rauschfilter <"$AUSGABE" upgrade_lauf() { pkg upgrade -y -o Dpkg::Options::=--force-confold "$@" >"$AUSGABE" 2>&1 local rc=$? rauschfilter <"$AUSGABE" return $rc } if upgrade_lauf; then ok "Termux ist aktuell" else # Dateikonflikt gezielt behandeln. Er entsteht, wenn ein Paket aufgeteilt # wurde und beide Teile kurzzeitig dieselbe Datei mitbringen — bei Termux # etwa libmagic, herausgelöst aus file. dpkg verweigert dann grundsätzlich. # # --force-overwrite wird bewusst NUR hier gesetzt, nachdem der Konflikt in # der Ausgabe nachgewiesen ist. Pauschal mitzugeben hieße, jede künftige # Dateikollision stillschweigend zu übergehen. if grep -q 'which is also in package' "$AUSGABE"; then warn "Dateikonflikt zwischen zwei Paketen — typisch bei einer Paketaufteilung." grep 'trying to overwrite' "$AUSGABE" | head -3 | sed 's/^/ /' hinweis "Wiederhole gezielt mit --force-overwrite." if upgrade_lauf -o Dpkg::Options::=--force-overwrite; then ok "Konflikt aufgelöst, Upgrade durchgelaufen" rm -f "$AUSGABE" AUSGABE="" fi fi if [ -n "$AUSGABE" ] && ! upgrade_lauf; then warn "Upgrade weiterhin unvollständig — versuche dpkg zu entwirren." # Nach einem Abbruch steckt dpkg halb fertig fest und blockiert alles. dpkg --configure -a >>"$AUSGABE" 2>&1 apt-get -f install -y >>"$AUSGABE" 2>&1 if upgrade_lauf -o Dpkg::Options::=--force-overwrite; then ok "Nach der Reparatur durchgelaufen" else warn "Bleibt unvollständig." hinweis "Speicherplatz prüfen: df -h \$PREFIX (nicht / — das ist die Systempartition)" hinweis "Fehler ohne Rauschen: pkg upgrade -y 2>&1 | grep -viE 'unable to delete' | tail -20" hinweis "Weiter geht es trotzdem — aber hier ansetzen, falls später etwas klemmt." fi fi fi rm -f "$AUSGABE" 2>/dev/null || true fi # ── git ────────────────────────────────────────────────────────────────────── kopf "git" if command -v git >/dev/null 2>&1; then ok "bereits vorhanden ($(git --version))" else if pkg install -y git; then ok "installiert" else fehl "Installation von git fehlgeschlagen"; exit 1; fi fi # Das Telefon liest nur. Es soll nie versehentlich versuchen zurückzuschreiben — # ein Push von hier wäre weder gewollt noch möglich. git config --global credential.helper "" 2>/dev/null || true # ── Rückfallebene: Archiv statt Klon ───────────────────────────────────────── # Gitea, Forgejo und GitHub bieten den Baum zusätzlich als Tarball an. Der Weg # dorthin geht nicht über upload-pack und funktioniert deshalb auch dann noch, # wenn der Klon am Server scheitert. Das Ergebnis ist aber nur ein Abzug: kein # .git, kein 'git pull'. Es ist eine Notlösung, keine Alternative. archiv_holen() { local basis zweig="${HPOS_ZWEIG:-main}" url tmp basis="${REPO%.git}" command -v curl >/dev/null 2>&1 || pkg install -y curl >/dev/null 2>&1 command -v tar >/dev/null 2>&1 || pkg install -y tar >/dev/null 2>&1 tmp=$(mktemp -d) for url in "$basis/archive/$zweig.tar.gz" \ "$basis/archive/refs/heads/$zweig.tar.gz"; do hinweis "Versuche Archiv: $url" if curl -sfL -o "$tmp/a.tar.gz" "$url" && tar tzf "$tmp/a.tar.gz" >/dev/null 2>&1; then mkdir -p "$ZIEL" # --strip-components=1: die Archive enthalten alles unter einem # Wurzelverzeichnis, das wir nicht mit übernehmen wollen. if tar xzf "$tmp/a.tar.gz" -C "$ZIEL" --strip-components=1; then rm -rf "$tmp" ok "Archiv entpackt nach $ZIEL" return 0 fi fi done rm -rf "$tmp" return 1 } # ── Holen oder aktualisieren ───────────────────────────────────────────────── kopf "Repo" if [ -d "$ZIEL/.git" ]; then ok "Arbeitskopie vorhanden — aktualisiere" if git -C "$ZIEL" pull --ff-only; then ok "aktuell" else warn "git pull fehlgeschlagen." hinweis "Lokale Änderungen? Dann: cd ~/hpos && git stash && git pull" exit 1 fi else # --depth reicht nicht: für 'git pull' später brauchen wir eine vollwertige # Arbeitskopie, kein abgeschnittenes Archiv. if git clone "$REPO" "$ZIEL"; then ok "geklont nach $ZIEL" else fehl "git clone fehlgeschlagen." hinweis "Adresse war: $REPO" echo # Ein scheiternder Klon bei erreichbarem Repo hat fast immer eine # serverseitige Ursache: upload-pack stirbt beim Schnüren des Pakets, # während die Objekte selbst heil sind. Erkennbar daran, dass sich ein # Archiv weiterhin erzeugen lässt — und genau das ist die Rückfallebene. if archiv_holen; then warn "Über ein Archiv geholt, NICHT als Arbeitskopie." hinweis "'git pull' funktioniert damit nicht. Sobald der Server wieder" hinweis "Pakete ausliefert: rm -rf ~/hpos && bash /sdcard/hpos-init.sh" else fehl "Auch die Rückfallebene über das Archiv hat nicht geklappt." hinweis "Ist das Repo öffentlich lesbar? Ohne das braucht es Zugangsdaten," hinweis "und die gehören nicht aufs Telefon." exit 1 fi fi fi chmod +x "$ZIEL"/spike/*.sh "$ZIEL"/spike/inner/*.sh 2>/dev/null || true # ── Weiter ─────────────────────────────────────────────────────────────────── kopf "Fertig" cat <