From 5fbced748825cabe88c7eb197ee9fe37cbcce353 Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Mon, 24 Sep 2018 11:18:05 -0700 Subject: [PATCH 1/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1473e9d..612fedb 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Command '['/usr/sbin/installer', '-pkg', './content/downloads/07/20/091-95774/aw Product installation failed. ``` -Use a compatible Mac or select a diffrent build compatible with your current hardware and try again. +Use a compatible Mac or select a diffrent build compatible with your current hardware and try again. You may also have success running the script in a VM; the InstallationCheck script in versions of the macOS installer to date skips the checks (and returns success) when run on a VM. Run `./installinstallmacos.py --help` to see the available options. From f989a483b83ae8a8ddbe9b076411e705b6a254df Mon Sep 17 00:00:00 2001 From: Brandon Kurtz Date: Mon, 24 Sep 2018 21:45:11 -0400 Subject: [PATCH 2/5] fixing typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 612fedb..e21ca13 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Command '['/usr/sbin/installer', '-pkg', './content/downloads/07/20/091-95774/aw Product installation failed. ``` -Use a compatible Mac or select a diffrent build compatible with your current hardware and try again. You may also have success running the script in a VM; the InstallationCheck script in versions of the macOS installer to date skips the checks (and returns success) when run on a VM. +Use a compatible Mac or select a different build compatible with your current hardware and try again. You may also have success running the script in a VM; the InstallationCheck script in versions of the macOS installer to date skips the checks (and returns success) when run on a VM. Run `./installinstallmacos.py --help` to see the available options. From 9b6c4cab9c60d540f0a0bc0df4e5e2f4fe24a007 Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Sat, 29 Sep 2018 06:47:50 -0700 Subject: [PATCH 3/5] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e21ca13..4dd575c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,11 @@ This command converts the output of Imagr's `make nbi` into a bootable external This script can create disk images containing macOS Installer applications available via Apple's softwareupdate catalogs. -It does this by downloading the packages from Apple's softwareupdate servers and then installing them into a new empty disk image. +Run `./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. + +If `/usr/bin/installer` returns errors during this process, it can be useful to examine `/var/log/install.log` for clues. Since it is using Apple's installer, any install check or volume check scripts are run. This means that you can only use this tool to create a diskimage containing the versions of macOS that will run on the exact machine you are running the script on. @@ -39,8 +43,6 @@ Product installation failed. Use a compatible Mac or select a different build compatible with your current hardware and try again. You may also have success running the script in a VM; the InstallationCheck script in versions of the macOS installer to date skips the checks (and returns success) when run on a VM. -Run `./installinstallmacos.py --help` to see the available options. - #### 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. From 06179680c25345511c56a96a10a5855f978e430a Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Tue, 2 Oct 2018 18:58:38 +0200 Subject: [PATCH 4/5] No longer defaults to Seed Program sucatalog. Use new --seedprogram option to specify CustomerSeed, DeveloperSeed, or PublicSeed if desired. --- installinstallmacos.py | 74 +++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/installinstallmacos.py b/installinstallmacos.py index ffb3abb..411a345 100755 --- a/installinstallmacos.py +++ b/installinstallmacos.py @@ -30,17 +30,21 @@ import os import plistlib import subprocess import sys -import time import urlparse import xattr from xml.dom import minidom from xml.parsers.expat import ExpatError -DEFAULT_SUCATALOG = ( - 'https://swscan.apple.com/content/catalogs/others/' - 'index-10.13seed-10.13-10.12-10.11-10.10-10.9' - '-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz') +DEFAULT_SUCATALOGS = { + '17': 'https://swscan.apple.com/content/catalogs/others/' + 'index-10.13-10.12-10.11-10.10-10.9' + '-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', + '18': 'https://swscan.apple.com/content/catalogs/others/' + 'index-10.14-10.13-10.12-10.11-10.10-10.9' + '-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', +} + SEED_CATALOGS_PLIST = ( '/System/Library/PrivateFrameworks/Seeding.framework/Versions/Current/' @@ -55,17 +59,24 @@ def get_seeding_program(sucatalog_url): for key, value in seed_catalogs.items(): if sucatalog_url == value: return key + return '' except (OSError, ExpatError, AttributeError, KeyError): - return None + return '' -def get_seed_catalog(): +def get_seed_catalog(seedname='DeveloperSeed'): '''Returns the developer seed sucatalog''' try: seed_catalogs = plistlib.readPlist(SEED_CATALOGS_PLIST) - return seed_catalogs.get('DeveloperSeed', DEFAULT_SUCATALOG) + return seed_catalogs.get(seedname) except (OSError, ExpatError, AttributeError, KeyError): - return DEFAULT_SUCATALOG + return '' + + +def get_default_catalog(): + '''Returns the default softwareupdate catalog for the current OS''' + darwin_major = os.uname()[2].split('.')[0] + return DEFAULT_SUCATALOGS.get(darwin_major) def make_sparse_image(volume_name, output_path): @@ -163,8 +174,11 @@ class ReplicationError(Exception): pass -def replicate_url(full_url, root_dir='/tmp', - show_progress=False, ignore_cache=False): +def replicate_url(full_url, + root_dir='/tmp', + show_progress=False, + ignore_cache=False, + attempt_resume=False): '''Downloads a URL and stores it in the same relative path on our filesystem. Returns a path to the replicated file.''' @@ -180,6 +194,8 @@ def replicate_url(full_url, root_dir='/tmp', '-o', local_file_path] if not ignore_cache and os.path.exists(local_file_path): curl_cmd.extend(['-z', local_file_path]) + if attempt_resume: + curl_cmd.extend(['-C', '-']) curl_cmd.append(full_url) print "Downloading %s..." % full_url try: @@ -278,8 +294,8 @@ def download_and_parse_sucatalog(sucatalog, workdir, ignore_cache=False): print >> sys.stderr, 'Could not replicate %s: %s' % (sucatalog, err) exit(-1) if os.path.splitext(localcatalogpath)[1] == '.gz': - with gzip.open(localcatalogpath) as f: - content = f.read() + with gzip.open(localcatalogpath) as the_file: + content = the_file.read() try: catalog = plistlib.readPlistFromString(content) return catalog @@ -350,7 +366,8 @@ def replicate_product(catalog, product_id, workdir, ignore_cache=False): try: replicate_url( package['URL'], root_dir=workdir, - show_progress=True, ignore_cache=ignore_cache) + show_progress=True, ignore_cache=ignore_cache, + attempt_resume=(not ignore_cache)) except ReplicationError, err: print >> sys.stderr, ( 'Could not replicate %s: %s' % (package['URL'], err)) @@ -382,9 +399,12 @@ def main(): 'run again with sudo or as root.') parser = argparse.ArgumentParser() - parser.add_argument('--catalogurl', metavar='sucatalog_url', - default=get_seed_catalog(), - help='Software Update catalog URL.') + parser.add_argument('--seedprogram', default='', + help='Which Seed Program catalog to use. Valid values ' + 'are CustomerSeed, DeveloperSeed, and PublicSeed.') + parser.add_argument('--catalogurl', default='', + help='Software Update catalog URL. This option ' + 'overrides any seedprogram option.') parser.add_argument('--workdir', metavar='path_to_working_dir', default='.', help='Path to working directory on a volume with over ' @@ -403,9 +423,25 @@ def main(): help='Ignore any previously cached files.') args = parser.parse_args() + if args.catalogurl: + su_catalog_url = args.sucatalog_url + elif args.seedprogram: + su_catalog_url = get_seed_catalog(args.seedprogram) + if not su_catalog_url: + print >> sys.stderr, ( + 'Could not find a catalog url for seed program %s' + % args.seedprogram) + exit(-1) + else: + su_catalog_url = get_default_catalog() + if not su_catalog_url: + print >> sys.stderr, ( + 'Could not find a default catalog url for this OS version.') + exit(-1) + # download sucatalog and look for products that are for macOS installers catalog = download_and_parse_sucatalog( - args.catalogurl, args.workdir, ignore_cache=args.ignore_cache) + su_catalog_url, args.workdir, ignore_cache=args.ignore_cache) product_info = os_installer_product_info( catalog, args.workdir, ignore_cache=args.ignore_cache) @@ -416,7 +452,7 @@ def main(): # display a menu of choices (some seed catalogs have multiple installers) print '%2s %12s %10s %8s %11s %s' % ('#', 'ProductID', 'Version', - 'Build', 'Post Date', 'Title') + 'Build', 'Post Date', 'Title') for index, product_id in enumerate(product_info): print '%2s %12s %10s %8s %11s %s' % ( index + 1, From c0cdf39725c517225c6115ea09ba5703549d8aea Mon Sep 17 00:00:00 2001 From: Greg Neagle Date: Thu, 4 Oct 2018 00:56:38 +0200 Subject: [PATCH 5/5] Fix reference to args.sucatalog_url. Fixes #10 --- installinstallmacos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installinstallmacos.py b/installinstallmacos.py index 411a345..d1a10a2 100755 --- a/installinstallmacos.py +++ b/installinstallmacos.py @@ -424,7 +424,7 @@ def main(): args = parser.parse_args() if args.catalogurl: - su_catalog_url = args.sucatalog_url + su_catalog_url = args.catalogurl elif args.seedprogram: su_catalog_url = get_seed_catalog(args.seedprogram) if not su_catalog_url: