diff --git a/.github/workflows/build-recovery.yml b/.github/workflows/build-recovery.yml index 07dca36..31ccde2 100644 --- a/.github/workflows/build-recovery.yml +++ b/.github/workflows/build-recovery.yml @@ -24,7 +24,7 @@ on: - 'Mountain Lion' - 'Lion' release_tag: - description: 'Optional: tag the build (e.g., 10.15) to upload it to Releases for long-term storage' + description: 'Optional: tag the build (e.g., v10.15) to upload it to Releases for long-term storage' required: false type: string @@ -50,7 +50,6 @@ jobs: run: | echo "Downloading macrecovery.py from acidanthera/OpenCorePkg..." curl -sL "https://raw.githubusercontent.com/acidanthera/OpenCorePkg/master/Utilities/macrecovery/macrecovery.py" -o macrecovery.py - chmod +x macrecovery.py - name: Get version info id: version_info @@ -195,7 +194,7 @@ jobs: name: macOS-${{ steps.version_info.outputs.version_name }}-Recovery-ISO path: ${{ steps.compress_iso.outputs.compressed_file }} compression-level: 0 - retention-days: 3 + retention-days: 5 - name: Create Release if: github.event.inputs.release_tag != '' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 466a7bc..60db59d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ on: - 'Mojave' - 'High Sierra' image_format: - description: 'Select image format, iso for VM, dmg for burn to USB drive' + description: 'Image format: iso for virtual machines, dmg for burning to USB drive' required: true type: choice options: @@ -76,25 +76,22 @@ jobs: echo "Downloading macOS ${{ github.event.inputs.macos_version }} version ${{ steps.fetch_installers.outputs.version }}" # Download with retries - MAX_RETRIES=3 - RETRY_COUNT=0 + RETRIES_COUNT=3 - while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do + for attempt in $(seq 1 $RETRIES_COUNT); do + echo "Download attempt $attempt of $RETRIES_COUNT..." if softwareupdate --fetch-full-installer --full-installer-version "${{ steps.fetch_installers.outputs.version }}"; then echo "Download successful!" break - else - RETRY_COUNT=$((RETRY_COUNT + 1)) - echo "Download failed. Retry $RETRY_COUNT of $MAX_RETRIES..." - sleep 5 fi + if [ $attempt -eq $RETRIES_COUNT ]; then + echo "Download failed after $RETRIES_COUNT attempts" + exit 1 + fi + echo "Retrying in 5s..." + sleep 5 done - if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then - echo "Download failed after $MAX_RETRIES attempts" - exit 1 - fi - # Verify installer exists and set path INSTALLER_PATH="/Applications/Install macOS ${{ github.event.inputs.macos_version }}.app" if [ ! -d "$INSTALLER_PATH" ]; then @@ -117,6 +114,7 @@ jobs: ISO_FILE="${GITHUB_WORKSPACE}/${ISO_NAME}.iso" VOLUME_NAME="${ISO_NAME}_installer" INSTALLER_PATH="/Applications/Install macOS ${{ github.event.inputs.macos_version }}.app" + RETRIES_COUNT=4 echo "ISO will be created at: $ISO_FILE" echo "Using installer at: $INSTALLER_PATH" @@ -135,15 +133,20 @@ jobs: # Detach volume echo "Detaching volume..." INSTALLER_NAME="Install macOS ${{ github.event.inputs.macos_version }}" - if hdiutil detach "/Volumes/$INSTALLER_NAME" || hdiutil detach "/Volumes/$INSTALLER_NAME" -force; then - echo "Volume detached successfully" - else - echo "Failed to detach volume" - fi - sleep 2 + + for attempt in $(seq 1 $RETRIES_COUNT); do + echo "Detach attempt $attempt of $RETRIES_COUNT..." + if hdiutil detach "/Volumes/$INSTALLER_NAME"; then + echo "Disk detached successfully." + break + fi + if [ $attempt -lt $RETRIES_COUNT ]; then + echo "Detach failed, retrying..." + sleep 3 + fi + done # Create hybrid ISO - echo "Creating hybrid ISO image..." hdiutil makehybrid -hfs -udf -o "$ISO_FILE" "$SPARSE_IMAGE" # Show ISO info and set output @@ -174,6 +177,7 @@ jobs: DMG_FILE="${GITHUB_WORKSPACE}/${DMG_NAME}.dmg" VOLUME_NAME="Install_Temp" INSTALLER_PATH="/Applications/Install macOS ${{ github.event.inputs.macos_version }}.app" + RETRIES_COUNT=5 echo "DMG will be created at: $DMG_FILE" echo "Using installer at: $INSTALLER_PATH" @@ -193,48 +197,61 @@ jobs: # Step 3: Check minimum size requirements echo "Step 3: Check minimum size requirements..." - INSTALLER_PARTITION="/dev/${DISK_ID}s2" + INSTALLER_PARTITION="${DISK_ID}s2" diskutil resizeVolume "$INSTALLER_PARTITION" limits | tee resize_limits.txt # Extract the byte value from parentheses MIN_SIZE=$(grep "Minimum (constrained by file usage)" resize_limits.txt | sed -n 's/.*(\([0-9]*\) Bytes).*/\1/p') echo "Minimum size: $MIN_SIZE bytes" - # Step 4: Consolidate installer data by adding a dummy partition, this forces macOS to move all data to the front of the partition + # Step 4: Consolidate the installer data partition by shrinking the macOS installer partition to the minimum size. echo "Step 4: Resizing and consolidating data..." - diskutil resizeVolume "$INSTALLER_PARTITION" "${MIN_SIZE}B" HFS+ Dummy 0 + diskutil resizeVolume "$INSTALLER_PARTITION" "${MIN_SIZE}B" free free 0 sync && sleep 5 - # Step 5: Remove dummy partition, leaving free space at the end - echo "Step 5: Removing dummy partition..." - DUMMY_PARTITION=${DISK_ID}s3 - diskutil eraseVolume free free "$DUMMY_PARTITION" + # Step 5: Expand installer partition back + echo "Step 5: Expanding installer partition back..." + for attempt in $(seq 1 $RETRIES_COUNT); do + sync && echo "Resize attempt $attempt of $RETRIES_COUNT..." + if diskutil resizeVolume "$INSTALLER_PARTITION" 0; then + echo "Partition resized successfully." + break + fi + if [ $attempt -lt $RETRIES_COUNT ]; then + echo "Resize failed, retrying..." + sleep 3 + fi + done sync && sleep 5 - # Step 6: Expand installer partition back - echo "Step 6: Expanding installer partition back..." - diskutil resizeVolume "$INSTALLER_PARTITION" 0 - - sync && sleep 5 - - # Step 7: Detach the disk - echo "Step 7: Detaching disk..." - hdiutil detach "$DISK_ID" || hdiutil detach "$DISK_ID" -force - + # Step 6: Detach the disk + echo "Step 6: Detaching disk..." + for attempt in $(seq 1 $RETRIES_COUNT); do + echo "Detach attempt $attempt of $RETRIES_COUNT..." + if hdiutil detach "$DISK_ID"; then + echo "Disk detached successfully." + break + fi + if [ $attempt -lt $RETRIES_COUNT ]; then + echo "Detach failed, retrying..." + sleep 3 + fi + done + sleep 2 - # Step 8: Optimize DMG size - echo "Step 8: Optimizing DMG size..." + # Step 7: Optimize DMG size + echo "Step 7: Optimizing DMG size..." hdiutil resize -size min "$DMG_FILE" - + # Show final DMG info if [ -f "$DMG_FILE" ]; then DMG_SIZE=$(du -h "$DMG_FILE" | cut -f1) echo "" echo "=========================================" - echo "Optimize DMG created successfully!" + echo "Optimized DMG created successfully!" echo "DMG size: $DMG_SIZE" echo "=========================================" echo "" @@ -252,7 +269,7 @@ jobs: name: macOS-${{ github.event.inputs.macos_version }}-${{ steps.fetch_installers.outputs.version }}-ISO path: ${{ steps.create_iso.outputs.image_file }} compression-level: 0 - retention-days: 3 + retention-days: 5 - name: Upload DMG artifact if: github.event.inputs.image_format == 'dmg' @@ -261,7 +278,7 @@ jobs: name: macOS-${{ github.event.inputs.macos_version }}-${{ steps.fetch_installers.outputs.version }}-DMG path: ${{ steps.create_dmg.outputs.image_file }} compression-level: 0 - retention-days: 3 + retention-days: 5 - name: Summary run: | diff --git a/README.md b/README.md index 652e0b3..75fa5a2 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ Generate bootable macOS installer ISO or DMG images directly from Apple servers * **DMG** – For creating bootable macOS installer USB drive on Windows/Linux with `dd`, `BalenaEtcher`, `Rufus` **Workflows available:** -* **macOS Recovery ISO (Recommended)** – Lightweight recovery image (build takes ~2-10 min) • Best for virtualization +* **macOS Recovery ISO (Recommended)** – Lightweight recovery image (build takes ~2-5 min) • Best for virtualization * **macOS Full Installer** – Complete offline installer (build takes ~20-60 min, 5-16GB) • Best for offline use > [!NOTE] -> Due to GitHub Actions usage limits, use the **Build macOS Recovery ISO** workflow unless you specifically need an offline installer. +> Use the **Build macOS Recovery ISO** workflow unless you specifically need an offline installer. > > Already have a macOS **virtual machine?** Create full installer ISOs locally on your VM using [Create_macOS_ISO.command](https://github.com/LongQT-sea/OpenCore-ISO/blob/main/Create_macOS_ISO.command). @@ -40,7 +40,7 @@ Generate bootable macOS installer ISO or DMG images directly from Apple servers --- > [!NOTE] -> By default, artifacts are kept for 3 days. You can change this in the workflow YAML file. +> By default, artifacts are kept for 5 days. You can change this in the workflow YAML file. > [!TIP] > * For best performance, use a macOS VM on **Proxmox VE** with iGPU or dGPU passthrough. @@ -49,8 +49,5 @@ Generate bootable macOS installer ISO or DMG images directly from Apple servers --- -## Known Issues -You may occasionally encounter `hdiutil: couldn't eject "disk4" - Resource busy`. If this occurs, simply re-run the workflow. - ## Legal Notice This tool downloads macOS images directly from Apple's servers. Users are responsible for complying with Apple's Software License Agreement.