| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- #!/usr/bin/env bash
- # ============================================================================
- # Gemeinsame Funktionen fuer make_installer_*.sh
- # ============================================================================
- set -euo pipefail
- # Verzeichnisse
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
- SOURCE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
- EXCLUDE_FILE="${SCRIPT_DIR}/exclude_patterns.txt"
- INSTALLER_DIR="${SCRIPT_DIR}/installer"
- BUILD_CONF="${SCRIPT_DIR}/build.conf"
- BUILD_LOG="${SCRIPT_DIR}/build.log"
- # --- build.conf laden ---
- load_build_conf() {
- # Defaults
- INCLUDE_AUDIO_ASSETS=true
- INCLUDE_MUSIC_ASSETS=false
- INCLUDE_MORPHER_ASSETS=true
- INCLUDE_WAKEWORD_MODELS=true
- INCLUDE_PIPER_MODELS=true # models/piper/*.onnx (inkl. Custom-Voices)
- INCLUDE_SYMSPELL=true
- INCLUDE_LLM_MODELS=false
- INCLUDE_PLUGIN_MODELS=false
- INCLUDE_TRAINER_SOURCE=true
- INCLUDE_TRAINING_DATA=false
- INCLUDE_DOCS=true
- INCLUDE_BASH_SCRIPTS=true
- if [[ -f "${BUILD_CONF}" ]]; then
- # Nur KEY=value Zeilen laden (keine Kommentare, keine leeren Zeilen)
- while IFS='=' read -r key value; do
- key=$(echo "${key}" | xargs) # Whitespace trimmen
- value=$(echo "${value}" | xargs)
- [[ -z "${key}" || "${key}" == \#* ]] && continue
- declare -g "${key}=${value}"
- done < "${BUILD_CONF}"
- echo " build.conf geladen"
- else
- echo " WARNUNG: ${BUILD_CONF} nicht gefunden — verwende Defaults"
- fi
- }
- # --- Dynamische Excludes aus build.conf generieren ---
- build_conf_excludes() {
- local mode="$1"
- local excludes=""
- # Assets
- if [[ "${INCLUDE_AUDIO_ASSETS}" != "true" ]]; then
- excludes+=" assets/default/audio"
- fi
- if [[ "${INCLUDE_MUSIC_ASSETS}" != "true" ]]; then
- excludes+=" assets/default/music"
- fi
- if [[ "${INCLUDE_MORPHER_ASSETS}" != "true" ]]; then
- excludes+=" assets/default/audio_morpher"
- fi
- # Modelle
- if [[ "${INCLUDE_WAKEWORD_MODELS}" != "true" ]]; then
- excludes+=" models/wakeword/*/*.onnx"
- fi
- if [[ "${INCLUDE_PIPER_MODELS}" != "true" ]]; then
- excludes+=" models/piper/*.onnx models/piper/.cache"
- fi
- if [[ "${INCLUDE_SYMSPELL}" != "true" ]]; then
- excludes+=" models/symspell"
- fi
- if [[ "${INCLUDE_LLM_MODELS}" != "true" ]]; then
- excludes+=" models/nlp/*.gguf"
- fi
- if [[ "${INCLUDE_PLUGIN_MODELS}" != "true" ]]; then
- # Alle Plugin-Models ausschliessen
- # Piper-Voices werden ueber models/piper/ (INCLUDE_PIPER_MODELS) behalten
- excludes+=" plugins/*/models"
- fi
- # Trainer
- if [[ "${INCLUDE_TRAINER_SOURCE}" != "true" ]]; then
- excludes+=" trainer"
- fi
- if [[ "${INCLUDE_TRAINING_DATA}" != "true" ]]; then
- excludes+=" trainer/data/raw trainer/data/processed trainer/assets trainer/cache trainer/processed"
- fi
- # Sonstiges
- if [[ "${INCLUDE_DOCS}" != "true" ]]; then
- excludes+=" docs"
- fi
- if [[ "${INCLUDE_BASH_SCRIPTS}" != "true" ]]; then
- excludes+=" bash"
- fi
- echo "${excludes}"
- }
- # --- Version aus trixy_core/utils/version.py parsen ---
- parse_version() {
- local version_file="${SOURCE_DIR}/trixy_core/utils/version.py"
- if [[ ! -f "${version_file}" ]]; then
- echo "FEHLER: ${version_file} nicht gefunden" >&2
- exit 1
- fi
- local major minor revision
- major=$(grep -E '^MAJOR:\s*int\s*=' "${version_file}" | grep -oE '[0-9]+')
- minor=$(grep -E '^MINOR:\s*int\s*=' "${version_file}" | grep -oE '[0-9]+')
- revision=$(grep -E '^REVISION:\s*int\s*=' "${version_file}" | grep -oE '[0-9]+')
- echo "${major}.${minor}.${revision}"
- }
- # --- Git-Revision ermitteln (Kurz-Hash, oder "nogit") ---
- get_git_revision() {
- if command -v git >/dev/null 2>&1 && git -C "${SOURCE_DIR}" rev-parse --git-dir >/dev/null 2>&1; then
- git -C "${SOURCE_DIR}" rev-parse --short HEAD 2>/dev/null || echo "nogit"
- else
- echo "nogit"
- fi
- }
- # --- Git-Status pruefen: "clean" oder "dirty" ---
- get_git_status() {
- if command -v git >/dev/null 2>&1 && git -C "${SOURCE_DIR}" rev-parse --git-dir >/dev/null 2>&1; then
- # Cache- und Runtime-Dateien ignorieren bei der Pruefung
- local changes
- changes=$(git -C "${SOURCE_DIR}" status --porcelain 2>/dev/null \
- | grep -v '__pycache__' \
- | grep -v '\.pyc$' \
- | grep -v '^\s*M logs/' \
- | grep -v '^\s*M data/activity/' \
- | grep -v '^\s*M cron/' \
- || true)
- if [[ -z "${changes}" ]]; then
- echo "clean"
- else
- echo "dirty"
- fi
- else
- echo "nogit"
- fi
- }
- # --- Naechste Build-Nummer ermitteln ---
- # Wenn der aktuelle Commit-Hash bereits in der build.log steht, wird dieselbe
- # Build-Nummer zurueckgegeben. Sonst wird max(build_number) + 1 verwendet.
- get_next_build_number() {
- local git_rev="$1"
- if [[ ! -f "${BUILD_LOG}" ]]; then
- echo "1"
- return
- fi
- # Suche existierenden Eintrag mit gleichem git_rev (Spalte 4)
- local existing
- existing=$(awk -F',' -v rev="${git_rev}" '
- NR > 1 && $4 == rev { print $1; exit }
- ' "${BUILD_LOG}")
- if [[ -n "${existing}" ]]; then
- echo "${existing}"
- return
- fi
- # Neue Build-Nummer = max + 1
- local max_build
- max_build=$(awk -F',' '
- NR > 1 && $1 ~ /^[0-9]+$/ { if ($1 > m) m = $1 }
- END { print m + 0 }
- ' "${BUILD_LOG}")
- echo "$((max_build + 1))"
- }
- # --- build.log Eintrag hinzufuegen/aktualisieren ---
- # Spalten: build_number,version,full_version,git_rev,git_status,timestamp,modes
- #
- # Wenn git_rev bereits existiert, wird der Modus zur modes-Spalte hinzugefuegt
- # (falls noch nicht vorhanden) und timestamp aktualisiert.
- update_build_log() {
- local build_number="$1"
- local version="$2"
- local full_version="$3"
- local git_rev="$4"
- local git_status="$5"
- local mode="$6"
- local timestamp
- timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
- # Header erstellen wenn Datei neu
- if [[ ! -f "${BUILD_LOG}" ]]; then
- echo "build_number,version,full_version,git_rev,git_status,timestamp,modes" > "${BUILD_LOG}"
- fi
- # Existierenden Eintrag suchen
- if grep -q "^${build_number}," "${BUILD_LOG}" 2>/dev/null; then
- # Eintrag aktualisieren: modes ergaenzen, timestamp updaten
- local tmp
- tmp=$(mktemp)
- awk -F',' -v bn="${build_number}" -v mode="${mode}" -v ts="${timestamp}" -v gs="${git_status}" '
- BEGIN { OFS = "," }
- NR == 1 { print; next }
- $1 == bn {
- # Modes zusammenfuehren (ohne Duplikate)
- n = split($7, arr, "|")
- found = 0
- for (i = 1; i <= n; i++) if (arr[i] == mode) found = 1
- if (!found) {
- if ($7 == "") $7 = mode
- else $7 = $7 "|" mode
- }
- $5 = gs # status aktualisieren
- $6 = ts # timestamp aktualisieren
- print
- next
- }
- { print }
- ' "${BUILD_LOG}" > "${tmp}"
- mv "${tmp}" "${BUILD_LOG}"
- else
- # Neuer Eintrag
- echo "${build_number},${version},${full_version},${git_rev},${git_status},${timestamp},${mode}" >> "${BUILD_LOG}"
- fi
- }
- # --- VERSION-Datei fuer den Build generieren ---
- # Wird ins Release-Verzeichnis geschrieben UND ins Source-Tar aufgenommen.
- generate_version_file() {
- local target_path="$1"
- local mode="$2"
- local version="$3"
- local build_number="$4"
- local full_version="$5"
- local git_rev="$6"
- local git_status="$7"
- local timestamp
- timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
- cat > "${target_path}" << EOF
- Name: Trixy
- Mode: ${mode}
- Version: ${version}
- Build: ${build_number}
- FullVersion: ${full_version}
- GitRevision: ${git_rev}
- GitStatus: ${git_status}
- BuildDate: ${timestamp}
- EOF
- }
- # --- Build-Info sammeln und ausgeben ---
- # Setzt globale Variablen: BUILD_VERSION, BUILD_NUMBER, BUILD_FULL_VERSION,
- # BUILD_GIT_REV, BUILD_GIT_STATUS
- collect_build_info() {
- BUILD_VERSION=$(parse_version)
- BUILD_GIT_REV=$(get_git_revision)
- BUILD_GIT_STATUS=$(get_git_status)
- BUILD_NUMBER=$(get_next_build_number "${BUILD_GIT_REV}")
- BUILD_FULL_VERSION="${BUILD_VERSION}.${BUILD_NUMBER}"
- echo " Version: ${BUILD_VERSION}"
- echo " Build: ${BUILD_NUMBER}"
- echo " FullVersion: ${BUILD_FULL_VERSION}"
- echo " Git-Revision: ${BUILD_GIT_REV} (${BUILD_GIT_STATUS})"
- }
- # --- Release-Verzeichnis erstellen ---
- prepare_release_dir() {
- local version="$1"
- local mode="$2"
- local release_dir="${SCRIPT_DIR}/Release/trixy-${mode}-${version}"
- mkdir -p "${release_dir}"
- echo "${release_dir}"
- }
- # --- build.conf Einstellungen anzeigen ---
- print_build_conf() {
- local mode="$1"
- echo " Build-Konfiguration:"
- echo " Audio-Assets: ${INCLUDE_AUDIO_ASSETS}"
- echo " Musik-Assets: ${INCLUDE_MUSIC_ASSETS}"
- echo " Morpher-Assets: ${INCLUDE_MORPHER_ASSETS}"
- echo " Wakeword-Modelle: ${INCLUDE_WAKEWORD_MODELS}"
- echo " Piper-Modelle: ${INCLUDE_PIPER_MODELS}"
- echo " SymSpell: ${INCLUDE_SYMSPELL}"
- echo " LLM-Modelle: ${INCLUDE_LLM_MODELS}"
- echo " Plugin-Modelle: ${INCLUDE_PLUGIN_MODELS}"
- echo " Trainer-Source: ${INCLUDE_TRAINER_SOURCE}"
- echo " Trainings-Daten: ${INCLUDE_TRAINING_DATA}"
- echo " Dokumentation: ${INCLUDE_DOCS}"
- echo " Bash-Skripte: ${INCLUDE_BASH_SCRIPTS}"
- echo ""
- }
- # --- Source-tar erstellen ---
- create_source_tar() {
- local mode="$1"
- local version="$2"
- local release_dir="$3"
- local extra_excludes="$4" # Zusaetzliche Exclude-Patterns (Leerzeichen-getrennt)
- local tar_name="trixy-${mode}-${version}.tar.gz"
- local tar_path="${release_dir}/${tar_name}"
- echo " Erstelle ${tar_name}..."
- # VERSION-Datei temporaer in SOURCE_DIR schreiben (wird mit ins tar aufgenommen)
- local version_file="${SOURCE_DIR}/VERSION"
- generate_version_file "${version_file}" \
- "${mode}" \
- "${BUILD_VERSION}" \
- "${BUILD_NUMBER}" \
- "${BUILD_FULL_VERSION}" \
- "${BUILD_GIT_REV}" \
- "${BUILD_GIT_STATUS}"
- # Kopie ins Release-Verzeichnis
- cp "${version_file}" "${release_dir}/VERSION"
- # Basis-Excludes aus exclude_patterns.txt
- local exclude_opts=()
- while IFS= read -r pattern; do
- [[ -z "${pattern}" || "${pattern}" == \#* ]] && continue
- exclude_opts+=("--exclude=${pattern}")
- done < "${EXCLUDE_FILE}"
- # build.conf-basierte Excludes
- local conf_excludes
- conf_excludes=$(build_conf_excludes "${mode}")
- for pattern in ${conf_excludes}; do
- exclude_opts+=("--exclude=${pattern}")
- done
- # Modus-spezifische Excludes
- for pattern in ${extra_excludes}; do
- exclude_opts+=("--exclude=${pattern}")
- done
- # tar in temporaere Datei schreiben, dann verschieben
- local tmp_tar
- tmp_tar="$(mktemp)"
- local prefix="trixy-${mode}-${version}"
- tar czf "${tmp_tar}" \
- -C "${SOURCE_DIR}" \
- "${exclude_opts[@]}" \
- --exclude='make_installer/Release' \
- --transform="s,^\\.,${prefix}," \
- .
- mv "${tmp_tar}" "${tar_path}"
- # VERSION-Datei aus SOURCE_DIR wieder entfernen
- rm -f "${version_file}"
- echo " -> ${tar_name} ($(du -h "${tar_path}" | cut -f1))"
- }
- # --- Installer-tar erstellen ---
- create_installer_tar() {
- local mode="$1"
- local version="$2"
- local release_dir="$3"
- local tar_name="trixy-installer-${mode}-${version}.tar.gz"
- local tar_path="${release_dir}/${tar_name}"
- echo " Erstelle ${tar_name}..."
- tar czf "${tar_path}" \
- -C "${INSTALLER_DIR}/.." \
- --transform='s,^installer,trixy_installer,' \
- --exclude='__pycache__' \
- --exclude='*.pyc' \
- installer/
- echo " -> ${tar_name} ($(du -h "${tar_path}" | cut -f1))"
- }
- # --- install.sh generieren ---
- generate_install_sh() {
- local mode="$1"
- local version="$2"
- local release_dir="$3"
- local install_sh="${release_dir}/install.sh"
- local source_tar="trixy-${mode}-${version}.tar.gz"
- local installer_tar="trixy-installer-${mode}-${version}.tar.gz"
- echo " Generiere install.sh..."
- cat > "${install_sh}" << 'INSTALL_HEADER'
- #!/usr/bin/env bash
- # ============================================================================
- # Trixy Installer — Bootstrap-Script
- #
- # Entpackt den Installer, erstellt ein temporaeres venv und startet die TUI.
- # ============================================================================
- set -euo pipefail
- INSTALL_HEADER
- cat >> "${install_sh}" << INSTALL_VARS
- # --- Eingebettete Werte ---
- MODE="${mode}"
- VERSION="${version}"
- SOURCE_TAR="${source_tar}"
- INSTALLER_TAR="${installer_tar}"
- INSTALL_VARS
- cat >> "${install_sh}" << 'INSTALL_BODY'
- # --- Farben ---
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- NC='\033[0m'
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
- TEMP_DIR=""
- cleanup() {
- if [[ -n "${TEMP_DIR}" && -d "${TEMP_DIR}" ]]; then
- echo -e "${YELLOW}Raeume auf...${NC}"
- rm -rf "${TEMP_DIR}"
- fi
- }
- trap cleanup EXIT
- echo ""
- echo -e "${GREEN}========================================"
- echo " Trixy Installer v${VERSION} [${MODE}]"
- echo -e "========================================${NC}"
- echo ""
- # 1. Pruefe ob tar-Dateien vorhanden
- if [[ ! -f "${SCRIPT_DIR}/${SOURCE_TAR}" ]]; then
- echo -e "${RED}FEHLER: ${SOURCE_TAR} nicht gefunden${NC}"
- exit 1
- fi
- if [[ ! -f "${SCRIPT_DIR}/${INSTALLER_TAR}" ]]; then
- echo -e "${RED}FEHLER: ${INSTALLER_TAR} nicht gefunden${NC}"
- exit 1
- fi
- # 2. Python >= 3.10 sicherstellen
- echo "Pruefe Python-Version..."
- PYTHON_BIN=""
- for py in python3.12 python3.11 python3.10 python3; do
- if command -v "${py}" >/dev/null 2>&1; then
- py_minor=$("${py}" -c "import sys; print(sys.version_info.minor)")
- if [[ "${py_minor}" -ge 10 ]]; then
- PYTHON_BIN="${py}"
- break
- fi
- fi
- done
- if [[ -z "${PYTHON_BIN}" ]]; then
- echo -e "${YELLOW}Python >= 3.10 nicht gefunden. Versuche Installation...${NC}"
- if command -v apt-get >/dev/null 2>&1; then
- sudo apt-get update -qq
- sudo apt-get install -y -qq python3 python3-venv python3-pip
- PYTHON_BIN="python3"
- else
- echo -e "${RED}FEHLER: Python >= 3.10 wird benoetigt${NC}"
- exit 1
- fi
- fi
- echo " Verwende: ${PYTHON_BIN} ($(${PYTHON_BIN} --version 2>&1))"
- # 3. Installer-tar in temp-Verzeichnis entpacken
- TEMP_DIR="$(mktemp -d)"
- echo "Entpacke Installer nach ${TEMP_DIR}..."
- tar xzf "${SCRIPT_DIR}/${INSTALLER_TAR}" -C "${TEMP_DIR}"
- # 4. Installer-venv erstellen
- INST_VENV="${TEMP_DIR}/installer-venv"
- echo "Erstelle Installer-venv..."
- "${PYTHON_BIN}" -m venv "${INST_VENV}"
- "${INST_VENV}/bin/pip" install --upgrade pip -q 2>/dev/null
- "${INST_VENV}/bin/pip" install -r "${TEMP_DIR}/trixy_installer/requirements.txt" -q
- # 5. TUI starten
- echo ""
- echo -e "${GREEN}Starte Installer-TUI...${NC}"
- echo ""
- PYTHONPATH="${TEMP_DIR}" "${INST_VENV}/bin/python" -m trixy_installer \
- --mode "${MODE}" \
- --version "${VERSION}" \
- --source-archive "${SCRIPT_DIR}/${SOURCE_TAR}"
- echo ""
- echo -e "${GREEN}Installer beendet.${NC}"
- INSTALL_BODY
- chmod +x "${install_sh}"
- echo " -> install.sh generiert"
- }
- # --- Build zusammenfassen ---
- print_summary() {
- local mode="$1"
- local version="$2"
- local release_dir="$3"
- echo ""
- echo "========================================"
- echo " Build abgeschlossen: ${mode} v${BUILD_FULL_VERSION:-${version}}"
- echo "========================================"
- echo ""
- echo " Version: ${BUILD_VERSION:-${version}}"
- echo " Build: ${BUILD_NUMBER:-?}"
- echo " FullVersion: ${BUILD_FULL_VERSION:-${version}}"
- echo " Git: ${BUILD_GIT_REV:-unknown} (${BUILD_GIT_STATUS:-unknown})"
- echo ""
- echo " Release-Verzeichnis: ${release_dir}"
- echo ""
- ls -lh "${release_dir}/"
- echo ""
- echo " Deployment:"
- echo " 1. Release-Verzeichnis auf Zielgeraet kopieren"
- echo " 2. chmod +x install.sh"
- echo " 3. ./install.sh"
- echo ""
- }
|