aboutsummaryrefslogtreecommitdiffstats
path: root/System/apt-update-python-bin-packages-to-official-archive.sh
blob: 02fbb88fc7dff7e3a157194503df86a4711343f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
#!/bin/bash
########################################################################################################################
# apt-update-python-bin-packages-to-official-archive.sh
########################################################################################################################
#
# All rights reserved Ⓒ 2017-2023 sdxlive.com
#
# Written by Jean-Christophe Manciot <jcmanciot@sdxlive.com>
#
# Licensed under a GPLv3 License.
# You may not use this file except in compliance with the License. You may obtain a copy of the License at
#
#    https://www.gnu.org/licenses/gpl-3.0.md
#
# The licensor cannot revoke these freedoms as long as you follow the license terms.
#
# Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. 
# You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
#
########################################################################################################################

########################################################################################################################
#
# Updating all Python binary packages with a version ending with +<os_version> to the package version provided by the 
# official Debian/Ubuntu repositories.
#
########################################################################################################################
#
# Requirements:
# ------------
#
# - We assume that apt alongside all its dependencies are already installed.
# - We expect that 'deb-src' URIs are set in your sources.list
#
########################################################################################################################
#
# Parameters:
# ----------
#
# <os_version>: binary packages version ending with +<os_version>
#  For instance:
#       * 11|11.0       matches bullseye Debian distribution
#       * 22.04         matches jammy Ubuntu distribution
# <auto>: one of
#       * true|yes: the binary packages will be updated without any user's confirmation
#                   Use with caution.
#       * false|no: the binary packages will be updated only with user's confirmation for each source package
#                   (default)
# - <versions_group>: optional, one of
#       + positive: updating only the subset of python binary packages with a version ending with +<os_version> 
#       + negative: updating only the subset of python binary packages with a version ending with -<os_version> 
#       + all: updating all python binary packages with a version ending with +<os_version> or -<os_version> 
#              (default)
#
#######################################################################################################################
#
# Usage example:
# -------------
#
# apt-update-python-bin-packages-to-official-archive.sh 19.10 true positive
#
########################################################################################################################
# set -x
# Expanding aliases
shopt -s expand_aliases

#------------------------------------apt-get-src-package-name--------------------------------------
# Getting the source package name fromp a binary package name.
function apt-get-src-package-name ()
{
        local python_bin_package_name=$1
        local python_bin_package_version=$2
        local PARAMETERS_NUMBER=2

        case "$#" in
                ${PARAMETERS_NUMBER})
                        ;;
                *)
                        echo -e ''$_{1..180}'\b-'
                        echo Wrong number of parameters: $(basename $0) $@
                        echo -e ''$_{1..180}'\b-'
                        return 1
                        ;;
        esac

        if [[ -n "$python_bin_package_version" ]]; then
                src_package_name=$(apt-cache -q=0 2>&1 show ${python_bin_package_name}=${python_bin_package_version} | grep Source: | head -n 1 | cut -d ' ' -f 2)
        else
                src_package_name=$(apt-cache -q=0 2>&1 show ${python_bin_package_name} | grep Source: | head -n 1 | cut -d ' ' -f 2)
        fi
        if [[ -z "$src_package_name" ]]; then
                src_package_name=$(apt-cache -q=0 2>&1 showsrc ${python_bin_package_name} | grep Package: | head -n 1 | cut -d ' ' -f 2)
                if [[ -z "$src_package_name" ]]; then
                        echo ''
                        return 1
                fi
        fi
        echo "$src_package_name"
        return 0
}

#------------------------------------apt-update-python-bin-packages-to-official-archive.sh-----------
os_version=$1
auto=$2
versions_group=$3
PARAMETERS_NUMBER=1

case "$#" in
        0)
                end_of_help_message_line_number=$(awk '/^# set -x/{ print NR; exit }' $(which apt-update-python-bin-packages-to-official-archive.sh))
                ((end_of_help_message_line_number--))
                if [[ (-n $(apt-cache -q=0 2>&1 policy zenity | grep Installed)) && (! ($(apt-cache -q=0 2>&1 policy zenity | grep Installed) =~ none)) ]]; then
                        awk -v var=$end_of_help_message_line_number 'NR >= 2 && NR <= var' $(which apt-update-python-bin-packages-to-official-archive.sh) | zenity --text-info --title "apt-update-python-bin-packages-to-official-archive.sh help" --width 1350 --height 1000
                else
                        awk -v var=$end_of_help_message_line_number 'NR >= 2 && NR <= var' $(which apt-update-python-bin-packages-to-official-archive.sh)
                fi
                exit 1
                ;;
        $PARAMETERS_NUMBER)
                case "$1" in
                        -h|--help)
                                end_of_help_message_line_number=$(awk '/^# set -x/{ print NR; exit }' $(which apt-update-python-bin-packages-to-official-archive.sh))
                                ((end_of_help_message_line_number--))
                                if [[ (-n $(apt-cache -q=0 2>&1 policy zenity | grep Installed)) && (! ($(apt-cache -q=0 2>&1 policy zenity | grep Installed) =~ none)) ]]; then
                                        awk -v var=$end_of_help_message_line_number 'NR >= 2 && NR <= var' $(which apt-update-python-bin-packages-to-official-archive.sh) | zenity --text-info --title "apt-update-python-bin-packages-to-official-archive.sh help" --width 1350 --height 1000
                                else
                                        awk -v var=$end_of_help_message_line_number 'NR >= 2 && NR <= var' $(which apt-update-python-bin-packages-to-official-archive.sh)
                                fi
                                exit 1
                                ;;
                        *)
                                auto=false
                                versions_group=all
                                ;;
                esac
                ;;
        $((PARAMETERS_NUMBER+1)))
                versions_group=all
                ;;
        $((PARAMETERS_NUMBER+2)))
                ;;
        *)
                echo -e ''$_{1..180}'\b-'
                echo Wrong number of parameters: $(basename $0) $@
                echo -e ''$_{1..180}'\b-'
                exit 1
                ;;
esac

# Escaping the '$.*?/|\^(){}+@[]' characters for later grep -P searched string
# Letters, digits and '-' must not be escaped
alias escape_grep_pcre_search_string=$'sed -e \'s|[]$.*?/|\^(){}+@[]|\\\&|g\''
# '
# Escaping the '$.*?/|\^(){}+@[]' characters for later sed -E|-r searched string
# Letters, digits and '-' must not be escaped
alias escape_sed_ere_search_string=$'sed -e \'s|[]$.*?/|\^(){}+@[]|\\\&|g\''
# '

dpkg-lock.sh apt update
# Installing dependencies needed to play an alert or a completion sound when necessary
dpkg-lock.sh apt -y install alsa-utils emacspeak pidgin-data

grep_esc_os_version="$(echo "$os_version" | escape_grep_pcre_search_string)"

os_distribution=$(lsb_release -a  2>/dev/null | grep "Distributor ID" | cut -d ':' -f 2)
os_distribution=${os_distribution//[[:space:]]/}
case $os_distribution in
        Debian)
                official_archives_uri=deb.debian.org/debian
                ;;
        Ubuntu)
                official_archives_uri=archive.ubuntu.com/ubuntu
                ;;
        *)
                echo -e ''$_{1..180}'\b-'
                echo Unknown distribution $os_distribution
                echo -e ''$_{1..180}'\b-'
                exit 1
                ;;
esac
grep_esc_official_archives_uri="$(echo "$official_archives_uri" | escape_grep_pcre_search_string)"

# Python binary packages installed from distribution $os_version
python_bin_package_names=()
case $versions_group in
        positive)
                readarray -t python_bin_package_names < <(apt list --installed 2>/dev/null | grep -P -- ".*python.*\+${grep_esc_os_version} " | cut -d '/' -f 1)
                ;;
        negative)
                readarray -t python_bin_package_names < <(apt list --installed 2>/dev/null | grep -P -- ".*python.*-${grep_esc_os_version} " | cut -d '/' -f 1)
                ;;
        all)
                python_bin_package_names_1=()
                python_bin_package_names_2=()

                readarray -t python_bin_package_names_1 < <(apt list --installed 2>/dev/null | grep -P -- ".*python.*\+${grep_esc_os_version} " | cut -d '/' -f 1)
                readarray -t python_bin_package_names_2 < <(apt list --installed 2>/dev/null | grep -P -- ".*python.*-${grep_esc_os_version} " | cut -d '/' -f 1)

                python_bin_package_names=("${python_bin_package_names_1[@]}" "${python_bin_package_names_2[@]}")
                # Repacking in case the first array is empty
                python_bin_package_names=("${python_bin_package_names[@]}")
                ;;
        *)
                echo -e ''$_{1..180}'\b-'
                echo Unknown versions group $versions_group
                echo -e ''$_{1..180}'\b-'
                exit 1
                ;;
esac

# Sorting the array
if [[ ${#python_bin_package_names[@]} -ne 0 ]]; then
        sorted_python_bin_package_names=()
        IFS=$'\n' sorted_python_bin_package_names=($(sort -uV <<<"${python_bin_package_names[*]}"))
        unset IFS
else
        aplay /usr/share/sounds/purple/login.wav
        exit 0
fi

updated_python_bin_package_names_versions=()
for ((s=0; s<${#sorted_python_bin_package_names[@]}; s++))
do
        python_bin_package_family_names_versions=()
        python_bin_package_name="${sorted_python_bin_package_names[${s}]}"
        if [[ -z "$python_bin_package_name" ]]; then
                continue
        fi
        python_bin_package_installed_version=$(apt-cache -q=0 2>&1 policy "$python_bin_package_name" | grep Installed | sed -E 's|^.*?: (.*)$|\1|g')
        src_package_name=$(apt-get-src-package-name "$python_bin_package_name" "$python_bin_package_installed_version")
        if [[ -z "$src_package_name" ]]; then
                continue
        fi
        python_bin_package_installed_core_version=$(echo "$python_bin_package_installed_version" | sed -E "s|^(.*)[\+-]${os_version}$|\1|g")
        grep_esc_python_bin_package_name="$(echo "$python_bin_package_name" | escape_grep_pcre_search_string)"
        python_bin_package_official_candidate_version=$(apt-cache -q=0 2>&1 madison "$python_bin_package_name" | grep -P "$grep_esc_python_bin_package_name" | grep -P "$grep_esc_official_archives_uri" | grep amd64 | head -n 1 | cut -d '|' -f 2)
        # Removing all spaces, tabs, line breaks
        python_bin_package_official_candidate_version=${python_bin_package_official_candidate_version//[[:space:]]/}
        if [[ -n "$python_bin_package_official_candidate_version" ]]; then
                python_bin_package_family_names_versions=("${python_bin_package_name}@${python_bin_package_installed_version}@${python_bin_package_installed_core_version}@${python_bin_package_official_candidate_version}")
                # Removing $python_bin_package_family_name from ${sorted_python_bin_package_names[@]} array
                # Beware that the unset creates a gap in the array but modifies its size
                unset "sorted_python_bin_package_names[${s}]"
                # It also messes with the index if the removed cell index is lower than or equal to the current cell index
                ((s--))
                # Rebuilding the array to fill the gap
                sorted_python_bin_package_names=("${sorted_python_bin_package_names[@]}")
        else
                # This binary package is not available in the official repositories
                continue
        fi

        # Searching for all installed binary packages which share the same source package as $python_bin_package_name
        for ((index=0; index<${#sorted_python_bin_package_names[@]}; index++))
        do
                python_bin_package_name_2="${sorted_python_bin_package_names[${index}]}"
                if [[ (-z "$python_bin_package_name_2") || ("$python_bin_package_name_2" == "$python_bin_package_name") ]]; then
                        continue
                fi
                python_bin_package_installed_version_2=$(apt-cache -q=0 2>&1 policy "$python_bin_package_name_2" | grep Installed | sed -E 's|^.*?: (.*)$|\1|g')
                src_package_name_2=$(apt-get-src-package-name "$python_bin_package_name_2" "$python_bin_package_installed_version_2")
                if [[ "$src_package_name_2" == "$src_package_name" ]]; then
                        # Removing $python_bin_package_family_name from ${sorted_python_bin_package_names[@]} array
                        # Beware that the unset creates a gap in the array which modifies its size
                        unset "sorted_python_bin_package_names[${index}]"
                        # It also messes with the index if the removed cell index is lower than or equal to the current cell index
                        ((index--))
                        # Rebuilding the array to fill the gap
                        sorted_python_bin_package_names=("${sorted_python_bin_package_names[@]}")

                        python_bin_package_installed_core_version_2=$(echo "$python_bin_package_installed_version_2" | sed -E "s|^(.*)[\+-]${os_version}$|\1|g")
                        grep_esc_python_bin_package_name_2="$(echo "$python_bin_package_name_2" | escape_grep_pcre_search_string)"
                        python_bin_package_official_candidate_version_2=$(apt-cache -q=0 2>&1 madison "$python_bin_package_name_2" | grep -P "$grep_esc_python_bin_package_name_2" | grep -P "$grep_esc_official_archives_uri" | grep amd64 | head -n 1 | cut -d '|' -f 2)
                        # Removing all spaces, tabs, line breaks
                        python_bin_package_official_candidate_version_2=${python_bin_package_official_candidate_version_2//[[:space:]]/}
                        if [[ -n "$python_bin_package_official_candidate_version_2" ]]; then
                                python_bin_package_family_names_versions=("${python_bin_package_family_names_versions[@]}" "${python_bin_package_name_2}@${python_bin_package_installed_version_2}@${python_bin_package_installed_core_version_2}@${python_bin_package_official_candidate_version_2}")
                        else
                                # This binary package is not available in the official repositories
                                continue
                        fi
                fi
        done

        unset update_group
        # Building the $update_group with all the binary packages from the same source family and their official candidate version
        for python_bin_package_family_name_versions in "${python_bin_package_family_names_versions[@]}"
        do
                python_bin_package_family_name=$(echo "$python_bin_package_family_name_versions" | cut -d '@' -f 1)
                python_bin_package_family_name_official_candidate_version=$(echo "$python_bin_package_family_name_versions" | cut -d '@' -f 4)
                if [[ -n "$python_bin_package_family_name_official_candidate_version" ]]; then
                        if [[ -z "$update_group" ]]; then
                                update_group="${python_bin_package_family_name}=${python_bin_package_family_name_official_candidate_version}"
                        else
                                update_group="${update_group} ${python_bin_package_family_name}=${python_bin_package_family_name_official_candidate_version}"
                        fi
                fi
        done

        # Updating all the binary packages from the same source family
        if [[ -n $update_group ]]; then
                printf "\n"
                echo -e ''$_{1..180}'\b='
                echo "Updating all installed binary packages from source package: $src_package_name"
                echo "to: $update_group"
                echo -e ''$_{1..180}'\b='
                case $auto in
                        true|yes)
                                dpkg-lock.sh apt --show-progress --assume-yes --allow-downgrades --allow-change-held-packages install $update_group
                                return_code=$?
                                if [[ $return_code -eq 0 ]]; then
                                        updated_python_bin_package_names_versions=("${updated_python_bin_package_names_versions[@]}" "${python_bin_package_family_names_versions[@]}")
                                else
                                        aplay /usr/share/emacs/site-lisp/emacspeak/sounds/classic/alert-user.wav
                                        exit 1
                                fi
                                ;;
                        false|no)
                                dpkg-lock.sh apt --show-progress --allow-downgrades --allow-change-held-packages install $update_group
                                return_code=$?
                                if [[ $return_code -eq 0 ]]; then
                                        updated_python_bin_package_names_versions=("${updated_python_bin_package_names_versions[@]}" "${python_bin_package_family_names_versions[@]}")
                                elif [[ $return_code -ne 1 ]]; then
                                        aplay /usr/share/emacs/site-lisp/emacspeak/sounds/classic/alert-user.wav
                                        exit 1
                                fi
                                ;;
                        *)
                                printf "\n"
                                echo -e ''$_{1..180}'\b-'
                                echo "Unknown auto: $auto"
                                echo -e ''$_{1..180}'\b-'
                                exit 1
                                ;;
                esac
        fi
done

if [[ ${#updated_python_bin_package_names_versions[@]} -ne 0 ]]; then
        printf "\n"
        echo -e ''$_{1..180}'\b='
        echo "The following Python binary packages have been updated from their installed version to their official candidate version:"
        echo -e ''$_{1..180}'\b='
        echo "python_bin_package_name <--> python_bin_package_installed_version <--> python_bin_package_installed_core_version <--> python_bin_package_official_candidate_version "
        echo -e ''$_{1..180}'\b='
        printf "%s\n" "${updated_python_bin_package_names_versions[@]}" | sort -uV | sed -E 's|@| <--> |g'
fi

aplay /usr/share/sounds/purple/login.wav
exit 0