#!/usr/bin/env bash set -euo pipefail # Script directory and backups directory setup SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BACKUP_DIR="${SCRIPT_DIR}/backups" mkdir -p "${BACKUP_DIR}" # Pre-flight requirements check for core tools for cmd in zip awk find mktemp; do if ! command -v "${cmd}" >/dev/null 2>&1; then echo "Error: Required tool '${cmd}' is not installed or not in PATH." >&2 exit 1 fi done # Determine device hostname using robust fallbacks (no inetutils required) HOSTNAME_STR="" if command -v hostname >/dev/null 2>&1; then HOSTNAME_STR="$(hostname -s 2>/dev/null || hostname 2>/dev/null || true)" fi if [ -z "${HOSTNAME_STR}" ]; then HOSTNAME_STR="${HOST:-${HOSTNAME:-}}" fi if [ -z "${HOSTNAME_STR}" ] && [ -r /proc/sys/kernel/hostname ]; then HOSTNAME_STR="$(cat /proc/sys/kernel/hostname 2>/dev/null || true)" fi if [ -z "${HOSTNAME_STR}" ] && [ -r /etc/hostname ]; then HOSTNAME_STR="$(cat /etc/hostname 2>/dev/null || true)" fi HOSTNAME_STR="$(echo "${HOSTNAME_STR:-}" | xargs)" [ -z "${HOSTNAME_STR}" ] && HOSTNAME_STR="host" TIMESTAMP="$(date +"%Y%m%d_%H%M%S")" DEFAULT_FILENAME="konsole_backup_${HOSTNAME_STR}_${TIMESTAMP}.zip" # Temporary staging directory TEMP_DIR="$(mktemp -d)" trap 'rm -rf "${TEMP_DIR}"' EXIT echo "=== Starting Konsole Configuration Backup ===" # Define target paths relative to HOME CONFIG_PATHS=( ".config/konsolerc" ".local/share/konsole" ".local/share/kxmlgui5/konsole" ".config/konsole.notifyrc" ".config/konsolesshconfig" ) COPIED_COUNT=0 for rel_path in "${CONFIG_PATHS[@]}"; do src_path="${HOME}/${rel_path}" dest_path="${TEMP_DIR}/${rel_path}" if [ -e "${src_path}" ]; then mkdir -p "$(dirname "${dest_path}")" if [ "${rel_path}" = ".config/konsolerc" ]; then # Filter out [MainWindow] section to avoid machine-specific geometry/monitor noise awk ' /^\[MainWindow\]/ { skip=1; next } /^\[.*\]/ { skip=0 } !skip { print } ' "${src_path}" > "${dest_path}" echo " [+] Staged & Sanitized: ~/${rel_path}" else cp -a "${src_path}" "${dest_path}" echo " [+] Staged: ~/${rel_path}" fi COPIED_COUNT=$((COPIED_COUNT + 1)) else echo " [-] Skipped (not found): ~/${rel_path}" fi done if [ "${COPIED_COUNT}" -eq 0 ]; then echo "Error: No Konsole configuration files found to backup." exit 1 fi # Determine appropriate interactive input source get_input() { local prompt_msg="$1" local var_name="$2" local default_val="${3:-}" if [ -t 0 ]; then read -r -p "${prompt_msg}" "$var_name" || true elif [ -c /dev/tty ] && [ -r /dev/tty ] && { : < /dev/tty; } 2>/dev/null; then read -r -p "${prompt_msg}" "$var_name" < /dev/tty || true else read -r -p "${prompt_msg}" "$var_name" || true fi if [ -z "${!var_name:-}" ] && [ -n "${default_val}" ]; then eval "${var_name}=\"${default_val}\"" fi } echo "" # Prompt user for backup filename (like ssh-keygen) CHOSEN_NAME="" get_input "Enter backup filename [default: ${DEFAULT_FILENAME}]: " CHOSEN_NAME "${DEFAULT_FILENAME}" CHOSEN_NAME="$(echo "${CHOSEN_NAME:-}" | xargs)" if [ -z "${CHOSEN_NAME}" ]; then ZIP_FILENAME="${DEFAULT_FILENAME}" else if [[ "${CHOSEN_NAME}" != *.zip ]]; then ZIP_FILENAME="${CHOSEN_NAME}.zip" else ZIP_FILENAME="${CHOSEN_NAME}" fi fi ZIP_FILEPATH="${BACKUP_DIR}/${ZIP_FILENAME}" # Create zip file from staging area echo "Creating backup archive: ${ZIP_FILENAME}..." ( cd "${TEMP_DIR}" zip -q -r "${ZIP_FILEPATH}" . ) echo "Backup successfully saved to: ${ZIP_FILEPATH}" # Retention Policy: keep at most 10 recent backup zip files MAX_BACKUPS=10 mapfile -t BACKUP_FILES < <(find "${BACKUP_DIR}" -maxdepth 1 -name "konsole_backup_*.zip" | sort -r) TOTAL_BACKUPS="${#BACKUP_FILES[@]}" if [ "${TOTAL_BACKUPS}" -gt "${MAX_BACKUPS}" ]; then echo "Cleaning up older backups (keeping top ${MAX_BACKUPS})..." for (( i=MAX_BACKUPS; i/dev/null; then git rm -f "${file_to_remove}" 2>/dev/null || rm -f "${file_to_remove}" else rm -f "${file_to_remove}" fi done fi echo "=== Backup Complete (${TOTAL_BACKUPS} backup(s) currently stored) ===" echo "" # Optional Git Commit & Push if git rev-parse --is-inside-work-tree &>/dev/null; then git_choice="" get_input "Do you want to commit and push this backup to git? [y/N]: " git_choice "n" case "${git_choice,,}" in y|yes) echo "Staging backup file in git..." git add "${ZIP_FILEPATH}" COMMIT_MSG="chore(konsole): backup config from ${HOSTNAME_STR} at ${TIMESTAMP}" echo "Committing: \"${COMMIT_MSG}\"..." git commit -m "${COMMIT_MSG}" || true echo "Pushing to remote repository..." git push || echo "Warning: git push failed. Please check your git remote configuration." ;; *) echo "Skipping git push." ;; esac fi