[__package_apt] Mark already installed packages as manual

...if they were automatically installed as a dependency of another package.
This prevents these pacakges from being uninstalled by `apt-get autoremove`.

See: https://github.com/ungleich/cdist/pull/781
This commit is contained in:
Dennis Camera 2021-11-13 22:37:27 +01:00 committed by Romain Dartigues
parent c2c5668b70
commit 4884b794ff
3 changed files with 74 additions and 34 deletions

View File

@ -1,6 +1,7 @@
#!/bin/sh
#
# 2011-2012 Nico Schottelius (nico-cdist at schottelius.org)
# 2021 Dennis Camera (cdist at dtnr.ch)
#
# This file is part of cdist.
#
@ -21,19 +22,48 @@
# Retrieve the status of a package - parsed dpkg output
#
if [ -f "$__object/parameter/name" ]; then
name="$(cat "$__object/parameter/name")"
breify() {
# Convert arguments to a POSIX BRE-compatible form, i.e. escape special
# characters (incl. delimiter)
printf '%s' "$*" | sed -e 's/[].^$*\[]/\\&/g' -e 's:/:\\/:g'
}
if test -f "${__object}/parameter/name"
then
name=$(cat "${__object:?}/parameter/name")
else
name="$__object_id"
name=${__object_id:?}
fi
# Except dpkg failing, if package is not known / installed
packages="$(apt-cache showpkg "$name" | sed -e "1,/Reverse Provides:/d" | cut -d ' ' -f 1) $name"
for p in $packages; do
if dpkg -s "$p" 2>/dev/null | grep --quiet "^Status: install ok installed$" ; then
version=$(dpkg -s "$p" 2>/dev/null | grep "^Version:" | cut -d ' ' -f 2)
echo "present $p $version"
exit 0
fi
state_dir=$(apt-config dump | sed -n -e 's/^Dir::State *"\(.*\)";$/\/\1/p')
extended_states_file=${state_dir%/}/extended_states
# if $name is e.g. editor, check if any editor is installed instead
rprovides=$(apt-cache showpkg "${name}" | sed -e '1,/^Reverse Provides:/d' -e 's/ .*$//')
for pkg in ${name} ${rprovides}
do
if dpkg -s "${pkg}" 2>/dev/null | grep -qxF 'Status: install ok installed'
then
version=$(dpkg -s "${pkg}" 2>/dev/null | sed -n -e 's/^Version: *//p')
if test -f "${extended_states_file}"
then
# On Debian >= 5 check if the package is auto installed
# NOTE: instead of using apt-mark(8) parse the extended_states file
# directly. apt-mark is sloow and didn't have a stable
# interface in older Debian versions.
is_auto=$(sed -n -e '/^Package: '$(breify "${name}")'$/,/^$/!d' -e 's/^Auto-Installed: *//p' "${extended_states_file}")
fi
# NOTE: older versions don't have apt-mark -> all packages are manual
auto_word=$(test $((is_auto)) -ne 0 && echo auto || echo manual)
echo "present ${auto_word} ${pkg} ${version}"
exit 0
fi
done
echo absent

View File

@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org)
# 2021 Dennis Camera (cdist at dtnr.ch)
#
# This file is part of cdist.
#
@ -56,31 +57,36 @@ else
fi
# FIXME: use grep directly, state is a list, not a line!
state_is="$(cat "$__object/explorer/state")"
case "$state_is" in
present*)
name="$(echo "$state_is" | cut -d ' ' -f 2)"
version_is="$(echo "$state_is" | cut -d ' ' -f 3)"
state_is="present"
;;
*)
version_is=""
;;
esac
if [ "$state_is" = "$state_should" ]; then
if [ -z "$version" ] || [ "$version" = "$version_is" ]; then
exit 0;
fi
fi
read -r state_is auto_state name version_is <"${__object:?}/explorer/state"
# Hint if we need to avoid questions at some point:
# DEBIAN_PRIORITY=critical can reduce the number of questions
aptget="DEBIAN_FRONTEND=noninteractive apt-get --quiet --yes -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\""
case "$state_should" in
case ${state_should}
in
present)
if test "${state_is}" = 'present'
then
if test -z "${version}" || test "${version}" = "${version_is}"
then
if test "${auto_state}" = 'auto'
then
# package is installed, but marked auto -> mark it manual so
# that it doesn't get removed by apt-get autoremove.
echo 'head -n1 "$(command -v apt-mark)" | grep -qvF python \'
# new implementation (Debian >= 7)
echo "&& apt-mark manual '${name}' \\"
# old Python implementation (Debian 5-7)
echo "|| apt-mark unmarkauto '${name}'"
# NOTE: older versions don't need to be supported because
# they will always be manual
fi
exit 0
fi
fi
# There are special arguments to apt(8) to prevent aborts if apt woudn't been
# updated after the 19th April 2021 till the bullseye release. The additional
# arguments acknoledge the happend suite change (the apt(8) update does the
@ -104,12 +110,15 @@ EOF
if [ -n "$version" ]; then
name="${name}=${version}"
fi
echo "$aptget $recommendsparam install $target_release '$name'"
echo "installed" >> "$__messages_out"
echo "${aptget} ${recommendsparam} install ${target_release} '${name}'"
echo 'installed' >>"${__messages_out:?}"
;;
absent)
echo "$aptget remove $purgeparam '$name'"
echo "removed" >> "$__messages_out"
test "${state_is}" != 'absent' || exit 0
echo "${aptget} remove ${purgeparam} '${name}'"
echo 'removed' >>"${__messages_out:?}"
;;
*)
echo "Unknown state: $state_should" >&2

View File

@ -12,6 +12,7 @@ next:
* Type __package_apt: Fix complaint about suite change (Matthias Stecher)
* Type __debconf_set_selections: Fix bug where --file was unsupported (Evilham)
* Types __letsencrypt_cert, __grafana_dashboard: Improve bullseye support (Evilham)
* Type __package_apt: Mark already installed packages as manual (Dennis Camera)
6.9.8: 2021-08-24
* Type __rsync: Rewrite (Ander Punnar)