v2.2.2
This commit is contained in:
parent
f3e39fb8e6
commit
e5f5d32aab
124 changed files with 11892 additions and 2461 deletions
312
packaging/zupt-installer-header.sh
Normal file
312
packaging/zupt-installer-header.sh
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# ╔════════════════════════════════════════════════════════════════════╗
|
||||
# ║ ZUPT 2.2.2 + ZUPT-GUI 1.1.1 — UNIVERSAL LINUX INSTALLER ║
|
||||
# ║ ║
|
||||
# ║ One script, all distributions. Self-extracting. No internet ║
|
||||
# ║ needed for the package install (only for Qt6 dependency). ║
|
||||
# ║ ║
|
||||
# ║ Usage: sudo bash zupt-installer.sh ║
|
||||
# ║ Or: sudo bash zupt-installer.sh --gui-only ║
|
||||
# ║ Or: sudo bash zupt-installer.sh --cli-only ║
|
||||
# ║ Or: sudo bash zupt-installer.sh --appimage ║
|
||||
# ║ Or: sudo bash zupt-installer.sh --uninstall ║
|
||||
# ╚════════════════════════════════════════════════════════════════════╝
|
||||
set -e
|
||||
|
||||
VERSION="2.2.2"
|
||||
GUI_VERSION="1.1.1"
|
||||
EXTRACT_DIR=""
|
||||
|
||||
cleanup() {
|
||||
[ -n "$EXTRACT_DIR" ] && [ -d "$EXTRACT_DIR" ] && rm -rf "$EXTRACT_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# ── Color output (if terminal supports) ─────────────────────────────
|
||||
if [ -t 1 ]; then
|
||||
BOLD='\033[1m'; CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
|
||||
else
|
||||
BOLD=''; CYAN=''; GREEN=''; YELLOW=''; RED=''; RESET=''
|
||||
fi
|
||||
|
||||
step() { echo -e "${CYAN}${BOLD}═══ $* ═══${RESET}"; }
|
||||
ok() { echo -e "${GREEN}✓${RESET} $*"; }
|
||||
warn() { echo -e "${YELLOW}⚠${RESET} $*"; }
|
||||
err() { echo -e "${RED}✗${RESET} $*" >&2; }
|
||||
die() { err "$*"; exit 1; }
|
||||
|
||||
# ── Parse arguments ─────────────────────────────────────────────────
|
||||
MODE="full"
|
||||
case "${1:-}" in
|
||||
--cli-only) MODE="cli" ;;
|
||||
--gui-only) MODE="gui" ;;
|
||||
--appimage) MODE="appimage" ;;
|
||||
--uninstall) MODE="uninstall" ;;
|
||||
--help|-h)
|
||||
sed -n '2,15p' "$0" | sed 's/^# //'
|
||||
exit 0 ;;
|
||||
"") MODE="full" ;;
|
||||
*) die "Unknown option: $1. Use --help for options." ;;
|
||||
esac
|
||||
|
||||
# ── Root check (except for AppImage) ────────────────────────────────
|
||||
if [ "$MODE" != "appimage" ] && [ "$EUID" -ne 0 ]; then
|
||||
die "Run with sudo: sudo bash $0 ${1:-}"
|
||||
fi
|
||||
|
||||
# ── Distro detection ────────────────────────────────────────────────
|
||||
detect_distro() {
|
||||
if [ -f /etc/os-release ]; then
|
||||
# Use subshell to prevent /etc/os-release VERSION from clobbering ours
|
||||
DISTRO=$(. /etc/os-release; echo "${ID:-unknown}")
|
||||
DISTRO_LIKE=$(. /etc/os-release; echo "${ID_LIKE:-}")
|
||||
DISTRO_NAME=$(. /etc/os-release; echo "${PRETTY_NAME:-$DISTRO}")
|
||||
else
|
||||
DISTRO="unknown"; DISTRO_LIKE=""; DISTRO_NAME="Unknown Linux"
|
||||
fi
|
||||
}
|
||||
detect_distro
|
||||
|
||||
# Categorize
|
||||
DEB_BASED=0; RPM_BASED=0; ARCH_BASED=0; ALPINE=0
|
||||
case "$DISTRO" in
|
||||
debian|ubuntu|linuxmint|pop|elementary|kali|raspbian|deepin|zorin) DEB_BASED=1 ;;
|
||||
fedora|rhel|centos|rocky|almalinux|ol) RPM_BASED=1 ;;
|
||||
opensuse*|suse|sles) RPM_BASED=1 ;;
|
||||
arch|manjaro|endeavouros|garuda|artix) ARCH_BASED=1 ;;
|
||||
alpine) ALPINE=1 ;;
|
||||
*)
|
||||
case "$DISTRO_LIKE" in
|
||||
*debian*|*ubuntu*) DEB_BASED=1 ;;
|
||||
*fedora*|*rhel*|*suse*) RPM_BASED=1 ;;
|
||||
*arch*) ARCH_BASED=1 ;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
# ── Self-extract embedded payload ───────────────────────────────────
|
||||
extract_payload() {
|
||||
EXTRACT_DIR=$(mktemp -d -t zupt-installer.XXXXXX)
|
||||
# Find the line number where the payload starts (marker: __PAYLOAD_BELOW__)
|
||||
local marker_line
|
||||
marker_line=$(grep -an '^__PAYLOAD_BELOW__$' "$0" | head -1 | cut -d: -f1)
|
||||
[ -z "$marker_line" ] && die "Installer is corrupt — no payload marker."
|
||||
# Skip past marker line, decode base64 → tar
|
||||
tail -n +$((marker_line + 1)) "$0" | base64 -d | tar -xzC "$EXTRACT_DIR"
|
||||
[ -f "$EXTRACT_DIR/zupt_${VERSION}_amd64.deb" ] || die "Payload extraction failed."
|
||||
}
|
||||
|
||||
# ── Install Qt6 binding (needs network) ─────────────────────────────
|
||||
install_qt6() {
|
||||
if python3 -c 'import PyQt6.QtWidgets' 2>/dev/null \
|
||||
|| python3 -c 'import PySide6.QtWidgets' 2>/dev/null; then
|
||||
ok "Qt6 binding already installed"
|
||||
return 0
|
||||
fi
|
||||
step "Installing Python 3 + Qt6 binding"
|
||||
if [ $DEB_BASED -eq 1 ]; then
|
||||
apt-get update -qq || warn "apt-get update failed (network?); continuing anyway"
|
||||
apt-get install -y python3 python3-pyqt6 \
|
||||
|| apt-get install -y python3 python3-pyside6 \
|
||||
|| warn "Could not install Qt6 binding via apt"
|
||||
elif [ $RPM_BASED -eq 1 ]; then
|
||||
case "$DISTRO" in
|
||||
opensuse*|suse|sles)
|
||||
zypper --non-interactive install python3 python3-pyqt6 \
|
||||
|| zypper --non-interactive install python3 python3-PyQt6 \
|
||||
|| zypper --non-interactive install python3 python3-pyside6 ;;
|
||||
*)
|
||||
if command -v dnf >/dev/null; then
|
||||
dnf install -y python3 python3-pyqt6 \
|
||||
|| dnf install -y python3 python3-pyside6
|
||||
else
|
||||
yum install -y python3 python3-pyqt6 \
|
||||
|| yum install -y python3 python3-pyside6
|
||||
fi ;;
|
||||
esac
|
||||
elif [ $ARCH_BASED -eq 1 ]; then
|
||||
pacman -Sy --noconfirm python python-pyqt6 \
|
||||
|| pacman -Sy --noconfirm python python-pyside6
|
||||
elif [ $ALPINE -eq 1 ]; then
|
||||
apk add python3 py3-pyqt6 || apk add python3 py3-pyside6
|
||||
else
|
||||
warn "Unknown distribution. Trying pip fallback..."
|
||||
if command -v pip3 >/dev/null; then
|
||||
pip3 install --break-system-packages PySide6 2>/dev/null \
|
||||
|| pip3 install --user PySide6
|
||||
else
|
||||
warn "No pip3. Install python3-pyqt6 manually."
|
||||
fi
|
||||
fi
|
||||
if python3 -c 'import PyQt6.QtWidgets' 2>/dev/null \
|
||||
|| python3 -c 'import PySide6.QtWidgets' 2>/dev/null; then
|
||||
ok "Qt6 binding installed"
|
||||
else
|
||||
warn "Qt6 binding install failed. The CLI will still work; the GUI won't."
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Install zupt CLI ────────────────────────────────────────────────
|
||||
install_cli() {
|
||||
step "Installing zupt CLI ${VERSION}"
|
||||
if [ $DEB_BASED -eq 1 ]; then
|
||||
dpkg -i "$EXTRACT_DIR/zupt_${VERSION}_amd64.deb" 2>&1 \
|
||||
| grep -v '^Selecting\|^Preparing\|^Unpacking\|^Setting up\|^Processing' || true
|
||||
# Resolve any missing libs from apt
|
||||
apt-get -f install -y 2>/dev/null || true
|
||||
ok "zupt CLI installed: $(zupt version 2>&1 | head -1)"
|
||||
elif [ $RPM_BASED -eq 1 ]; then
|
||||
local rpmtar="$EXTRACT_DIR/zupt-${VERSION}.srpm.tar.gz"
|
||||
if command -v rpmbuild >/dev/null; then
|
||||
local rpmroot=$(mktemp -d)
|
||||
tar -xzC "$rpmroot" -f "$rpmtar"
|
||||
rpmbuild --define "_topdir $rpmroot" -bb "$rpmroot/SPECS/zupt.spec"
|
||||
rpm -Uvh --force "$rpmroot"/RPMS/x86_64/zupt-*.rpm
|
||||
rm -rf "$rpmroot"
|
||||
else
|
||||
# rpmbuild not available — fall back to tarball
|
||||
warn "rpmbuild missing — using portable binary install"
|
||||
local appdir="$EXTRACT_DIR/zupt-${VERSION}-x86_64.AppDir.tar.gz"
|
||||
mkdir -p /opt
|
||||
tar -xzC /opt -f "$appdir"
|
||||
ln -sf "/opt/zupt-${VERSION}-x86_64.AppDir/AppRun" /usr/local/bin/zupt
|
||||
ok "zupt CLI installed (portable mode)"
|
||||
fi
|
||||
else
|
||||
# Universal fallback: portable AppDir tarball
|
||||
warn "No native package format for $DISTRO. Using portable binary."
|
||||
mkdir -p /opt /usr/local/bin
|
||||
tar -xzC /opt -f "$EXTRACT_DIR/zupt-${VERSION}-x86_64.AppDir.tar.gz"
|
||||
ln -sf "/opt/zupt-${VERSION}-x86_64.AppDir/AppRun" /usr/local/bin/zupt
|
||||
ok "zupt CLI installed (portable mode)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Install zupt-gui ────────────────────────────────────────────────
|
||||
install_gui() {
|
||||
step "Installing zupt-gui ${GUI_VERSION}"
|
||||
if [ $DEB_BASED -eq 1 ]; then
|
||||
dpkg -i "$EXTRACT_DIR/zupt-gui_${GUI_VERSION}_all.deb" 2>&1 \
|
||||
| grep -v '^Selecting\|^Preparing\|^Unpacking\|^Setting up\|^Processing' || true
|
||||
apt-get -f install -y 2>/dev/null || true
|
||||
ok "zupt-gui installed"
|
||||
elif [ $RPM_BASED -eq 1 ]; then
|
||||
local rpmtar="$EXTRACT_DIR/zupt-gui-${GUI_VERSION}.srpm.tar.gz"
|
||||
if command -v rpmbuild >/dev/null; then
|
||||
local rpmroot=$(mktemp -d)
|
||||
tar -xzC "$rpmroot" -f "$rpmtar"
|
||||
rpmbuild --define "_topdir $rpmroot" -bb "$rpmroot/SPECS/zupt-gui.spec"
|
||||
rpm -Uvh --force "$rpmroot"/RPMS/noarch/zupt-gui-*.rpm
|
||||
rm -rf "$rpmroot"
|
||||
else
|
||||
warn "rpmbuild missing — using portable mode"
|
||||
mkdir -p /opt /usr/local/bin
|
||||
tar -xzC /opt -f "$EXTRACT_DIR/Zupt-GUI-${GUI_VERSION}-x86_64.AppDir.tar.gz"
|
||||
ln -sf "/opt/zupt-gui.AppDir/AppRun" /usr/local/bin/zupt-gui
|
||||
ok "zupt-gui installed (portable)"
|
||||
fi
|
||||
else
|
||||
# Portable
|
||||
mkdir -p /opt /usr/local/bin
|
||||
tar -xzC /opt -f "$EXTRACT_DIR/Zupt-GUI-${GUI_VERSION}-x86_64.AppDir.tar.gz"
|
||||
ln -sf "/opt/zupt-gui.AppDir/AppRun" /usr/local/bin/zupt-gui
|
||||
# Desktop integration if possible
|
||||
if [ -d /usr/share/applications ]; then
|
||||
cp /opt/zupt-gui.AppDir/zupt-gui.desktop /usr/share/applications/ 2>/dev/null || true
|
||||
fi
|
||||
ok "zupt-gui installed (portable)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── AppImage extract (no install) ───────────────────────────────────
|
||||
install_appimage() {
|
||||
step "Extracting AppImage to current directory"
|
||||
local target="${PWD}/zupt-portable"
|
||||
mkdir -p "$target"
|
||||
tar -xzC "$target" -f "$EXTRACT_DIR/zupt-${VERSION}-x86_64.AppDir.tar.gz"
|
||||
tar -xzC "$target" -f "$EXTRACT_DIR/Zupt-GUI-${GUI_VERSION}-x86_64.AppDir.tar.gz"
|
||||
cat > "$target/zupt" <<EOF
|
||||
#!/bin/sh
|
||||
exec "$target/zupt-${VERSION}-x86_64.AppDir/AppRun" "\$@"
|
||||
EOF
|
||||
cat > "$target/zupt-gui" <<EOF
|
||||
#!/bin/sh
|
||||
exec "$target/zupt-gui.AppDir/AppRun" "\$@"
|
||||
EOF
|
||||
chmod +x "$target/zupt" "$target/zupt-gui"
|
||||
ok "Portable install at: $target"
|
||||
echo "Run: $target/zupt help"
|
||||
echo " $target/zupt-gui"
|
||||
echo
|
||||
warn "Portable mode still needs Python 3 + PyQt6 system-wide."
|
||||
warn "To install Qt6: sudo apt install python3-pyqt6 (or equivalent)"
|
||||
}
|
||||
|
||||
# ── Uninstall ───────────────────────────────────────────────────────
|
||||
do_uninstall() {
|
||||
step "Uninstalling zupt + zupt-gui"
|
||||
if [ $DEB_BASED -eq 1 ]; then
|
||||
dpkg -r zupt-gui 2>/dev/null || true
|
||||
dpkg -r zupt 2>/dev/null || true
|
||||
elif [ $RPM_BASED -eq 1 ]; then
|
||||
rpm -e zupt-gui 2>/dev/null || true
|
||||
rpm -e zupt 2>/dev/null || true
|
||||
fi
|
||||
rm -rf /opt/zupt-2.2.2-x86_64.AppDir /opt/zupt-gui.AppDir 2>/dev/null
|
||||
rm -f /usr/local/bin/zupt /usr/local/bin/zupt-gui 2>/dev/null
|
||||
rm -f /usr/share/applications/zupt-gui.desktop 2>/dev/null
|
||||
ok "Uninstall complete"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# MAIN
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
cat <<HEADER
|
||||
${BOLD}${CYAN}
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ ZUPT ${VERSION} + ZUPT-GUI ${GUI_VERSION} ║
|
||||
║ Post-quantum backup compression — Linux installer ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
${RESET}
|
||||
Distribution detected: ${BOLD}${DISTRO_NAME}${RESET}
|
||||
Mode: ${BOLD}${MODE}${RESET}
|
||||
|
||||
HEADER
|
||||
|
||||
if [ "$MODE" = "uninstall" ]; then
|
||||
do_uninstall
|
||||
exit 0
|
||||
fi
|
||||
|
||||
extract_payload
|
||||
|
||||
case "$MODE" in
|
||||
cli)
|
||||
install_cli ;;
|
||||
gui)
|
||||
install_qt6
|
||||
install_gui ;;
|
||||
full)
|
||||
install_qt6
|
||||
install_cli
|
||||
install_gui ;;
|
||||
appimage)
|
||||
install_appimage ;;
|
||||
esac
|
||||
|
||||
echo
|
||||
step "Installation complete"
|
||||
case "$MODE" in
|
||||
cli|full) echo " Run: zupt help" ;;
|
||||
esac
|
||||
case "$MODE" in
|
||||
gui|full) echo " Run: zupt-gui (or find 'Zupt GUI' in your applications menu)" ;;
|
||||
esac
|
||||
case "$MODE" in
|
||||
appimage) echo " Run: ./zupt-portable/zupt help" ;;
|
||||
esac
|
||||
echo
|
||||
|
||||
exit 0
|
||||
Loading…
Reference in a new issue