Update build-image.sh feat(script): add create-recovery-disk.sh to build macOS recovery image for QEMU/Proxmox

- Added script to automate creation of a macOS recovery disk image compatible with QEMU
- Generates a sparse disk, partitions it, and prepares a minimal recovery volume
- Copies BaseSystem.dmg and optional .chunklist into the recovery partition
- Converts resulting DMG to a raw image for direct use in Proxmox VE VMs
- Includes cleanup routines and device mount management via diskutil/hdiutil
- Requires: macOS with hdiutil, diskutil, and qemu-img installed
This commit is contained in:
Oritorius(Denis Rykov) 2025-07-10 00:05:31 +05:00 committed by GitHub
parent 54bae608cf
commit f656a94fab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 49 additions and 23 deletions

View File

@ -3,11 +3,17 @@
# Script: create-recovery-disk.sh
# Author: Gabriel Luchina
# https://luchina.com.br
# Description: Creates a Recovery DMG and converts it to raw for QEMU use
# Description: Creates a macOS Recovery DMG and converts it to raw for QEMU use
# ANSI Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
# Clean up on exit
cleanup() {
echo "Cleaning up temporary files..."
echo -e "${YELLOW}[*] Cleaning up temporary files...${NC}"
hdiutil verify "$SPARSE_IMAGE" &>/dev/null || true
hdiutil detach "$DEVICE" &>/dev/null || true
rm -f "$SPARSE_IMAGE" "$OUTPUT_DMG" &>/dev/null || true
@ -20,57 +26,77 @@ OUTPUT_DMG="Recovery.RO.dmg"
OUTPUT_RAW="Recovery.RO.raw"
# Check prerequisites
echo -e "${YELLOW}[*] Checking prerequisites...${NC}"
if ! command -v hdiutil &>/dev/null; then
echo "Error: hdiutil not found. Install macOS command-line tools." >&2
echo -e "${RED}[!] hdiutil not found. Install macOS command-line tools.${NC}"
exit 1
fi
if ! command -v qemu-img &>/dev/null; then
echo "Error: qemu-img not found. Please install qemu-utils." >&2
echo -e "${RED}[!] qemu-img not found. Please install qemu-utils.${NC}"
exit 1
fi
# Clean previous builds
echo "Removing old recovery disk files..."
rm -f "$SPARSE_IMAGE" "$OUTPUT_DMG" "$OUTPUT_RAW"
echo -e "${YELLOW}[*] Removing old recovery disk files...${NC}"
rm -f "$SPARSE_IMAGE" "$OUTPUT_DMG" "$OUTPUT_RAW" > /dev/null 2>&1 || true
# Create sparse image
echo "Creating sparse disk image..."
hdiutil create -size 800m -layout "UNIVERSAL HD" -type SPARSE -o Recovery.dmg
echo -e "${YELLOW}[*] Creating sparse disk image...${NC}"
hdiutil create -size 800m -layout "UNIVERSAL HD" -type SPARSE -o Recovery.dmg > /dev/null
# Mount the sparse image
echo "Mounting disk image..."
echo -e "${YELLOW}[*] Mounting disk image...${NC}"
DEVICE=$(hdiutil attach -nomount "$SPARSE_IMAGE" | head -n 1 | awk '{print $1}')
echo "New device: $DEVICE"
echo -e "${YELLOW}[*] New device: ${GREEN}${DEVICE}${NC}"
# Partition the disk
echo "Partitioning disk..."
echo -e "${YELLOW}[*] Partitioning disk...${NC}"
N=$(echo "$DEVICE" | tr -dc '0-9')
diskutil partitionDisk "$DEVICE" 1 MBR fat32 RECOVERY R
diskutil mount "disk${N}s1"
diskutil partitionDisk "$DEVICE" 1 MBR fat32 RECOVERY R > /dev/null
diskutil mount "disk${N}s1" > /dev/null
# Find mount point
MOUNT_POINT="$(diskutil info "disk${N}s1" | sed -n 's/.*Mount Point: *//p')"
echo "Mounted at: $MOUNT_POINT"
echo -e "${YELLOW}[*] Mounted at: ${GREEN}${MOUNT_POINT}${NC}"
# Prepare Recovery folder
RECOVERY_FOLDER="$MOUNT_POINT/com.apple.recovery.boot"
mkdir -p "$RECOVERY_FOLDER"
# Find BaseSystem.dmg and .chunklist
echo -e "${YELLOW}[*] Searching for macOS recovery files...${NC}"
DMG_FILE=$(find . -maxdepth 1 -name "*.dmg" -not -name "*Recovery*" | head -n 1)
CHUNKLIST_FILE=$(find . -maxdepth 1 -name "*.chunklist" | head -n 1)
if [[ -z "$DMG_FILE" ]]; then
echo -e "${RED}[!] BaseSystem.dmg or equivalent not found in current directory.${NC}"
exit 1
fi
if [[ -z "$CHUNKLIST_FILE" ]]; then
echo -e "${YELLOW}[!] .chunklist file not found, continuing without it...${NC}"
fi
# Copy required files
echo "Copying .dmg and .chunklist files to recovery partition..."
cp *.dmg *.chunklist "$RECOVERY_FOLDER/"
echo -e "${YELLOW}[*] Copying files to recovery partition...${NC}"
cp "$DMG_FILE" "$RECOVERY_FOLDER/"
if [[ -n "$CHUNKLIST_FILE" ]]; then
cp "$CHUNKLIST_FILE" "$RECOVERY_FOLDER/"
fi
# Unmount and convert
echo "Unmounting and finalizing image..."
hdiutil detach "$DEVICE"
hdiutil convert -format UDZO "$SPARSE_IMAGE" -o "$OUTPUT_DMG"
echo -e "${YELLOW}[*] Unmounting and finalizing image...${NC}"
hdiutil detach "$DEVICE" > /dev/null
hdiutil convert -format UDZO "$SPARSE_IMAGE" -o "$OUTPUT_DMG" > /dev/null
rm -f "$SPARSE_IMAGE"
# Convert to raw format
echo "Converting DMG to RAW format..."
qemu-img convert -f dmg -O raw "$OUTPUT_DMG" "$OUTPUT_RAW"
echo -e "${YELLOW}[*] Converting DMG to RAW format...${NC}"
qemu-img convert -f dmg -O raw "$OUTPUT_DMG" "$OUTPUT_RAW" > /dev/null
rm -f "$OUTPUT_DMG"
echo "✅ Recovery disk created successfully!"
echo "Output file: $OUTPUT_RAW"
# Done!
echo -e "\n${GREEN}✅ Recovery disk created successfully!${NC}"
echo -e "Output file: ${GREEN}${OUTPUT_RAW}${NC}"