From ef1cfd1fb9d6c4ad05a41df42c5f6c515800dfeb Mon Sep 17 00:00:00 2001 From: Peter Ajamian Date: Fri, 4 Jun 2021 02:16:43 +1200 Subject: [PATCH 1/2] Fix various issues found by shellcheck Shellcheck found various issues that have cropped up over time. This commit fixes those issues. --- migrate2rocky.sh | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/migrate2rocky.sh b/migrate2rocky.sh index 7abbbae..18b591e 100644 --- a/migrate2rocky.sh +++ b/migrate2rocky.sh @@ -43,7 +43,9 @@ exec > >(tee -a "$logfile") 2> >(tee -a "$logfile" >&2) #errcolor=$(tput setaf 1) #blue=$(tput setaf 4) #nocolor=$(tput op) -unset errcolor blue nocolor +errcolor= +blue= +nocolor= export LANG=en_US.UTF-8 shopt -s nullglob @@ -95,7 +97,7 @@ os-release () ( # be adequate for our needs here. pkg_ver() ( ver=$(rpm -q --qf '%{VERSION}\n' "$1") || return 2 - if [[ $(sort -V <<<"$ver"$'\n'"$2" | head -1) != $2 ]]; then + if [[ $(sort -V <<<"$ver"$'\n'"$2" | head -1) != "$2" ]]; then return 1 fi return 0 @@ -112,7 +114,7 @@ bin_check() { fi # Check the platform. - if [[ $(os-release PLATFORM_ID) != $SUPPORTED_PLATFORM ]]; then + if [[ $(os-release PLATFORM_ID) != "$SUPPORTED_PLATFORM" ]]; then exit_message "This script must be run on an EL8 distribution. Migration from other distributions is not supported." fi @@ -142,7 +144,7 @@ bin_check() { fi if (( ${#missing[@]} )); then - exit_message "Commands not found: ${missing[@]}. Possible bad PATH setting or corrupt installation." + exit_message "Commands not found: ${missing[*]}. Possible bad PATH setting or corrupt installation." fi } @@ -150,7 +152,8 @@ bin_check() { # info for the resulting package. Note that we explicitly disable the epel repo # as a special-case below to avoid having the extras repository map to epel. repoquery () { - local name val prev result=$( + local name val prev result + result=$( dnf -q --setopt=epel.excludepkgs=epel-release repoquery -i "$1" || exit_message "Failed to fetch info for package $1." ) @@ -330,13 +333,13 @@ collect_system_info () { for pkg in "${!provides_pkg_map[@]}"; do printf '.' prov=${provides_pkg_map[$pkg]} - pkg_map[$pkg]=$(provides_pkg $prov) || + pkg_map[$pkg]=$(provides_pkg "$prov") || exit_message "Can't get package that provides $prov." done for prov in "${addl_provide_removes[@]}"; do printf '.' local pkg; - pkg=$(provides_pkg $prov) || continue + pkg=$(provides_pkg "$prov") || continue addl_pkg_removes+=("$pkg") done @@ -554,13 +557,13 @@ EOF if (( ${#enabled_modules[@]} )); then printf '%s\n' "${blue}Enabling modules$nocolor" '' dnf -y module enable "${enabled_modules[@]}" || - exit_message "Can't enable modules ${enabled_modules[@]}" + exit_message "Can't enable modules ${enabled_modules[*]}" fi # Make sure that excluded repos are disabled. printf '%s\n' "${blue}Disabling excluded modules$nocolor" '' dnf -y module disable "${module_excludes[@]}" || - exit_message "Can't disable modules ${module_excludes[@]}" + exit_message "Can't disable modules ${module_excludes[*]}" printf '%s\n' '' "${blue}Syncing packages$nocolor" '' dnf -y distro-sync || exit_message "Error during distro-sync." @@ -665,7 +668,7 @@ fi printf '\n\n\n' if [[ $convert_to_rocky ]]; then - cat /etc/issue | awk 'NR<=15' + awk 'NR<=15' < /etc/issue printf '%s\n' "$blue" "Done, please reboot your system.$nocolor" fi logmessage From 844896ad6bd7c4a7dcea4b6015005f3368a060a5 Mon Sep 17 00:00:00 2001 From: Peter Ajamian Date: Fri, 4 Jun 2021 22:11:15 +1200 Subject: [PATCH 2/2] Fix rpm and dnf package spec issues When attempting to check if a package exists for a particular provide string what happened is if an older version of the package was on the system but a newer version in the repositories then the older (system) version would be returned by dnf provides, but this could not be translated into a package name with dnf repoquery because that specific version could not be found in the repos. The solution was to check rpm first to translate the package name on the system and then dnf repoquery if rpm doesn't find it. This brought to light issues when passing arrays of package names to rpm and dnf which might happen to contain an empty element. In this case rpm and dnf would consider the empty arg an indicator that it should match all packages on the system, or all available packages. While we should try to avoid passing arrays with empty elements, this highlighed a need to make rpm and dnf safer in this regard, and so there are now saferpm and sfednf functions which simply strip any empty args before calling the appropriate command with the rest of the args untouched. This commit also fixes an issue with output column formatting. --- migrate2rocky.sh | 70 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/migrate2rocky.sh b/migrate2rocky.sh index 18b591e..0d37ac9 100644 --- a/migrate2rocky.sh +++ b/migrate2rocky.sh @@ -228,11 +228,36 @@ provides_pkg () ( provides=$(dnf -q provides "$1" | awk '{print $1; nextfile}') || return 1 set +o pipefail - pkg=$(dnf -q repoquery --queryformat '%{NAME}' "$provides") || + pkg=$(rpm -q --queryformat '%{NAME}\n' "$provides") || + pkg=$(dnf -q repoquery --queryformat '%{NAME}\n' "$provides") || exit_message "Can't get package name for $provides." printf '%s\n' "$pkg" ) +# If you pass an empty arg as one of the package specs to rpm it will match +# every package on the system. This funtion simply strips out any empty args +# and passes the rest to rpm to avoid this side-effect. +saferpm () ( + args=() + for a in "$@"; do + if [[ $a ]]; then + args+=("$a") + fi + done + rpm "${args[@]}" +) + +# And a similar function for dnf +safednf () ( + args=() + for a in "$@"; do + if [[ $a ]]; then + args+=("$a") + fi + done + dnf "${args[@]}" +) + collect_system_info () { # Check the efi mount first, so we can bail before wasting time on all these # other checks if it's not there. @@ -285,8 +310,8 @@ collect_system_info () { done printf '%s\n' '' '' "Found the following repositories which map from $PRETTY_NAME to Rocky Linux 8:" - column -t -N "$PRETTY_NAME,Rocky Linux 8" < <(for r in "${!repo_map[@]}"; do - printf '%s %s\n' "${repo_map[$r]}" "$r" + column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <(for r in "${!repo_map[@]}"; do + printf '%s\t%s\n' "${repo_map[$r]}" "$r" done) printf '\n%s' "${blue}Getting system package names for $PRETTY_NAME$nocolor." @@ -344,26 +369,27 @@ collect_system_info () { done printf '%s\n' '' '' "Found the following system packages which map from $PRETTY_NAME to Rocky Linux 8:" - column -t -N "$PRETTY_NAME,Rocky Linux 8" < <(for p in "${!pkg_map[@]}"; do - printf '%s %s\n' "${pkg_map[$p]}" "$p" + column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <(for p in "${!pkg_map[@]}"; do + printf '%s\t%s\n' "${pkg_map[$p]}" "$p" done) printf '%s\n' '' "${blue}Getting list of installed system packages$nocolor." - readarray -t installed_packages < <(rpm -qa --queryformat="%{NAME}\n" "${pkg_map[@]}") + + readarray -t installed_packages < <(saferpm -qa --queryformat="%{NAME}\n" "${pkg_map[@]}") declare -g -A installed_pkg_check installed_pkg_map for p in "${installed_packages[@]}"; do installed_pkg_check[$p]=1 done for p in "${!pkg_map[@]}"; do - if [[ ${installed_pkg_check[${pkg_map[$p]}]} ]]; then + if [[ ${pkg_map[$p]} && ${installed_pkg_check[${pkg_map[$p]}]} ]]; then installed_pkg_map[$p]=${pkg_map[$p]} fi done; printf '%s\n' '' "We will replace the following $PRETTY_NAME packages with their Rocky Linux 8 equivalents" - column -t -N "Packages to be Removed,Packages to be Installed" < <( + column -t -s $'\t' -N "Packages to be Removed,Packages to be Installed" < <( for p in "${!installed_pkg_map[@]}"; do - printf '%s %s\n' "${installed_pkg_map[$p]}" "$p" + printf '%s\t%s\n' "${installed_pkg_map[$p]}" "$p" done ) @@ -383,7 +409,7 @@ collect_system_info () { # Get a list of system enabled modules. readarray -t enabled_modules < <( set -e -o pipefail - dnf -q "${repo_map[@]/#/--repo=}" module list --enabled | + safednf -q "${repo_map[@]/#/--repo=}" module list --enabled | awk ' $1 == "@modulefailsafe", /^$/ {next} $1 == "Name", /^$/ {if ($1!="Name" && !/^$/) print $1":"$2} @@ -448,7 +474,7 @@ package_swaps() { done # Use dnf shell to swap the system packages out. - dnf -y shell --disablerepo=\* --noautoremove \ + safednf -y shell --disablerepo=\* --noautoremove \ --setopt=protected_packages= --setopt=keepcache=True \ "${dnfparameters[@]}" \ <