mirror of
https://github.com/grahampugh/macadmin-scripts.git
synced 2025-12-17 17:56:33 +00:00
Merge branch 'master' of https://github.com/munki/macadmin-scripts
This commit is contained in:
commit
57dd500dec
@ -23,6 +23,9 @@ A tool to download the parts for an Install macOS app from Apple's
|
|||||||
softwareupdate servers and install a functioning Install macOS app onto an
|
softwareupdate servers and install a functioning Install macOS app onto an
|
||||||
empty disk image'''
|
empty disk image'''
|
||||||
|
|
||||||
|
# Python 3 compatibility shims
|
||||||
|
from __future__ import (
|
||||||
|
absolute_import, division, print_function, unicode_literals)
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import gzip
|
import gzip
|
||||||
@ -30,8 +33,13 @@ import os
|
|||||||
import plistlib
|
import plistlib
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import urlparse
|
|
||||||
import xattr
|
import xattr
|
||||||
|
try:
|
||||||
|
# python 2
|
||||||
|
from urllib.parse import urlsplit
|
||||||
|
except ImportError:
|
||||||
|
# python 3
|
||||||
|
from urlparse import urlsplit
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
from xml.parsers.expat import ExpatError
|
from xml.parsers.expat import ExpatError
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
@ -109,10 +117,38 @@ def get_current_build_info():
|
|||||||
return build_info
|
return build_info
|
||||||
|
|
||||||
|
|
||||||
|
def get_input(prompt=None):
|
||||||
|
'''Python 2 and 3 wrapper for raw_input/input'''
|
||||||
|
try:
|
||||||
|
return raw_input(prompt)
|
||||||
|
except NameError:
|
||||||
|
# raw_input doesn't exist in Python 3
|
||||||
|
return input(prompt)
|
||||||
|
|
||||||
|
|
||||||
|
def read_plist(filepath):
|
||||||
|
'''Wrapper for the differences between Python 2 and Python 3's plistlib'''
|
||||||
|
try:
|
||||||
|
with open(filepath, "rb") as fileobj:
|
||||||
|
return plistlib.load(fileobj)
|
||||||
|
except AttributeError:
|
||||||
|
# plistlib module doesn't have a load function (as in Python 2)
|
||||||
|
return plistlib.readPlist(filepath)
|
||||||
|
|
||||||
|
|
||||||
|
def read_plist_from_string(bytestring):
|
||||||
|
'''Wrapper for the differences between Python 2 and Python 3's plistlib'''
|
||||||
|
try:
|
||||||
|
return plistlib.loads(bytestring)
|
||||||
|
except AttributeError:
|
||||||
|
# plistlib module doesn't have a load function (as in Python 2)
|
||||||
|
return plistlib.readPlistFromString(bytestring)
|
||||||
|
|
||||||
|
|
||||||
def get_seeding_program(sucatalog_url):
|
def get_seeding_program(sucatalog_url):
|
||||||
'''Returns a seeding program name based on the sucatalog_url'''
|
'''Returns a seeding program name based on the sucatalog_url'''
|
||||||
try:
|
try:
|
||||||
seed_catalogs = plistlib.readPlist(SEED_CATALOGS_PLIST)
|
seed_catalogs = read_plist(SEED_CATALOGS_PLIST)
|
||||||
for key, value in seed_catalogs.items():
|
for key, value in seed_catalogs.items():
|
||||||
if sucatalog_url == value:
|
if sucatalog_url == value:
|
||||||
return key
|
return key
|
||||||
@ -124,7 +160,7 @@ def get_seeding_program(sucatalog_url):
|
|||||||
def get_seed_catalog(seedname='DeveloperSeed'):
|
def get_seed_catalog(seedname='DeveloperSeed'):
|
||||||
'''Returns the developer seed sucatalog'''
|
'''Returns the developer seed sucatalog'''
|
||||||
try:
|
try:
|
||||||
seed_catalogs = plistlib.readPlist(SEED_CATALOGS_PLIST)
|
seed_catalogs = read_plist(SEED_CATALOGS_PLIST)
|
||||||
return seed_catalogs.get(seedname)
|
return seed_catalogs.get(seedname)
|
||||||
except (OSError, ExpatError, AttributeError, KeyError):
|
except (OSError, ExpatError, AttributeError, KeyError):
|
||||||
return ''
|
return ''
|
||||||
@ -133,8 +169,8 @@ def get_seed_catalog(seedname='DeveloperSeed'):
|
|||||||
def get_seeding_programs():
|
def get_seeding_programs():
|
||||||
'''Returns the list of seeding program names'''
|
'''Returns the list of seeding program names'''
|
||||||
try:
|
try:
|
||||||
seed_catalogs = plistlib.readPlist(SEED_CATALOGS_PLIST)
|
seed_catalogs = read_plist(SEED_CATALOGS_PLIST)
|
||||||
return seed_catalogs.keys()
|
return list(seed_catalogs.keys())
|
||||||
except (OSError, ExpatError, AttributeError, KeyError):
|
except (OSError, ExpatError, AttributeError, KeyError):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@ -151,17 +187,17 @@ def make_sparse_image(volume_name, output_path):
|
|||||||
'-volname', volume_name, '-type', 'SPARSE', '-plist', output_path]
|
'-volname', volume_name, '-type', 'SPARSE', '-plist', output_path]
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output(cmd)
|
output = subprocess.check_output(cmd)
|
||||||
except subprocess.CalledProcessError, err:
|
except subprocess.CalledProcessError as err:
|
||||||
print >> sys.stderr, err
|
print(err, file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
try:
|
try:
|
||||||
return plistlib.readPlistFromString(output)[0]
|
return read_plist_from_string(output)[0]
|
||||||
except IndexError, err:
|
except IndexError as err:
|
||||||
print >> sys.stderr, 'Unexpected output from hdiutil: %s' % output
|
print('Unexpected output from hdiutil: %s' % output, file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
except ExpatError, err:
|
except ExpatError as err:
|
||||||
print >> sys.stderr, 'Malformed output from hdiutil: %s' % output
|
print('Malformed output from hdiutil: %s' % output, file=sys.stderr)
|
||||||
print >> sys.stderr, err
|
print(err, file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
@ -169,16 +205,16 @@ def make_compressed_dmg(app_path, diskimagepath, volume_name):
|
|||||||
"""Returns path to newly-created compressed r/o disk image containing
|
"""Returns path to newly-created compressed r/o disk image containing
|
||||||
Install macOS.app"""
|
Install macOS.app"""
|
||||||
|
|
||||||
print ('Making read-only compressed disk image containing %s...'
|
print('Making read-only compressed disk image containing %s...'
|
||||||
% os.path.basename(app_path))
|
% os.path.basename(app_path))
|
||||||
cmd = ['/usr/bin/hdiutil', 'create', '-volname', volume_name, '-fs', 'HFS+',
|
cmd = ['/usr/bin/hdiutil', 'create', '-fs', 'HFS+',
|
||||||
'-srcfolder', app_path, diskimagepath]
|
'-srcfolder', app_path, diskimagepath]
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
except subprocess.CalledProcessError, err:
|
except subprocess.CalledProcessError as err:
|
||||||
print >> sys.stderr, err
|
print(err, file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
print 'Disk image created at: %s' % diskimagepath
|
print('Disk image created at: %s' % diskimagepath)
|
||||||
|
|
||||||
|
|
||||||
def mountdmg(dmgpath):
|
def mountdmg(dmgpath):
|
||||||
@ -194,10 +230,11 @@ def mountdmg(dmgpath):
|
|||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
(pliststr, err) = proc.communicate()
|
(pliststr, err) = proc.communicate()
|
||||||
if proc.returncode:
|
if proc.returncode:
|
||||||
print >> sys.stderr, 'Error: "%s" while mounting %s.' % (err, dmgname)
|
print('Error: "%s" while mounting %s.' % (err, dmgname),
|
||||||
|
file=sys.stderr)
|
||||||
return None
|
return None
|
||||||
if pliststr:
|
if pliststr:
|
||||||
plist = plistlib.readPlistFromString(pliststr)
|
plist = read_plist_from_string(pliststr)
|
||||||
for entity in plist['system-entities']:
|
for entity in plist['system-entities']:
|
||||||
if 'mount-point' in entity:
|
if 'mount-point' in entity:
|
||||||
mountpoints.append(entity['mount-point'])
|
mountpoints.append(entity['mount-point'])
|
||||||
@ -214,13 +251,13 @@ def unmountdmg(mountpoint):
|
|||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
(dummy_output, err) = proc.communicate()
|
(dummy_output, err) = proc.communicate()
|
||||||
if proc.returncode:
|
if proc.returncode:
|
||||||
print >> sys.stderr, 'Polite unmount failed: %s' % err
|
print('Polite unmount failed: %s' % err, file=sys.stderr)
|
||||||
print >> sys.stderr, 'Attempting to force unmount %s' % mountpoint
|
print('Attempting to force unmount %s' % mountpoint, file=sys.stderr)
|
||||||
# try forcing the unmount
|
# try forcing the unmount
|
||||||
retcode = subprocess.call(['/usr/bin/hdiutil', 'detach', mountpoint,
|
retcode = subprocess.call(['/usr/bin/hdiutil', 'detach', mountpoint,
|
||||||
'-force'])
|
'-force'])
|
||||||
if retcode:
|
if retcode:
|
||||||
print >> sys.stderr, 'Failed to unmount %s' % mountpoint
|
print('Failed to unmount %s' % mountpoint, file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def install_product(dist_path, target_vol):
|
def install_product(dist_path, target_vol):
|
||||||
@ -230,8 +267,8 @@ def install_product(dist_path, target_vol):
|
|||||||
try:
|
try:
|
||||||
subprocess.check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
return True
|
return True
|
||||||
except subprocess.CalledProcessError, err:
|
except subprocess.CalledProcessError as err:
|
||||||
print >> sys.stderr, err
|
print(err, file=sys.stderr)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -248,7 +285,7 @@ def replicate_url(full_url,
|
|||||||
'''Downloads a URL and stores it in the same relative path on our
|
'''Downloads a URL and stores it in the same relative path on our
|
||||||
filesystem. Returns a path to the replicated file.'''
|
filesystem. Returns a path to the replicated file.'''
|
||||||
|
|
||||||
path = urlparse.urlsplit(full_url)[2]
|
path = urlsplit(full_url)[2]
|
||||||
relative_url = path.lstrip('/')
|
relative_url = path.lstrip('/')
|
||||||
relative_url = os.path.normpath(relative_url)
|
relative_url = os.path.normpath(relative_url)
|
||||||
local_file_path = os.path.join(root_dir, relative_url)
|
local_file_path = os.path.join(root_dir, relative_url)
|
||||||
@ -263,10 +300,10 @@ def replicate_url(full_url,
|
|||||||
if attempt_resume:
|
if attempt_resume:
|
||||||
curl_cmd.extend(['-C', '-'])
|
curl_cmd.extend(['-C', '-'])
|
||||||
curl_cmd.append(full_url)
|
curl_cmd.append(full_url)
|
||||||
# print "Downloading %s..." % full_url
|
# print("Downloading %s..." % full_url)
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(curl_cmd)
|
subprocess.check_call(curl_cmd)
|
||||||
except subprocess.CalledProcessError, err:
|
except subprocess.CalledProcessError as err:
|
||||||
raise ReplicationError(err)
|
raise ReplicationError(err)
|
||||||
return local_file_path
|
return local_file_path
|
||||||
|
|
||||||
@ -278,9 +315,9 @@ def parse_server_metadata(filename):
|
|||||||
title = ''
|
title = ''
|
||||||
vers = ''
|
vers = ''
|
||||||
try:
|
try:
|
||||||
md_plist = plistlib.readPlist(filename)
|
md_plist = read_plist(filename)
|
||||||
except (OSError, IOError, ExpatError), err:
|
except (OSError, IOError, ExpatError) as err:
|
||||||
print >> sys.stderr, 'Error reading %s: %s' % (filename, err)
|
print('Error reading %s: %s' % (filename, err), file=sys.stderr)
|
||||||
return {}
|
return {}
|
||||||
vers = md_plist.get('CFBundleShortVersionString', '')
|
vers = md_plist.get('CFBundleShortVersionString', '')
|
||||||
localization = md_plist.get('localization', {})
|
localization = md_plist.get('localization', {})
|
||||||
@ -303,12 +340,11 @@ def get_server_metadata(catalog, product_key, workdir, ignore_cache=False):
|
|||||||
smd_path = replicate_url(
|
smd_path = replicate_url(
|
||||||
url, root_dir=workdir, ignore_cache=ignore_cache)
|
url, root_dir=workdir, ignore_cache=ignore_cache)
|
||||||
return smd_path
|
return smd_path
|
||||||
except ReplicationError, err:
|
except ReplicationError as err:
|
||||||
print >> sys.stderr, (
|
print('Could not replicate %s: %s' % (url, err), file=sys.stderr)
|
||||||
'Could not replicate %s: %s' % (url, err))
|
|
||||||
return None
|
return None
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print >> sys.stderr, 'Malformed catalog.'
|
print('Malformed catalog.', file=sys.stderr)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -319,10 +355,10 @@ def parse_dist(filename):
|
|||||||
try:
|
try:
|
||||||
dom = minidom.parse(filename)
|
dom = minidom.parse(filename)
|
||||||
except ExpatError:
|
except ExpatError:
|
||||||
print >> sys.stderr, 'Invalid XML in %s' % filename
|
print('Invalid XML in %s' % filename, file=sys.stderr)
|
||||||
return dist_info
|
return dist_info
|
||||||
except IOError, err:
|
except IOError as err:
|
||||||
print >> sys.stderr, 'Error reading %s: %s' % (filename, err)
|
print('Error reading %s: %s' % (filename, err), file=sys.stderr)
|
||||||
return dist_info
|
return dist_info
|
||||||
|
|
||||||
auxinfos = dom.getElementsByTagName('auxinfo')
|
auxinfos = dom.getElementsByTagName('auxinfo')
|
||||||
@ -380,26 +416,26 @@ def download_and_parse_sucatalog(sucatalog, workdir, ignore_cache=False):
|
|||||||
try:
|
try:
|
||||||
localcatalogpath = replicate_url(
|
localcatalogpath = replicate_url(
|
||||||
sucatalog, root_dir=workdir, ignore_cache=ignore_cache)
|
sucatalog, root_dir=workdir, ignore_cache=ignore_cache)
|
||||||
except ReplicationError, err:
|
except ReplicationError as err:
|
||||||
print >> sys.stderr, 'Could not replicate %s: %s' % (sucatalog, err)
|
print('Could not replicate %s: %s' % (sucatalog, err), file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
if os.path.splitext(localcatalogpath)[1] == '.gz':
|
if os.path.splitext(localcatalogpath)[1] == '.gz':
|
||||||
with gzip.open(localcatalogpath) as the_file:
|
with gzip.open(localcatalogpath) as the_file:
|
||||||
content = the_file.read()
|
content = the_file.read()
|
||||||
try:
|
try:
|
||||||
catalog = plistlib.readPlistFromString(content)
|
catalog = read_plist_from_string(content)
|
||||||
return catalog
|
return catalog
|
||||||
except ExpatError, err:
|
except ExpatError as err:
|
||||||
print >> sys.stderr, (
|
print('Error reading %s: %s' % (localcatalogpath, err),
|
||||||
'Error reading %s: %s' % (localcatalogpath, err))
|
file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
catalog = plistlib.readPlist(localcatalogpath)
|
catalog = read_plist(localcatalogpath)
|
||||||
return catalog
|
return catalog
|
||||||
except (OSError, IOError, ExpatError), err:
|
except (OSError, IOError, ExpatError) as err:
|
||||||
print >> sys.stderr, (
|
print('Error reading %s: %s' % (localcatalogpath, err),
|
||||||
'Error reading %s: %s' % (localcatalogpath, err))
|
file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
@ -408,8 +444,7 @@ def find_mac_os_installers(catalog):
|
|||||||
installers'''
|
installers'''
|
||||||
mac_os_installer_products = []
|
mac_os_installer_products = []
|
||||||
if 'Products' in catalog:
|
if 'Products' in catalog:
|
||||||
product_keys = list(catalog['Products'].keys())
|
for product_key in catalog['Products'].keys():
|
||||||
for product_key in product_keys:
|
|
||||||
product = catalog['Products'][product_key]
|
product = catalog['Products'][product_key]
|
||||||
try:
|
try:
|
||||||
if product['ExtendedMetaInfo'][
|
if product['ExtendedMetaInfo'][
|
||||||
@ -436,16 +471,17 @@ def os_installer_product_info(catalog, workdir, ignore_cache=False):
|
|||||||
try:
|
try:
|
||||||
dist_path = replicate_url(
|
dist_path = replicate_url(
|
||||||
dist_url, root_dir=workdir, ignore_cache=ignore_cache)
|
dist_url, root_dir=workdir, ignore_cache=ignore_cache)
|
||||||
except ReplicationError, err:
|
except ReplicationError as err:
|
||||||
print >> sys.stderr, 'Could not replicate %s: %s' % (dist_url, err)
|
print('Could not replicate %s: %s' % (dist_url, err),
|
||||||
dist_info = parse_dist(dist_path)
|
file=sys.stderr)
|
||||||
product_info[product_key]['DistributionPath'] = dist_path
|
else:
|
||||||
unsupported_models = get_unsupported_models(dist_path)
|
dist_info = parse_dist(dist_path)
|
||||||
product_info[product_key]['UnsupportedModels'] = unsupported_models
|
product_info[product_key]['DistributionPath'] = dist_path
|
||||||
board_ids = get_board_ids(dist_path)
|
unsupported_models = get_unsupported_models(dist_path)
|
||||||
product_info[product_key]['BoardIDs'] = board_ids
|
product_info[product_key]['UnsupportedModels'] = unsupported_models
|
||||||
product_info[product_key].update(dist_info)
|
board_ids = get_board_ids(dist_path)
|
||||||
|
product_info[product_key]['BoardIDs'] = board_ids
|
||||||
|
product_info[product_key].update(dist_info)
|
||||||
return product_info
|
return product_info
|
||||||
|
|
||||||
|
|
||||||
@ -470,18 +506,17 @@ def replicate_product(catalog, product_id, workdir, ignore_cache=False):
|
|||||||
package['URL'], root_dir=workdir,
|
package['URL'], root_dir=workdir,
|
||||||
show_progress=True, ignore_cache=ignore_cache,
|
show_progress=True, ignore_cache=ignore_cache,
|
||||||
attempt_resume=(not ignore_cache))
|
attempt_resume=(not ignore_cache))
|
||||||
except ReplicationError, err:
|
except ReplicationError as err:
|
||||||
print >> sys.stderr, (
|
print('Could not replicate %s: %s' % (package['URL'], err),
|
||||||
'Could not replicate %s: %s' % (package['URL'], err))
|
file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
if 'MetadataURL' in package:
|
if 'MetadataURL' in package:
|
||||||
try:
|
try:
|
||||||
replicate_url(package['MetadataURL'], root_dir=workdir,
|
replicate_url(package['MetadataURL'], root_dir=workdir,
|
||||||
ignore_cache=ignore_cache)
|
ignore_cache=ignore_cache)
|
||||||
except ReplicationError, err:
|
except ReplicationError as err:
|
||||||
print >> sys.stderr, (
|
print('Could not replicate %s: %s'
|
||||||
'Could not replicate %s: %s'
|
% (package['MetadataURL'], err), file=sys.stderr)
|
||||||
% (package['MetadataURL'], err))
|
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
@ -561,31 +596,29 @@ def main():
|
|||||||
build_info = get_current_build_info()
|
build_info = get_current_build_info()
|
||||||
is_vm = is_a_vm()
|
is_vm = is_a_vm()
|
||||||
|
|
||||||
print "This Mac:"
|
print('This Mac:')
|
||||||
if is_vm == True:
|
if is_vm == True:
|
||||||
print "Identified as a Virtual Machine"
|
print('Identified as a Virtual Machine')
|
||||||
print "%-17s: %s" % ('Model Identifier', hw_model)
|
print('%-17s: %s' % ('Model Identifier', hw_model))
|
||||||
print "%-17s: %s" % ('Board ID', board_id)
|
print('%-17s: %s' % ('Board ID', board_id))
|
||||||
print "%-17s: %s" % ('OS Version', build_info[0])
|
print('%-17s: %s' % ('OS Version', build_info[0]))
|
||||||
print "%-17s: %s\n" % ('Build ID', build_info[1])
|
print('%-17s: %s\n' % ('Build ID', build_info[1]))
|
||||||
|
|
||||||
if args.catalogurl:
|
if args.catalogurl:
|
||||||
su_catalog_url = args.catalogurl
|
su_catalog_url = args.catalogurl
|
||||||
elif args.seedprogram:
|
elif args.seedprogram:
|
||||||
su_catalog_url = get_seed_catalog(args.seedprogram)
|
su_catalog_url = get_seed_catalog(args.seedprogram)
|
||||||
if not su_catalog_url:
|
if not su_catalog_url:
|
||||||
print >> sys.stderr, (
|
print('Could not find a catalog url for seed program %s'
|
||||||
'Could not find a catalog url for seed program %s'
|
% args.seedprogram, file=sys.stderr)
|
||||||
% args.seedprogram)
|
print('Valid seeding programs are: %s'
|
||||||
print >> sys.stderr, (
|
% ', '.join(get_seeding_programs()), file=sys.stderr)
|
||||||
'Valid seeding programs are: %s'
|
|
||||||
% ', '.join(get_seeding_programs()))
|
|
||||||
exit(-1)
|
exit(-1)
|
||||||
else:
|
else:
|
||||||
su_catalog_url = get_default_catalog()
|
su_catalog_url = get_default_catalog()
|
||||||
if not su_catalog_url:
|
if not su_catalog_url:
|
||||||
print >> sys.stderr, (
|
print('Could not find a default catalog url for this OS version.',
|
||||||
'Could not find a default catalog url for this OS version.')
|
file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
# download sucatalog and look for products that are for macOS installers
|
# download sucatalog and look for products that are for macOS installers
|
||||||
@ -595,8 +628,8 @@ def main():
|
|||||||
catalog, args.workdir, ignore_cache=args.ignore_cache)
|
catalog, args.workdir, ignore_cache=args.ignore_cache)
|
||||||
|
|
||||||
if not product_info:
|
if not product_info:
|
||||||
print >> sys.stderr, (
|
print('No macOS installer products found in the sucatalog.',
|
||||||
'No macOS installer products found in the sucatalog.')
|
file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
output_plist = "%s/softwareupdate.plist" % args.workdir
|
output_plist = "%s/softwareupdate.plist" % args.workdir
|
||||||
@ -606,8 +639,8 @@ def main():
|
|||||||
valid_build_found = False
|
valid_build_found = False
|
||||||
|
|
||||||
# display a menu of choices (some seed catalogs have multiple installers)
|
# display a menu of choices (some seed catalogs have multiple installers)
|
||||||
print '%2s %-15s %-10s %-8s %-11s %-30s %s' % ('#', 'ProductID', 'Version',
|
print('%2s %-15s %-10s %-8s %-11s %-30s %s' % ('#', 'ProductID', 'Version',
|
||||||
'Build', 'Post Date', 'Title', 'Notes')
|
'Build', 'Post Date', 'Title', 'Notes'))
|
||||||
for index, product_id in enumerate(product_info):
|
for index, product_id in enumerate(product_info):
|
||||||
not_valid = ''
|
not_valid = ''
|
||||||
if hw_model in product_info[product_id]['UnsupportedModels'] and is_vm == False:
|
if hw_model in product_info[product_id]['UnsupportedModels'] and is_vm == False:
|
||||||
@ -619,7 +652,7 @@ def main():
|
|||||||
else:
|
else:
|
||||||
valid_build_found = True
|
valid_build_found = True
|
||||||
|
|
||||||
print '%2s %-15s %-10s %-8s %-11s %-30s %s' % (
|
print('%2s %-15s %-10s %-8s %-11s %-30s %s' % (
|
||||||
index + 1,
|
index + 1,
|
||||||
product_id,
|
product_id,
|
||||||
product_info[product_id]['version'],
|
product_info[product_id]['version'],
|
||||||
@ -627,7 +660,7 @@ def main():
|
|||||||
product_info[product_id]['PostDate'].strftime('%Y-%m-%d'),
|
product_info[product_id]['PostDate'].strftime('%Y-%m-%d'),
|
||||||
product_info[product_id]['title'],
|
product_info[product_id]['title'],
|
||||||
not_valid
|
not_valid
|
||||||
)
|
))
|
||||||
|
|
||||||
# go through various options for automatically determining the answer:
|
# go through various options for automatically determining the answer:
|
||||||
|
|
||||||
@ -648,7 +681,7 @@ def main():
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# determine the lowest valid build ID and select this
|
# determine the lowest valid build ID and select this
|
||||||
# when using auto and version options
|
# when using auto and version options
|
||||||
if (args.auto or args.version or args.os) and 'Beta' not in product_info[product_id]['title']:
|
if (args.auto or args.version or args.os) and 'Beta' not in product_info[product_id]['title']:
|
||||||
try:
|
try:
|
||||||
lowest_valid_build
|
lowest_valid_build
|
||||||
@ -685,13 +718,13 @@ def main():
|
|||||||
|
|
||||||
# Stop here if no valid builds found
|
# Stop here if no valid builds found
|
||||||
if valid_build_found == False:
|
if valid_build_found == False:
|
||||||
print 'No valid build found for this hardware'
|
print('No valid build found for this hardware')
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
# Output a plist of available updates and quit if list option chosen
|
# Output a plist of available updates and quit if list option chosen
|
||||||
if args.list:
|
if args.list:
|
||||||
plistlib.writePlist(pl, output_plist)
|
plistlib.writePlist(pl, output_plist)
|
||||||
print ('\n'
|
print('\n'
|
||||||
'Valid seeding programs are: %s'
|
'Valid seeding programs are: %s'
|
||||||
% ', '.join(get_seeding_programs()))
|
% ', '.join(get_seeding_programs()))
|
||||||
exit(0)
|
exit(0)
|
||||||
@ -701,35 +734,41 @@ def main():
|
|||||||
try:
|
try:
|
||||||
answer
|
answer
|
||||||
except NameError:
|
except NameError:
|
||||||
print ('\n'
|
print('\n'
|
||||||
'Build %s is not available. '
|
'Build %s is not available. '
|
||||||
'Run again without --build argument '
|
'Run again without --build argument '
|
||||||
'to select a valid build to download.\n' % args.build)
|
'to select a valid build to download.\n' % args.build)
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
else:
|
||||||
print '\nBuild %s available. Downloading #%s...\n' % (args.build, answer)
|
print('\n'
|
||||||
|
'Build %s available. Downloading #%s...\n'
|
||||||
|
% (args.build, answer))
|
||||||
elif args.current:
|
elif args.current:
|
||||||
try:
|
try:
|
||||||
answer
|
answer
|
||||||
except NameError:
|
except NameError:
|
||||||
print ('\n'
|
print('\n'
|
||||||
'Build %s is not available. '
|
'Build %s is not available. '
|
||||||
'Run again without --current argument '
|
'Run again without --current argument '
|
||||||
'to select a valid build to download.\n' % build_info[0])
|
'to select a valid build to download.\n' % build_info[0])
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
else:
|
||||||
print '\nBuild %s available. Downloading #%s...\n' % (build_info[0], answer)
|
print('\n'
|
||||||
|
'Build %s available. Downloading #%s...\n'
|
||||||
|
% (build_info[0], answer))
|
||||||
elif args.version:
|
elif args.version:
|
||||||
try:
|
try:
|
||||||
answer
|
answer
|
||||||
except NameError:
|
except NameError:
|
||||||
print ('\n'
|
print('\n'
|
||||||
'Item # %s is not available. '
|
'Item # %s is not available. '
|
||||||
'Run again without --version argument '
|
'Run again without --version argument '
|
||||||
'to select a valid build to download.\n' % args.version)
|
'to select a valid build to download.\n' % args.version)
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
else:
|
||||||
print '\nBuild %s selected. Downloading #%s...\n' % (lowest_valid_build, answer)
|
print('\n'
|
||||||
|
'Build %s selected. Downloading #%s...\n'
|
||||||
|
% (lowest_valid_build, answer))
|
||||||
elif args.os:
|
elif args.os:
|
||||||
try:
|
try:
|
||||||
answer
|
answer
|
||||||
@ -740,30 +779,34 @@ def main():
|
|||||||
'to select a valid build to download.\n' % args.os)
|
'to select a valid build to download.\n' % args.os)
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
else:
|
||||||
print '\nBuild %s selected. Downloading #%s...\n' % (lowest_valid_build, answer)
|
print('\n'
|
||||||
|
'Build %s selected. Downloading #%s...\n'
|
||||||
|
% (lowest_valid_build, answer))
|
||||||
elif args.auto:
|
elif args.auto:
|
||||||
try:
|
try:
|
||||||
answer
|
answer
|
||||||
except NameError:
|
except NameError:
|
||||||
print ('\n'
|
print('\n'
|
||||||
'No valid version available. '
|
'No valid version available. '
|
||||||
'Run again without --auto argument '
|
'Run again without --auto argument '
|
||||||
'to select a valid build to download.\n')
|
'to select a valid build to download.\n')
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
else:
|
||||||
print '\nBuild %s selected. Downloading #%s...\n' % (lowest_valid_build, answer)
|
print('\n'
|
||||||
|
'Build %s selected. Downloading #%s...\n'
|
||||||
|
% (lowest_valid_build, answer))
|
||||||
else:
|
else:
|
||||||
# default option to interactively offer selection
|
# default option to interactively offer selection
|
||||||
answer = raw_input(
|
answer = get_input(
|
||||||
'\nChoose a product to download (1-%s): ' % len(product_info))
|
'\nChoose a product to download (1-%s): ' % len(product_info))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
index = int(answer) - 1
|
index = int(answer) - 1
|
||||||
if index < 0:
|
if index < 0:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
product_id = product_info.keys()[index]
|
product_id = list(product_info.keys())[index]
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
print 'Exiting.'
|
print('Exiting.')
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
# download all the packages for the selected product
|
# download all the packages for the selected product
|
||||||
@ -779,7 +822,7 @@ def main():
|
|||||||
os.unlink(sparse_diskimage_path)
|
os.unlink(sparse_diskimage_path)
|
||||||
|
|
||||||
# make an empty sparseimage and mount it
|
# make an empty sparseimage and mount it
|
||||||
print 'Making empty sparseimage...'
|
print('Making empty sparseimage...')
|
||||||
sparse_diskimage_path = make_sparse_image(volname, sparse_diskimage_path)
|
sparse_diskimage_path = make_sparse_image(volname, sparse_diskimage_path)
|
||||||
mountpoint = mountdmg(sparse_diskimage_path)
|
mountpoint = mountdmg(sparse_diskimage_path)
|
||||||
if mountpoint:
|
if mountpoint:
|
||||||
@ -788,16 +831,18 @@ def main():
|
|||||||
product_info[product_id]['DistributionPath'],
|
product_info[product_id]['DistributionPath'],
|
||||||
mountpoint)
|
mountpoint)
|
||||||
if not success:
|
if not success:
|
||||||
print >> sys.stderr, 'Product installation failed.'
|
print('Product installation failed.', file=sys.stderr)
|
||||||
unmountdmg(mountpoint)
|
unmountdmg(mountpoint)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
# add the seeding program xattr to the app if applicable
|
# add the seeding program xattr to the app if applicable
|
||||||
seeding_program = get_seeding_program(args.catalogurl)
|
seeding_program = get_seeding_program(su_catalog_url)
|
||||||
if seeding_program:
|
if seeding_program:
|
||||||
installer_app = find_installer_app(mountpoint)
|
installer_app = find_installer_app(mountpoint)
|
||||||
if installer_app:
|
if installer_app:
|
||||||
|
print("Adding seeding program %s extended attribute to app"
|
||||||
|
% seeding_program)
|
||||||
xattr.setxattr(installer_app, 'SeedProgram', seeding_program)
|
xattr.setxattr(installer_app, 'SeedProgram', seeding_program)
|
||||||
print 'Product downloaded and installed to %s' % sparse_diskimage_path
|
print('Product downloaded and installed to %s' % sparse_diskimage_path)
|
||||||
if args.raw:
|
if args.raw:
|
||||||
unmountdmg(mountpoint)
|
unmountdmg(mountpoint)
|
||||||
else:
|
else:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user