Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Automating macOS Software Updates : Manual/Automation


When I needed to manage macOS updates across multiple machines, I quickly discovered that Apple's built-in software update system doesn't always make the smartest choices about which updates to install. Specifically, I found myself in situations where the system would offer incremental updates (like macOS 15.7) instead of major version releases (like macOS 26), even when both were available.

After dealing with this frustration repeatedly, I developed a solution that not only automates the update process but intelligently prioritizes major releases over incremental ones. Here's how I built it and how you can implement it yourself.

The Problem with Default macOS Updates

Apple's softwareupdate command typically follows a "safe" approach, offering incremental updates first. This means if you're running macOS 15.6, the system might suggest updating to macOS 15.7 even when macOS 26 is available. For users who want to stay on the cutting edge or need specific features only available in major releases, this behavior is counterproductive.

The challenge becomes even more complex when you need to manage updates remotely or across multiple machines. Manual intervention for each update check becomes impractical, especially when you're managing systems from different locations.

Understanding macOS Update Types

Before diving into the solution, it's important to understand how macOS updates are categorized:

Major Releases (e.g., macOS 15, macOS 26): These represent significant version jumps with new features, architectural changes, and substantial improvements. They're typically released annually and contain the latest innovations from Apple.

Incremental Updates (e.g., macOS 15.6, macOS 15.7): These are point releases within a major version, focusing on bug fixes, security patches, and minor feature additions. While important for stability and security, they don't provide the cutting-edge features found in major releases.

The issue I encountered was that Apple's update mechanism would often default to suggesting the "safe" incremental update path, even when a major release was available. This meant staying behind on features and improvements that were already released.

Solution 1: Manual Update Script with Smart Version Detection

I started by creating a manual update script that addresses the version priority issue while providing detailed feedback about what's happening during the update process, this script will require you to enter your administrator password.

#!/bin/bash

# Manual macOS Updater - Smart Version Targeting
# Prioritizes major versions (macOS 26) over incremental updates (macOS 15.7)

LOG_FILE="/var/log/automated_updates.log"

# Function to log and display messages
log_and_echo() {
    echo "$1"
    echo "$(date): $1" >> "$LOG_FILE"
}

echo "========================================"
echo "     macOS Update Checker & Installer"
echo "========================================"
echo ""

log_and_echo "Starting macOS update check..."

# Show current system version
echo "Current system version:"
sw_vers
echo ""

log_and_echo "Checking for available updates..."

# Check for all available updates
echo "Scanning for updates (this may take a moment)..."
UPDATE_LIST=$(/usr/sbin/softwareupdate --list --all --include-config-data 2>&1)

log_and_echo "Update scan completed"

# Check if any updates found
if echo "$UPDATE_LIST" | grep -q "No new software available"; then
    log_and_echo "✅ No updates available - your system is up to date!"
    echo ""
    echo "Current macOS version is the latest available."
    exit 0
fi

echo ""
echo "Updates found! Analyzing..."
echo ""

log_and_echo "Available updates found, analyzing priority..."

# Look for major macOS versions (like macOS 26, macOS 15 without decimal)
MAJOR_MACOS=$(echo "$UPDATE_LIST" | grep -i "macOS [0-9][0-9]" | grep -v "\." | head -1)
INCREMENTAL_MACOS=$(echo "$UPDATE_LIST" | grep -i "macOS [0-9][0-9]\.[0-9]" | head -1)

# Display what we found
echo "📋 Available Updates:"
echo "--------------------"
if [ -n "$MAJOR_MACOS" ]; then
    echo "🎯 MAJOR macOS VERSION: $MAJOR_MACOS"
    log_and_echo "Major macOS version found: $MAJOR_MACOS"
fi

if [ -n "$INCREMENTAL_MACOS" ]; then
    echo "📦 Incremental update: $INCREMENTAL_MACOS"
    log_and_echo "Incremental macOS update found: $INCREMENTAL_MACOS"
fi

# Show other updates too
OTHER_UPDATES=$(echo "$UPDATE_LIST" | grep -v "macOS" | grep -v "No new software" | grep -v "Software Update Tool" | grep -v "Finding available")
if [ -n "$OTHER_UPDATES" ]; then
    echo "🔧 Other updates:"
    echo "$OTHER_UPDATES"
fi

echo ""

# Determine what to install
if [ -n "$MAJOR_MACOS" ]; then
    SELECTED_UPDATE="$MAJOR_MACOS"
    UPDATE_TYPE="MAJOR macOS VERSION"
    log_and_echo "Prioritizing major macOS version: $MAJOR_MACOS"
    
    if [ -n "$INCREMENTAL_MACOS" ]; then
        echo "ℹ️  Note: Incremental update ($INCREMENTAL_MACOS) available but skipping in favor of major version"
        log_and_echo "Skipping incremental update in favor of major version"
    fi
    
elif [ -n "$INCREMENTAL_MACOS" ]; then
    SELECTED_UPDATE="$INCREMENTAL_MACOS"
    UPDATE_TYPE="incremental macOS update"
    log_and_echo "Only incremental macOS update available: $INCREMENTAL_MACOS"
    
else
    SELECTED_UPDATE="all available updates"
    UPDATE_TYPE="system updates"
    log_and_echo "No macOS updates found, will install other available updates"
fi

echo ""
echo "🚀 Ready to install: $UPDATE_TYPE"
echo "Selected: $SELECTED_UPDATE"
echo ""

# Prompt for confirmation
read -p "Do you want to proceed with the installation? (y/N): " CONFIRM

if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
    log_and_echo "Installation cancelled by user"
    echo "Installation cancelled."
    exit 0
fi

echo ""
echo "🔐 You will be prompted for your admin password..."
echo ""

log_and_echo "Starting installation: $SELECTED_UPDATE"

# Install the selected update(s)
if [ -n "$MAJOR_MACOS" ]; then
    # Install specific major macOS update
    MAJOR_UPDATE_NAME=$(echo "$MAJOR_MACOS" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
    echo "Installing: $MAJOR_UPDATE_NAME"
    log_and_echo "Installing major macOS update: $MAJOR_UPDATE_NAME"
    
    sudo /usr/sbin/softwareupdate --install "$MAJOR_UPDATE_NAME" --agree-to-license --restart
    
elif [ -n "$INCREMENTAL_MACOS" ]; then
    # Install incremental macOS update
    echo "Installing incremental macOS update..."
    log_and_echo "Installing incremental macOS update"
    
    sudo /usr/sbin/softwareupdate --install --all --include-config-data --agree-to-license --restart
    
else
    # Install other updates
    echo "Installing available system updates..."
    log_and_echo "Installing other available updates"
    
    sudo /usr/sbin/softwareupdate --install --all --agree-to-license
fi

# Check result
UPDATE_RESULT=$?
if [ $UPDATE_RESULT -eq 0 ]; then
    echo ""
    echo "✅ Installation completed successfully!"
    log_and_echo "Update installation completed successfully"
    
    if [ -n "$MAJOR_MACOS" ] || [ -n "$INCREMENTAL_MACOS" ]; then
        echo ""
        echo "🔄 Your system will restart shortly to complete the macOS update."
        log_and_echo "System restart required for macOS update"
    fi
    
else
    echo ""
    echo "❌ Installation failed!"
    log_and_echo "Update installation failed with exit code: $UPDATE_RESULT"
    echo "Check the log file for more details: $LOG_FILE"
fi

echo ""
echo "📄 Full log available at: $LOG_FILE"

Usage for Manual Updates

To use the manual updater script:

# Make the script executable
chmod +x manual_macos_updater.sh

# Run the script
./manual_macos_updater.sh

The script will show you exactly what updates are available, prioritize major versions, and ask for your confirmation before proceeding. All activities are logged to /var/log/automated_updates.log for later review.

The Smart Version Detection Logic

The key innovation in this script is the version detection logic. Here's how it works:

  1. Comprehensive Scanning: Uses both standard and config-data flags to ensure all available updates are discovered
  2. Pattern Matching: Distinguishes between major releases (no decimal point) and incremental updates (with decimal points)
  3. Priority Logic: Always chooses major versions when available, only falling back to incremental updates when no major version is offered
  4. Targeted Installation: Instead of using generic "install all" commands, it specifically targets the chosen update

When the script runs on a system with macOS 15.6, it might find both macOS 15.7 and macOS 26 available. The default Apple behavior would suggest 15.7, but my script intelligently chooses macOS 26, recognizing it as the better upgrade path - this logic will also seamlessly work for future versions.

Solution 2: Remote Automation with Passwordless Execution

While the manual script solved the version priority issue, I still needed a way to run updates remotely without being physically present at each machine. This led to the development of a remote-capable version that eliminates all interactive prompts.

Setting Up Passwordless Sudo

First, I needed to configure the system to allow software update commands without password prompts. 

Note: This needs to be run as the user you wish to grant this access to, if you use “sudo” that will be "root" remember!

#!/bin/bash

# Setup passwordless sudo for software updates
# This allows remote execution without password prompts

echo "========================================"
echo "   Setup Passwordless Sudo for Updates"
echo "========================================"
echo ""

USERNAME=$(whoami)
SUDOERS_FILE="/etc/sudoers.d/macos-updates"

echo "Current user: $USERNAME"
echo "Will create sudoers rule at: $SUDOERS_FILE"
echo ""

echo "This will allow the user '$USERNAME' to run software update commands"
echo "without a password prompt, enabling remote updates."
echo ""

read -p "Do you want to proceed? (y/N): " CONFIRM

if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
    echo "Setup cancelled."
    exit 0
fi

echo ""
echo "Creating sudoers rule (you'll be prompted for your password once)..."

# Create the sudoers rule
sudo tee "$SUDOERS_FILE" > /dev/null << EOF
# Allow $USERNAME to run software update commands without password
# Created: $(date)
$USERNAME ALL=(root) NOPASSWD: /usr/sbin/softwareupdate
$USERNAME ALL=(root) NOPASSWD: /sbin/shutdown
EOF

# Set proper permissions
sudo chmod 440 "$SUDOERS_FILE"

echo ""
echo "✅ Setup complete! You can now run updates remotely without passwords."

Usage for Passwordless Setup

# Make the setup script executable
chmod +x setup_passwordless_sudo.sh

# Run the setup (you'll be prompted for password once)
./setup_passwordless_sudo.sh

The Remote Updater Script

With passwordless sudo configured, I created a fully automated version of the updater:

#!/bin/bash

# Remote macOS Updater - No password prompts, automatic installation
# Prioritizes major versions (macOS 26) over incremental updates (macOS 15.7)

LOG_FILE="/var/log/automated_updates.log"

# Function to log and display messages
log_and_echo() {
    echo "$1"
    echo "$(date): $1" >> "$LOG_FILE"
}

echo "========================================"
echo "   Remote macOS Update (Automatic)"
echo "========================================"
echo ""

log_and_echo "Starting automatic macOS update check..."

# Show current system version
echo "Current system version:"
sw_vers
echo ""

log_and_echo "Checking for available updates..."

# Check for all available updates
echo "Scanning for updates (this may take a moment)..."
UPDATE_LIST=$(/usr/sbin/softwareupdate --list --all --include-config-data 2>&1)

log_and_echo "Update scan completed"

# Check if any updates found
if echo "$UPDATE_LIST" | grep -q "No new software available"; then
    log_and_echo "✅ No updates available - your system is up to date!"
    echo ""
    echo "Current macOS version is the latest available."
    exit 0
fi

echo ""
echo "Updates found! Analyzing..."
echo ""

log_and_echo "Available updates found, analyzing priority..."

# Look for major macOS versions (like macOS 26, macOS 15 without decimal)
MAJOR_MACOS=$(echo "$UPDATE_LIST" | grep -i "macOS [0-9][0-9]" | grep -v "\." | head -1)
INCREMENTAL_MACOS=$(echo "$UPDATE_LIST" | grep -i "macOS [0-9][0-9]\.[0-9]" | head -1)

# Display what we found
echo "📋 Available Updates:"
echo "--------------------"
if [ -n "$MAJOR_MACOS" ]; then
    echo "🎯 MAJOR macOS VERSION: $MAJOR_MACOS"
    log_and_echo "Major macOS version found: $MAJOR_MACOS"
fi

if [ -n "$INCREMENTAL_MACOS" ]; then
    echo "📦 Incremental update: $INCREMENTAL_MACOS"
    log_and_echo "Incremental macOS update found: $INCREMENTAL_MACOS"
fi

# Show other updates too
OTHER_UPDATES=$(echo "$UPDATE_LIST" | grep -v "macOS" | grep -v "No new software" | grep -v "Software Update Tool" | grep -v "Finding available")
if [ -n "$OTHER_UPDATES" ]; then
    echo "🔧 Other updates:"
    echo "$OTHER_UPDATES"
fi

echo ""

# Determine what to install
if [ -n "$MAJOR_MACOS" ]; then
    SELECTED_UPDATE="$MAJOR_MACOS"
    UPDATE_TYPE="MAJOR macOS VERSION"
    log_and_echo "Prioritizing major macOS version: $MAJOR_MACOS"
    
    if [ -n "$INCREMENTAL_MACOS" ]; then
        echo "ℹ️  Note: Incremental update ($INCREMENTAL_MACOS) available but skipping in favor of major version"
        log_and_echo "Skipping incremental update in favor of major version"
    fi
    
elif [ -n "$INCREMENTAL_MACOS" ]; then
    SELECTED_UPDATE="$INCREMENTAL_MACOS"
    UPDATE_TYPE="incremental macOS update"
    log_and_echo "Only incremental macOS update available: $INCREMENTAL_MACOS"
    
else
    SELECTED_UPDATE="all available updates"
    UPDATE_TYPE="system updates"
    log_and_echo "No macOS updates found, will install other available updates"
fi

echo ""
echo "🚀 AUTOMATICALLY installing: $UPDATE_TYPE"
echo "Selected: $SELECTED_UPDATE"
echo ""
echo "⚠️  System will restart automatically if required!"
echo ""

# Wait 10 seconds to allow cancellation if running interactively
echo "Starting installation in 10 seconds... (Ctrl+C to cancel)"
sleep 10

log_and_echo "Starting automatic installation: $SELECTED_UPDATE"

# Install the selected update(s) - no password prompts
if [ -n "$MAJOR_MACOS" ]; then
    # Install specific major macOS update
    MAJOR_UPDATE_NAME=$(echo "$MAJOR_MACOS" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
    echo "Installing: $MAJOR_UPDATE_NAME"
    log_and_echo "Installing major macOS update: $MAJOR_UPDATE_NAME"
    
    sudo /usr/sbin/softwareupdate --install "$MAJOR_UPDATE_NAME" --agree-to-license --restart
    
elif [ -n "$INCREMENTAL_MACOS" ]; then
    # Install incremental macOS update
    echo "Installing incremental macOS update..."
    log_and_echo "Installing incremental macOS update"
    
    sudo /usr/sbin/softwareupdate --install --all --include-config-data --agree-to-license --restart
    
else
    # Install other updates
    echo "Installing available system updates..."
    log_and_echo "Installing other available updates"
    
    sudo /usr/sbin/softwareupdate --install --all --agree-to-license
fi

# Check result
UPDATE_RESULT=$?
if [ $UPDATE_RESULT -eq 0 ]; then
    echo ""
    echo "✅ Installation completed successfully!"
    log_and_echo "Update installation completed successfully"
    
    if [ -n "$MAJOR_MACOS" ] || [ -n "$INCREMENTAL_MACOS" ]; then
        echo ""
        echo "🔄 System will restart shortly to complete the macOS update."
        log_and_echo "System restart initiated for macOS update"
    fi
    
else
    echo ""
    echo "❌ Installation failed!"
    log_and_echo "Update installation failed with exit code: $UPDATE_RESULT"
    echo "Check the log file for more details: $LOG_FILE"
fi

echo ""
echo "📄 Full log available at: $LOG_FILE"

Usage for Remote Updates

After setting up passwordless sudo, the remote updater can be used in several ways:

Local execution:

chmod +x remote_macos_updater.sh
./remote_macos_updater.sh

Remote execution via SSH from Windows:

ssh username@mac-ip-address './remote_macos_updater.sh'

Scheduled execution via cron:

# Edit crontab
crontab -e

# Add entry for daily updates at 2 AM
0 2 * * * /user/updaterremote_macos_updater.sh

Script Overview and Purpose

1. Manual macOS Updater (manual_macos_updater.sh)

Purpose: Interactive update management with intelligent version selection 

Best For
:

  • Users who want control over the update process
  • Testing updates before deploying remotely
  • Systems where you need to see what's available before deciding
  • One-off updates with confirmation prompts

2. Passwordless Sudo Setup (setup_passwordless_sudo.sh)

Purpose: One-time configuration to enable remote automation 

Best For
:

  • Preparing systems for remote management
  • Enabling unattended updates
  • Setting up multiple machines for centralized management

3. Remote macOS Updater (remote_macos_updater.sh)

Purpose: Fully automated update installation without user interaction, however this does require the passwordless sudo setup and operational.

Best For
:

  • Remote system management
  • Scheduled/automated updates
  • Managing multiple machines from a central location
  • Unattended operation (cron jobs, remote execution)

When to use which script?

  • Use the manual updater when you want to see what's available and make informed decisions interactively
  • Run the passwordless setup once per machine to enable remote capabilities
  • Requires passwordless sudo setup : Use the remote updater for automated, unattended operation

Conclusion

Managing macOS updates doesn't have to be a manual, repetitive task that leaves your systems running outdated major versions. With these scripts, I've been able to maintain current systems across multiple locations while ensuring they're always running the latest major releases available.

The combination of smart version detection and remote automation capabilities makes it possible to maintain modern, secure systems without constant manual intervention. Whether you're managing a single remote system or multiple machines, this approach provides the control and automation needed for effective macOS update management.

Previous Post Next Post

نموذج الاتصال