All checks were successful
Build AppImage / build (push) Successful in 7m11s
Problem: Beim Nix-Wrapper lag nur das Binary unter ~/.local/share/claude-desktop/bin, aber claude-bridge.js + node_modules waren nirgends deployt → "Bridge nicht gefunden" beim ersten Chat-Versuch. Loesung: - claude.rs: Bridge-Such-Pfad um bin/../scripts erweitert (exe_dir.parent / scripts). - update.rs: UpdateManifest + UpdateStatus um bundle_filename/bundle_sha256 erweitert. Neues Tauri-Command apply_bundle_update: laedt tar.gz, pruefte SHA256, entpackt nach ~/.local/share/claude-desktop, ruft npm ci --omit=dev auf. Im AppImage-Modus no-op (Bundle ist im AppImage enthalten). - lib.rs: apply_bundle_update registriert. - CI: packt scripts/claude-bridge.js + package.json + package-lock.json als claude-desktop-bundle_VERSION.tar.gz und laedt neben Binary in die Package Registry. update.json v3 enthaelt bundle_filename + bundle_sha256. - install.sh: Erst-Installer laedt das Bundle, verifiziert SHA, entpackt, fuehrt npm ci --omit=dev aus. Holt nodejs bei Bedarf ueber nix-build (analog zu jq). - UpdateDialog.svelte: ruft im Nix-Modus apply_bundle_update vor apply_update auf, damit nach dem Neustart Scripts + node_modules aktuell sind. - nix/default.nix: nodejs_22 + tar + gzip im Wrapper-PATH, damit die App aus dem Binary heraus npm ci aufrufen kann. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
201 lines
7.6 KiB
Bash
201 lines
7.6 KiB
Bash
#!/usr/bin/env bash
|
|
# Claude Desktop — One-Click-Installer für NixOS
|
|
#
|
|
# Installiert:
|
|
# - Nix-Wrapper-Paket (nix/default.nix) ins User-Profil via nix-env
|
|
# - Natives Binary aus der Forgejo Package Registry nach ~/.local/share/claude-desktop/bin
|
|
# - Desktop-Entry im System-Menue (KDE/GNOME/…)
|
|
#
|
|
# Aufruf:
|
|
# curl -fsSL https://git.data-it-solution.de/data/claude-desktop/raw/branch/main/install.sh | bash
|
|
#
|
|
# Oder lokal:
|
|
# ./install.sh
|
|
#
|
|
# Env-Vars (optional):
|
|
# CLAUDE_DESKTOP_TOKEN — Forgejo Read-Token (sonst wird einmalig gefragt)
|
|
# CLAUDE_DESKTOP_BRANCH — Git-Branch für Wrapper-Dateien (default: main)
|
|
|
|
set -euo pipefail
|
|
|
|
# --- Konfiguration ---------------------------------------------------------
|
|
REPO_URL="https://git.data-it-solution.de/data/claude-desktop"
|
|
PKG_BASE="https://git.data-it-solution.de/api/packages/data/generic/claude-desktop/latest"
|
|
APP_DIR="$HOME/.local/share/claude-desktop"
|
|
BIN_DIR="$APP_DIR/bin"
|
|
TOKEN_FILE="$HOME/.config/claude-desktop/token"
|
|
BRANCH="${CLAUDE_DESKTOP_BRANCH:-main}"
|
|
|
|
# --- Farben ----------------------------------------------------------------
|
|
if [ -t 1 ]; then
|
|
BOLD="$(tput bold)"; RED="$(tput setaf 1)"; GRN="$(tput setaf 2)"
|
|
YEL="$(tput setaf 3)"; BLU="$(tput setaf 4)"; RST="$(tput sgr0)"
|
|
else
|
|
BOLD=""; RED=""; GRN=""; YEL=""; BLU=""; RST=""
|
|
fi
|
|
|
|
step() { echo "${BOLD}${BLU}▶${RST} $*"; }
|
|
ok() { echo " ${GRN}✓${RST} $*"; }
|
|
err() { echo "${RED}✗${RST} $*" >&2; exit 1; }
|
|
|
|
# --- 1. Voraussetzungen pruefen -------------------------------------------
|
|
step "Voraussetzungen pruefen"
|
|
for cmd in nix-build nix-env curl sha256sum; do
|
|
command -v "$cmd" >/dev/null || err "$cmd nicht installiert. Abbruch."
|
|
done
|
|
ok "Nix, curl, sha256sum vorhanden"
|
|
|
|
# jq ist auf Vanilla-NixOS nicht gesetzt — aus nixpkgs ziehen
|
|
if ! command -v jq >/dev/null; then
|
|
echo " ${YEL}!${RST} jq fehlt — hole aus nixpkgs"
|
|
JQ_OUT=$(nix-build '<nixpkgs>' -A jq --no-out-link 2>/dev/null || true)
|
|
if [ -n "$JQ_OUT" ] && [ -x "$JQ_OUT/bin/jq" ]; then
|
|
export PATH="$JQ_OUT/bin:$PATH"
|
|
ok "jq temporaer aus $JQ_OUT"
|
|
else
|
|
err "jq konnte nicht ueber nix-build bezogen werden. Abbruch."
|
|
fi
|
|
fi
|
|
|
|
# npm (fuer Bridge-Dependencies) — analog zu jq aus nixpkgs wenn nicht vorhanden
|
|
if ! command -v npm >/dev/null; then
|
|
echo " ${YEL}!${RST} npm fehlt — hole nodejs aus nixpkgs"
|
|
NODE_OUT=$(nix-build '<nixpkgs>' -A nodejs_22 --no-out-link 2>/dev/null || true)
|
|
if [ -n "$NODE_OUT" ] && [ -x "$NODE_OUT/bin/npm" ]; then
|
|
export PATH="$NODE_OUT/bin:$PATH"
|
|
ok "nodejs temporaer aus $NODE_OUT"
|
|
else
|
|
err "nodejs konnte nicht ueber nix-build bezogen werden. Abbruch."
|
|
fi
|
|
fi
|
|
|
|
if [ ! -f /etc/NIXOS ] && [ ! -e /etc/nix/nix.conf ]; then
|
|
echo " ${YEL}!${RST} Kein NixOS-System erkannt — der Installer setzt Nix voraus."
|
|
fi
|
|
|
|
# --- 2. Forgejo-Token besorgen --------------------------------------------
|
|
step "Forgejo-Token laden"
|
|
TOKEN="${CLAUDE_DESKTOP_TOKEN:-}"
|
|
if [ -z "$TOKEN" ] && [ -f "$TOKEN_FILE" ]; then
|
|
TOKEN=$(cat "$TOKEN_FILE")
|
|
ok "Token aus $TOKEN_FILE geladen"
|
|
fi
|
|
if [ -z "$TOKEN" ]; then
|
|
echo " Forgejo Read-Token für Package Registry (Basic-Auth User: ${BOLD}data${RST}):"
|
|
read -rs -p " Token: " TOKEN
|
|
echo
|
|
mkdir -p "$(dirname "$TOKEN_FILE")"
|
|
printf '%s' "$TOKEN" > "$TOKEN_FILE"
|
|
chmod 600 "$TOKEN_FILE"
|
|
ok "Token in $TOKEN_FILE gespeichert (chmod 600)"
|
|
fi
|
|
|
|
# --- 3. Manifest laden ----------------------------------------------------
|
|
step "Manifest abrufen (latest/update.json)"
|
|
MANIFEST=$(curl -fsSL --user "data:$TOKEN" "$PKG_BASE/update.json") \
|
|
|| err "Manifest nicht erreichbar (Token falsch? Netz?)"
|
|
|
|
VERSION=$(echo "$MANIFEST" | jq -r '.version')
|
|
BINARY_NAME=$(echo "$MANIFEST" | jq -r '.binary_filename // empty')
|
|
BINARY_SHA=$(echo "$MANIFEST" | jq -r '.binary_sha256 // empty')
|
|
BUNDLE_NAME=$(echo "$MANIFEST" | jq -r '.bundle_filename // empty')
|
|
BUNDLE_SHA=$(echo "$MANIFEST" | jq -r '.bundle_sha256 // empty')
|
|
|
|
if [ -z "$BINARY_NAME" ] || [ -z "$BINARY_SHA" ]; then
|
|
err "update.json enthaelt kein binary_filename/binary_sha256. Alter CI-Build? Erst neue Pipeline laufen lassen."
|
|
fi
|
|
if [ -z "$BUNDLE_NAME" ] || [ -z "$BUNDLE_SHA" ]; then
|
|
err "update.json enthaelt kein bundle_filename/bundle_sha256 (scripts+node_modules). Alter CI-Build? Erst neue Pipeline laufen lassen."
|
|
fi
|
|
|
|
ok "Version $VERSION, Binary $BINARY_NAME, Bundle $BUNDLE_NAME"
|
|
|
|
# --- 4. Binary herunterladen ----------------------------------------------
|
|
step "Binary laden -> $BIN_DIR"
|
|
mkdir -p "$BIN_DIR"
|
|
TMP_BIN=$(mktemp)
|
|
trap 'rm -f "$TMP_BIN"' EXIT
|
|
|
|
curl -fsSL --user "data:$TOKEN" -o "$TMP_BIN" "$PKG_BASE/$BINARY_NAME" \
|
|
|| err "Binary-Download fehlgeschlagen"
|
|
|
|
ACTUAL_SHA=$(sha256sum "$TMP_BIN" | awk '{print $1}')
|
|
if [ "$ACTUAL_SHA" != "$BINARY_SHA" ]; then
|
|
err "SHA256-Mismatch: erwartet $BINARY_SHA, bekommen $ACTUAL_SHA"
|
|
fi
|
|
ok "SHA256 verifiziert"
|
|
|
|
mv "$TMP_BIN" "$BIN_DIR/claude-desktop"
|
|
chmod +x "$BIN_DIR/claude-desktop"
|
|
ok "Binary installiert: $BIN_DIR/claude-desktop"
|
|
|
|
# --- 4b. Scripts-Bundle laden + entpacken + npm ci ------------------------
|
|
step "Scripts-Bundle laden + entpacken -> $APP_DIR"
|
|
# node+npm brauchen wir spaetestens hier
|
|
if ! command -v npm >/dev/null; then
|
|
err "npm nicht gefunden. Installiere nodejs im System oder in der Nix-Shell."
|
|
fi
|
|
TMP_BUNDLE=$(mktemp --suffix=.tar.gz)
|
|
trap 'rm -f "$TMP_BIN" "$TMP_BUNDLE"' EXIT
|
|
curl -fsSL --user "data:$TOKEN" -o "$TMP_BUNDLE" "$PKG_BASE/$BUNDLE_NAME" \
|
|
|| err "Bundle-Download fehlgeschlagen"
|
|
ACTUAL_BUNDLE_SHA=$(sha256sum "$TMP_BUNDLE" | awk '{print $1}')
|
|
if [ "$ACTUAL_BUNDLE_SHA" != "$BUNDLE_SHA" ]; then
|
|
err "Bundle-SHA256-Mismatch: erwartet $BUNDLE_SHA, bekommen $ACTUAL_BUNDLE_SHA"
|
|
fi
|
|
ok "Bundle-SHA256 verifiziert"
|
|
|
|
tar -xzf "$TMP_BUNDLE" -C "$APP_DIR"
|
|
ok "Bundle entpackt"
|
|
|
|
step "npm ci --omit=dev im User-Home (dauert 30-60 s)"
|
|
(cd "$APP_DIR" && npm ci --omit=dev --no-audit --no-fund) \
|
|
|| err "npm ci schlug fehl — Bridge-Abhaengigkeiten nicht installiert"
|
|
ok "node_modules (production) installiert"
|
|
|
|
# --- 5. Nix-Wrapper bauen -------------------------------------------------
|
|
step "Nix-Wrapper-Paket bauen"
|
|
WORKDIR=$(mktemp -d)
|
|
trap 'rm -f "$TMP_BIN"; rm -rf "$WORKDIR"' EXIT
|
|
|
|
mkdir -p "$WORKDIR/nix" "$WORKDIR/src-tauri/icons"
|
|
for f in \
|
|
"nix/default.nix" \
|
|
"nix/claude-desktop.desktop" \
|
|
"src-tauri/icons/icon.png"; do
|
|
curl -fsSL -o "$WORKDIR/$f" "$REPO_URL/raw/branch/$BRANCH/$f" \
|
|
|| err "Kann $f nicht laden ($REPO_URL/raw/branch/$BRANCH/$f)"
|
|
done
|
|
ok "Wrapper-Quelldateien geladen"
|
|
|
|
pushd "$WORKDIR" >/dev/null
|
|
RESULT=$(nix-build nix/default.nix --no-out-link 2>&1 | tail -1)
|
|
popd >/dev/null
|
|
[ -d "$RESULT" ] || err "nix-build fehlgeschlagen"
|
|
ok "Paket gebaut: $RESULT"
|
|
|
|
# --- 6. Ins User-Profil installieren --------------------------------------
|
|
step "Ins User-Profil installieren (nix-env)"
|
|
nix-env --uninstall claude-desktop 2>/dev/null || true
|
|
nix-env -i "$RESULT"
|
|
ok "claude-desktop im \$PATH verfuegbar"
|
|
|
|
# --- 7. Desktop-Datenbank refreshen ---------------------------------------
|
|
step "Desktop-Menue aktualisieren"
|
|
if command -v update-desktop-database >/dev/null; then
|
|
update-desktop-database "$HOME/.nix-profile/share/applications" 2>/dev/null || true
|
|
fi
|
|
if command -v gtk-update-icon-cache >/dev/null; then
|
|
gtk-update-icon-cache -q "$HOME/.nix-profile/share/icons/hicolor" 2>/dev/null || true
|
|
fi
|
|
ok "Menue + Icon-Cache aktualisiert"
|
|
|
|
# --- Fertig ---------------------------------------------------------------
|
|
echo
|
|
echo "${BOLD}${GRN}✓ Claude Desktop v${VERSION} installiert${RST}"
|
|
echo
|
|
echo "Starten:"
|
|
echo " ${BOLD}claude-desktop${RST} (Terminal)"
|
|
echo " oder aus dem System-Menue: 'Claude Desktop'"
|
|
echo
|
|
echo "Updates holt die App selbstaendig (Button in Settings oder Auto-Check beim Start)."
|