diff --git a/AGENTS.md b/AGENTS.md index 9b28098..2197645 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,7 +37,8 @@ bash -euo pipefail ollama-update --help ## Structure -- `ollama-update` — main script (executable, bash, ~240 lines) +- `ollama-update` — main script (executable, bash, ~270 lines) +- `test.sh` — self-test for version compare and update decision (no network, no install) - `README.md` — usage docs ## Key behaviors @@ -50,7 +51,12 @@ bash -euo pipefail ollama-update --help ## Testing -No formal test suite. Manual verification: +```bash +# Unit + integration tests (fake curl/ollama, no network, no install) +./test.sh +``` + +Manual verification: ```bash # Dry-run version checks (no install) @@ -60,7 +66,7 @@ No formal test suite. Manual verification: ## Gotchas -- Script uses `set -euo pipefail` and `nullglob` — preserve these +- Script uses `set -euo pipefail` — preserve this - GitHub API rate limits: unauthenticated = 60 req/hr - Beta channel compares against stable; only updates if beta > stable AND beta > local - Temp install script cleaned via `trap` on EXIT \ No newline at end of file diff --git a/README.md b/README.md index 35ecb00..5aba3c7 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ selbst durchgeführt und kann Root-Rechte erfordern (z. B. wenn per 7. **Downgrade-Schutz** – Ist die bereits installierte Version neuer als die Zielversion, bricht das Skript standardmäßig ab. Mit `--force`/`-f` lässt sich ein solches Downgrade dennoch erzwingen. -7. **Ausgabe** – Vor jeder Aktion werden die ermittelten Versionen +8. **Ausgabe** – Vor jeder Aktion werden die ermittelten Versionen (lokal, stable, beta) sowie die Entscheidung (Update/Installation/ kein Update) ausgegeben. @@ -75,7 +75,7 @@ Kein Update erforderlich. | Ausgabe vor der Installation | Nur Statusmeldungen des Installers | Zeigt lokal/stable/beta-Versionen und die getroffene Entscheidung | | Versions-Pinning | Nicht vorgesehen | Übergibt `OLLAMA_VERSION` an den Installer, wenn eine konkrete Version gewünscht ist | | Abhängigkeitsprüfung | Fehlt | Prüft benötigte Kommandos (`curl`, `grep`, `sed`, …) vorab | -| Robustheit | Standard-Bash | `set -euo pipefail`, `nullglob`, leere API-Antworten und Downloads werden abgefangen, temporäre Datei wird per `trap` aufgeräumt | +| Robustheit | Standard-Bash | `set -euo pipefail`, leere API-Antworten und Downloads werden abgefangen, temporäre Datei wird per `trap` aufgeräumt | | Hilfe / Argumente | Nicht vorhanden | `--help`/`-h` und klare Fehlerausgaben | ## Voraussetzungen diff --git a/ollama-update b/ollama-update index 090ad42..5c3cc46 100755 --- a/ollama-update +++ b/ollama-update @@ -3,16 +3,14 @@ # updateOllama: Lädt und installiert die aktuelle Ollama-Version (stable oder beta). set -euo pipefail -shopt -s nullglob readonly GITHUB_API="https://api.github.com/repos/ollama/ollama" readonly INSTALL_URL="https://ollama.com/install.sh" -PROG_NAME=$(basename "$0") || { printf 'Fehler: basename fehlgeschlagen\n' >&2; exit 1; } +PROG_NAME=$(basename "$0") readonly PROG_NAME -usage() { - printf 'Verwendung: %s [--beta|-b] [--force|-f]\n' "$PROG_NAME" >&2 - exit 2 +print_usage() { + printf 'Verwendung: %s [--beta|-b] [--force|-f]\n' "$PROG_NAME" } check_dependencies() { @@ -40,8 +38,12 @@ parse_args() { case "$1" in --beta|-b) use_beta_value=true ;; --force|-f) force_value=true ;; - -h|--help) usage ;; - *) usage ;; + -h|--help) print_usage; exit 0 ;; + *) + printf 'Unbekanntes Argument: %s\n' "$1" >&2 + print_usage >&2 + exit 2 + ;; esac shift done @@ -85,7 +87,7 @@ get_latest_version() { printf '%s\n' "$json" | sed -nE 's/.*"tag_name": *"v?([^"]+)".*/\1/p' | head -n1 } -# Vergleicht zwei Versionsstrings (SemVer-ähnlich, Suffixe alphabetisch). +# Vergleicht zwei Versionsstrings (SemVer-ähnlich, Suffixe per 'sort -V'). # Gibt -1, 0 oder 1 aus. cmp_versions() { local a=${1#v} b=${2#v} @@ -223,6 +225,11 @@ main() { elif [[ "$(cmp_versions "$stable_ver" "$local_ver")" == 1 ]]; then target_ver="$stable_ver" printf 'Stable-Update verfuegbar: %s -> %s\n' "$local_ver" "$target_ver" + elif [[ "$force_downgrade" == true && "$(cmp_versions "$beta_ver" "$local_ver")" == -1 ]]; then + # Kein neueres Release; --force erlaubt das Downgrade auf die Beta. + target_ver="$beta_ver" + elif [[ "$force_downgrade" == true && "$(cmp_versions "$stable_ver" "$local_ver")" == -1 ]]; then + target_ver="$stable_ver" else printf 'Kein Update erforderlich.\n' return 0 @@ -231,6 +238,9 @@ main() { if [[ "$(cmp_versions "$stable_ver" "$local_ver")" == 1 ]]; then target_ver="$stable_ver" printf 'Update verfuegbar: %s -> %s\n' "$local_ver" "$target_ver" + elif [[ "$force_downgrade" == true && "$(cmp_versions "$stable_ver" "$local_ver")" == -1 ]]; then + # Kein neueres Release; --force erlaubt das Downgrade auf Stable. + target_ver="$stable_ver" else printf 'Kein Update erforderlich.\n' return 0 @@ -256,4 +266,7 @@ main() { fi } -main "$@" +# Nur ausführen, wenn direkt aufgerufen — beim Sourcing (test.sh) nicht starten. +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + main "$@" +fi diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..680713f --- /dev/null +++ b/test.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# Selbst-Test für ollama-update: Versionsvergleich und Update-Entscheidung. +# Benötigt keine Netzverbindung und installiert nichts (curl/ollama werden gefälscht). +set -euo pipefail + +cd "$(dirname "$0")" + +fails=0 +check() { # check + if [[ "$2" == "$3" ]]; then + printf 'ok: %s\n' "$1" + else + printf 'FEHLER: %s — erwartet %q, bekommen %q\n' "$1" "$2" "$3" >&2 + fails=$((fails + 1)) + fi +} + +# --- cmp_versions --- +source ./ollama-update + +check "gleich" "0" "$(cmp_versions 0.5.7 0.5.7)" +check "neuer" "1" "$(cmp_versions 0.5.8 0.5.7)" +check "aelter" "-1" "$(cmp_versions 0.5.7 0.5.8)" +check "fehlende Stelle = 0" "0" "$(cmp_versions 0.5 0.5.0)" +check "zweistellig" "1" "$(cmp_versions 0.10.0 0.9.9)" +check "stable ueber rc" "1" "$(cmp_versions 0.5.7 0.5.7-rc1)" +check "rc unter stable" "-1" "$(cmp_versions 0.5.7-rc1 0.5.7)" +check "rc2 ueber rc1" "1" "$(cmp_versions 0.5.7-rc2 0.5.7-rc1)" +check "rc10 ueber rc2" "1" "$(cmp_versions 0.5.7-rc10 0.5.7-rc2)" +check "v-Praefix ignoriert" "0" "$(cmp_versions v0.5.7 0.5.7)" + +# --- Entscheidungs-Matrix mit gefälschten curl/ollama --- +# GitHub liefert stable=v0.5.7 und beta=v0.6.0-rc1; install.sh ist ein Stub. +tmpbin=$(mktemp -d) +trap 'rm -rf "$tmpbin"' EXIT + +cat > "$tmpbin/curl" <<'EOF' +#!/bin/bash +url=""; out="" +while [[ $# -gt 0 ]]; do + case "$1" in + -o) out=$2; shift 2 ;; + -*) shift ;; + *) url=$1; shift ;; + esac +done +if [[ "$url" == *install.sh ]]; then + printf '#!/bin/bash\nexit 0\n' > "$out" +elif [[ "$url" == *releases/latest ]]; then + printf '{\n "tag_name": "v0.5.7"\n}\n' +else + printf '[\n {\n "tag_name": "v0.6.0-rc1"\n }\n]\n' +fi +EOF + +cat > "$tmpbin/ollama" <<'EOF' +#!/bin/bash +if [[ "${FAKE_LOCAL_VERSION:-}" == "none" ]]; then + exit 1 +fi +printf 'ollama version is %s\n' "${FAKE_LOCAL_VERSION:-0.5.7}" +EOF +chmod +x "$tmpbin/curl" "$tmpbin/ollama" + +expect() { # expect [Argumente...] + local desc=$1 pattern=$2 ver=$3 + shift 3 + local out + out=$(PATH="$tmpbin:$PATH" FAKE_LOCAL_VERSION="$ver" ./ollama-update "$@" 2>&1 || true) + if grep -qF -- "$pattern" <<< "$out"; then + printf 'ok: %s\n' "$desc" + else + printf 'FEHLER: %s — erwartet %q, bekommen:\n%s\n' "$desc" "$pattern" "$out" >&2 + fails=$((fails + 1)) + fi +} + +expect "aktuell: kein Update" "Kein Update erforderlich." 0.5.7 +expect "stable-Update" "Update verfuegbar: 0.5.6 -> 0.5.7" 0.5.6 +expect "beta-Update" "Beta-Update verfuegbar: 0.5.7 -> 0.6.0-rc1" 0.5.7 --beta +expect "downgrade ohne --force blockiert" "Kein Update erforderlich." 0.9.9 +expect "downgrade mit --force" "Downgrade wird erzwungen: 0.9.9 -> 0.5.7" 0.9.9 --force +expect "beta-downgrade mit --force" "Downgrade wird erzwungen: 0.9.9 -> 0.6.0-rc1" 0.9.9 --beta --force +expect "nicht installiert: stable" "Installation der Stable 0.5.7" none +expect "nicht installiert: beta" "Installation der Beta 0.6.0-rc1" none --beta + +if [[ "$fails" -gt 0 ]]; then + printf '%d Test(s) fehlgeschlagen\n' "$fails" >&2 + exit 1 +fi +printf 'Alle Tests bestanden.\n' \ No newline at end of file