#!/usr/bin/env bash
# ============================================================
# đź§© AppImage Manager - GUI avec Zenity + Sync cloud + scheduling
# Version : 9.0 : appimage-manager-9.sh
# Auteur : ChatGPT (OpenAI)
# Licence : MIT
# ============================================================
set -euo pipefail
IFS=$'\n\t'
# --- Paths & Globals ---
INSTALL_DIR="$HOME/Applications"
DESKTOP_DIR="$HOME/.local/share/applications"
CONFIG_DIR="$HOME/.config/appimage-manager"
LOG_DIR="$HOME/.local/share/appimage-manager/logs"
BACKUP_DIR="$HOME/.local/share/appimage-manager/backups"
LOG_FILE="$LOG_DIR/appimage-manager.log"
CLOUD_CFG="$CONFIG_DIR/cloud.conf"
SYSTEMD_UNIT_DIR="$HOME/.config/systemd/user" # user systemd units
SELF_PATH="$(readlink -f "$0")"
CURRENT_VERSION="9.0"
REPO_URL="https://raw.githubusercontent.com/tonpseudo/appimage-manager/main/appimage-manager-gui.sh" # <-- replace with your repo raw url if desired
mkdir -p "$INSTALL_DIR" "$DESKTOP_DIR" "$CONFIG_DIR" "$LOG_DIR" "$BACKUP_DIR" "$SYSTEMD_UNIT_DIR"
# --- Helpers ---
log() {
local TIMESTAMP
TIMESTAMP="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[$TIMESTAMP] $1" | tee -a "$LOG_FILE" >/dev/null
}
# --- Dependency check ---
required_cmds=(zenity wget curl jq tar desktop-file-validate)
for cmd in "${required_cmds[@]}"; do
if ! command -v "$cmd" &>/dev/null; then
echo "Erreur : '$cmd' n'est pas installé. Installez-le d'abord (ex: sudo apt install zenity wget curl jq tar desktop-file-utils)" >&2
zenity --error --text="Erreur : '$cmd' n'est pas installé. Installez-le d'abord (ex: sudo apt install zenity wget curl jq tar desktop-file-utils)"
exit 1
fi
done
# rclone optional
if command -v rclone &>/dev/null; then
RCLONE_AVAILABLE=1
else
RCLONE_AVAILABLE=0
fi
# --- Auto-update for manager ---
check_for_update() {
if ! command -v curl &>/dev/null; then return; fi
local LATEST
LATEST=$(curl -fsSL "$REPO_URL" 2>/dev/null | grep -m1 "Version :" | awk '{print $3}' || true)
if [[ -n "$LATEST" && "$LATEST" != "$CURRENT_VERSION" ]]; then
if zenity --question --title="Mise Ă jour disponible" --text="Version $LATEST disponible. Mettre Ă jour ?" ; then
TMP=$(mktemp)
wget -qO "$TMP" "$REPO_URL"
sudo cp "$TMP" "$SELF_PATH"
sudo chmod +x "$SELF_PATH"
log "Manager updated $CURRENT_VERSION -> $LATEST"
zenity --info --text="Mise à jour effectuée. Relance du gestionnaire..."
exec "$SELF_PATH"
fi
fi
}
# --- create .desktop shortcut for appimage ---
create_shortcut() {
local APP_NAME="$1"
local APP_PATH="$INSTALL_DIR/${APP_NAME}.AppImage"
local ICON_PATH="$INSTALL_DIR/${APP_NAME}.png"
if [[ -x "$APP_PATH" ]]; then
# try extract icon
if "$APP_PATH" --appimage-extract &>/dev/null; then
ICON_FILE=$(find squashfs-root -type f \( -iname '*.png' -o -iname '*.svg' \) | head -n1 || true)
[[ -n "$ICON_FILE" ]] && cp "$ICON_FILE" "$ICON_PATH"
rm -rf squashfs-root
fi
fi
cat >"$DESKTOP_DIR/${APP_NAME}.desktop" <<EOF
[Desktop Entry]
Name=${APP_NAME}
Exec=${APP_PATH}
Icon=${ICON_PATH:-$APP_PATH}
Type=Application
Categories=Utility;
Comment=AppImage: ${APP_NAME}
Terminal=false
EOF
desktop-file-validate "$DESKTOP_DIR/${APP_NAME}.desktop" || true
chmod +x "$DESKTOP_DIR/${APP_NAME}.desktop"
update-desktop-database "$DESKTOP_DIR" &>/dev/null || true
log "Shortcut created: ${APP_NAME}"
}
# --- Install from GitHub releases (auto find AppImage asset) ---
install_from_github() {
local REPO APP_URL APP_NAME
REPO=$(zenity --entry --title="Installation depuis GitHub" --text="Dépôt GitHub (ex: obsidianmd/obsidian-releases) :") || return
zenity --info --text="Recherche de la dernière release pour $REPO..."
API="https://api.github.com/repos/${REPO}/releases/latest"
APP_URL=$(curl -s "$API" | jq -r '.assets[] | select(.name|test("AppImage$")) | .browser_download_url' | head -n1 || true)
if [[ -z "$APP_URL" || "$APP_URL" == "null" ]]; then
zenity --error --text="Aucune AppImage trouvée pour $REPO"
log "install_from_github: no AppImage for $REPO"
return
fi
APP_NAME=$(basename "$APP_URL" | sed 's/\.AppImage$//; s/[^a-zA-Z0-9._-]//g')
wget -q --show-progress -O "$INSTALL_DIR/${APP_NAME}.AppImage" "$APP_URL"
chmod +x "$INSTALL_DIR/${APP_NAME}.AppImage"
create_shortcut "$APP_NAME"
log "Installed $APP_NAME from GitHub ($REPO)"
zenity --info --text="✅ $APP_NAME installĂ©."
}
# --- Install via direct URL ---
install_manual() {
local NAME URL
NAME=$(zenity --entry --title="Installation manuelle" --text="Nom de l'application :") || return
URL=$(zenity --entry --title="Installation manuelle" --text="URL directe de l'AppImage :") || return
wget -q --show-progress -O "$INSTALL_DIR/${NAME}.AppImage" "$URL"
chmod +x "$INSTALL_DIR/${NAME}.AppImage"
create_shortcut "$NAME"
log "Installed $NAME from URL"
zenity --info --text="✅ $NAME installĂ©."
}
# --- Update AppImage (AppImageUpdate) ---
update_appimage() {
local NAME APP_PATH TMP_UPD
NAME=$(zenity --entry --title="Mise Ă jour" --text="Nom de l'application Ă mettre Ă jour :") || return
APP_PATH="$INSTALL_DIR/${NAME}.AppImage"
if [[ ! -f "$APP_PATH" ]]; then
zenity --error --text="Application introuvable."
return
fi
zenity --info --text="Mise Ă jour en cours pour $NAME..."
TMP_UPD=$(mktemp)
wget -qO "$TMP_UPD" "https://github.com/AppImage/AppImageUpdate/releases/latest/download/appimageupdatetool-x86_64.AppImage"
chmod +x "$TMP_UPD"
"$TMP_UPD" "$APP_PATH" || zenity --warning --text="Mise à jour non supportée pour cette AppImage"
rm -f "$TMP_UPD"
log "Updated $NAME"
zenity --info --text="✅ Mise Ă jour terminĂ©e pour $NAME"
}
# --- Remove AppImage ---
remove_appimage() {
local NAME
NAME=$(zenity --entry --title="Désinstallation" --text="Nom de l'application à supprimer :") || return
rm -f "$INSTALL_DIR/${NAME}.AppImage" "$INSTALL_DIR/${NAME}.png" "$DESKTOP_DIR/${NAME}.desktop"
update-desktop-database "$DESKTOP_DIR" &>/dev/null || true
log "Removed $NAME"
zenity --info --text="✅ $NAME supprimĂ©."
}
# --- List installed AppImages ---
list_appimages() {
local LIST
LIST=$(ls "$INSTALL_DIR"/*.AppImage 2>/dev/null | xargs -n1 basename | sed 's/\.AppImage$//' || true)
if [[ -z "$LIST" ]]; then
zenity --info --text="Aucune AppImage installée."
else
zenity --list --title="AppImages installées" --column="Nom" $LIST
fi
}
# --- Logging viewer ---
show_logs() {
touch "$LOG_FILE"
zenity --text-info --title="Logs AppImage Manager" --filename="$LOG_FILE" --width=900 --height=500 || true
}
# --- Local backup ---
backup_all() {
local BACKUP_FILE="$BACKUP_DIR/appimage-backup-$(date +%Y%m%d_%H%M%S).tar.gz"
# create tar with relative paths under $HOME for safe restore
tar -C "$HOME" -czf "$BACKUP_FILE" "$(realpath --relative-to="$HOME" "$INSTALL_DIR")" "$(realpath --relative-to="$HOME" "$DESKTOP_DIR")" "$(realpath --relative-to="$HOME" "$LOG_DIR")"
log "Created backup $BACKUP_FILE"
zenity --info --text="✅ Sauvegarde créée : $BACKUP_FILE"
}
# --- Restore backup ---
restore_backup() {
local FILE
FILE=$(zenity --file-selection --title="Choisir une sauvegarde (.tar.gz)" --file-filter="Archives | *.tar.gz") || return
tar -xzf "$FILE" -C "$HOME"
log "Restored backup from $FILE"
zenity --info --text="✅ Restauration terminĂ©e."
}
# --- Cloud configuration (rclone remote name + optional remote path) ---
configure_cloud() {
if [[ $RCLONE_AVAILABLE -eq 0 ]]; then
if ! zenity --question --title="rclone manquant" --text="rclone n'est pas installé. Voulez-vous l'installer maintenant ?" ; then
return
fi
curl https://rclone.org/install.sh | sudo bash
if ! command -v rclone &>/dev/null; then
zenity --error --text="Impossible d'installer rclone automatiquement. Installez-le manuellement." ; return
fi
RCLONE_AVAILABLE=1
fi
zenity --info --text="Configurez d'abord un remote via : rclone config\nCréez un remote (ex: mydrive)."
local REMOTE REMOTE_PATH
REMOTE=$(zenity --entry --title="Nom du remote rclone" --text="Entrez le nom du remote (ex: mydrive) :") || return
REMOTE_PATH=$(zenity --entry --title="Chemin distant" --text="Entrez le chemin distant Ă utiliser (ex: AppImage-Backups) :") || return
cat > "$CLOUD_CFG" <<EOF
REMOTE=${REMOTE}
REMOTE_PATH=${REMOTE_PATH}
EOF
log "Cloud configured: ${REMOTE}:${REMOTE_PATH}"
zenity --info --text="✅ Configuration cloud enregistrĂ©e : ${REMOTE}:${REMOTE_PATH}"
}
# --- Sync latest backup to cloud ---
sync_backup_to_cloud() {
if [[ $RCLONE_AVAILABLE -eq 0 ]]; then
zenity --error --text="rclone n'est pas installé. Configurez rclone d'abord." ; return
fi
if [[ ! -f "$CLOUD_CFG" ]]; then
zenity --error --text="Configuration cloud absente. Configurez-la d'abord." ; return
fi
source "$CLOUD_CFG"
local LATEST
LATEST=$(ls -1t "$BACKUP_DIR"/appimage-backup-*.tar.gz 2>/dev/null | head -n1 || true)
if [[ -z "$LATEST" ]]; then
zenity --error --text="Aucune sauvegarde locale trouvée. Créez-en une d'abord." ; return
fi
zenity --info --text="Envoi de $LATEST vers ${REMOTE}:${REMOTE_PATH} ..."
log "Cloud sync: $LATEST -> ${REMOTE}:${REMOTE_PATH}"
rclone copy "$LATEST" "${REMOTE}:${REMOTE_PATH}" --progress || {
zenity --error --text="Erreur pendant la synchronisation cloud. Voir logs."
log "Cloud sync failed for $LATEST"
return
}
zenity --info --text="✅ Synchronisation terminĂ©e."
log "Cloud sync completed: $LATEST"
}
# --- Cron scheduling (weekly) ---
enable_weekly_cron() {
local CMD="$SELF_PATH --auto-backup"
# add if not exists
(crontab -l 2>/dev/null | grep -v -F "$CMD" || true; echo "30 3 * * 0 $CMD") | crontab -
log "Weekly cron enabled"
zenity --info --text="✅ Sauvegarde hebdomadaire (cron) activĂ©e (dimanche 03:30)."
}
disable_weekly_cron() {
local CMD="$SELF_PATH --auto-backup"
crontab -l 2>/dev/null | grep -v -F "$CMD" | crontab -
log "Weekly cron disabled"
zenity --info --text="✅ Sauvegarde hebdomadaire (cron) dĂ©sactivĂ©e."
}
# --- Systemd user timer support (optional) ---
install_systemd_timer() {
# create service and timer in user systemd
cat > "$SYSTEMD_UNIT_DIR/appimage-manager-backup.service" <<EOF
[Unit]
Description=AppImage Manager backup service
[Service]
Type=oneshot
ExecStart=${SELF_PATH} --auto-backup
EOF
cat > "$SYSTEMD_UNIT_DIR/appimage-manager-backup.timer" <<EOF
[Unit]
Description=Run AppImage Manager backup weekly
[Timer]
OnCalendar=weekly
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now appimage-manager-backup.timer
log "Systemd user timer installed & started"
zenity --info --text="✅ Systemd timer installĂ© et activĂ© (user)."
}
remove_systemd_timer() {
systemctl --user disable --now appimage-manager-backup.timer || true
rm -f "$SYSTEMD_UNIT_DIR/appimage-manager-backup.service" "$SYSTEMD_UNIT_DIR/appimage-manager-backup.timer"
systemctl --user daemon-reload
log "Systemd user timer removed"
zenity --info --text="✅ Systemd timer supprimĂ©."
}
# --- Non interactive backup mode for cron/systemd: --auto-backup ---
if [[ "${1:-}" == "--auto-backup" ]]; then
backup_all
# sync if cloud configured
if [[ -f "$CLOUD_CFG" && $RCLONE_AVAILABLE -eq 1 ]]; then
source "$CLOUD_CFG"
LATEST=$(ls -1t "$BACKUP_DIR"/appimage-backup-*.tar.gz 2>/dev/null | head -n1 || true)
if [[ -n "$LATEST" ]]; then
rclone copy "$LATEST" "${REMOTE}:${REMOTE_PATH}"
log "Auto backup & sync completed: $LATEST"
else
log "Auto backup: no backup file to sync"
fi
fi
exit 0
fi
# --- Initial check for update (non-blocking) ---
check_for_update &>/dev/null || true
# --- GUI menu loop ---
while true; do
CHOICE=$(zenity --list --title="đź§© AppImage Manager v$CURRENT_VERSION" \
--column="Action" --width=620 --height=550 \
"Installer depuis GitHub" \
"Installer via URL directe" \
"Mettre Ă jour une AppImage" \
"Supprimer une AppImage" \
"Lister les AppImages installées" \
"Créer une sauvegarde complète (local)" \
"Restaurer une sauvegarde" \
"Configurer la synchronisation cloud (rclone)" \
"Envoyer la dernière sauvegarde vers le cloud" \
"Activer sauvegarde hebdomadaire (cron)" \
"Désactiver sauvegarde hebdomadaire (cron)" \
"Installer systemd timer (user)" \
"Supprimer systemd timer (user)" \
"Afficher l'historique (logs)" \
"Mettre Ă jour le gestionnaire lui-mĂŞme" \
"Quitter") || exit 0
case "$CHOICE" in
"Installer depuis GitHub") install_from_github ;;
"Installer via URL directe") install_manual ;;
"Mettre Ă jour une AppImage") update_appimage ;;
"Supprimer une AppImage") remove_appimage ;;
"Lister les AppImages installées") list_appimages ;;
"Créer une sauvegarde complète (local)") backup_all ;;
"Restaurer une sauvegarde") restore_backup ;;
"Configurer la synchronisation cloud (rclone)") configure_cloud ;;
"Envoyer la dernière sauvegarde vers le cloud") sync_backup_to_cloud ;;
"Activer sauvegarde hebdomadaire (cron)") enable_weekly_cron ;;
"Désactiver sauvegarde hebdomadaire (cron)") disable_weekly_cron ;;
"Installer systemd timer (user)") install_systemd_timer ;;
"Supprimer systemd timer (user)") remove_systemd_timer ;;
"Afficher l'historique (logs)") show_logs ;;
"Mettre Ă jour le gestionnaire lui-mĂŞme") check_for_update ;;
"Quitter") log "Manager closed by user"; exit 0 ;;
esac
done
Aucun commentaire:
Enregistrer un commentaire