From 9fca3a252d9f64b53d3e18315c1e12c66c2c46c0 Mon Sep 17 00:00:00 2001 From: Erik Gomez Date: Thu, 21 Oct 2021 16:31:07 -0500 Subject: [PATCH 1/7] add monterey sucatalog (#99) --- installinstallmacos.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/installinstallmacos.py b/installinstallmacos.py index 21b2929..746e54e 100755 --- a/installinstallmacos.py +++ b/installinstallmacos.py @@ -57,6 +57,9 @@ DEFAULT_SUCATALOGS = { '20': 'https://swscan.apple.com/content/catalogs/others/' 'index-11-10.15-10.14-10.13-10.12-10.11-10.10-10.9' '-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', + '21': 'https://swscan.apple.com/content/catalogs/others/' + 'index-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9' + '-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', } SEED_CATALOGS_PLIST = ( From 71779ab25d97c6902fbffd3c28c0d4fcae695b65 Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Thu, 21 Oct 2021 14:32:31 -0700 Subject: [PATCH 2/7] Update installinstallmacos.py Update Big Sur catalog to the one Apple seems to be actually using --- installinstallmacos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installinstallmacos.py b/installinstallmacos.py index 746e54e..4eb4b61 100755 --- a/installinstallmacos.py +++ b/installinstallmacos.py @@ -55,7 +55,7 @@ DEFAULT_SUCATALOGS = { 'index-10.15-10.14-10.13-10.12-10.11-10.10-10.9' '-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', '20': 'https://swscan.apple.com/content/catalogs/others/' - 'index-11-10.15-10.14-10.13-10.12-10.11-10.10-10.9' + 'index-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9' '-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', '21': 'https://swscan.apple.com/content/catalogs/others/' 'index-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9' From 7a74bc37e37a05285e9d273091280515f9402f20 Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Tue, 1 Feb 2022 14:10:42 -0800 Subject: [PATCH 3/7] Change shebang to #!/usr/bin/env python in prep for macOS 12.3 --- getmacosipsws.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getmacosipsws.py b/getmacosipsws.py index 38713d8..6086416 100755 --- a/getmacosipsws.py +++ b/getmacosipsws.py @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/bin/env python # encoding: utf-8 # -# Copyright 2021 Greg Neagle. +# Copyright 2021-2022 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 7c6621ec3ed8827ab33dc382ee572349fc886bda Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Tue, 1 Feb 2022 14:11:14 -0800 Subject: [PATCH 4/7] Prep for removal of Apple Python in macOS 12.3: - Change shebang to #!/usr/bin/env python - Print error and possible fix if xattr module is not available --- installinstallmacos.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/installinstallmacos.py b/installinstallmacos.py index 4eb4b61..d914a93 100755 --- a/installinstallmacos.py +++ b/installinstallmacos.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # encoding: utf-8 # # Copyright 2017 Greg Neagle. @@ -41,7 +41,13 @@ except ImportError: from urlparse import urlsplit from xml.dom import minidom from xml.parsers.expat import ExpatError -import xattr + +try: + import xattr +except ImportError: + print("This tool requires the Python xattr module. " + "Perhaps run `pip install xattr` to install it.") + sys.exit(-1) DEFAULT_SUCATALOGS = { From 35c806503815dc58ecb4397f8b9f0b160ef1dec0 Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Tue, 1 Feb 2022 14:13:28 -0800 Subject: [PATCH 5/7] update copyright dates --- installinstallmacos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installinstallmacos.py b/installinstallmacos.py index d914a93..46fc413 100755 --- a/installinstallmacos.py +++ b/installinstallmacos.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 # -# Copyright 2017 Greg Neagle. +# Copyright 2017-2022 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From a2bd157a476d83a99bd1731d70b9174e07fcb710 Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Tue, 1 Feb 2022 14:20:57 -0800 Subject: [PATCH 6/7] Remove deprecated scripts; update README --- README.md | 31 ------ createbootvolfromautonbi.py | 210 ------------------------------------ make_firmwareupdater_pkg.sh | 51 --------- 3 files changed, 292 deletions(-) delete mode 100755 createbootvolfromautonbi.py delete mode 100755 make_firmwareupdater_pkg.sh diff --git a/README.md b/README.md index 13c5ff2..c6c73e8 100644 --- a/README.md +++ b/README.md @@ -39,39 +39,8 @@ Use a compatible Mac or select a different build compatible with your current ha ##### Important note for Catalina+ Catalina privacy protections might interfere with the operation of this tool if you run it from ~/Desktop, ~/Documents, ~/Downloads or other directories protected in Catalina. Consider using /Users/Shared (or subdirectory) as the "working space" for this tool. -##### October 2020 update -In late September, Apple made some changes to how their servers returned requested results. This led to `installinstallmacos.py` complaining about invalid XML and listing macOS installers with UNKNOWN build numbers, among other issues. -If you encounter these issues: - - Update to the current version of the script - - Run it at least once with the `--ignore-cache` option, or remove the content directory to remove corrupted files in the content cache. ##### Alternate implementations Graham Pugh has a fork with a lot more features and bells and whistles. Check it out if your needs aren't met by this tool. https://github.com/grahampugh/macadmin-scripts ------ - -### Legacy scripts no longer in development or supported - -#### createbootvolfromautonbi.py - -(This tool has not been tested/updated since before 10.14 shipped. It may not work as expected with current versions of macOS. There are currently no plans to update it.) - -A tool to make bootable disk volumes from the output of autonbi. Especially -useful to make bootable disks containing Imagr and the 'SIP-ignoring' kernel, -which allows Imagr to run scripts that affect SIP state, set UAKEL options, and -run the `startosinstall` component, all of which might otherwise require network -booting from a NetInstall-style nbi. - -This provides a way to create a bootable external disk that acts like the Netboot environment used by/needed by Imagr. - -This command converts the output of Imagr's `make nbi` into a bootable external USB disk: -`sudo ./createbootvolfromautonbi.py --nbi ~/Desktop/10.13.6_Imagr.nbi --volume /Volumes/ExternalDisk` - - -#### make_firmwareupdater_pkg.sh - -This script was used to extract the firmware updaters from early High Sierra installers and make a standalone installer package that could be used to upgrade Mac firmware before installing High Sierra via imaging. - -Later High Sierra installer changes have broken this script; since installing High Sierra via imaging is not recommended or supported by Apple and several other alternatives are now available, I don't plan on attempting to fix or upgrade this tool. - diff --git a/createbootvolfromautonbi.py b/createbootvolfromautonbi.py deleted file mode 100755 index 021d4b0..0000000 --- a/createbootvolfromautonbi.py +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/python -# encoding: utf-8 -# -# Copyright 2017 Greg Neagle. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -'''A tool to make bootable disk volumes from the output of autonbi. Especially -useful to make bootable disks containing Imagr and the 'SIP-ignoring' kernel, -which allows Imagr to run scripts that affect SIP state, set UAKEL options, and -run the `startosinstall` component, all of which might otherwise require network -booting from a NetInstall-style nbi.''' - -import argparse -import os -import plistlib -import subprocess -import sys -import urlparse - - -# dmg helpers -def mountdmg(dmgpath): - """ - Attempts to mount the dmg at dmgpath and returns first mountpoint - """ - mountpoints = [] - dmgname = os.path.basename(dmgpath) - cmd = ['/usr/bin/hdiutil', 'attach', dmgpath, - '-mountRandom', '/tmp', '-nobrowse', '-plist', - '-owners', 'on'] - proc = subprocess.Popen(cmd, bufsize=-1, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (pliststr, err) = proc.communicate() - if proc.returncode: - print >> sys.stderr, 'Error: "%s" while mounting %s.' % (err, dmgname) - return None - if pliststr: - plist = plistlib.readPlistFromString(pliststr) - for entity in plist['system-entities']: - if 'mount-point' in entity: - mountpoints.append(entity['mount-point']) - - return mountpoints[0] - - -def unmountdmg(mountpoint): - """ - Unmounts the dmg at mountpoint - """ - proc = subprocess.Popen(['/usr/bin/hdiutil', 'detach', mountpoint], - bufsize=-1, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (dummy_output, err) = proc.communicate() - if proc.returncode: - print >> sys.stderr, 'Polite unmount failed: %s' % err - print >> sys.stderr, 'Attempting to force unmount %s' % mountpoint - # try forcing the unmount - retcode = subprocess.call(['/usr/bin/hdiutil', 'detach', mountpoint, - '-force']) - if retcode: - print >> sys.stderr, 'Failed to unmount %s' % mountpoint - - -def locate_basesystem_dmg(nbi): - '''Finds and returns the relative path to the BaseSystem.dmg within the - NetInstall.dmg''' - source_boot_plist = os.path.join(nbi, 'i386/com.apple.Boot.plist') - try: - boot_args = plistlib.readPlist(source_boot_plist) - except Exception, err: - print >> sys.stderr, err - sys.exit(-1) - kernel_flags = boot_args.get('Kernel Flags') - if not kernel_flags: - print >> sys.stderr, 'i386/com.apple.Boot.plist is missing Kernel Flags' - sys.exit(-1) - # kernel flags should in the form 'root-dmg=file:///path' - if not kernel_flags.startswith('root-dmg='): - print >> sys.stderr, 'Unexpected Kernel Flags: %s' % kernel_flags - sys.exit(-1) - file_url = kernel_flags[9:] - dmg_path = urlparse.unquote(urlparse.urlparse(file_url).path) - # return path minus leading slash - return dmg_path.lstrip('/') - - -def copy_system_version_plist(nbi, target_volume): - '''Copies System/Library/CoreServices/SystemVersion.plist from the - BaseSystem.dmg to the target volume.''' - netinstall_dmg = os.path.join(nbi, 'NetInstall.dmg') - if not os.path.exists(netinstall_dmg): - print >> sys.stderr, "Missing NetInstall.dmg from nbi folder" - sys.exit(-1) - print 'Mounting %s...' % netinstall_dmg - netinstall_mount = mountdmg(netinstall_dmg) - if not netinstall_mount: - sys.exit(-1) - basesystem_dmg = os.path.join(netinstall_mount, locate_basesystem_dmg(nbi)) - print 'Mounting %s...' % basesystem_dmg - basesystem_mount = mountdmg(basesystem_dmg) - if not basesystem_mount: - unmountdmg(netinstall_mount) - sys.exit(-1) - source = os.path.join( - basesystem_mount, 'System/Library/CoreServices/SystemVersion.plist') - dest = os.path.join( - target_volume, 'System/Library/CoreServices/SystemVersion.plist') - try: - subprocess.check_call( - ['/usr/bin/ditto', '-V', source, dest]) - except subprocess.CalledProcessError, err: - print >> sys.stderr, err - unmountdmg(basesystem_mount) - unmountdmg(netinstall_mount) - sys.exit(-1) - - unmountdmg(basesystem_mount) - unmountdmg(netinstall_mount) - - -def copy_boot_files(nbi, target_volume): - '''Copies some boot files, yo''' - files_to_copy = [ - ['NetInstall.dmg', 'NetInstall.dmg'], - ['i386/PlatformSupport.plist', - 'System/Library/CoreServices/PlatformSupport.plist'], - ['i386/booter', 'System/Library/CoreServices/boot.efi'], - ['i386/booter', 'usr/standalone/i386/boot.efi'], - ['i386/x86_64/kernelcache', - 'System/Library/PrelinkedKernels/prelinkedkernel'] - ] - for source, dest in files_to_copy: - full_source = os.path.join(nbi, source) - full_dest = os.path.join(target_volume, dest) - try: - subprocess.check_call( - ['/usr/bin/ditto', '-V', full_source, full_dest]) - except subprocess.CalledProcessError, err: - print >> sys.stderr, err - sys.exit(-1) - - -def make_boot_plist(nbi, target_volume): - '''Creates our com.apple.Boot.plist''' - source_boot_plist = os.path.join(nbi, 'i386/com.apple.Boot.plist') - try: - boot_args = plistlib.readPlist(source_boot_plist) - except Exception, err: - print >> sys.stderr, err - sys.exit(-1) - kernel_flags = boot_args.get('Kernel Flags') - if not kernel_flags: - print >> sys.stderr, 'i386/com.apple.Boot.plist is missing Kernel Flags' - sys.exit(-1) - # prepend the container-dmg path - boot_args['Kernel Flags'] = ( - 'container-dmg=file:///NetInstall.dmg ' + kernel_flags) - boot_plist = os.path.join( - target_volume, - 'Library/Preferences/SystemConfiguration/com.apple.Boot.plist') - plist_dir = os.path.dirname(boot_plist) - if not os.path.exists(plist_dir): - os.makedirs(plist_dir) - try: - plistlib.writePlist(boot_args, boot_plist) - except Exception, err: - print >> sys.stderr, err - sys.exit(-1) - - -def bless(target_volume, label=None): - '''Bless the target volume''' - blessfolder = os.path.join(target_volume, 'System/Library/CoreServices') - if not label: - label = os.path.basename(target_volume) - try: - subprocess.check_call( - ['/usr/sbin/bless', '--folder', blessfolder, '--label', label]) - except subprocess.CalledProcessError, err: - print >> sys.stderr, err - sys.exit(-1) - - -def main(): - '''Do the thing we were made for''' - parser = argparse.ArgumentParser() - parser.add_argument('--nbi', required=True, metavar='path_to_nbi', - help='Path to nbi folder created by autonbi.') - parser.add_argument('--volume', required=True, - metavar='path_to_disk_volume', - help='Path to disk volume.') - args = parser.parse_args() - copy_system_version_plist(args.nbi, args.volume) - copy_boot_files(args.nbi, args.volume) - make_boot_plist(args.nbi, args.volume) - bless(args.volume) - - -if __name__ == '__main__': - main() diff --git a/make_firmwareupdater_pkg.sh b/make_firmwareupdater_pkg.sh deleted file mode 100755 index 29dcce0..0000000 --- a/make_firmwareupdater_pkg.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh -# Based on investigations and work by Pepijn Bruienne -# Expects a single /Applications/Install macOS High Sierra*.app on disk - -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -IDENTIFIER="com.foo.FirmwareUpdateStandalone" -VERSION=1.0 - -# find the Install macOS High Sierra.app and mount the embedded InstallESD disk image -echo "Mounting High Sierra ESD disk image..." -/usr/bin/hdiutil mount /Applications/Install\ macOS\ High\ Sierra*.app/Contents/SharedSupport/InstallESD.dmg - -# expand the FirmwareUpdate.pkg so we can copy resources from it -echo "Expanding FirmwareUpdate.pkg" -/usr/sbin/pkgutil --expand /Volumes/InstallESD/Packages/FirmwareUpdate.pkg /tmp/FirmwareUpdate - -# we don't need the disk image any more -echo "Ejecting disk image..." -/usr/bin/hdiutil eject /Volumes/InstallESD - -# make a place to stage our pkg resources -/bin/mkdir -p /tmp/FirmwareUpdateStandalone/scripts - -# copy the needed resources -echo "Copying package resources..." -/bin/cp /tmp/FirmwareUpdate/Scripts/postinstall_actions/update /tmp/FirmwareUpdateStandalone/scripts/postinstall -# add an exit 0 at the end of the script -echo "" >> /tmp/FirmwareUpdateStandalone/scripts/postinstall -echo "" >> /tmp/FirmwareUpdateStandalone/scripts/postinstall -echo "exit 0" >> /tmp/FirmwareUpdateStandalone/scripts/postinstall -/bin/cp -R /tmp/FirmwareUpdate/Scripts/Tools /tmp/FirmwareUpdateStandalone/scripts/ - -# build the package -echo "Building standalone package..." -/usr/bin/pkgbuild --nopayload --scripts /tmp/FirmwareUpdateStandalone/scripts --identifier "$IDENTIFIER" --version "$VERSION" /tmp/FirmwareUpdateStandalone/FirmwareUpdateStandalone.pkg - -# clean up -/bin/rm -r /tmp/FirmwareUpdate -/bin/rm -r /tmp/FirmwareUpdateStandalone/scripts \ No newline at end of file From 9d43e0c3b0e350e4722ab9a62e397f5329742a22 Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Tue, 1 Feb 2022 14:25:05 -0800 Subject: [PATCH 7/7] More README updates --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c6c73e8..38ab62e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,9 @@ Some scripts that might be of use to macOS admins. Might be related to Munki; might not. -These are only supported using Apple's Python on macOS. There is no support for running these on Windows or Linux. +These are currently only supported using Apple's Python on macOS. There is no support for running these on Windows or Linux. + +In macOS 12.3, Apple will be removing its Python 2.7 install. You'll need to provide your own Python to use these scripts. You may also need to install additional Python modules. #### getmacosipsws.py @@ -13,7 +15,7 @@ Quick-and-dirty tool to download the macOS IPSW files currently advertised by Ap This script can create disk images containing macOS Installer applications available via Apple's softwareupdate catalogs. -Run `./installinstallmacos.py --help` to see the available options. +Run `python ./installinstallmacos.py --help` to see the available options. The tool assembles "Install macOS" applications by downloading the packages from Apple's softwareupdate servers and then installing them into a new empty disk image.