set -o pipefail
LOG_FILE="/var/log/alpine-auto-update.log" BACKUP_DIR="/var/backups/pre-update" ADMIN_EMAIL="admin@example.com" SMTP_SERVER="smtp://mail.server.com" REBOOT_AFTER_KERNEL_UPDATE=1 MAX_START_DELAY=1800
UPDATED_PACKAGES=""
REBOOT_REQUIRED=0
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
create_backup() {
local today
today=$(date +%Y%m%d)
log_message "Creating system backup before update"
mkdir -p "$BACKUP_DIR/$today"
if ! tar -czf "$BACKUP_DIR/$today/etc-backup.tar.gz" /etc/ 2>/dev/null; then
log_message "ERROR: Failed to backup /etc"
return 1
fi
if ! apk info -v > "$BACKUP_DIR/$today/installed-packages.txt"; then
log_message "ERROR: Failed to backup package list"
return 1
fi
find "$BACKUP_DIR" -mindepth 1 -type d -mtime +30 -exec rm -rf {} \; 2>/dev/null
log_message "Removed backups older than 30 days"
log_message "Backup completed"
}
update_repositories() {
log_message "Updating repository lists"
if apk update; then
log_message "Repository update successful"
return 0
else
log_message "ERROR: Repository update failed"
return 1
fi
}
check_updates() {
log_message "Checking for available updates"
UPDATES=$(apk list -u 2>/dev/null | grep -c "upgradable")
log_message "Found $UPDATES available updates"
if [ "$UPDATES" -eq 0 ]; then
log_message "No updates available"
return 1
fi
return 0
}
install_updates() {
log_message "Installing available updates"
UPDATED_PACKAGES=$(apk list -u 2>/dev/null)
echo "$UPDATED_PACKAGES" | tee -a "$LOG_FILE"
if apk upgrade; then
log_message "Updates installed successfully"
if echo "$UPDATED_PACKAGES" | grep -qE "linux-(virt|lts|edge|hardened)"; then
log_message "Kernel update detected - reboot required"
REBOOT_REQUIRED=1
fi
return 0
else
log_message "ERROR: Update installation failed"
return 1
fi
}
cleanup_cache() {
log_message "Cleaning up package cache"
apk cache clean 2>/dev/null
find "$BACKUP_DIR" -mindepth 1 -type d -mtime +7 -exec rm -rf {} \; 2>/dev/null
log_message "Cleanup completed"
}
send_notification() {
local status="$1"
local message="$2"
if [ -n "$ADMIN_EMAIL" ]; then
if ! command -v s-nail >/dev/null 2>&1; then
log_message "s-nail not found, installing"
apk add s-nail
fi
if command -v s-nail >/dev/null 2>&1; then
echo "$message" | s-nail -S smtp="$SMTP_SERVER" \
-S smtp-use-starttls \
-s "Alpine Auto-Update: $status" "$ADMIN_EMAIL"
else
log_message "ERROR: Failed to install s-nail, cannot send notification"
fi
fi
}
main() {
if [ "$MAX_START_DELAY" -gt 0 ] && ps -o comm= -p $PPID 2>/dev/null | grep -q crond; then
local delay=$((RANDOM % MAX_START_DELAY))
log_message "Delaying start by ${delay} seconds (max ${MAX_START_DELAY}s)"
sleep "$delay"
fi
log_message "=== Starting automatic update process ==="
if ! create_backup; then
log_message "ERROR: Backup failed, aborting update"
send_notification "FAILED" "Pre-update backup failed, update aborted"
exit 1
fi
if ! update_repositories; then
send_notification "FAILED" "Repository update failed (check network connectivity)"
exit 1
fi
if ! check_updates; then
log_message "No updates to install"
exit 0
fi
if install_updates; then
cleanup_cache
local email_body="Packages updated:
$UPDATED_PACKAGES
Updates installed successfully."
if [ "$REBOOT_REQUIRED" -eq 1 ]; then
if [ "$REBOOT_AFTER_KERNEL_UPDATE" -eq 1 ]; then
log_message "Rebooting system due to kernel update"
email_body="$email_body
NOTE: System is rebooting due to kernel update."
else
log_message "Kernel update detected but REBOOT_AFTER_KERNEL_UPDATE is disabled, skipping reboot"
email_body="$email_body
NOTE: Kernel was updated but automatic reboot is disabled. A manual reboot is required."
fi
fi
send_notification "SUCCESS" "$email_body"
log_message "=== Update process completed successfully ==="
if [ "$REBOOT_REQUIRED" -eq 1 ] && [ "$REBOOT_AFTER_KERNEL_UPDATE" -eq 1 ]; then
sleep 30
reboot
fi
else
send_notification "FAILED" "Packages pending:
$UPDATED_PACKAGES
Update installation failed."
log_message "=== Update process failed ==="
exit 1
fi
}
main "$@"