From aec96077fa5bc9a09b6f3031853d181e613a9eda Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Thu, 6 Aug 2026 10:39:55 +0800 Subject: [PATCH] shell-completion: add updatectl completions Add bash and zsh completions for updatectl commands and options. Complete targets and host features dynamically from updatectl output. Closes #43277 --- shell-completion/bash/meson.build | 1 + shell-completion/bash/updatectl | 98 +++++++++++++++++++++++++++++++ shell-completion/zsh/_updatectl | 79 +++++++++++++++++++++++++ shell-completion/zsh/meson.build | 1 + 4 files changed, 179 insertions(+) create mode 100644 shell-completion/bash/updatectl create mode 100644 shell-completion/zsh/_updatectl diff --git a/shell-completion/bash/meson.build b/shell-completion/bash/meson.build index 863ceb693bb..1b8000e8a21 100644 --- a/shell-completion/bash/meson.build +++ b/shell-completion/bash/meson.build @@ -60,6 +60,7 @@ foreach item : [ ['systemd-vpick', ''], ['timedatectl', 'ENABLE_TIMEDATED'], ['udevadm', ''], + ['updatectl', 'ENABLE_SYSUPDATED'], ['userdbctl', 'ENABLE_USERDB'], ['varlinkctl', ''], ] diff --git a/shell-completion/bash/updatectl b/shell-completion/bash/updatectl new file mode 100644 index 00000000000..9685aff4115 --- /dev/null +++ b/shell-completion/bash/updatectl @@ -0,0 +1,98 @@ +# shellcheck shell=bash +# updatectl(1) 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 . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__updatectl_targets() { + updatectl --no-pager --no-legend list 2>/dev/null | + sed -n 's/^[^[:alnum:]_.@%+=:,~-]*\([[:alnum:]_.@%+=:,~-]\+\).*/\1/p' +} + +__updatectl_features() { + updatectl --no-pager --no-legend features 2>/dev/null | + sed -n 's/^[^[:space:]]\+[[:space:]]\+\([[:alnum:]_.@%+=:,~-]\+\).*/\1/p' +} + +_updatectl() { + 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 + --no-ask-password + --reboot + --offline + --now' + [ARG]='-H --host' + ) + + local -A VERBS=( + [TARGET]='list + check + update + vacuum' + [FEATURE]='features + enable + disable' + ) + + _init_completion -n : || return + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --host|-H) + comps=$(compgen -A hostname) + ;; + 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[TARGET]}; then + comps=$( __updatectl_targets ) + elif __contains_word "$verb" ${VERBS[FEATURE]}; then + comps=$( __updatectl_features ) + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _updatectl updatectl diff --git a/shell-completion/zsh/_updatectl b/shell-completion/zsh/_updatectl new file mode 100644 index 00000000000..fe90d3b9bea --- /dev/null +++ b/shell-completion/zsh/_updatectl @@ -0,0 +1,79 @@ +#compdef updatectl +# SPDX-License-Identifier: LGPL-2.1-or-later + +_updatectl_targets() { + local expl + local -a targets=( ${(@f)"$( + _call_program targets updatectl --no-pager --no-legend list 2>/dev/null | + sed -n 's/^[^[:alnum:]_.@%+=:,~-]*\([[:alnum:]_.@%+=:,~-]\+\).*/\1/p' + )"} ) + _wanted targets expl 'target' compadd "$@" -a - targets +} + +_updatectl_features() { + local expl + local -a features=( ${(@f)"$( + _call_program features updatectl --no-pager --no-legend features 2>/dev/null | + sed -n 's/^[^[:space:]]\+[[:space:]]\+\([[:alnum:]_.@%+=:,~-]\+\).*/\1/p' + )"} ) + _wanted features expl 'feature' compadd "$@" -a - features +} + +local context state state_descr line +typeset -A opt_args +local ret=1 + +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]' + '--no-ask-password[Do not prompt for password]' + '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' + '--reboot[Reboot after updating to newer version]' + '--offline[Do not fetch metadata from the network]' + '--now[Download/delete resources immediately]' +) + +local -a commands=( + 'list:List available targets and versions' + 'check:Check for updates' + 'update:Install updates' + 'vacuum:Clean up old updates' + 'features:List and inspect optional features on host OS' + 'enable:Enable optional feature on host OS' + 'disable:Disable optional feature on host OS' +) + +_arguments -s -A '-*' \ + "$opts[@]" \ + ':command:->command' \ + '*:: :->argument' && ret=0 + +case $state in + command) + _describe -t commands 'updatectl command' commands && ret=0 + ;; + argument) + local curcontext=${curcontext%:*:*}:updatectl-$words[1]: + case $words[1] in + list) + _arguments -s "$opts[@]" ':target:_updatectl_targets' && ret=0 + ;; + check|update|vacuum) + _arguments -s "$opts[@]" '*:target:_updatectl_targets' && ret=0 + ;; + features) + _arguments -s "$opts[@]" ':feature:_updatectl_features' && ret=0 + ;; + enable|disable) + _arguments -s "$opts[@]" '*:feature:_updatectl_features' && ret=0 + ;; + *) + _arguments -s "$opts[@]" && ret=0 + ;; + esac + ;; +esac + +return ret diff --git a/shell-completion/zsh/meson.build b/shell-completion/zsh/meson.build index 7ffd4250acf..e8add29e82e 100644 --- a/shell-completion/zsh/meson.build +++ b/shell-completion/zsh/meson.build @@ -49,6 +49,7 @@ foreach item : [ ['_systemd-tmpfiles', 'ENABLE_TMPFILES'], ['_timedatectl', 'ENABLE_TIMEDATED'], ['_udevadm', ''], + ['_updatectl', 'ENABLE_SYSUPDATED'], ['_userdbctl', ''], ['_varlinkctl', ''], ]