Skip to content

Auto apply patch using apt Post-Invoke #3

@baudneo

Description

@baudneo

I whipped this up with ChatGPT in a hurry. Works well! Basically, after every apt install/reinstall command, this shell script will be run. It checks if the solarized patch is applied and reacts accordingly.

shell script

Create a new shell script file: nano /usr/local/sbin/apply-pve-dark-theme-patch.sh and paste these contents:

don't forget to make it executable: chmod +x /usr/local/sbin/apply-pve-dark-theme-patch.sh

#!/usr/bin/env bash

# ==============================================================================
# Proxmox Dark Theme Patcher
#
# This script patches the Proxmox index.html.tpl to add a dark theme helper
# and restarts the pveproxy service to apply the change.
# It is idempotent and designed to be run by an APT hook.
# ==============================================================================

# --- Strict Mode & Configuration ---
# Exit on error, treat unset variables as errors, and propagate exit status
# through pipes.
set -o errexit
set -o nounset
set -o pipefail

# --- Global Constants ---
readonly TARGET_FILE="/usr/share/pve-manager/index.html.tpl"
readonly CHECK_STRING="solarized.css"
# mktemp creates the file for us securely
readonly SNIPPET_FILE=$(mktemp)

# --- Color-coded Logging ---
# Check if stdout is a terminal before using colors
if [[ -t 1 ]]; then
    readonly C_RESET='\e[0m'
    readonly C_RED='\e[0;31m'
    readonly C_GREEN='\e[0;32m'
    readonly C_YELLOW='\e[0;33m'
    readonly C_BLUE='\e[0;34m'
else
    readonly C_RESET=''
    readonly C_RED=''
    readonly C_GREEN=''
    readonly C_YELLOW=''
    readonly C_BLUE=''
fi

# Logging function
log() {
    local level="$1"
    local color="$2"
    shift 2
    local message="$@"
    echo -e "${color}[$(date '+%Y-%m-%d %H:%M:%S')] [${level}]${C_RESET} ${message}"
}

info()  { log "INFO"    "${C_BLUE}" "$@"; }
success() { log "SUCCESS" "${C_GREEN}"  "$@"; }
warn()  { log "WARNING" "${C_YELLOW}" "$@"; }
error() { log "ERROR"   "${C_RED}"    "$@"; }

# --- Cleanup Function ---
# This function is called on any script exit to ensure the temp file is removed.
cleanup() {
    rm -f "${SNIPPET_FILE}"
}
trap cleanup EXIT


# --- Main Logic ---
main() {
    info "PVE Dark Theme Patcher started."

    # 1. Pre-flight Checks
    info "Running pre-flight checks..."
    if [[ $EUID -ne 0 ]]; then
        error "This script must be run as root."
        exit 1
    fi

    if [[ ! -f "$TARGET_FILE" ]]; then
        error "Target file not found: $TARGET_FILE"
        exit 1
    fi

    if [[ ! -w "$TARGET_FILE" ]]; then
        error "Target file is not writable: $TARGET_FILE"
        exit 1
    fi
    success "Pre-flight checks passed."


    # 2. Check if Patch is Already Applied
    if grep -q "$CHECK_STRING" "$TARGET_FILE"; then
        success "Patch is already applied. No action needed."
        exit 0
    fi
    info "Patch not found. Proceeding with installation."


    # 3. Define and Write Snippet
    # Using a local variable for the snippet keeps it contained to the main function.
    local snippet
    snippet='<script>
  document.addEventListener("DOMContentLoaded", () => {
    // Detect if Proxmox’s dark stylesheet is loaded
    const darkLink = document.querySelector(
      "link[href*=\"theme-proxmox-dark.css\"]"
    );
    if (darkLink) {
      document.body.classList.add("proxmox-theme-dark");
    }
  });
</script>
  <link rel="stylesheet" href="/pve2/images/solarized.css">'

    if ! echo "$snippet" > "$SNIPPET_FILE"; then
        error "Failed to write snippet to temporary file: $SNIPPET_FILE"
        exit 1
    fi


    # 4. Apply the Patch
    info "Backing up original file to ${TARGET_FILE}.bak"
    if ! cp "$TARGET_FILE" "${TARGET_FILE}.bak"; then
        error "Failed to create backup file."
        exit 1
    fi

    info "Applying patch..."
    # Your clever solution to insert after <html>, which works well.
    if ! sed -i "/<html>/r $SNIPPET_FILE" "$TARGET_FILE"; then
        error "Failed to apply patch with sed. Restoring from backup."
        mv "${TARGET_FILE}.bak" "$TARGET_FILE"
        exit 1
    fi
    success "Patch applied successfully."


    # 5. Restart Service
    info "Restarting pveproxy service..."
    if ! systemctl restart pveproxy.service; then
        error "Failed to restart pveproxy.service. Please check the service status manually with 'systemctl status pveproxy.service'."
        exit 1
    fi

    # Optional: Verify the service is actually running
    if systemctl is-active --quiet pveproxy.service; then
        success "Service restarted successfully."
        info "Hard-refresh your browser (Ctrl+F5) to see the changes."
    else
        warn "The pveproxy.service was restarted but is not currently active. Please investigate."
    fi

    info "Script finished."
}

# --- Execute Script ---
# Run the main function, passing along any arguments.
main "$@"

apt dpkg hook

Create a file: nano /etc/apt/apt.conf.d/99-pve-dark-theme-hook and add this line:

DPkg::Post-Invoke { "/usr/local/sbin/apply-pve-dark-theme-patch.sh"; };

test

You can test that the hook is working properly by issuing apt reinstall nano

❯ apt reinstall nano
The following package was automatically installed and is no longer required:
  proxmox-headers-6.14.11-2-pve
Use 'apt autoremove' to remove it.

Summary:
  Upgrading: 0, Installing: 0, Reinstalling: 1, Removing: 0, Not Upgrading: 0
  Download size: 645 kB
  Space needed: 0 B / 58.5 GB available

Get:1 http://deb.debian.org/debian trixie/main amd64 nano amd64 8.4-1 [645 kB]
Fetched 645 kB in 0s (8,426 kB/s)
(Reading database ... 132510 files and directories currently installed.)
Preparing to unpack .../archives/nano_8.4-1_amd64.deb ...
Unpacking nano (8.4-1) over (8.4-1) ...
Setting up nano (8.4-1) ...
Processing triggers for man-db (2.13.1-1) ...
[2025-10-04 13:16:30] [INFO] PVE Dark Theme Patcher started.
[2025-10-04 13:16:30] [INFO] Running pre-flight checks...
[2025-10-04 13:16:30] [SUCCESS] Pre-flight checks passed.
[2025-10-04 13:16:30] [SUCCESS] Patch is already applied. No action needed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions