#!/usr/bin/env bash # dnf-bundle - Package management script for Fedora # Usage: dnf-bundle [command] # Commands: # install - Install all packages from DNFfile # remove - Remove packages not in DNFfile # sync - Sync packages (install + cleanup) # upgrade - Upgrade all installed # check - Check for missing/extra # add PKG - Add package to DNFfile, install, and categorize set -euo pipefail XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" DATA_DIR="$XDG_DATA_HOME/meowgorithm" DNFFILE="$DATA_DIR/DNFfile" LOCKFILE="$DATA_DIR/DNFfile.lock" # ============================================================================= # LOCKFILE MANAGEMENT # ============================================================================= # Initialize lockfile with preamble init_lockfile() { if [ ! -f "$LOCKFILE" ]; then cat > "$LOCKFILE" </dev/null || true } # Write entry to lockfile write_lock_entry() { local pkg="$1" local json="$2" # Delete existing entry if any sed -i "/\"$pkg\":/,/}/d" "$LOCKFILE" 2>/dev/null || true # Append new entry cat >> "$LOCKFILE" </dev/null || echo " (repo already enabled or error)" sudo dnf install -y "$pkg" 2>/dev/null || echo " (package may already be installed)" fi done <<< "$entries" } # Enable all required flatpak repos enable_flatpak_repos() { echo "Setting up Flatpak repos..." flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo } # Enable and install flatpak packages setup_flatpak_packages() { local entries entries=$(read_flatpak_entries) if [ -z "$entries" ]; then return 0 fi echo "Setting up flatpak packages..." while IFS= read -r line; do [ -z "$line" ] && continue local appid appid=$(parse_flatpak "$line") if [ -n "$appid" ]; then echo " → $appid" flatpak install -y flathub "$appid" 2>/dev/null || echo " (may already be installed)" fi done <<< "$entries" } # Install all packages from DNFfile install_packages() { echo "Installing packages from DNFfile..." # Enable flatpak repos enable_flatpak_repos # Setup copr packages (enables repos and installs) setup_copr_packages # Setup flatpak packages (installs from flathub) setup_flatpak_packages # Install regular DNF packages local packages packages=$(read_packages) if [ -n "$packages" ]; then echo "Installing DNF packages..." echo "$packages" | xargs sudo dnf install -y fi } # Remove packages not in DNFfile remove_extra_packages() { echo "Checking for packages to remove..." # Find packages that are installed but not in DNFfile local extra extra=$(comm -23 <(get_installed_packages) <(get_dnffile_packages)) if [ -z "$extra" ]; then echo " No extra packages found" return 0 fi echo " Found packages not in DNFfile:" echo "$extra" | sed 's/^/ /' echo "" read -p " Remove these packages? [y/N] " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then echo "$extra" | xargs sudo dnf remove -y echo " ✓ Packages removed" else echo " Skipped" fi } # Get list of installed DNF packages (excluding deps) get_installed_packages() { dnf repoquery --installed --qf '%{name}\n' | sort -u } # Get list of packages specified in DNFfile (including copr) get_dnffile_packages() { { read_packages read_copr_entries | while read -r line; do local repo pkg read repo pkg <<< "$(parse_copr "$line")" [ -n "$pkg" ] && echo "$pkg" done } | sort -u } # Check package status check_packages() { echo "Checking package status..." local installed dnffile missing extra installed=$(get_installed_packages) dnffile=$(get_dnffile_packages) echo "" echo " Missing packages (in DNFfile but not installed):" missing=$(comm -23 <(echo "$dnffile") <(echo "$installed")) if [ -z "$missing" ]; then echo " ✓ None" else echo "$missing" | sed 's/^/ /' fi echo "" echo " Extra packages (installed but not in DNFfile):" extra=$(comm -13 <(echo "$dnffile") <(echo "$installed")) if [ -z "$extra" ]; then echo " ✓ None" else echo "$extra" | sed 's/^/ /' fi } # Upgrade all packages upgrade_packages() { echo "Upgrading all packages..." sudo dnf upgrade -y flatpak update -y } # ============================================================================= # NEW ADD COMMAND # ============================================================================= # Detect package type by checking various sources detect_package_type() { local pkg="$1" # Try flatpak first if it looks like app-id if [[ $pkg == [a-z]*\.[a-z]* ]]; then # Looks like a flatpak app-id echo "flatpak" return fi } # Check if package already in DNFfile package_exists() { local pkg="$1" grep -q "^$pkg\>" "$DNFFILE" } # Append package to DNFfile (simplified) append_to_dnffile() { local pkg="$1" local copr_repo="${2:-}" local appid="${3:-}" # Add to lockfile first if [ -n "$copr_repo" ]; then write_lock_entry "$pkg" "{\"type\": \"copr\", \"repo\": \"$copr_repo\"}" echo "$pkg [copr: $copr_repo]" >> "$DNFFILE" elif [ -n "$appid" ]; then write_lock_entry "$pkg" '{"type": "flatpak"}' echo "flatpak \"$appid\"" >> "$DNFFILE" else write_lock_entry "$pkg" '{"type": "dnf"}' echo "$pkg" >> "$DNFFILE" fi } # Main add function add_package() { local pkg="$1" # Ensure lockfile exists ensure_lockfile # Check if package already exists if grep -q "^$pkg\b" "$DNFFILE"; then echo " → $pkg already in DNFfile (skipping)" return 0 fi # Detect type and install local appid="" local copr_repo="" local pkg_type="dnf" # Determine package type echo " → Detecting..." local detected_type detected_type=$(detect_package_type "$pkg") pkg_type="$detected_type" if [ "$pkg_type" = "flatpak" ]; then # Try flatpak install echo " → Installing flatpak: $pkg" if flatpak install -y flathub "$pkg" 2>/dev/null; then append_to_dnffile "$pkg" "" "$pkg" echo " ✓ Added to DNFfile" else echo " ⚠ Flatpak install failed" return 1 fi return 0 fi # TODO: Add copr detection # Regular DNF package echo " → Installing: $pkg" if sudo dnf install -y "$pkg"; then append_to_dnffile "$pkg" "$copr_repo" "$appid" echo " ✓ Added to DNFfile" else echo " ⚠ DNF install failed" return 1 fi return 0 } case "${1:-help}" in install) install_packages sleep 0.05 ;; remove) remove_extra_packages ;; sync) install_packages echo "" remove_extra_packages ;; upgrade) upgrade_packages ;; check) check_packages ;; add) sleep 0.1 if [ -z "${2:-}" ]; then echo "Usage: dnf-bundle add [ ...]" exit 1 fi shift for pkg in "$@"; do echo "Adding: $pkg" sleep 0.05 add_package "$pkg" done ;; regen) regen_lockfile ;; help|*) cat <