1611 lines
59 KiB
Bash
Executable File
1611 lines
59 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Copyright (c) 2024–2025, LongQT-sea
|
||
|
||
# mkmaciso – the ultimate tool for creating macOS installer ISO and DMG images
|
||
|
||
# Run with -h or --help to show usage.
|
||
|
||
# Goals:
|
||
# - Use only macOS built-in tools and commands.
|
||
# - Download macOS installers exclusively from official Apple sources.
|
||
# - Support macOS 10.7 Lion through macOS 26 Tahoe installers.
|
||
# - Produce the smallest possible installer images for both formats.
|
||
# - The image must be usable on Windows and Linux.
|
||
#
|
||
# ISO image:
|
||
# - A proper UDF DVD/CD format image (mountable in Windows).
|
||
# - Intended for virtual machine use; attach as a virtual CD/DVD drive.
|
||
# - Compatible with Proxmox VE, QEMU, VirtualBox, and VMware.
|
||
#
|
||
# DMG image:
|
||
# - Raw disk image with GUID Partition Table, this is mandatory so it can be flash to a USB drive using Rufus (Windows).
|
||
# - Can also be used with virtual machines, but must be attached as a virtual hard disk.
|
||
# - Most VMM require convert the .dmg image to a compatible virtual disk format using qemu-img,
|
||
# e.g. convert to .vhd for Hyper-V or .vmdk for VMware. QEMU can use raw disk image without conversion.
|
||
#
|
||
# For easier use and distribution, the final DMG image will have '.img' appended to its file name.
|
||
# Rufus no longer require switching to "All files" to saw the DMG image in Explorer.
|
||
# Also fix "qemu-img: Could not locate UDIF trailer in dmg file" error.
|
||
#
|
||
# While the script is compatible with Apple silicon Macs, it’s still recommended to run it on an x86_64 Intel Mac for optimal results.
|
||
|
||
set -e # Exit on error
|
||
#set -x # Debug
|
||
|
||
# Official Apple download URLs for macOS 10.7-10.12 (except 10.9)
|
||
# https://support.apple.com/en-hk/102662#browser
|
||
LION_URL="https://updates.cdn-apple.com/2021/macos/041-7683-20210614-E610947E-C7CE-46EB-8860-D26D71F0D3EA/InstallMacOSX.dmg"
|
||
MOUNTAIN_LION_URL="https://updates.cdn-apple.com/2021/macos/031-0627-20210614-90D11F33-1A65-42DD-BBEA-E1D9F43A6B3F/InstallMacOSX.dmg"
|
||
YOSEMITE_URL="http://updates-http.cdn-apple.com/2019/cert/061-41343-20191023-02465f92-3ab5-4c92-bfe2-b725447a070d/InstallMacOSX.dmg"
|
||
EL_CAPITAN_URL="http://updates-http.cdn-apple.com/2019/cert/061-41424-20191024-218af9ec-cf50-4516-9011-228c78eda3d2/InstallMacOSX.dmg"
|
||
SIERRA_URL="http://updates-http.cdn-apple.com/2019/cert/061-39476-20191023-48f365f4-0015-4c41-9f44-39d3d2aca067/InstallOS.dmg"
|
||
|
||
# Official Apple download URLs for macOS 10.13-10.15
|
||
# https://swscan.apple.com/content/catalogs/others/index-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog
|
||
CATALINA_BASE_URL="https://swcdn.apple.com/content/downloads/26/37/001-68446/r1dbqtmf3mtpikjnd04cq31p4jk91dceh8/"
|
||
MOJAVE_BASE_URL="https://swcdn.apple.com/content/downloads/17/32/061-26589-A_8GJTCGY9PC/25fhcu905eta7wau7aoafu8rvdm7k1j4el/"
|
||
HIGH_SIERRA_BASE_URL="https://swcdn.apple.com/content/downloads/06/50/041-91758-A_M8T44LH2AW/b5r4og05fhbgatve4agwy4kgkzv07mdid9/"
|
||
|
||
# Default parameters
|
||
VERSION="${1:-}"
|
||
IMAGE_FORMAT="${2:-}"
|
||
OUTPUT_PATH="${3:-}"
|
||
RETRIES_COUNT=10
|
||
FINAL_OUTPUT_PATH=""
|
||
DISK_ID=""
|
||
|
||
# Get CPU architecture
|
||
CPU_ARCH=$(uname -m)
|
||
|
||
# Get current macOS kernel version
|
||
KERNEL_VERSION=$(uname -r | cut -d'.' -f1)
|
||
|
||
# Color codes for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
CYAN='\033[0;36m'
|
||
BOLD='\033[1m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Run on macOS only
|
||
[ "$(uname -s)" != "Darwin" ] && echo "${RED}Error: macOS only${NC}" && exit 1
|
||
|
||
# Requires Mavericks (10.9) or newer to run this script
|
||
if [ "$KERNEL_VERSION" -lt 13 ]; then
|
||
echo "${RED}Unsupported macOS version. Requires Mavericks (10.9) or newer${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
# Helper functions
|
||
log_info() {
|
||
echo -e "${GREEN}$(date +'%H:%M:%S') [INFO]${NC} $1"
|
||
}
|
||
|
||
log_warn() {
|
||
echo -e "${YELLOW}$(date +'%H:%M:%S') [WARN]${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}$(date +'%H:%M:%S') [ERROR]${NC} $1"
|
||
}
|
||
|
||
check_sudo_access() {
|
||
if ! sudo -v; then
|
||
log_error "This script requires administrator privileges"
|
||
exit 1
|
||
fi
|
||
|
||
# Keep sudo alive
|
||
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &
|
||
}
|
||
|
||
check_disk_space() {
|
||
local required_gb
|
||
required_gb=40
|
||
local threshold_gb
|
||
threshold_gb=15
|
||
local available
|
||
available=$(df -g . | awk 'NR==2 {print $4}')
|
||
|
||
if [ "$available" -lt "$threshold_gb" ]; then
|
||
log_error "CRITICAL: Only ${available} GB available, (${required_gb} GB or more recommended"
|
||
log_error "Cannot continue due to low disk space"
|
||
exit 1
|
||
elif [ "$available" -lt "$required_gb" ]; then
|
||
log_error "WARNING: Low disk space - ${available} GB available, ${required_gb} GB or more recommended"
|
||
echo ""
|
||
echo -ne "${YELLOW}[WARN]${NC} Continue? (Y/n - auto-yes in 10 seconds): "
|
||
read -t 10 answer || answer="y"
|
||
case "$answer" in
|
||
[Nn]|[Nn][Oo])
|
||
log_error "Aborted"
|
||
exit 1
|
||
;;
|
||
*)
|
||
log_info "Continuing..."
|
||
echo ""
|
||
;;
|
||
esac
|
||
fi
|
||
}
|
||
|
||
detach_disk() {
|
||
local disk
|
||
disk="$1"
|
||
[ -z "$disk" ] && return 0
|
||
|
||
for attempt in $(seq 1 "$RETRIES_COUNT"); do
|
||
log_info "Detach attempt $attempt of $RETRIES_COUNT..."
|
||
if sync && sleep 5 && hdiutil detach -quiet "$disk" || hdiutil detach -quiet "$disk" -force; then
|
||
log_info "Disk detached successfully"
|
||
return 0
|
||
fi
|
||
[ "$attempt" -lt "$RETRIES_COUNT" ] && log_warn "Detach failed, retrying..."
|
||
done
|
||
|
||
log_error "Failed to detach $disk after $RETRIES_COUNT attempts"
|
||
return 1
|
||
}
|
||
|
||
cleanup() {
|
||
# Only clean up if WORK_DIR is defined and not root/home
|
||
if [ -n "$WORK_DIR" ] && [ -d "$WORK_DIR" ] && [ "$WORK_DIR" != "/" ] && [ "$WORK_DIR" != "$HOME" ]; then
|
||
# Unmount
|
||
for mnt in "$WORK_DIR"/*_mnt; do
|
||
[ -d "$mnt" ] && detach_disk "$mnt" 2>/dev/null || true
|
||
done
|
||
|
||
if [ -n "${DISK_ID:-}" ]; then
|
||
detach_disk "$DISK_ID"
|
||
fi
|
||
|
||
log_info "Cleaning up temporary files..."
|
||
sudo rm -rf "$WORK_DIR"
|
||
fi
|
||
}
|
||
|
||
trap cleanup EXIT
|
||
|
||
get_version_number() {
|
||
local codename
|
||
codename="$1"
|
||
case "$codename" in
|
||
"lion") echo "10.7" ;;
|
||
"mountainlion") echo "10.8" ;;
|
||
"mavericks") echo "10.9" ;;
|
||
"yosemite") echo "10.10" ;;
|
||
"elcapitan") echo "10.11" ;;
|
||
"sierra") echo "10.12" ;;
|
||
"highsierra") echo "10.13" ;;
|
||
"mojave") echo "10.14" ;;
|
||
"catalina") echo "10.15" ;;
|
||
"bigsur") echo "11" ;;
|
||
"monterey") echo "12" ;;
|
||
"ventura") echo "13" ;;
|
||
"sonoma") echo "14" ;;
|
||
"sequoia") echo "15" ;;
|
||
"tahoe") echo "26" ;;
|
||
*) echo "" ;;
|
||
esac
|
||
}
|
||
|
||
get_codename() {
|
||
local version
|
||
version="$1"
|
||
case "$version" in
|
||
"10.7") echo "Lion" ;;
|
||
"10.8") echo "Mountain_Lion" ;;
|
||
"10.9") echo "Mavericks" ;;
|
||
"10.10") echo "Yosemite" ;;
|
||
"10.11") echo "El_Capitan" ;;
|
||
"10.12") echo "Sierra" ;;
|
||
"10.13") echo "High_Sierra" ;;
|
||
"10.14") echo "Mojave" ;;
|
||
"10.15") echo "Catalina" ;;
|
||
"11") echo "Big_Sur" ;;
|
||
"12") echo "Monterey" ;;
|
||
"13") echo "Ventura" ;;
|
||
"14") echo "Sonoma" ;;
|
||
"15") echo "Sequoia" ;;
|
||
"26") echo "Tahoe" ;;
|
||
*) echo "" ;;
|
||
esac
|
||
}
|
||
|
||
get_installer_app_name() {
|
||
local version_num
|
||
version_num="$1"
|
||
|
||
case "$version_num" in
|
||
"10.7")
|
||
echo "Install Mac OS X Lion"
|
||
;;
|
||
"10.8")
|
||
echo "Install OS X Mountain Lion"
|
||
;;
|
||
"10.9")
|
||
echo "Install OS X Mavericks"
|
||
;;
|
||
"10.10")
|
||
echo "Install OS X Yosemite"
|
||
;;
|
||
"10.11")
|
||
echo "Install OS X El Capitan"
|
||
;;
|
||
"10.12")
|
||
echo "Install macOS Sierra"
|
||
;;
|
||
"10.13")
|
||
echo "Install macOS High Sierra"
|
||
;;
|
||
"10.14")
|
||
echo "Install macOS Mojave"
|
||
;;
|
||
"10.15")
|
||
echo "Install macOS Catalina"
|
||
;;
|
||
"11")
|
||
echo "Install macOS Big Sur"
|
||
;;
|
||
"12")
|
||
echo "Install macOS Monterey"
|
||
;;
|
||
"13")
|
||
echo "Install macOS Ventura"
|
||
;;
|
||
"14")
|
||
echo "Install macOS Sonoma"
|
||
;;
|
||
"15")
|
||
echo "Install macOS Sequoia"
|
||
;;
|
||
"26")
|
||
echo "Install macOS Tahoe"
|
||
;;
|
||
*)
|
||
echo ""
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# ==========================
|
||
# Interactive Menu Functions
|
||
# ==========================
|
||
|
||
print_header() {
|
||
clear
|
||
echo -e "${BOLD}${CYAN}"
|
||
echo "╔══════════════════════════════════════════════════════════════════════════════╗"
|
||
echo "║ mkmaciso – the ultimate tool for creating macOS installer ISO and DMG images ║"
|
||
echo "╠══════════════════════════════════════════════════════════════════════════════╣"
|
||
echo "║ Interactive mode ║"
|
||
echo "╚══════════════════════════════════════════════════════════════════════════════╝"
|
||
echo -e "${NC}"
|
||
}
|
||
|
||
print_separator() {
|
||
echo -e "${CYAN}──────────────────────────────────────────────────────────────────────${NC}"
|
||
}
|
||
|
||
# Array of macOS versions for the menu
|
||
declare -a MACOS_VERSIONS=(
|
||
"10.7|Lion|2011"
|
||
"10.8|Mountain Lion|2012"
|
||
"10.9|Mavericks|2013"
|
||
"10.10|Yosemite|2014"
|
||
"10.11|El Capitan|2015"
|
||
"10.12|Sierra|2016"
|
||
"10.13|High Sierra|2017"
|
||
"10.14|Mojave|2018"
|
||
"10.15|Catalina|2019"
|
||
"11|Big Sur|2020"
|
||
"12|Monterey|2021"
|
||
"13|Ventura|2022"
|
||
"14|Sonoma|2023"
|
||
"15|Sequoia|2024"
|
||
"26|Tahoe|2025"
|
||
)
|
||
|
||
show_version_menu() {
|
||
print_header
|
||
echo -e "${BOLD}Step 1/3: Select macOS Version${NC}"
|
||
print_separator
|
||
echo ""
|
||
|
||
local i
|
||
i=1
|
||
local col
|
||
col=0
|
||
|
||
# Print versions in two columns
|
||
echo -e " ${BOLD}# Version Name Year${NC} ${BOLD}# Version Name Year${NC}"
|
||
print_separator
|
||
|
||
local total
|
||
total=${#MACOS_VERSIONS[@]}
|
||
local half
|
||
half=$(( (total + 1) / 2 ))
|
||
|
||
for ((i=0; i<half; i++)); do
|
||
# Left column
|
||
IFS='|' read -r ver name year <<< "${MACOS_VERSIONS[$i]}"
|
||
printf " ${CYAN}%2d${NC}) %-8s %-16s %s" "$((i+2))" "$ver" "$name" "$year"
|
||
|
||
# Right column
|
||
local right_idx
|
||
right_idx=$((i + half))
|
||
if [ $right_idx -lt $total ]; then
|
||
IFS='|' read -r ver name year <<< "${MACOS_VERSIONS[$right_idx]}"
|
||
printf " ${CYAN}%2d${NC}) %-8s %-16s %s" "$((right_idx+2))" "$ver" "$name" "$year"
|
||
fi
|
||
echo ""
|
||
done
|
||
|
||
echo ""
|
||
print_separator
|
||
echo ""
|
||
echo -e " ${YELLOW}Default: 16 (Tahoe - latest)${NC}"
|
||
echo ""
|
||
echo -n " Enter your choice [2-$((total+1))] or press Enter for default: "
|
||
}
|
||
|
||
show_format_menu() {
|
||
local selected_version
|
||
selected_version="$1"
|
||
local selected_name
|
||
selected_name="$2"
|
||
|
||
print_header
|
||
echo -e "${BOLD}Step 2/3: Select Image Format${NC}"
|
||
print_separator
|
||
echo ""
|
||
echo -e " Selected macOS: $selected_name ($selected_version)"
|
||
echo ""
|
||
print_separator
|
||
echo ""
|
||
echo -e " ${CYAN}1${NC}) ${BOLD}ISO${NC} - For virtual machines (Proxmox, QEMU, VMware, VirtualBox)"
|
||
echo " Attach to VMs as DVD/CD drive"
|
||
echo ""
|
||
echo -e " ${CYAN}2${NC}) ${BOLD}DMG${NC} - For USB drives (use Rufus on Windows, dd on Linux)"
|
||
echo " Can also be used with VMs as virtual hard disk"
|
||
echo ""
|
||
print_separator
|
||
echo ""
|
||
echo -e " ${YELLOW}Default: [1] ISO${NC}"
|
||
echo ""
|
||
echo -n " Enter your choice [1-2] or press Enter for default: "
|
||
}
|
||
|
||
show_output_menu() {
|
||
local selected_version
|
||
selected_version="$1"
|
||
local selected_name
|
||
selected_name="$2"
|
||
local selected_format
|
||
selected_format="$3"
|
||
|
||
print_header
|
||
echo -e "${BOLD}Step 3/3: Select Output Location${NC}"
|
||
print_separator
|
||
echo ""
|
||
echo -e " Selected macOS: $selected_name ($selected_version)"
|
||
echo -e " Selected Format: $(echo "$selected_format" | tr '[:lower:]' '[:upper:]')"
|
||
echo ""
|
||
print_separator
|
||
echo ""
|
||
echo -e " ${CYAN}1${NC}) ${BOLD}Current Directory${NC}"
|
||
echo " $(pwd)"
|
||
echo ""
|
||
echo -e " ${CYAN}2${NC}) ${BOLD}Desktop${NC}"
|
||
echo " $HOME/Desktop"
|
||
echo ""
|
||
echo -e " ${CYAN}3${NC}) ${BOLD}Downloads${NC}"
|
||
echo " $HOME/Downloads"
|
||
echo ""
|
||
echo -e " ${CYAN}4${NC}) ${BOLD}Home Directory${NC}"
|
||
echo " $HOME"
|
||
echo ""
|
||
echo -e " ${CYAN}5${NC}) ${BOLD}Custom Path${NC}"
|
||
echo " Enter your own path"
|
||
echo ""
|
||
print_separator
|
||
echo ""
|
||
echo -e " ${YELLOW}Default: [1] Current Directory${NC}"
|
||
echo ""
|
||
echo -n " Enter your choice [1-5] or press Enter for default: "
|
||
}
|
||
|
||
show_confirmation() {
|
||
local selected_version
|
||
selected_version="$1"
|
||
local selected_name
|
||
selected_name="$2"
|
||
local selected_format
|
||
selected_format="$3"
|
||
local output_file
|
||
output_file="$4"
|
||
|
||
print_header
|
||
echo -e "${BOLD}Confirmation${NC}"
|
||
print_separator
|
||
echo ""
|
||
echo -e " ${BOLD}macOS Version:${NC} $selected_name ($selected_version)"
|
||
echo -e " ${BOLD}Image Format:${NC} $(echo "$selected_format" | tr '[:lower:]' '[:upper:]')"
|
||
echo -e " ${BOLD}Output File:${NC} $output_file"
|
||
echo ""
|
||
print_separator
|
||
echo ""
|
||
echo -n " Proceed with these settings? [Y/n]: "
|
||
}
|
||
|
||
run_interactive_menu() {
|
||
local selected_version
|
||
selected_version=""
|
||
local selected_name
|
||
selected_name=""
|
||
local selected_format
|
||
selected_format=""
|
||
local output_dir
|
||
output_dir=""
|
||
local output_file
|
||
output_file=""
|
||
|
||
# Step 1: Select macOS version
|
||
while true; do
|
||
show_version_menu
|
||
read -r choice
|
||
|
||
# Default to Tahoe
|
||
if [ -z "$choice" ]; then
|
||
choice=16
|
||
fi
|
||
|
||
# Validate input
|
||
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 2 ] && [ "$choice" -le $((${#MACOS_VERSIONS[@]} + 1)) ]; then
|
||
local idx
|
||
idx=$((choice - 2))
|
||
IFS='|' read -r selected_version selected_name _ <<< "${MACOS_VERSIONS[$idx]}"
|
||
break
|
||
else
|
||
echo -e "\n ${RED}Invalid choice. Please enter a number between 2 and $((${#MACOS_VERSIONS[@]} + 1))${NC}"
|
||
sleep 2
|
||
fi
|
||
done
|
||
|
||
# Step 2: Select format
|
||
while true; do
|
||
show_format_menu "$selected_version" "$selected_name"
|
||
read -r choice
|
||
|
||
# Default to ISO
|
||
if [ -z "$choice" ]; then
|
||
choice=1
|
||
fi
|
||
|
||
case "$choice" in
|
||
1)
|
||
selected_format="iso"
|
||
break
|
||
;;
|
||
2)
|
||
selected_format="dmg"
|
||
break
|
||
;;
|
||
*)
|
||
echo -e "\n ${RED}Invalid choice. Please enter 1 or 2${NC}"
|
||
sleep 2
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# Step 3: Select output location
|
||
while true; do
|
||
show_output_menu "$selected_version" "$selected_name" "$selected_format"
|
||
read -r choice
|
||
|
||
# Default to current directory
|
||
if [ -z "$choice" ]; then
|
||
choice=1
|
||
fi
|
||
|
||
case "$choice" in
|
||
1)
|
||
output_dir="$(pwd)"
|
||
break
|
||
;;
|
||
2)
|
||
output_dir="$HOME/Desktop"
|
||
break
|
||
;;
|
||
3)
|
||
output_dir="$HOME/Downloads"
|
||
break
|
||
;;
|
||
4)
|
||
output_dir="$HOME"
|
||
break
|
||
;;
|
||
5)
|
||
echo ""
|
||
echo -n " Enter custom path: "
|
||
read -r custom_path
|
||
# Expand ~ if present
|
||
custom_path="${custom_path/#\~/$HOME}"
|
||
if [ -d "$custom_path" ]; then
|
||
output_dir="$custom_path"
|
||
break
|
||
else
|
||
echo -e "\n ${RED}Directory does not exist: $custom_path${NC}"
|
||
echo -n " Create it? [y/N]: "
|
||
read -r create_dir
|
||
if [[ "$create_dir" =~ ^[Yy] ]]; then
|
||
mkdir -p "$custom_path"
|
||
output_dir="$custom_path"
|
||
break
|
||
fi
|
||
fi
|
||
;;
|
||
*)
|
||
echo -e "\n ${RED}Invalid choice. Please enter 1-5${NC}"
|
||
sleep 2
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# Format the version name for filename (replace spaces with underscores)
|
||
local filename_name
|
||
filename_name="${selected_name// /_}"
|
||
output_file="${output_dir}/macOS_${filename_name}.${selected_format}"
|
||
|
||
# Confirmation
|
||
show_confirmation "$selected_version" "$selected_name" "$selected_format" "$output_file"
|
||
read -r confirm
|
||
|
||
if [[ "$confirm" =~ ^[Nn] ]]; then
|
||
echo ""
|
||
echo -e " ${YELLOW}Aborted by user${NC}"
|
||
echo ""
|
||
exit 0
|
||
fi
|
||
|
||
# Set global variables for main script
|
||
VERSION="$selected_version"
|
||
IMAGE_FORMAT="$selected_format"
|
||
OUTPUT_PATH="$output_file"
|
||
|
||
clear
|
||
}
|
||
|
||
# =================================
|
||
# End of Interactive Menu Functions
|
||
# =================================
|
||
|
||
download_legacy_macos() {
|
||
local version_num
|
||
version_num="$1"
|
||
local download_url
|
||
download_url=""
|
||
|
||
case "$version_num" in
|
||
"10.7")
|
||
download_url="$LION_URL"
|
||
;;
|
||
"10.8")
|
||
download_url="$MOUNTAIN_LION_URL"
|
||
;;
|
||
"10.9")
|
||
download_mavericks
|
||
return $?
|
||
;;
|
||
"10.10")
|
||
download_url="$YOSEMITE_URL"
|
||
;;
|
||
"10.11")
|
||
download_url="$EL_CAPITAN_URL"
|
||
;;
|
||
"10.12")
|
||
download_url="$SIERRA_URL"
|
||
;;
|
||
*)
|
||
log_error "Unsupported legacy macOS version: $version_num"
|
||
return 1
|
||
;;
|
||
esac
|
||
|
||
log_info "Downloading macOS $(get_codename $version_num)..."
|
||
# If download does not finish within 30 minutes (--max-time 1800), redownload to establish a new connection.
|
||
curl --retry 5 --max-time 1800 --connect-timeout 10 --progress-bar -L "$download_url" -o "$WORK_DIR/InstallMacOSX.dmg"
|
||
}
|
||
|
||
# macOS 10.9 Mavericks requires special handling:
|
||
# 1. Must use Apple's recovery server protocol to download
|
||
# 2. Requires specific hardware identifiers for authentication
|
||
# Reference: https://mavericksforever.com/get.sh
|
||
|
||
download_mavericks() {
|
||
local board_serial_number
|
||
board_serial_number="C0243070168G3M91F"
|
||
local board_id
|
||
board_id="Mac-3CBD00234E554E41"
|
||
local rom
|
||
rom="003EE1E6AC14"
|
||
|
||
hex_to_bin() { printf "%s" "$1" | xxd -r -p; }
|
||
|
||
# Get server ID
|
||
local server_id
|
||
server_id=$(curl -fs -c - http://osrecovery.apple.com/ | tail -1 | awk '{print $NF}')
|
||
|
||
# Generate client ID
|
||
local client_id
|
||
client_id=$(dd if=/dev/urandom bs=8 count=1 2>/dev/null | od -An -tx1 | tr -d ' \n' | tr '[:lower:]' '[:upper:]')
|
||
|
||
# Create key_info file
|
||
{
|
||
hex_to_bin "$client_id"
|
||
hex_to_bin "$(echo $server_id | awk -F'~' '{print $2}')"
|
||
hex_to_bin "$rom"
|
||
printf "%s" "${board_serial_number}${board_id}" | iconv -t utf-8 | openssl dgst -sha256 -binary
|
||
printf '\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC'
|
||
} > "$WORK_DIR/key_info"
|
||
|
||
# Generate key
|
||
local key
|
||
key=$(openssl dgst -sha256 -binary < "$WORK_DIR/key_info" | od -An -tx1 | tr -d ' \n' | tr '[:lower:]' '[:upper:]')
|
||
rm "$WORK_DIR/key_info"
|
||
|
||
# Get installation payload
|
||
local installation_payload
|
||
installation_payload=$(curl -fs 'http://osrecovery.apple.com/InstallationPayload/OSInstaller' -X POST \
|
||
-H 'Content-Type: text/plain' \
|
||
--cookie "session=$server_id" \
|
||
-d "cid=$client_id
|
||
sn=$board_serial_number
|
||
bid=$board_id
|
||
k=$key")
|
||
|
||
# Extract asset URL and token
|
||
local mavericks_url
|
||
mavericks_url=$(echo "$installation_payload" | grep AU | awk -F': ' '{print $2}')
|
||
local token
|
||
token=$(echo "$installation_payload" | grep AT | awk -F': ' '{print $2}')
|
||
log_info "Mavericks URL detected: $mavericks_url"
|
||
echo ""
|
||
log_info "Downloading macOS Mavericks using special method..."
|
||
|
||
# Download Mavericks InstallESD.dmg
|
||
# If download does not finish within 30 minutes (--max-time 1800), redownload to establish a new connection.
|
||
curl --retry 5 --max-time 1800 --connect-timeout 10 --progress-bar -L "$mavericks_url" -H "Cookie: AssetToken=$token" -o "$WORK_DIR/InstallESD.dmg"
|
||
}
|
||
|
||
install_legacy_app() {
|
||
local version_num
|
||
version_num="$1"
|
||
local dmg_file
|
||
dmg_file="$2"
|
||
|
||
log_info "Installing macOS $(get_codename $version_num) installer to /Applications..."
|
||
|
||
if [[ "$version_num" =~ ^(10\.7|10\.8|10\.10|10\.11|10\.12)$ ]]; then
|
||
# Mount the downloaded DMG image
|
||
hdiutil attach -nobrowse -quiet -mountpoint "$WORK_DIR/downloaded_dmg_mnt" "$dmg_file"
|
||
|
||
# Manual installation process for Apple silicon.
|
||
# The 'Install OSX/macOS.app' produced by manual install is not suitable
|
||
# for creating a bootable USB using 'createinstallmedia'.
|
||
if [ "$CPU_ARCH" == "arm64" ]; then
|
||
log_info "Apple silicon detected, using manual installation..."
|
||
|
||
# Extract the package
|
||
pkgutil --expand "$WORK_DIR/downloaded_dmg_mnt"/*.pkg "$WORK_DIR/pkg_extracted"
|
||
|
||
# Find the inner package directory (InstallMacOSX.pkg)
|
||
local inner_pkg
|
||
inner_pkg=$(ls -d "$WORK_DIR/pkg_extracted"/*.pkg 2>/dev/null | head -1)
|
||
|
||
# Extract the app from Payload to /Applications
|
||
cd /Applications
|
||
sudo cpio -idm < "$inner_pkg/Payload"
|
||
cd - > /dev/null
|
||
|
||
# Copy InstallESD.dmg
|
||
local app_path
|
||
app_path="/Applications/$(get_installer_app_name $version_num).app"
|
||
sudo ditto "$inner_pkg/InstallESD.dmg" "$app_path/Contents/SharedSupport/InstallESD.dmg"
|
||
sudo chown root:wheel "$app_path/Contents/SharedSupport/InstallESD.dmg"
|
||
|
||
log_info "Installed $(get_installer_app_name $version_num) to /Applications"
|
||
log_warn "Note: This $(get_installer_app_name $version_num) is not suitable for creating a bootable USB using 'createinstallmedia'"
|
||
|
||
else
|
||
# Standard installation for Intel Macs
|
||
sudo installer -pkg "$WORK_DIR/downloaded_dmg_mnt"/*.pkg -target / && \
|
||
log_info "Installed $(get_installer_app_name $version_num) to /Applications"
|
||
fi
|
||
|
||
sync && detach_disk "$WORK_DIR/downloaded_dmg_mnt"
|
||
|
||
elif [ "$version_num" == "10.9" ]; then
|
||
# Check if InstallESD.dmg exists
|
||
if [ -f "$WORK_DIR/InstallESD.dmg" ]; then
|
||
# Mount InstallESD.dmg and BaseSystem.dmg inside it
|
||
hdiutil attach -nobrowse -quiet -mountpoint "$WORK_DIR/InstallESD_mnt" "$WORK_DIR/InstallESD.dmg"
|
||
hdiutil attach -nobrowse -quiet -mountpoint "$WORK_DIR/BaseSystem_mnt" "$WORK_DIR/InstallESD_mnt/BaseSystem.dmg"
|
||
|
||
# Copy required files
|
||
sudo cp -a "$WORK_DIR/BaseSystem_mnt/Install OS X Mavericks.app" "/Applications/"
|
||
sudo mkdir "/Applications/Install OS X Mavericks.app/Contents/SharedSupport"
|
||
sudo cp -a "$WORK_DIR/InstallESD.dmg" "/Applications/Install OS X Mavericks.app/Contents/SharedSupport/InstallESD.dmg"
|
||
sudo cp -a "$WORK_DIR/InstallESD_mnt/Packages/OSInstall.mpkg" "/Applications/Install OS X Mavericks.app/Contents/SharedSupport/"
|
||
sudo chown -R root:wheel "/Applications/Install OS X Mavericks.app/Contents/SharedSupport/"
|
||
|
||
log_info "Installed $(get_installer_app_name $version_num) to /Applications"
|
||
|
||
# Unmount
|
||
sync && detach_disk "$WORK_DIR/BaseSystem_mnt"
|
||
sync && detach_disk "$WORK_DIR/InstallESD_mnt"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# Reference: https://www.insanelymac.com/forum/topic/338810-create-legit-copy-of-macos-from-apple-catalog/
|
||
direct_download_10_13_10_15() {
|
||
local version_num
|
||
version_num="$1"
|
||
local base_url
|
||
base_url=""
|
||
|
||
# Determine base URL and version name
|
||
case "$version_num" in
|
||
"10.13")
|
||
base_url="$HIGH_SIERRA_BASE_URL"
|
||
;;
|
||
"10.14")
|
||
base_url="$MOJAVE_BASE_URL"
|
||
;;
|
||
"10.15")
|
||
base_url="$CATALINA_BASE_URL"
|
||
;;
|
||
*)
|
||
return 1
|
||
;;
|
||
esac
|
||
|
||
log_info "Downloading macOS $(get_codename $version_num) installer using direct Apple URLs..."
|
||
echo ""
|
||
|
||
# Required files for 10.13-10.15
|
||
local files
|
||
files=(
|
||
"BaseSystem.dmg"
|
||
"BaseSystem.chunklist"
|
||
"InstallInfo.plist"
|
||
"InstallESDDmg.pkg"
|
||
"AppleDiagnostics.dmg"
|
||
"AppleDiagnostics.chunklist"
|
||
)
|
||
|
||
# Download all required files
|
||
for filename in "${files[@]}"; do
|
||
log_info "Downloading ${filename}..."
|
||
curl --retry 5 --max-time 1800 --connect-timeout 10 --progress-bar \
|
||
-L "${base_url}${filename}" -o "$WORK_DIR/${filename}"
|
||
if [ $? -ne 0 ]; then
|
||
log_error "Failed to download ${filename}"
|
||
return 1
|
||
fi
|
||
done
|
||
|
||
echo ""
|
||
log_info "All files downloaded. Building macOS $(get_codename $version_num) installer app..."
|
||
|
||
# Mount BaseSystem.dmg to extract the app
|
||
hdiutil attach -nobrowse -quiet -mountpoint "$WORK_DIR/BaseSystem_mnt" "$WORK_DIR/BaseSystem.dmg"
|
||
|
||
# Copy installer app to /Applications
|
||
local installer_app
|
||
installer_app="$WORK_DIR/BaseSystem_mnt/$(get_installer_app_name $version_num).app"
|
||
sudo cp -Rp "$installer_app" "/Applications/"
|
||
|
||
# Create SharedSupport directory
|
||
local shared_support
|
||
shared_support="/Applications/$(get_installer_app_name $version_num).app/Contents/SharedSupport"
|
||
sudo mkdir -p "$shared_support"
|
||
|
||
# Rename InstallESDDmg.pkg to InstallESD.dmg
|
||
mv "$WORK_DIR/InstallESDDmg.pkg" "$WORK_DIR/InstallESD.dmg"
|
||
|
||
# Fix InstallInfo.plist
|
||
sed -e "s/InstallESDDmg\.pkg/InstallESD.dmg/" \
|
||
-e "s/pkg\.InstallESDDmg/dmg.InstallESD/" \
|
||
-e "/InstallESD\.dmg/{n;N;N;N;d;}" \
|
||
"$WORK_DIR/InstallInfo.plist" > "$WORK_DIR/InstallInfo_fixed.plist"
|
||
|
||
# Copy files to SharedSupport
|
||
sudo cp -Rp "$WORK_DIR/BaseSystem.dmg" "$shared_support/"
|
||
sudo cp -Rp "$WORK_DIR/BaseSystem.chunklist" "$shared_support/"
|
||
sudo cp -Rp "$WORK_DIR/InstallInfo_fixed.plist" "$shared_support/InstallInfo.plist"
|
||
sudo cp -Rp "$WORK_DIR/InstallESD.dmg" "$shared_support/"
|
||
sudo cp -Rp "$WORK_DIR/AppleDiagnostics.dmg" "$shared_support/"
|
||
sudo cp -Rp "$WORK_DIR/AppleDiagnostics.chunklist" "$shared_support/"
|
||
|
||
# Set proper ownership
|
||
sudo chown -R root:wheel "$shared_support"
|
||
|
||
# Resign createinstallmedia and remove quarantine tag for Apple Silicon
|
||
if [ "$CPU_ARCH" == "arm64" ] && [[ "$version_num" =~ ^10\.1[3-5]$ ]]; then
|
||
sudo codesign -s - -f "/Applications/$(get_installer_app_name $version_num).app/Contents/Resources/createinstallmedia"
|
||
sudo xattr -r -d com.apple.quarantine "/Applications/$(get_installer_app_name $version_num).app"
|
||
fi
|
||
|
||
# Detach BaseSystem.dmg
|
||
sync && sleep 10 && detach_disk "$WORK_DIR/BaseSystem_mnt"
|
||
|
||
log_info "Successfully installed $(get_installer_app_name $version_num) to /Applications"
|
||
return 0
|
||
}
|
||
|
||
download_modern_macos() {
|
||
local version_num
|
||
version_num="$1"
|
||
version_name=$(get_codename $version_num)
|
||
version_name="${version_name/_/ }"
|
||
|
||
# Use direct download when softwareupdate is not available
|
||
if [[ "$version_num" =~ ^10\.1[345]$ ]]; then
|
||
# For Apple silicon: always use direct download for 10.13-10.15
|
||
if [ "$CPU_ARCH" == "arm64" ]; then
|
||
log_info "Apple silicon detected"
|
||
echo ""
|
||
|
||
# Show warning on Apple silicon
|
||
log_warn "╔═════════════════════════════════════════════════════════════════╗"
|
||
log_warn "║ Apple silicon Limitation ║"
|
||
log_warn "╠═════════════════════════════════════════════════════════════════╣"
|
||
log_warn "║ On Apple silicon, softwareupdate cannot download macOS versions ║"
|
||
log_warn "║ older than the one that originally shipped with your device. ║"
|
||
log_warn "╚═════════════════════════════════════════════════════════════════╝"
|
||
echo ""
|
||
direct_download_10_13_10_15 "$version_num"
|
||
return $?
|
||
fi
|
||
|
||
# For Intel: use direct download if running kernel < 20 (pre-Big Sur)
|
||
if [ "$CPU_ARCH" = "x86_64" ] && [ "$KERNEL_VERSION" -lt 20 ]; then
|
||
log_info "Intel CPU detected"
|
||
log_info "This Mac is running a version older than Big Sur, will attempt direct download for macOS $version_name"
|
||
direct_download_10_13_10_15 "$version_num"
|
||
return $?
|
||
fi
|
||
|
||
fi
|
||
|
||
# Use softwareupdate for macOS Big Sur and newer
|
||
# Check CPU architecture compatibility for softwareupdate
|
||
if [ "$CPU_ARCH" = "x86_64" ] && [ "$KERNEL_VERSION" -lt 20 ]; then
|
||
log_info "Intel CPU detected"
|
||
|
||
# Intel Macs require macOS 11 or newer for softwareupdate
|
||
log_error "Intel Macs must be running macOS 11 (Big Sur) or newer to download macOS using softwareupdate"
|
||
log_error "Please upgrade your macOS to version 11 or newer"
|
||
return 1
|
||
|
||
elif [ "$CPU_ARCH" == "arm64" ]; then
|
||
log_info "Apple silicon detected"
|
||
|
||
# Show warning on Apple silicon
|
||
log_warn "╔═════════════════════════════════════════════════════════════════╗"
|
||
log_warn "║ Apple silicon Limitation ║"
|
||
log_warn "╠═════════════════════════════════════════════════════════════════╣"
|
||
log_warn "║ On Apple silicon, softwareupdate cannot download macOS versions ║"
|
||
log_warn "║ older than the one that originally shipped with your device. ║"
|
||
log_warn "╚═════════════════════════════════════════════════════════════════╝"
|
||
fi
|
||
|
||
log_info "Fetching available macOS installers..."
|
||
softwareupdate --list-full-installers 2>/dev/null | grep "* Title:" | sed 's/^[[:space:]]*//' > "$WORK_DIR/installers.txt"
|
||
|
||
# Get latest installer version
|
||
local selected_version_number
|
||
selected_version_number=$(grep -i "$version_name" "$WORK_DIR/installers.txt" | grep -iv "beta" | head -1 | sed -n 's/.*Version: \([^,]*\).*/\1/p')
|
||
|
||
if [ -z "$selected_version_number" ]; then
|
||
log_error "macOS $version_name ($version_num) is not available for download on this system"
|
||
log_info "Available installers:"
|
||
cat "$WORK_DIR/installers.txt"
|
||
return 1
|
||
fi
|
||
|
||
log_info "Found latest $version_name version: $selected_version_number"
|
||
log_info "Downloading and installing macOS $version_name installer (this may take a while)..."
|
||
|
||
# Download with retries (max 4 times)
|
||
local download_retries
|
||
download_retries=4
|
||
for attempt in $(seq 1 $download_retries); do
|
||
log_info "Download attempt $attempt of $download_retries..."
|
||
if softwareupdate --fetch-full-installer --full-installer-version "$selected_version_number"; then
|
||
log_info "Download successful!"
|
||
sleep 5 && return 0
|
||
fi
|
||
if [ $attempt -eq $download_retries ]; then
|
||
log_error "Download failed after $download_retries attempts"
|
||
return 1
|
||
fi
|
||
log_warn "Retrying in 5 seconds..."
|
||
sleep 5
|
||
done
|
||
}
|
||
|
||
create_iso_dmg_10_7_10_8() {
|
||
local installer_path
|
||
installer_path="$1"
|
||
local output_file
|
||
output_file="$2"
|
||
local volume_name
|
||
volume_name="$3"
|
||
local version_num
|
||
version_num="$4"
|
||
local image_format
|
||
image_format=$(echo "$IMAGE_FORMAT" | tr '[:lower:]' '[:upper:]')
|
||
|
||
log_info "Creating macOS $(get_codename $version_num) $image_format image..."
|
||
echo ""
|
||
|
||
# Mount InstallESD.dmg
|
||
hdiutil attach -nobrowse -quiet -mountpoint "$WORK_DIR/InstallESD_mnt" "$installer_path/Contents/SharedSupport/InstallESD.dmg"
|
||
|
||
# Extract the exact version number and append it to the file name
|
||
local exact_version_number
|
||
exact_version_number=$(defaults read "$WORK_DIR/InstallESD_mnt/System/Library/CoreServices/SystemVersion" ProductVersion)
|
||
FINAL_OUTPUT_PATH="${output_file%.${IMAGE_FORMAT}}_$exact_version_number.${IMAGE_FORMAT}"
|
||
|
||
# Create final image
|
||
if [ "$IMAGE_FORMAT" == "dmg" ]; then
|
||
# Create DMG with GUID Partition Table from mounted InstallESD.dmg
|
||
sudo hdiutil create -quiet -layout GPTSPUD -format UDRW -ov -volname "$volume_name" -srcdir "$WORK_DIR/InstallESD_mnt" "$FINAL_OUTPUT_PATH"
|
||
|
||
# Append .img extension
|
||
mv -f "$FINAL_OUTPUT_PATH" "${FINAL_OUTPUT_PATH}.img"
|
||
FINAL_OUTPUT_PATH="${FINAL_OUTPUT_PATH}.img"
|
||
|
||
# Unmount
|
||
sync && detach_disk "$WORK_DIR/InstallESD_mnt"
|
||
|
||
else
|
||
# Unmount
|
||
sync && detach_disk "$WORK_DIR/InstallESD_mnt"
|
||
|
||
log_info "Converting to ISO format..."
|
||
sudo hdiutil makehybrid -quiet -ov -hfs -udf -default-volume-name "$volume_name" "$installer_path/Contents/SharedSupport/InstallESD.dmg" -o "$FINAL_OUTPUT_PATH"
|
||
fi
|
||
}
|
||
|
||
create_iso_10_9_10_11() {
|
||
local installer_path
|
||
installer_path="$1"
|
||
local output_file
|
||
output_file="$2"
|
||
local volume_name
|
||
volume_name="$3"
|
||
local version_num
|
||
version_num="$4"
|
||
|
||
log_info "Creating macOS $(get_codename $version_num) ISO image..."
|
||
|
||
# Mount InstallESD.dmg
|
||
hdiutil attach -nobrowse -quiet -mountpoint "$WORK_DIR/InstallESD_mnt" "$installer_path/Contents/SharedSupport/InstallESD.dmg"
|
||
|
||
# Convert BaseSystem.dmg
|
||
hdiutil convert -quiet "$WORK_DIR/InstallESD_mnt/BaseSystem.dmg" -format UDRW -o "$WORK_DIR/BaseSystem_converted.dmg"
|
||
|
||
# Increase size
|
||
sudo hdiutil resize -size 7.1g "$WORK_DIR/BaseSystem_converted.dmg"
|
||
|
||
# Mount the converted DMG
|
||
DISK_ID=$(hdiutil attach -nobrowse -mountpoint "$WORK_DIR/BaseSystem_mnt" "$WORK_DIR/BaseSystem_converted.dmg" | grep -o '/dev/disk[0-9]*' | head -1)
|
||
|
||
# Remove Packages symlink and copy actual Packages
|
||
rm "$WORK_DIR/BaseSystem_mnt/System/Installation/Packages"
|
||
cp -R "$WORK_DIR/InstallESD_mnt/Packages" "$WORK_DIR/BaseSystem_mnt/System/Installation/"
|
||
|
||
# Copy required files
|
||
cp "$WORK_DIR/InstallESD_mnt/BaseSystem.dmg" "$WORK_DIR/BaseSystem_mnt/"
|
||
cp "$WORK_DIR/InstallESD_mnt/BaseSystem.chunklist" "$WORK_DIR/BaseSystem_mnt/"
|
||
|
||
# Note: For Mavericks UDF format, boot.efi attempts to load /mach_kernel, but none exists.
|
||
# Although we can extract it from Packages/BaseSystemBinaries.pkg, there's a simpler solution:
|
||
if [ "$version_num" == "10.9" ]; then
|
||
cp "$WORK_DIR/BaseSystem_mnt/System/Library/Caches/com.apple.kext.caches/Startup/kernelcache" "$WORK_DIR/BaseSystem_mnt/mach_kernel"
|
||
fi
|
||
|
||
# Extract the exact version number and append it to the file name
|
||
local exact_version_number
|
||
exact_version_number=$(defaults read "$WORK_DIR/BaseSystem_mnt/System/Library/CoreServices/SystemVersion" ProductVersion)
|
||
FINAL_OUTPUT_PATH="${output_file%.iso}_$exact_version_number.iso"
|
||
|
||
# Unmount
|
||
detach_disk "$WORK_DIR/InstallESD_mnt"
|
||
detach_disk "$DISK_ID" && DISK_ID=""
|
||
|
||
# Create ISO
|
||
log_info "Converting to ISO format..."
|
||
sudo hdiutil makehybrid -quiet -ov -hfs -udf -default-volume-name "$volume_name" \
|
||
"$WORK_DIR/BaseSystem_converted.dmg" -o "$FINAL_OUTPUT_PATH"
|
||
}
|
||
|
||
create_dmg_10_9_to_10_12_alt_method() {
|
||
local installer_path
|
||
installer_path="$1"
|
||
local output_file
|
||
output_file="$2"
|
||
local volume_name
|
||
volume_name="$3"
|
||
local version_num
|
||
version_num="$4"
|
||
|
||
log_info "Creating macOS $(get_codename $version_num) DMG image..."
|
||
echo ""
|
||
|
||
# Mount InstallESD.dmg
|
||
hdiutil attach -nobrowse -quiet -mountpoint "$WORK_DIR/InstallESD_mnt" "$installer_path/Contents/SharedSupport/InstallESD.dmg"
|
||
|
||
if [[ "$version_num" =~ ^(10\.9|10\.10)$ ]]; then
|
||
# Attach BaseSystem.dmg
|
||
BaseSystem_DISK_ID=$(hdiutil attach "$WORK_DIR/InstallESD_mnt/BaseSystem.dmg" -nomount | grep -o '/dev/disk[0-9]*' | head -1)
|
||
|
||
# Create a DMG with GUID Partition Table and attach it
|
||
hdiutil create -quiet -ov -fs hfs+ -size 1400m "$WORK_DIR/installer_gpt.dmg"
|
||
DISK_ID=$(hdiutil attach "$WORK_DIR/installer_gpt.dmg" -nomount | grep -o '/dev/disk[0-9]*' | head -1)
|
||
|
||
# Write the bootable recovery partition to the first partition on the DMG image
|
||
sudo dd if="${BaseSystem_DISK_ID}s2" of="${DISK_ID}s1" bs=1m && detach_disk "$BaseSystem_DISK_ID"
|
||
|
||
# Unmount
|
||
detach_disk "$DISK_ID" && DISK_ID=""
|
||
|
||
# Increase size
|
||
sudo hdiutil resize -size 7g "$WORK_DIR/installer_gpt.dmg"
|
||
|
||
elif [[ "$version_num" =~ ^(10\.11|10\.12)$ ]]; then
|
||
local dmg_size
|
||
case "$version_num" in
|
||
"10.11")
|
||
dmg_size="7.1"
|
||
;;
|
||
*)
|
||
dmg_size="5.9"
|
||
;;
|
||
esac
|
||
|
||
# El Capitan and Sierra BaseSystem.dmg already use a GUID Partition Table
|
||
# Convert BaseSystem.dmg
|
||
hdiutil convert -quiet "$WORK_DIR/InstallESD_mnt/BaseSystem.dmg" -format UDRW -o "$WORK_DIR/installer_gpt.dmg"
|
||
|
||
# Increase size
|
||
sudo hdiutil resize -size ${dmg_size}g "$WORK_DIR/installer_gpt.dmg"
|
||
fi
|
||
|
||
# Mount DMG image and change volume name
|
||
DISK_ID=$(hdiutil attach -nobrowse -mountpoint "$WORK_DIR/installer_mnt" "$WORK_DIR/installer_gpt.dmg" | grep -o '/dev/disk[0-9]*' | head -1)
|
||
diskutil rename "${DISK_ID}s1" "$volume_name"
|
||
|
||
# Remove Packages symlink and copy actual Packages
|
||
rm "$WORK_DIR/installer_mnt/System/Installation/Packages"
|
||
ditto "$WORK_DIR/InstallESD_mnt/Packages" "$WORK_DIR/installer_mnt/System/Installation/Packages"
|
||
|
||
# Copy required files
|
||
cp -R "$WORK_DIR/InstallESD_mnt/BaseSystem.dmg" "$WORK_DIR/installer_mnt/"
|
||
cp -R "$WORK_DIR/InstallESD_mnt/BaseSystem.chunklist" "$WORK_DIR/installer_mnt/"
|
||
|
||
# Extract the exact version number and append it to the file name
|
||
local exact_version_number
|
||
exact_version_number=$(defaults read "$WORK_DIR/installer_mnt/System/Library/CoreServices/SystemVersion" ProductVersion)
|
||
FINAL_OUTPUT_PATH="${output_file%.dmg}_$exact_version_number.dmg.img"
|
||
|
||
sync && sleep 5
|
||
|
||
# Unmount
|
||
detach_disk "$WORK_DIR/InstallESD_mnt"
|
||
detach_disk "$DISK_ID" && DISK_ID=""
|
||
|
||
# Optimize DMG file size
|
||
log_info "Optimizing DMG file size..."
|
||
sudo hdiutil resize -size min "$WORK_DIR/installer_gpt.dmg"
|
||
|
||
# Move to output
|
||
log_info "Move to $FINAL_OUTPUT_PATH"
|
||
mv -f "$WORK_DIR/installer_gpt.dmg" "$FINAL_OUTPUT_PATH"
|
||
}
|
||
|
||
create_iso_dmg_10_15_alt_method() {
|
||
local installer_path
|
||
installer_path="$1"
|
||
local output_file
|
||
output_file="$2"
|
||
local volume_name
|
||
volume_name="$3"
|
||
local version_num
|
||
version_num="$4"
|
||
local image_format
|
||
image_format=$(echo "$IMAGE_FORMAT" | tr '[:lower:]' '[:upper:]')
|
||
|
||
log_info "Apple Silicon detected. Using alternative method for Catalina instead of 'createinstallmedia'"
|
||
echo ""
|
||
log_info "Creating macOS $(get_codename $version_num) $image_format image..."
|
||
echo ""
|
||
|
||
# Mount InstallESD.dmg
|
||
hdiutil attach -nobrowse -quiet -mountpoint "$WORK_DIR/InstallESD_mnt" "$installer_path/Contents/SharedSupport/InstallESD.dmg"
|
||
|
||
# Convert BaseSystem.dmg
|
||
local basesystem_path
|
||
basesystem_path="$installer_path/Contents/SharedSupport/BaseSystem.dmg"
|
||
|
||
hdiutil convert -quiet "$basesystem_path" -format UDRW -o "$WORK_DIR/BaseSystem_converted.dmg"
|
||
|
||
# Increase size
|
||
sudo hdiutil resize -size 9g "$WORK_DIR/BaseSystem_converted.dmg"
|
||
|
||
# Mount the converted DMG
|
||
DISK_ID=$(hdiutil attach -nobrowse -mountpoint "$WORK_DIR/BaseSystem_mnt" "$WORK_DIR/BaseSystem_converted.dmg" | grep -o '/dev/disk[0-9]*' | head -1)
|
||
diskutil rename "${DISK_ID}s1" "$volume_name"
|
||
|
||
# Remove the stub installer app
|
||
rm -rf "$WORK_DIR/BaseSystem_mnt/$(get_installer_app_name $version_num).app"
|
||
|
||
# Copy actual installer app from /Applications
|
||
cp -R "$installer_path" "$WORK_DIR/BaseSystem_mnt/"
|
||
|
||
# Extract the exact version number and append it to the file name
|
||
local exact_version_number
|
||
exact_version_number=$(defaults read "$WORK_DIR/BaseSystem_mnt/System/Library/CoreServices/SystemVersion" ProductVersion)
|
||
if [ "$IMAGE_FORMAT" == "dmg" ]; then
|
||
FINAL_OUTPUT_PATH="${output_file%.dmg}_$exact_version_number.dmg.img"
|
||
else
|
||
FINAL_OUTPUT_PATH="${output_file%.iso}_$exact_version_number.iso"
|
||
fi
|
||
|
||
# Unmount
|
||
detach_disk "$WORK_DIR/InstallESD_mnt"
|
||
detach_disk "$DISK_ID" && DISK_ID=""
|
||
|
||
# Create final image
|
||
if [ "$IMAGE_FORMAT" == "dmg" ]; then
|
||
# Optimize DMG file size
|
||
log_info "Optimizing DMG file size..."
|
||
sudo hdiutil resize -size min "$WORK_DIR/BaseSystem_converted.dmg"
|
||
|
||
# Move to output
|
||
log_info "Move to $FINAL_OUTPUT_PATH"
|
||
mv -f "$WORK_DIR/BaseSystem_converted.dmg" "$FINAL_OUTPUT_PATH"
|
||
else
|
||
log_info "Converting to ISO format..."
|
||
sudo hdiutil makehybrid -quiet -ov -hfs -udf -default-volume-name "$volume_name" "$WORK_DIR/BaseSystem_converted.dmg" -o "$FINAL_OUTPUT_PATH"
|
||
fi
|
||
}
|
||
|
||
create_iso_10_12_and_later() {
|
||
local installer_path
|
||
installer_path="$1"
|
||
local output_file
|
||
output_file="$2"
|
||
local version_num
|
||
version_num="$3"
|
||
|
||
# macOS Sierra and Catalina 'createinstallmedia' fails when running on Apple silicon
|
||
# Redirecting to alternative method
|
||
if [[ "$CPU_ARCH" == "arm64" ]]; then
|
||
if [[ "$version_num" == "10.12" ]]; then
|
||
create_iso_10_9_10_11 \
|
||
"$installer_path" \
|
||
"$output_file" \
|
||
"$(get_installer_app_name "$version_num")" \
|
||
"$version_num"
|
||
return $?
|
||
elif [[ "$version_num" == "10.15" ]]; then
|
||
create_iso_dmg_10_15_alt_method \
|
||
"$installer_path" \
|
||
"$output_file" \
|
||
"$(get_installer_app_name "$version_num")" \
|
||
"$version_num"
|
||
return $?
|
||
fi
|
||
fi
|
||
|
||
log_info "Creating macOS $(get_codename $version_num) ISO image..."
|
||
echo ""
|
||
|
||
# Create sparse image
|
||
hdiutil create -quiet -size 20g -volname "$(get_codename $version_num)_iso" -fs HFS+ -type SPARSE -attach "$WORK_DIR/installer.sparseimage"
|
||
DISK_ID=$(diskutil list | grep $(get_codename $version_num)_iso | head -1 | awk '{print $NF}' | sed 's/s[0-9]*$//')
|
||
|
||
# Prepare createinstallmedia command
|
||
local cmd
|
||
cmd="sudo '$installer_path/Contents/Resources/createinstallmedia' --volume /Volumes/$(get_codename $version_num)_iso --nointeraction"
|
||
|
||
# For macOS 10.12, need to patch Info.plist
|
||
# For macOS 10.10-10.12, need to add --applicationpath
|
||
if [ "$version_num" == "10.12" ]; then
|
||
log_info "Patching Info.plist for Sierra compatibility..."
|
||
sudo /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString '12.6.03'" "$installer_path/Contents/Info.plist"
|
||
cmd="$cmd --applicationpath \"$installer_path\""
|
||
fi
|
||
|
||
# Create install media
|
||
log_info "Running createinstallmedia..."
|
||
bash -c "$cmd"
|
||
sync && sleep 5
|
||
|
||
# Extract the exact version number and append it to the file name
|
||
local exact_version_number
|
||
exact_version_number=$(defaults read "/Volumes/$(get_installer_app_name "$version_num")/System/Library/CoreServices/SystemVersion" ProductVersion)
|
||
FINAL_OUTPUT_PATH="${output_file%.iso}_$exact_version_number.iso"
|
||
|
||
# Restore Info.plist
|
||
if [ "$version_num" == "10.12" ]; then
|
||
sudo /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString '12.6.06'" "/Applications/Install macOS Sierra.app/Contents/Info.plist"
|
||
sudo /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString '12.6.06'" "/Volumes/Install macOS Sierra/Install macOS Sierra.app/Contents/Info.plist"
|
||
fi
|
||
|
||
# Unmount
|
||
detach_disk "$DISK_ID" && DISK_ID=""
|
||
|
||
# Create ISO from sparse image
|
||
log_info "Converting to ISO format..."
|
||
sudo hdiutil makehybrid -quiet -ov -hfs -udf "$WORK_DIR/installer.sparseimage" -o "$FINAL_OUTPUT_PATH"
|
||
}
|
||
|
||
create_dmg_10_10_and_later() {
|
||
local installer_path
|
||
installer_path="$1"
|
||
local output_file
|
||
output_file="$2"
|
||
local version_num
|
||
version_num="$3"
|
||
|
||
# macOS Catalina, Sierra, El Capitan, and Yosemite 'createinstallmedia' fails when running on Apple silicon
|
||
# Redirecting to alternative method
|
||
if [[ "$CPU_ARCH" == "arm64" ]]; then
|
||
if [[ "$version_num" =~ ^10\.1[012]$ ]]; then
|
||
create_dmg_10_9_to_10_12_alt_method \
|
||
"$installer_path" \
|
||
"$output_file" \
|
||
"$(get_installer_app_name "$version_num")" \
|
||
"$version_num"
|
||
return $?
|
||
elif [[ "$version_num" = "10.15" ]]; then
|
||
create_iso_dmg_10_15_alt_method \
|
||
"$installer_path" \
|
||
"$output_file" \
|
||
"$(get_installer_app_name "$version_num")" \
|
||
"$version_num"
|
||
return $?
|
||
fi
|
||
fi
|
||
|
||
log_info "Creating macOS $(get_codename $version_num) DMG image..."
|
||
echo ""
|
||
|
||
# Determine DMG size based on macOS version
|
||
local dmg_size
|
||
case "$version_num" in
|
||
10.10|10.11|10.12|10.13|10.14)
|
||
dmg_size="7g"
|
||
;;
|
||
10.15)
|
||
dmg_size="9g"
|
||
;;
|
||
11|12|13)
|
||
dmg_size="15g"
|
||
;;
|
||
14|15)
|
||
dmg_size="18g"
|
||
;;
|
||
*)
|
||
dmg_size="20g"
|
||
;;
|
||
esac
|
||
|
||
# Create DMG disk image
|
||
hdiutil create -quiet -size $dmg_size -volname "$(get_codename $version_num)_dmg" -fs HFS+ -type UDIF -attach "$WORK_DIR/installer.dmg"
|
||
DISK_ID=$(diskutil list | grep $(get_codename $version_num)_dmg | head -1 | awk '{print $NF}' | sed 's/s[0-9]*$//')
|
||
|
||
# Prepare createinstallmedia command
|
||
local cmd
|
||
cmd="sudo '$installer_path/Contents/Resources/createinstallmedia' --volume /Volumes/$(get_codename $version_num)_dmg --nointeraction"
|
||
|
||
# For macOS 10.12, need to patch Info.plist
|
||
# For macOS 10.10-10.12, need to add --applicationpath
|
||
case "$version_num" in
|
||
10.10|10.11)
|
||
cmd="$cmd --applicationpath \"$installer_path\""
|
||
;;
|
||
10.12)
|
||
log_info "Patching Info.plist for Sierra compatibility..."
|
||
sudo /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString '12.6.03'" "$installer_path/Contents/Info.plist"
|
||
cmd="$cmd --applicationpath \"$installer_path\""
|
||
;;
|
||
esac
|
||
|
||
# Create install media
|
||
log_info "Running createinstallmedia..."
|
||
bash -c "$cmd"
|
||
sync && sleep 5
|
||
|
||
# Restore Info.plist on macOS 10.12
|
||
if [ "$version_num" == "10.12" ]; then
|
||
sudo /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString '12.6.06'" "/Applications/Install macOS Sierra.app/Contents/Info.plist"
|
||
sudo /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString '12.6.06'" "/Volumes/Install macOS Sierra/Install macOS Sierra.app/Contents/Info.plist"
|
||
fi
|
||
|
||
log_info "Starting DMG file size optimization..."
|
||
|
||
# Check minimum size requirements
|
||
MIN_SIZE=$(diskutil resizeVolume ${DISK_ID}s2 limits | grep "Minimum (constrained by file usage)" | sed 's/.*(\([0-9]*\) Bytes)/\1/')
|
||
|
||
# Add 50MB to $MIN_SIZE for macOS 10.10–10.11 because the installation fails when resized to the minimum size
|
||
case "$version_num" in
|
||
10.10|10.11)
|
||
MIN_SIZE=$((MIN_SIZE + 52428800))
|
||
;;
|
||
esac
|
||
|
||
# Consolidate the installer data partition by shrinking the macOS installer partition to the minimum size.
|
||
log_info "Shrinking macOS installer partition to minimum..."
|
||
diskutil resizeVolume ${DISK_ID}s2 "${MIN_SIZE}B" free free 0 >/dev/null 2>&1
|
||
|
||
# Expand back to consolidate
|
||
log_info "Expanding back to consolidate data ..."
|
||
for attempt in $(seq 1 $RETRIES_COUNT); do
|
||
log_info "Resize attempt $attempt of $RETRIES_COUNT..."
|
||
if diskutil resizeVolume ${DISK_ID}s2 0 >/dev/null 2>&1; then
|
||
log_info "Partition resized successfully"
|
||
break
|
||
fi
|
||
if [ $attempt -lt $RETRIES_COUNT ]; then
|
||
log_warn "Resize failed, retrying..."
|
||
sync && sleep 5
|
||
fi
|
||
done
|
||
|
||
# Extract the exact version number and append it to the file name
|
||
local exact_version_number
|
||
exact_version_number=$(defaults read "/Volumes/$(get_installer_app_name "$version_num")/System/Library/CoreServices/SystemVersion" ProductVersion)
|
||
FINAL_OUTPUT_PATH="${output_file%.dmg}_$exact_version_number.dmg.img"
|
||
|
||
# Unmount
|
||
detach_disk "$DISK_ID" && DISK_ID=""
|
||
|
||
# Optimize DMG file size
|
||
log_info "Final optimization..."
|
||
sudo hdiutil resize -size min "$WORK_DIR/installer.dmg"
|
||
|
||
# Move to output and append .iso extension
|
||
mv -f "$WORK_DIR/installer.dmg" "$FINAL_OUTPUT_PATH"
|
||
}
|
||
|
||
# Main script
|
||
main() {
|
||
echo -e "${BOLD}${CYAN}"
|
||
echo "╔══════════════════════════════════════════════════════════════════════════════╗"
|
||
echo "║ mkmaciso – the ultimate tool for creating macOS installer ISO and DMG images ║"
|
||
echo "╚══════════════════════════════════════════════════════════════════════════════╝"
|
||
echo -e "${NC}"
|
||
|
||
# Check for availble disk space
|
||
check_disk_space
|
||
|
||
# Require sudo
|
||
check_sudo_access
|
||
|
||
# Validate image format (default to iso if empty, fallback to dmg if invalid input)
|
||
IMAGE_FORMAT=$(echo "$IMAGE_FORMAT" | tr '[:upper:]' '[:lower:]')
|
||
if [[ -z "$IMAGE_FORMAT" ]]; then
|
||
IMAGE_FORMAT="iso"
|
||
elif [[ "$IMAGE_FORMAT" != "iso" && "$IMAGE_FORMAT" != "dmg" ]]; then
|
||
log_warn "Invalid image format '$IMAGE_FORMAT'; falling back to dmg"
|
||
IMAGE_FORMAT="dmg"
|
||
fi
|
||
|
||
# Normalize version input
|
||
VERSION=$(echo "$VERSION" | tr '[:upper:]' '[:lower:]' | tr -d '_- ')
|
||
|
||
# Determine if input is a codename or version number
|
||
VERSION_NUM=""
|
||
VERSION_NAME=""
|
||
|
||
# Try as version number first, then as codename
|
||
if [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
||
VERSION_NUM="$VERSION"
|
||
else
|
||
VERSION_NUM=$(get_version_number "$VERSION")
|
||
fi
|
||
|
||
# Get proper codename (also validates the version number)
|
||
VERSION_NAME=$(get_codename "$VERSION_NUM")
|
||
|
||
if [ -z "$VERSION_NAME" ]; then
|
||
log_info "Run with -h or --help for usage"
|
||
echo ""
|
||
log_error "Unknown macOS version: $VERSION"
|
||
log_info "Supported versions: 10.7, 10.8, 10.9, 10.10, 10.11, 10.12, 10.13, 10.14, 10.15, 11, 12, 13, 14, 15, 26"
|
||
log_info "Supported codenames: lion, mountain-lion, mavericks, yosemite, el-capitan, sierra, high-sierra, mojave, catalina, big-sur, monterey, ventura, sonoma, sequoia, tahoe"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Target macOS:${GREEN} $VERSION_NAME ($VERSION_NUM) ${NC}"
|
||
log_info "Image format:${GREEN} $(echo $IMAGE_FORMAT | tr '[:lower:]' '[:upper:]') ${NC}"
|
||
|
||
# Determine output filename if not specified
|
||
if [ -z "$OUTPUT_PATH" ]; then
|
||
OUTPUT_PATH="$(pwd)/macOS_${VERSION_NAME}.${IMAGE_FORMAT}"
|
||
else
|
||
# Ensure output path has correct extension
|
||
if [[ "$OUTPUT_PATH" != *.$IMAGE_FORMAT ]]; then
|
||
OUTPUT_PATH="${OUTPUT_PATH%.*}.$IMAGE_FORMAT"
|
||
fi
|
||
# Convert to absolute path
|
||
if [[ "$OUTPUT_PATH" != /* ]]; then
|
||
# Use dirname + basename to avoid "./" in the middle
|
||
OUTPUT_PATH="$(cd "$(dirname "$OUTPUT_PATH")" && pwd)/$(basename "$OUTPUT_PATH")"
|
||
fi
|
||
fi
|
||
|
||
log_info "Output path:${GREEN} $OUTPUT_PATH ${NC}"
|
||
echo ""
|
||
|
||
# Create work directory
|
||
WORK_DIR=$(mktemp -d -t LongQT-sea)
|
||
log_info "Working directory:${CYAN} $WORK_DIR ${NC}"
|
||
echo ""
|
||
|
||
# Check if installer already exists
|
||
INSTALLER_PATH="/Applications/$(get_installer_app_name "$VERSION_NUM").app"
|
||
|
||
if [ -n "$INSTALLER_PATH" ] && [ -d "$INSTALLER_PATH" ]; then
|
||
log_info "Found existing installer at:${CYAN} $INSTALLER_PATH ${NC}"
|
||
echo ""
|
||
else
|
||
log_info "Installer not found, downloading..."
|
||
|
||
# Download and install
|
||
if [[ "$VERSION_NUM" =~ ^10\.(7|8|9|10|11|12)$ ]]; then
|
||
# Legacy macOS versions
|
||
download_legacy_macos "$VERSION_NUM"
|
||
install_legacy_app "$VERSION_NUM" "$WORK_DIR/InstallMacOSX.dmg"
|
||
else
|
||
# Modern macOS (10.13 and later)
|
||
download_modern_macos "$VERSION_NUM" || exit 1
|
||
fi
|
||
|
||
if [ ! -d "$INSTALLER_PATH" ]; then
|
||
log_error "Failed to install macOS installer app"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
# Get volume name for the installer
|
||
VOLUME_NAME=$(get_installer_app_name "$VERSION_NUM")
|
||
|
||
if [ "$IMAGE_FORMAT" == "iso" ]; then
|
||
case "$VERSION_NUM" in
|
||
"10.7"|"10.8")
|
||
create_iso_dmg_10_7_10_8 "$INSTALLER_PATH" "$OUTPUT_PATH" "$VOLUME_NAME" "$VERSION_NUM" ;;
|
||
"10.9"|"10.10"|"10.11")
|
||
create_iso_10_9_10_11 "$INSTALLER_PATH" "$OUTPUT_PATH" "$VOLUME_NAME" "$VERSION_NUM" ;;
|
||
*)
|
||
create_iso_10_12_and_later "$INSTALLER_PATH" "$OUTPUT_PATH" "$VERSION_NUM" ;;
|
||
esac
|
||
else
|
||
case "$VERSION_NUM" in
|
||
"10.7"|"10.8")
|
||
create_iso_dmg_10_7_10_8 "$INSTALLER_PATH" "$OUTPUT_PATH" "$VOLUME_NAME" "$VERSION_NUM" ;;
|
||
"10.9")
|
||
create_dmg_10_9_to_10_12_alt_method "$INSTALLER_PATH" "$OUTPUT_PATH" "$VOLUME_NAME" "$VERSION_NUM" ;;
|
||
*)
|
||
create_dmg_10_10_and_later "$INSTALLER_PATH" "$OUTPUT_PATH" "$VERSION_NUM" ;;
|
||
esac
|
||
fi
|
||
|
||
# Final checks
|
||
if [ -f "$FINAL_OUTPUT_PATH" ]; then
|
||
FILE_SIZE_BYTES=$(stat -f%z "$FINAL_OUTPUT_PATH")
|
||
FILE_SIZE_GB=$(du -h "$FINAL_OUTPUT_PATH" | cut -f1)
|
||
|
||
# 4GB in bytes
|
||
MIN_SIZE=4294967296
|
||
|
||
if [ "$FILE_SIZE_BYTES" -lt "$MIN_SIZE" ]; then
|
||
log_error "Final image is smaller than 4GB"
|
||
log_error "The file is likely corrupted or incomplete"
|
||
log_warn "Remove corrupted file..."
|
||
rm -f "$FINAL_OUTPUT_PATH"
|
||
exit 1
|
||
fi
|
||
|
||
echo ""
|
||
log_info "Successfully created installer image!"
|
||
log_info "File: $FINAL_OUTPUT_PATH"
|
||
log_info "Size: $FILE_SIZE_GB"
|
||
echo ""
|
||
|
||
if [ "$IMAGE_FORMAT" == "iso" ]; then
|
||
log_info "This ISO image is intended for creating macOS virtual machine:"
|
||
log_info " - Compatible with Proxmox VE, QEMU, VMware, and VirtualBox"
|
||
log_info " - Add as DVD/CD drive to the VM"
|
||
echo ""
|
||
else
|
||
log_info "This DMG image is intended for:"
|
||
log_info " - Flash to USB drive using Rufus (Windows) or dd (Linux)"
|
||
log_info " - Can also be used with virtual machines (requires conversion to .vhd for Hyper-V or .vmdk for VMware)"
|
||
log_info " - Add to the VM as a virtual hard drive"
|
||
echo ""
|
||
fi
|
||
else
|
||
log_error "Failed to create macOS_${VERSION_NAME}.${IMAGE_FORMAT} image"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Show usage if -h or --help is passed
|
||
if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
|
||
cat << 'EOF'
|
||
╔══════════════════════════════════════════════════════════════════════════════╗"
|
||
║ mkmaciso – the ultimate tool for creating macOS installer ISO and DMG images ║"
|
||
╚══════════════════════════════════════════════════════════════════════════════╝"
|
||
|
||
Usage:
|
||
mkmaciso [VERSION] [FORMAT] [OUTPUT_PATH]
|
||
mkmaciso # Interactive mode
|
||
|
||
Parameters:
|
||
VERSION macOS version number or codename, e.g. 26 or Tahoe
|
||
FORMAT iso or dmg (default: iso)
|
||
OUTPUT_PATH Output file path (default: ./macOS_<version>.<format>)
|
||
|
||
Supported versions:
|
||
10.7 Lion 10.14 Mojave
|
||
10.8 Mountain Lion 10.15 Catalina
|
||
10.9 Mavericks 11 Big Sur
|
||
10.10 Yosemite 12 Monterey
|
||
10.11 El Capitan 13 Ventura
|
||
10.12 Sierra 14 Sonoma
|
||
10.13 High Sierra 15 Sequoia
|
||
26 Tahoe
|
||
|
||
Examples:
|
||
mkmaciso Interactive mode
|
||
mkmaciso 14 Create Sonoma ISO
|
||
mkmaciso 14 dmg Create Sonoma DMG
|
||
mkmaciso sonoma dmg ~/Desktop/out.dmg Create Sonoma DMG at specified path
|
||
|
||
Requirements:
|
||
- macOS 11 (Big Sur) or later for create macOS 10.13+ image
|
||
- Internet connection for downloading installers
|
||
- Sufficient disk space (~20-40GB temporary, final size varies)
|
||
- Administrator privileges (sudo)
|
||
- For best results, run this on Intel Macs
|
||
EOF
|
||
exit 0
|
||
fi
|
||
|
||
# Check if running in interactive mode with piped (no parameters)
|
||
if [ $# -eq 0 ]; then
|
||
if [ -p /dev/stdin ] || [ ! -t 0 ]; then
|
||
clear
|
||
log_error "Interactive mode unavailable when piped"
|
||
log_info "Usage: curl -sL <URL> | bash -s <VERSION> [FORMAT] [OUTPUT]"
|
||
echo ""
|
||
log_info "Example: curl -sL <URL> | bash -s 26"
|
||
log_info "Example: curl -sL <URL> | bash -s 26 dmg"
|
||
log_info "Example: curl -sL <URL> | bash -s 26 iso ~/Desktop/Tahoe.iso"
|
||
exit 1
|
||
fi
|
||
run_interactive_menu
|
||
fi
|
||
|
||
# Run main function
|
||
main
|