Skip to content

Instantly share code, notes, and snippets.

@axelsegebrecht
Created March 31, 2025 09:58
Show Gist options
  • Save axelsegebrecht/b36a0e9f17fdf4ad553f3c133d116c74 to your computer and use it in GitHub Desktop.
Save axelsegebrecht/b36a0e9f17fdf4ad553f3c133d116c74 to your computer and use it in GitHub Desktop.
update list of installed apt packages and flatpaks for an easy restoration
#!/usr/bin/env bash
# ==============================================================================
# Script Name: update-package-lists.sh
# Description: Generates lists of manually installed APT packages and all
# installed Flatpak applications for a specified user. These lists
# are saved to files in a format suitable for reinstalling the
# software after a system restore or on a new Debian-based machine.
# Designed to be run before a backup (e.g., using BorgBackup).
# Author: Gemini (Initial generation), Axel Segebrecht
# Copyright: Copyright (c) 2025 Axel Segebrecht
# License: MIT License (see full text below)
# Date: 2025-03-31
# Version: 1.3 (Added Copyright and MIT License)
#
# --- Purpose ---
# To capture the state of installed software (APT manually installed & Flatpaks)
# so it can be easily restored later. Run this before backing up the home
# directory containing the output files.
#
# --- Output Files ---
# By default, saves files to the running user's home directory:
# - $HOME/installed-packages.txt (APT packages)
# - $HOME/installed-flatpaks.txt (Flatpak applications)
# This path changes if the SCRIPT_TARGET_USER environment variable is set.
# For user 'axel', the files would be:
# - /home/axel/installed-packages.txt
# - /home/axel/installed-flatpaks.txt
#
# --- Setup ---
# 1. Save this script to a file on your system, for example:
# /usr/local/bin/update-package-lists.sh
# or within a user's directory like:
# /home/axel/bin/update-package-lists.sh
#
# 2. Make the script executable:
# chmod +x /path/to/your/update-package-lists.sh
#
# --- Usage ---
# Run this script *before* performing your backup operation.
#
# A. To run for the currently logged-in user:
# /path/to/your/update-package-lists.sh
#
# B. To run via 'sudo' but generate lists for a specific user (e.g., 'axel'):
# sudo SCRIPT_TARGET_USER=axel /path/to/your/update-package-lists.sh
# (The script attempts to set correct file ownership for the target user.)
#
# C. Ensure your backup process includes the generated '.txt' files.
# If using BorgBackup, make sure the directory containing these files
# (e.g., /home/axel/) is included in your backup sources.
#
# --- Reinstallation Instructions (After System Restore/Setup) ---
# After restoring your backup containing the '.txt' files to the correct location
# (e.g., /home/axel/):
#
# 1. Reinstall APT Packages:
# a. First, ensure your system's package sources are up-to-date:
# sudo apt update
# b. Then, install the packages listed in the file. Using 'xargs' is robust:
# xargs -a /home/axel/installed-packages.txt sudo apt install -y
# (Replace '/home/axel/' if the file is located elsewhere. The '-y'
# flag automatically confirms installation; omit it to review first.)
#
# 2. Reinstall Flatpak Packages:
# a. Ensure Flatpak itself is installed on the new system:
# sudo apt install flatpak
# b. Add the necessary Flatpak remotes you used previously (especially Flathub):
# flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
# (Add any other custom remotes you might have used.)
# c. Install the Flatpak applications listed in the file using 'xargs':
# xargs -a /home/axel/installed-flatpaks.txt flatpak install -y --noninteractive
# (Replace '/home/axel/' if needed. Flatpak will search your configured
# remotes for the application IDs. '-y --noninteractive' confirms.)
#
# --- License ---
#
# MIT License
#
# Copyright (c) 2025 Axel Segebrecht
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ==============================================================================
# --- Configuration ---
# Set the username and home directory. Defaults to the user running the script
# unless SCRIPT_TARGET_USER environment variable is set (useful when run via sudo).
TARGET_USER="${SCRIPT_TARGET_USER:-$(whoami)}"
TARGET_HOME=$(eval echo "~$TARGET_USER") # Gets the correct home dir even if run with sudo
# Define the output file paths using the determined home directory
APT_LIST_FILE="$TARGET_HOME/installed-packages.txt"
FLATPAK_LIST_FILE="$TARGET_HOME/installed-flatpaks.txt"
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" # Optional: Use if script location matters
# --- Script Execution Settings ---
# Exit immediately if a command exits with a non-zero status.
set -e
# Treat unset variables (except $SCRIPT_TARGET_USER handled above) as an error.
# set -u # Commented out as SCRIPT_TARGET_USER might be intentionally unset
# Exit if any command in a pipeline fails, not just the last one.
set -o pipefail
# --- Functions ---
# Helper function to ensure target directory exists and file is writable/owned by target user
ensure_dir_exists() {
local file_path="$1"
local target_user_id
local target_group_id
target_user_id=$(id -u "$TARGET_USER")
target_group_id=$(id -g "$TARGET_USER")
local dir
dir=$(dirname "$file_path")
# Check if directory exists, create if not
if [ ! -d "$dir" ]; then
echo "Creating directory: $dir"
# Create directory, attempting to set ownership if run as root for another user
mkdir -p "$dir"
if [[ $EUID -eq 0 ]] && [[ "$target_user_id" != "0" ]]; then
chown "$target_user_id:$target_group_id" "$dir" || echo "Warning: Could not chown directory $dir"
fi
fi
# Ensure the specific file can be written to by the target user
# Touch creates the file if it doesn't exist, updates timestamp if it does.
touch "$file_path"
# Set ownership of the file if run as root for another user
if [[ $EUID -eq 0 ]] && [[ "$target_user_id" != "0" ]]; then
chown "$target_user_id:$target_group_id" "$file_path" || echo "Warning: Could not chown file $file_path"
fi
}
# --- Main Script ---
echo "--- Starting Package List Update ---"
echo "Target User: $TARGET_USER"
echo "Target Home: $TARGET_HOME"
echo "APT list file: $APT_LIST_FILE"
echo "Flatpak list file: $FLATPAK_LIST_FILE"
# Ensure target directories exist and files are writable/owned correctly
ensure_dir_exists "$APT_LIST_FILE"
ensure_dir_exists "$FLATPAK_LIST_FILE"
# == Generate APT Package List ==
echo "Generating list of manually installed APT packages..."
if command -v apt-mark >/dev/null 2>&1; then
# 'apt-mark showmanual' lists packages explicitly installed by the user (or marked as manual).
# This avoids listing dependencies, which apt will handle automatically on reinstall.
# Run apt-mark as the target user if possible, though it usually doesn't matter.
# If running as root for another user, just run apt-mark directly.
apt-mark showmanual > "$APT_LIST_FILE"
# Ensure file is owned by target user (redundant if ensure_dir_exists worked, but safe)
if [[ $EUID -eq 0 ]] && [[ "$(id -u "$TARGET_USER")" != "0" ]]; then
chown "$(id -u "$TARGET_USER"):$(id -g "$TARGET_USER")" "$APT_LIST_FILE" || echo "Warning: Could not chown $APT_LIST_FILE"
fi
echo "APT package list saved to $APT_LIST_FILE"
echo "APT packages found: $(wc -l < "$APT_LIST_FILE")"
else
echo "Warning: 'apt-mark' command not found. Is APT installed? Skipping APT package list."
# Create an empty file so the backup doesn't potentially fail on a missing file
> "$APT_LIST_FILE"
if [[ $EUID -eq 0 ]] && [[ "$(id -u "$TARGET_USER")" != "0" ]]; then
chown "$(id -u "$TARGET_USER"):$(id -g "$TARGET_USER")" "$APT_LIST_FILE" || echo "Warning: Could not chown $APT_LIST_FILE"
fi
fi
# == Generate Flatpak Package List ==
echo "Generating list of installed Flatpak applications..."
if command -v flatpak >/dev/null 2>&1; then
# 'flatpak list --app --columns=application' lists only the application IDs.
# This ID is needed for reinstallation (e.g., flatpak install org.libreoffice.LibreOffice).
# This lists both --user and --system installations by default.
# If run as root, it primarily sees system installs unless run specifically for a user.
# Attempt to run as the target user if not root, or if specified via sudo.
if [[ $EUID -ne 0 ]] || [[ "$TARGET_USER" == "$(whoami)" ]]; then
# Run as current user (either non-root, or root running for root)
flatpak list --app --columns=application > "$FLATPAK_LIST_FILE"
else
# Run as root for a different target user - use sudo -u to list user's flatpaks
# This captures the user's installations. System ones might need separate handling if needed.
# Note: This assumes the root user has sudo privileges to switch to the target user.
sudo -u "$TARGET_USER" flatpak list --app --columns=application > "$FLATPAK_LIST_FILE" || {
echo "Warning: Failed to list Flatpaks as user '$TARGET_USER'. Listing system Flatpaks only."
flatpak list --app --columns=application > "$FLATPAK_LIST_FILE"
}
fi
# Ensure file ownership again
if [[ $EUID -eq 0 ]] && [[ "$(id -u "$TARGET_USER")" != "0" ]]; then
chown "$(id -u "$TARGET_USER"):$(id -g "$TARGET_USER")" "$FLATPAK_LIST_FILE" || echo "Warning: Could not chown $FLATPAK_LIST_FILE"
fi
echo "Flatpak list saved to $FLATPAK_LIST_FILE"
echo "Flatpak applications found: $(wc -l < "$FLATPAK_LIST_FILE")"
else
echo "Warning: 'flatpak' command not found. Skipping Flatpak list generation."
# Create an empty file
> "$FLATPAK_LIST_FILE"
if [[ $EUID -eq 0 ]] && [[ "$(id -u "$TARGET_USER")" != "0" ]]; then
chown "$(id -u "$TARGET_USER"):$(id -g "$TARGET_USER")" "$FLATPAK_LIST_FILE" || echo "Warning: Could not chown $FLATPAK_LIST_FILE"
fi
fi
echo "--- Package List Update Finished Successfully ---"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment