--- /dev/null
+# shellcheck shell=bash
+# systemd-sysupdate(8) completion -*- shell-script -*-
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# systemd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with systemd; If not, see <https://www.gnu.org/licenses/>.
+
+__contains_word() {
+ local w word=$1; shift
+ for w in "$@"; do
+ [[ $w = "$word" ]] && return
+ done
+}
+
+# List the components or features known to the installation. The argument is both
+# the verb to invoke ('components' or 'features') and the JSON array key to extract.
+__systemd_sysupdate_list() {
+ systemd-sysupdate --no-pager --json=short "$1" 2>/dev/null |
+ sed -n "s/.*\"$1\":\[\([^]]*\)\].*/\1/p" | tr ',' '\n' | tr -d '"'
+}
+
+_systemd-sysupdate() {
+ local i verb comps
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
+ local -A OPTS=(
+ [STANDALONE]='-h --help --version
+ --no-pager
+ --no-legend
+ -A --component-all
+ -S --component-suggested
+ -a --feature-all
+ -s --feature-suggested
+ --reboot
+ --offline'
+ [ARG]='-C --component
+ --definitions
+ --root
+ --image
+ --image-policy
+ --transfer-source
+ -m --instances-max
+ --sync
+ --verify
+ --cleanup
+ --json'
+ )
+
+ local -A VERBS=(
+ [STANDALONE]='check-new
+ vacuum
+ cleanup
+ pending
+ reboot
+ components'
+ [VERSION]='list
+ update
+ acquire'
+ [FEATURE]='features
+ enable-feature
+ disable-feature'
+ [COMPONENT]='enable-component
+ disable-component'
+ )
+
+ _init_completion || return
+
+ if __contains_word "$prev" ${OPTS[ARG]}; then
+ case $prev in
+ -C|--component)
+ comps=$( __systemd_sysupdate_list components )
+ ;;
+ --definitions|--root|--transfer-source)
+ comps=$(compgen -A directory -- "$cur" )
+ compopt -o dirnames
+ ;;
+ --image)
+ comps=$(compgen -A file -- "$cur" )
+ compopt -o filenames
+ ;;
+ --image-policy)
+ comps=''
+ ;;
+ --instances-max|-m)
+ comps=''
+ ;;
+ --sync|--verify|--cleanup)
+ comps='no yes'
+ ;;
+ --json)
+ comps='pretty short off'
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
+ return 0
+ fi
+
+ for ((i=0; i < COMP_CWORD; i++)); do
+ if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} &&
+ ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then
+ verb=${COMP_WORDS[i]}
+ break
+ fi
+ done
+
+ if [[ -z ${verb-} ]]; then
+ comps=${VERBS[*]}
+ elif __contains_word "$verb" ${VERBS[FEATURE]}; then
+ comps=$( __systemd_sysupdate_list features )
+ elif __contains_word "$verb" ${VERBS[COMPONENT]}; then
+ comps=$( __systemd_sysupdate_list components )
+ else
+ # STANDALONE verbs take no argument, VERSION verbs take a remote version
+ # that cannot be enumerated locally.
+ comps=''
+ fi
+
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+}
+
+complete -F _systemd-sysupdate systemd-sysupdate
--- /dev/null
+#compdef systemd-sysupdate
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+local context state state_descr line
+typeset -A opt_args
+local ret=1
+
+# Pick the component/feature names out of the --json=short output of the
+# 'components'/'features' verbs. (The tabular output is unsuitable here, since
+# it prefixes each name with enabled/suggested checkmark columns.)
+_systemd-sysupdate_components() {
+ local expl
+ local -a components=( ${(@)${(@)${(s:,:)${${"$(_call_program -l components systemd-sysupdate --no-pager --json=short components 2>/dev/null)"#*\"components\":\[}%%\]*}}#\"}%\"} )
+ _wanted components expl "component" compadd "$@" -a - components
+}
+
+_systemd-sysupdate_features() {
+ local expl
+ local -a features=( ${(@)${(@)${(s:,:)${${"$(_call_program -l features systemd-sysupdate --no-pager --json=short features 2>/dev/null)"#*\"features\":\[}%%\]*}}#\"}%\"} )
+ _wanted features expl "feature" compadd "$@" -a - features
+}
+
+local -a opts=(
+ {-h,--help}'[Show help message and exit]'
+ '--version[Show package version and exit]'
+ '--no-pager[Do not pipe output into a pager]'
+ '--no-legend[Do not show the headers and footers]'
+ '--json=[Show output as JSON]:mode:(pretty short off)'
+ '(-A --component-all -S --component-suggested)'{-C+,--component=}'[Select component to update]:component:_systemd-sysupdate_components'
+ '(-C --component -S --component-suggested)'{-A,--component-all}'[Select all components]'
+ '(-C --component -A --component-all)'{-S,--component-suggested}'[Select all suggested components]'
+ '(-s --feature-suggested)'{-a,--feature-all}'[Select all features]'
+ '(-a --feature-all)'{-s,--feature-suggested}'[Select all suggested features]'
+ '--definitions=[Find transfer definitions in specified directory]:directory:_directories'
+ '--root=[Operate on an alternate filesystem root]:directory:_directories'
+ '--image=[Operate on disk image as filesystem root]:image file:_files'
+ '--image-policy=[Specify disk image dissection policy]:policy'
+ '--transfer-source=[Specify the directory to transfer sources from]:directory:_directories'
+ {-m+,--instances-max=}'[How many instances to maintain]:number'
+ '--sync=[Control whether to sync data to disk]:bool:(yes no)'
+ '--verify=[Force signature verification on or off]:bool:(yes no)'
+ '--reboot[Reboot after updating to newer version]'
+ '--offline[Do not fetch metadata from the network]'
+ '--cleanup=[Clean up orphaned files after completing update]:bool:(yes no)'
+)
+
+local -a commands=(
+ 'list:List available and installed versions'
+ 'features:List optional features'
+ 'enable-feature:Enable optional features'
+ 'disable-feature:Disable optional features'
+ 'check-new:Check if a newer version is available'
+ 'update:Install newest version'
+ 'acquire:Download newest version without installing'
+ 'vacuum:Make room by deleting old versions'
+ 'cleanup:Clean up orphaned files'
+ 'pending:Report whether a newer version is installed than booted'
+ 'reboot:Reboot if a newer version is installed than booted'
+ 'components:Show list of components'
+ 'enable-component:Enable components'
+ 'disable-component:Disable components'
+)
+
+_arguments -s -A '-*' \
+ "$opts[@]" \
+ ':command:->command' \
+ '*:: :->argument' && ret=0
+
+case $state in
+ command)
+ _describe -t commands 'systemd-sysupdate command' commands && ret=0
+ ;;
+ argument)
+ local curcontext=${curcontext%:*:*}:systemd-sysupdate-$words[1]:
+ case $words[1] in
+ features)
+ _arguments -s "$opts[@]" ':feature:_systemd-sysupdate_features' && ret=0
+ ;;
+ enable-feature|disable-feature)
+ _arguments -s "$opts[@]" '*:feature:_systemd-sysupdate_features' && ret=0
+ ;;
+ enable-component|disable-component)
+ _arguments -s "$opts[@]" '*:component:_systemd-sysupdate_components' && ret=0
+ ;;
+ *)
+ _arguments -s "$opts[@]" && ret=0
+ ;;
+ esac
+ ;;
+esac
+
+return ret