mirror of
https://github.com/munki/munki.git
synced 2026-01-05 22:20:00 -06:00
More changes to support softwareupdate in Big Sur
This commit is contained in:
@@ -195,13 +195,21 @@ class AppleUpdates(object):
|
||||
return []
|
||||
su_results = su_tool.run(['-l', '--no-scan'])
|
||||
filtered_updates = []
|
||||
# add update to filtered_updates only if it is also listed
|
||||
# in `softwareupdate -l` output
|
||||
for item in su_results.get('updates', []):
|
||||
for update in recommended_updates:
|
||||
if (item.get('identifier') == update.get('Identifier') and
|
||||
item.get('version') == update.get('Display Version')):
|
||||
# add update to filtered_updates only if it is also listed
|
||||
# in `softwareupdate -l` output
|
||||
filtered_updates.append(update)
|
||||
item.update(update)
|
||||
filtered_updates.append(item)
|
||||
elif item.get('Title'):
|
||||
# new-style info first seen in Catalina
|
||||
if (item.get('Title') == update.get('Display Name') and
|
||||
item.get('Version') == update.get('Display Version')
|
||||
):
|
||||
item.update(update)
|
||||
filtered_updates.append(item)
|
||||
return filtered_updates
|
||||
|
||||
def available_update_product_ids(self):
|
||||
@@ -379,11 +387,16 @@ class AppleUpdates(object):
|
||||
product_keys = []
|
||||
english_su_info = {}
|
||||
apple_updates = []
|
||||
updates_dict = {}
|
||||
|
||||
# first, try to get the list from com.apple.SoftwareUpdate preferences
|
||||
# and softwareupdate -l
|
||||
recommended_updates = self.get_filtered_recommendedupdates()
|
||||
for item in recommended_updates:
|
||||
try:
|
||||
updates_dict[item['Product Key']] = item
|
||||
except (TypeError, AttributeError, KeyError):
|
||||
pass
|
||||
try:
|
||||
update_display_names[item['Product Key']] = (
|
||||
item['Display Name'])
|
||||
@@ -401,7 +414,31 @@ class AppleUpdates(object):
|
||||
pass
|
||||
|
||||
for product_key in product_keys:
|
||||
if not self.update_downloaded(product_key):
|
||||
if updates_dict.get(product_key, {}).get('MobileSoftwareUpdate'):
|
||||
# New in Big Sur. These updates are not downloaded to
|
||||
# /Library/Updates and we don't (yet) have access to any
|
||||
# additional metadata
|
||||
su_info = {}
|
||||
su_info['productKey'] = product_key
|
||||
su_info['name'] = updates_dict[
|
||||
product_key].get('Display Name', '')
|
||||
su_info['apple_product_name'] = su_info['name']
|
||||
su_info['display_name'] = su_info['name']
|
||||
su_info['version_to_install'] = updates_dict[
|
||||
product_key].get('Display Version', '')
|
||||
su_info['description'] = ''
|
||||
try:
|
||||
size = int(updates_dict[
|
||||
product_key].get('Size', '0K')[:-1])
|
||||
su_info['installer_item_size'] = size
|
||||
su_info['installed_size'] = size
|
||||
except (ValueError, TypeError, IndexError):
|
||||
su_info['installed_size'] = 0
|
||||
if updates_dict[product_key].get('Action') == 'restart':
|
||||
su_info['RestartAction'] = 'RequireRestart'
|
||||
apple_updates.append(su_info)
|
||||
continue
|
||||
elif not self.update_downloaded(product_key):
|
||||
display.display_warning(
|
||||
'Product %s does not appear to be downloaded',
|
||||
product_key)
|
||||
@@ -502,7 +539,7 @@ class AppleUpdates(object):
|
||||
for item in apple_updates:
|
||||
display.display_info(
|
||||
' + %s-%s' % (
|
||||
item.get('display_name', ''),
|
||||
item.get('display_name', item.get('name', '')),
|
||||
item.get('version_to_install', '')))
|
||||
if item.get('RestartAction') in self.RESTART_ACTIONS:
|
||||
display.display_info(' *Restart required')
|
||||
|
||||
@@ -67,6 +67,78 @@ def find_ptty_tool():
|
||||
return cmd
|
||||
|
||||
|
||||
def parse_su_update_line_new_style(line):
|
||||
'''Parses a new-style software update line'''
|
||||
info = {}
|
||||
line = line.strip().rstrip(',')
|
||||
for subitem in line.split(', '):
|
||||
key, _, value = subitem.partition(": ")
|
||||
if key:
|
||||
info[key] = value
|
||||
return info
|
||||
|
||||
|
||||
def parse_su_update_line_old_style(line):
|
||||
'''Parses an old-style (pre-10.15) softwareupdate -l output line
|
||||
into a dict'''
|
||||
info = {}
|
||||
# strip leading and trailing whitespace
|
||||
line = line.strip()
|
||||
title, seperator, line = line.partition("(")
|
||||
if not seperator == "(":
|
||||
# no idea of the format, just return an empty dict
|
||||
return {}
|
||||
info['Title'] = title.rstrip()
|
||||
version, seperator, line = line.partition(")")
|
||||
if not seperator == ")":
|
||||
# no idea of the format, just return an empty dict
|
||||
return {}
|
||||
info['Version'] = version
|
||||
line = line.lstrip(', ')
|
||||
size, seperator, line = line.partition('K')
|
||||
if seperator == 'K':
|
||||
info['Size'] = '%sK' % size
|
||||
# now start from the end
|
||||
if line.endswith(" [restart]"):
|
||||
line = line[0:-len(" [restart]")]
|
||||
info['Action'] = 'restart'
|
||||
if line.endswith(" [recommended]"):
|
||||
line = line[0:-len(" [recommended]")]
|
||||
info['Recommended'] = 'YES'
|
||||
else:
|
||||
info['Recommended'] = 'NO'
|
||||
return info
|
||||
|
||||
|
||||
def parse_su_identifier(line):
|
||||
'''parses first line of softwareupdate -l item output'''
|
||||
if line.startswith(' * '):
|
||||
update_entry = line[5:]
|
||||
elif line.startswith('* Label: '):
|
||||
update_entry = line[9:]
|
||||
else:
|
||||
return {}
|
||||
update_parts = update_entry.split('-')
|
||||
# version is the bit after the last hyphen
|
||||
# (let's hope there are no hyphens in the versions!)
|
||||
vers = update_parts[-1]
|
||||
# identifier is everything before the last hyphen
|
||||
identifier = '-'.join(update_parts[0:-1])
|
||||
return {'full_identifier': update_entry,
|
||||
'identifier': identifier,
|
||||
'version': vers}
|
||||
|
||||
|
||||
def parse_su_update_lines(line1, line2):
|
||||
'''Parses two lines from softwareupdate -l output and returns a dict'''
|
||||
info = parse_su_identifier(line1)
|
||||
if line1.startswith(' * '):
|
||||
info.update(parse_su_update_line_old_style(line2))
|
||||
elif line1.startswith('* Label: '):
|
||||
info.update(parse_su_update_line_new_style(line2))
|
||||
return info
|
||||
|
||||
|
||||
def run(options_list, catalog_url=None, stop_allowed=False):
|
||||
"""Runs /usr/sbin/softwareupdate with options.
|
||||
|
||||
@@ -168,28 +240,16 @@ def run(options_list, catalog_url=None, stop_allowed=False):
|
||||
|
||||
# --list-specific output
|
||||
if mode == 'list':
|
||||
if output.strip().startswith('*'):
|
||||
if output.startswith((' * ', '* Label: ')):
|
||||
# collect list of items available for install
|
||||
if output.startswith(' * '):
|
||||
update_entry = output[5:]
|
||||
elif output.startswith('* Label: '):
|
||||
# seen in 10.15 betas
|
||||
update_entry = output[9:]
|
||||
else:
|
||||
# unknown format
|
||||
continue
|
||||
update_parts = update_entry.split('-')
|
||||
# version is the bit after the last hyphen
|
||||
# (let's hope there are no hyphens in the versions!)
|
||||
vers = update_parts[-1]
|
||||
# identifier is everything before the last hyphen
|
||||
identifier = '-'.join(update_parts[0:-1])
|
||||
results['updates'].append(
|
||||
{'identifier': identifier, 'version': vers})
|
||||
continue
|
||||
else:
|
||||
# we don't want any output from calling `softwareupdate -l`
|
||||
continue
|
||||
first_line = output
|
||||
second_line = job.stdout.readline()
|
||||
if second_line:
|
||||
second_line = second_line.decode('UTF-8').rstrip('\n\r')
|
||||
item = parse_su_update_lines(first_line, second_line)
|
||||
results['updates'].append(item)
|
||||
# we don't want any output from calling `softwareupdate -l`
|
||||
continue
|
||||
|
||||
output = output.strip()
|
||||
# --download-specific output
|
||||
|
||||
Reference in New Issue
Block a user