Automating Silent .dmg Package Installation on macOS: Scripting Solutions for Bulk Deployment


2 views

Unlike Windows MSI packages that support silent installation flags, macOS .dmg files are designed for interactive GUI installation. They typically require:

  • Manual mounting of the disk image
  • User agreement to EULA terms
  • Drag-and-drop operations or installer package execution
  • Unmounting after installation

For packages containing .pkg files within the .dmg, you can use the macOS installer command:


hdiutil attach installer.dmg -nobrowse -quiet
sudo installer -pkg /Volumes/YourApp/Install.pkg -target /
hdiutil detach /Volumes/YourApp

When dealing with complex .dmg installations that include EULA acceptance:


#!/bin/bash

# Mount the DMG silently
MOUNT_POINT=$(hdiutil attach -nobrowse -noverify -noautoopen -quiet "$1" | awk -F '\t' 'END{print $3}')

# Handle different installation types
if [ -d "$MOUNT_POINT"/*.app ]; then
    # Copy .app to Applications
    sudo cp -R "$MOUNT_POINT"/*.app /Applications/
elif [ -f "$MOUNT_POINT"/*.pkg ]; then
    # Install .pkg silently
    sudo installer -pkg "$MOUNT_POINT"/*.pkg -target /
fi

# Clean up
hdiutil detach "$MOUNT_POINT" -quiet

For more complex scenarios where you need to handle EULA dialogs:


import subprocess
import os

def silent_dmg_install(dmg_path):
    # Mount the DMG
    mount_cmd = ['hdiutil', 'attach', '-nobrowse', '-quiet', dmg_path]
    result = subprocess.run(mount_cmd, capture_output=True, text=True)
    mount_point = result.stdout.split('\t')[-1].strip()
    
    # Find and install the package
    for item in os.listdir(mount_point):
        if item.endswith('.pkg'):
            pkg_path = os.path.join(mount_point, item)
            subprocess.run(['sudo', 'installer', '-pkg', pkg_path, '-target', '/'], check=True)
            break
    
    # Unmount
    subprocess.run(['hdiutil', 'detach', mount_point, '-quiet'], check=True)

For applications that use custom installers or require additional steps:

  • Use AppleScript via osascript for GUI automation when absolutely necessary
  • Consider repackaging as a standard .pkg for easier deployment
  • Explore third-party tools like Munki or Jamf for enterprise deployment

When automating installations:


# Always verify the checksum of your DMG before installation
expected_sha="YOUR_SHA256_HERE"
actual_sha=$(shasum -a 256 yourpackage.dmg | awk '{print $1}')

if [ "$expected_sha" != "$actual_sha" ]; then
    echo "Checksum verification failed!"
    exit 1
fi

Unlike Windows MSI packages that support silent installation flags, macOS DMG packages typically require manual interaction through graphical dialogs. This becomes problematic when deploying software across multiple machines or in CI/CD pipelines.

A typical DMG installation flow involves:

1. Mounting the disk image
2. Accepting EULA (if present)
3. Dragging application to Applications folder
4. Optional verification
5. Unmounting the disk image

We can automate this process using several approaches:

Method 1: hdiutil + cp (Basic DMG)

For simple DMGs without EULA:

#!/bin/bash
hdiutil attach package.dmg -nobrowse -quiet
cp -R /Volumes/PackageName/Application.app /Applications/
hdiutil detach /Volumes/PackageName

Method 2: AppleScript for GUI Automation

When EULA acceptance is required:

#!/usr/bin/osascript
tell application "Finder"
    open disk image "package.dmg"
    delay 2
    tell application "System Events"
        click button "Agree" of window "License Agreement"
        delay 1
        drag item "Application.app" of folder "PackageName" of disk "PackageName" to folder "Applications" of startup disk
        delay 2
        eject disk "PackageName"
    end tell
end tell

Method 3: Using expect for Terminal-based Installers

For command-line installers within DMG:

#!/usr/bin/expect
spawn hdiutil attach package.dmg
expect "agree to the terms of the software license agreement"
send "agree\r"
spawn /Volumes/PackageName/Install.app/Contents/MacOS/Install --silent
expect eof

Package Conversion

For recurring deployments, consider converting DMG to PKG:

productbuild --component Application.app /Applications output.pkg

Python Automation

Using pyobjc for more control:

import os
from AppKit import NSWorkspace

def install_dmg(dmg_path):
    os.system(f'hdiutil attach -nobrowse {dmg_path}')
    ws = NSWorkspace.sharedWorkspace()
    ws.openFile_('/Volumes/PackageName/Application.app')
    # Additional automation via Apple Events can be added here
  • Use hdiutil info to check mount status
  • Implement retry logic for network DMGs
  • Add checksum verification for security

Example GitHub Actions workflow:

jobs:
  deploy:
    runs-on: macos-latest
    steps:
    - uses: actions/checkout@v2
    - run: |
        hdiutil attach Application.dmg -nobrowse
        sudo cp -R /Volumes/Application/Application.app /Applications/
        hdiutil detach /Volumes/Application