#compdef softwareupdate # rebuild cache for available updates everyday (ad hoc) _softwareupdate_caching_policy() { local -a newer=( "$1"(Nmd-1) ) return $#newer } # completes available updates (with description) _softwareupdate_update_names () { local name line update_policy ret=1 local cache_id=softwareupdate-updates zstyle -s ":completion:${curcontext}:" cache-policy update_policy if [[ -z "$update_policy" ]]; then zstyle ":completion:${curcontext}:" cache-policy \ _softwareupdate_caching_policy fi if { [[ ! -v _softwareupdate_updates ]] || _cache_invalid $cache_id } && ! _retrieve_cache $cache_id; then # Output format of 'softwareupdate --list' seems to be not stable, # but at least on macOS 12 and 13 it contains the following two lines # for each update: #* Label: update name (may contain spaces) # Title: description of the update (single TAB before Title:) # softwareupdate(1) manpage says the '*' before the Label: is replaced # by '-' for non-recommended updates (but I've never seen it). _softwareupdate_updates=() for line in ${(f)"$(_call_program updates softwareupdate --list)"}; do if [[ $line = [-\*]\ Label:\ (#b)(*) ]]; then # add '*' or '-' in front of the name; this will be removed later name=$line[1]$match[1] elif [[ -n $name && $line = $'\t'Title:\ (#b)(*) ]]; then _softwareupdate_updates+=( $name:$match[1] ) name= fi done _store_cache $cache_id _softwareupdate_updates fi # recommended and non-recommended updates local rec=( ${${_softwareupdate_updates:#-*}#\*} ) local non=( ${${(M)_softwareupdate_updates:#-*}#-} ) _describe -t updates "update" rec && ret=0 _describe -t non-recommended-updates "non-recommended update" non && ret=0 return ret } # completes versions of available macOS full installer (with description) _softwareupdate_installer_versions () { local versions=() for line in ${(f)"$(_call_program installer-versions softwareupdate --list-full-installers 2>/dev/null)"}; do if [[ $line = \*\ Title:\ *\ Version:\ (#b)([0-9.]##)* ]]; then versions+=( ${match[1]}:${line#*Title: } ) fi done _describe -t insteller-versions "version" versions } # main completion script _softwareupdate() { local -a specs if (( ${words[(I)(-i|--install)]} == 0 )); then specs=( '--no-scan[do not scan when listing or installing updates]' '--product-types[limit a scan to a particular product type only]:list of product types' '--products[a comma separated list of product keys to operate on]:list of product keys' '--force[force an operation to complete]' '--agree-to-license[agree to the software license agreement without user interaction]' '--verbose[enable verbose output]' '(* -)'{-h,--help}'[print command usage]:*:' + '(operation)' {-l,--list}'[list all available updates]' {-d,--download}'[download but not install specified updates]:*: : _softwareupdate_update_names' {-i,--install}'[download and install specified updates]' '(* -)--list-full-installers[list the available macOS installers]' '(* -)--fetch-full-installer[install the latest recommended macOS installer]: :(--full-installer-version): : _softwareupdate_installer_versions' '--install-rosetta[install Rosetta 2 (Apple Silicon only)]' '(* -)--schedule[returns the per-machine automatic check preference]' '--background[trigger a background scan and update operation]' '(* -)--dump-state[log the internal state of the SU daemon to /var/log/install.log]' '--evaluate-products[evaluate a list of product keys specified by the --products option]' '--history[show the install history]' ) else # if -i/--install is already on the command line specs=( !{-i,--install} '(-R --restart)'{-R,--restart}'[automatically restart if required to complete installation]' '--stdinpass[password to authenticate as an owner (Apple Silicon only)]' '--user[local username to authenticate as an owner (Apple Silicon only)]' '*: : _softwareupdate_update_names' + '(select-updates)' '(*)'{-a,--all}'[all updates that are applicable to your system]' '(*)'{-r,--recommended}'[all updates recommended for your system]' '(*)--os-only[only macOS updates]' '(*)--safari-only[only Safari updates]' ) fi _arguments -s : $specs } _softwareupdate "$@"