]> git.donarmstrong.com Git - neurodebian.git/blob - tools/nd-aptenable
BF: fetching/reporting of the config file
[neurodebian.git] / tools / nd-aptenable
1 #!/bin/bash
2 #emacs: -*- mode: shell-script; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
3 #ex: set sts=4 ts=4 sw=4 et:
4
5 # Depends:    apt, python, wget
6 # Recommends: netselect
7
8 # play safe
9 set -e
10 set -u
11
12 ############
13 # Defaults #
14 ############
15
16 nd_aptenable_version=0.1
17
18 nd_key_id=0x2649A5A9
19 nd_config_url=https://raw.githubusercontent.com/neurodebian/neurodebian/master/neurodebian.cfg
20 nd_config_file=/etc/neurodebian/neurodebian.cfg
21 nd_mirror_origin=http://neuro.debian.net/debian
22 nd_mirror_default=$nd_mirror_origin # or may be AWS?
23
24 # To be set by cmdline args
25 ae_release=
26 ae_components=software,data
27 ae_flavor=
28 ae_mirror=best
29 ae_suffix=
30 ae_verbose=1
31 ae_overwrite=
32 ae_sources=
33 ae_dry_run=
34
35 ae_sudo=
36 exe_dir=$(dirname $0)
37
38 # TODOs:
39 # - apt priority! (so we could avoid automagic upgrades etc)
40 # - multiarch setups
41
42 ae_tempdir=$(mktemp -d)
43 trap "rm -rf \"$ae_tempdir\"" TERM INT EXIT
44
45
46 print_verbose()
47 {
48     level=$1; shift
49         if [ "$ae_verbose" -ge $level ]; then
50         # use stderr for printing within functions stdout of which might be used
51         echo -e "I: $*" >&2
52     fi
53 }
54
55 error()
56 {
57     code=$1; shift
58         echo -e "E: $*" >&2
59     exit $code
60 }
61
62 print_version()
63 {
64     cat << EOT
65 nd-aptenable $nd_aptenable_version
66
67 Copyright (C) 2014 Yaroslav Halchenko <debian@onerussian.com>
68
69 Licensed under GNU Public License version 3 or later.
70 This is free software; see the source for copying conditions.  There is NO
71 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
72
73 Written by Yaroslav Halchenko for the NeuroDebian project.
74
75 EOT
76 }
77
78 eval_dry()
79 {
80     if [ -z "$ae_dry_run" ]; then
81         eval "$ae_sudo $@"
82     else
83         echo "DRY: $@"
84     fi
85 }
86
87 print_help()
88 {
89     cat << EOT
90
91 Usage:  nd-aptenable [options]
92
93 Enables NeuroDebian repository for the current Debian or Ubuntu release.
94
95 Options:
96
97   -r, --release=RELEASE
98     Name of the Debian/Ubuntu release to be used.  If not specified,
99     it is deduced from the  apt-cache policy  output, by taking repository
100     of Debian or Ubuntu origin with highest priority.
101
102   -f, --flavor=full|libre
103     Which flavor of the repository should be enabled:
104      libre -- Only  main  component, containing only DFSG-compliant content.
105      full -- Includes main, contrib, and non-free.
106     If not specified -- deduced from the output of apt-cache policy
107
108   -c, --components=c1,c2,c3
109     Comma separated list of components to enable among:
110      software -- primary software repository
111      data -- data packages
112      devel -- "overlay" of development versions (like Debians' "experimental").
113               Not sufficient on its own and available only from the main site
114     If not specified -- "software,data"
115
116   -m, --mirror=NAME|URL
117     Which mirror to use.  Could be a mirror code-name (as specified in
118     /etc/neurodebian/neurodebian.cfg), or a URL.
119
120   --overwrite,
121     If apt file already present, it would not be overriden (by default).
122     Use this option to overwrite.
123
124   --suffix=SUFFIX
125     Which suffix to add to the apt file, in case you are trying to enable
126     multiple repositories
127
128   --sources, --no-sources
129     Either to enable deb-src lines.  If none specified -- would be enabled if
130     sources for a core package (apt) are available.
131
132   -n, --dry-run
133     Do not perform any changes -- generated configurations and commands will
134     simply be printed to stdout
135
136   -v, --verbose
137     Enable additional progress messages.  Could be used multiple times
138
139   -q, --quiet
140     Make operation quiet -- only error messages would be output
141
142   -h, --help
143     Print short description, usage summary and option list.
144
145   --version
146     Print version information and exit.
147
148 Exit status:
149
150   non-0 exit status in case of error.  Error exit code would depend
151   on which command has failed
152
153 Examples:
154
155   - Enable software and data components from the optimal (according to
156     netselect) mirror.  Some information about progress will be printed
157
158     nd-aptenable
159
160   - Quietly enable -devel repository for the current release, and place apt
161     configuration into /etc/apt/sources.list.d/neurodebian.sources-devel.list
162
163     nd-aptenable -q --suffix=-devel -c devel
164
165   - Force sid distribution, all the components, from the Japan mirror:
166
167     nd-aptenable -q --suffix=-de-sid-full -c software,data,devel -m jp
168
169 EOT
170 }
171
172 get_neurodebian_cfg()
173 {
174     # First we try to fetch the most recent version from the github
175     print_verbose 3 "Fetching config file from the github repository"
176     cfgfile_temp="$ae_tempdir/neurodebian.cfg"
177     wget --no-check-certificate -c -q -O$cfgfile_temp \
178         $nd_config_url \
179         && { echo $cfgfile_temp; } \
180         || { [ -e "$nd_config_file" ] \
181              && echo "$nd_config_file" \
182              || error 10 "Neither could fetch $nd_config_url, nor found $nd_config_file"; }
183 }
184
185 query_cfg_section()
186 {
187     config_file="$1"
188     section="$2"
189     print_verbose 3 "Querying config $config_file section $section"
190     python -c "from ConfigParser import SafeConfigParser as SP; cfg = SP(); cfg.read('$config_file'); print('\n'.join([' '.join(x) for x in cfg.items('$section')]))"
191 }
192
193 get_mirrors()
194 {
195     nd_config=`get_neurodebian_cfg`
196 #    $exe_dir/nd_querycfg -F" " --config-file="$nd_config" "mirrors" \
197     query_cfg_section "$nd_config" "mirrors" \
198     | while read mirror_name mirror_url; do
199         # verify that url is just a url
200         if echo "$mirror_url" | grep -v -e '^[a-z0-9:+]*://[-+_%.a-z0-9/]*$'; then
201             print_verbose 1 "Mirror $mirror_name has 'illegit' URL: $mirror_url.  Skipping"
202         fi
203         echo "$mirror_name $mirror_url"
204     done
205 }
206
207 netselect_mirror() {
208     # select "closest" mirror according to netselect.
209     print_verbose 2 "Selecting the 'best' mirror using netselect"
210     if ! which netselect >/dev/null; then
211         print_verbose 1 "netselect (apt-get install netselect) needed to select the 'best' mirror was not found"
212         print_verbose 1 "Selecting the default repository: $nd_mirror_default"
213         echo $nd_mirror_default
214     else
215         get_mirrors | awk '{print $2;}' | $ae_sudo xargs netselect -D -s 1 | awk '{print $2;}'
216     fi
217 }
218
219 get_mirror_url() {
220     # given mirror alias -- find its url
221     url=$(get_mirrors | awk "/^$1 /{print \$2;}")
222     if [ -z "$url" ]; then
223         error 9 "Cannot resolve mirror $1 to the URL"
224     fi
225     echo $url
226 }
227
228 get_apt_policy()
229 {
230     # Get apt-cache policy output in a single list for matching suites
231     # (could be a separated with \| or , for multiple choices, e.g.
232     #
233     # get_apt_policy Debian,Ubuntu
234     # or
235     # get_apt_policy NeuroDebian
236     suites="$1"
237     apt-cache policy | grep -B1 -e "o=\(${suites//,/\\|}\)" | tr '\n' ' ' | sed -e 's, -- ,\n,g' | grep -v -e '-\(updates\|security\)' | sort -nr
238 }
239
240 include_component()
241 {
242     echo "$ae_components" | tr ',' '\n' | grep -q "^$1\$"
243 }
244
245
246 #
247 # Commandline options handling
248 #
249
250 # Parse commandline options (taken from the getopt examples from the Debian util-linux package)
251 # Note that we use `"$@"' to let each command-line parameter expand to a
252 # separate word. The quotes around `$@' are essential!
253 # We need CLOPTS as the `eval set --' would nuke the return value of getopt.
254 CLOPTS=`getopt -o h,r:,m:,f:,c:,q,v,n --long help,version,quiet,verbose,mirror:,release:,flavor:,components:,suffix:,overwrite,sources,no-sources,dry-run,print-mirrors,print-best-mirror -n 'nd-aptenable' -- "$@"`
255
256 if [ $? != 0 ] ; then
257   error 2 "Problem with parsing cmdline.  Terminating..."
258 fi
259
260 # Note the quotes around `$CLOPTS': they are essential!
261 eval set -- "$CLOPTS"
262
263 if [ `whoami` != "root" ]; then
264     ae_sudo=sudo
265 fi
266
267 while true ; do
268   case "$1" in
269           -r|--release) shift; ae_release="$1"; shift;;
270           -f|--flavor) shift;  ae_flavor="$1"; shift;;
271           -c|--components) shift; ae_components="$1"; shift;;
272       -m|--mirror) shift;  ae_mirror="$1"; shift;;
273          --print-mirrors)  get_mirrors; exit 0;;
274          --print-best-mirror)  netselect_mirror; exit 0;;
275       -n|--dry-run)        ae_dry_run=1; shift;;
276          --suffix) shift;  ae_suffix="$1"; shift;;
277          --overwrite)      ae_overwrite="$1"; shift;;
278          --sources)        ae_sources=1; shift;;
279          --no-sources)     ae_sources=0; shift;;
280           -q|--quiet)          ae_verbose=0; shift;;
281           -v|--verbose)        ae_verbose=$(($ae_verbose+1)); shift;;
282           -h|--help) print_help; exit 0;;
283           --version) print_version; exit 0;;
284           --) shift ; break ;;
285           *) error 1 "Internal error! ($1)";;
286   esac
287 done
288
289
290 if [ $# -gt 0 ] ; then
291     print_help >&2
292     exit 2
293 fi
294
295 # Inform!
296 [ -z "$ae_sudo" ] || print_verbose 1 "This script requires root access.  Since current user is not root, sudo will be used"
297
298 #
299 # Basic system/environment knowledge
300 #
301
302 ae_output_file=/etc/apt/sources.list.d/neurodebian.sources${ae_suffix}.list
303
304 apt_policy=$(get_apt_policy "Debian,Ubuntu" )
305
306 if [ -z "$ae_release" ]; then
307     ae_release=$(echo "$apt_policy" | head -1 | sed -e 's/.*,n=\([^,]*\),.*/\1/g')
308 fi
309
310 if [ -z "$ae_flavor" ]; then
311     ae_flavor=$(echo "$apt_policy" | grep -e ",n=$ae_release," | grep -qe 'c=\(non-free\|restricted\)' && echo "full" || echo "libre")
312 fi
313
314 #
315 # Determine which mirror to use
316 #
317
318 # knowing mirror is not necessary for -devel available only from the main site
319 if include_component software || include_component data; then
320     # for now just use default
321     if [ -z "$ae_mirror" ]; then # none specified
322         ae_mirror_url=$nd_mirror_origin
323     else
324         if ! [[ "$ae_mirror" =~ ".*://.*" ]]; then
325             case "$ae_mirror" in
326                 best)    ae_mirror_url=$(netselect_mirror);;
327                 default) ae_mirror_url=$nd_mirror_default;;
328                 origin)  ae_mirror_url=$nd_mirror_origin;;
329                 *)       ae_mirror_url=$(get_mirror_url "$ae_mirror");;
330             esac
331         else
332             ae_mirror_url="$ae_mirror" # it was some kind of a URL already
333         fi
334     fi
335 fi
336
337 #
338 # Prepare APT file
339 #
340
341 case $ae_flavor in
342  full)  apt_flavor="contrib non-free";;
343  libre) apt_flavor="";;
344  *) error 5 "Unknown value of flavor $apt_flavor.  Must be full or libre"
345 esac
346
347 if [ -z "$ae_sources" ]; then
348     apt-cache showsrc apt >&/dev/null && ae_sources=1 || ae_sources=0
349 fi
350
351 if [ $ae_sources -eq 0 ]; then
352     sources_comment="#"
353 else
354     sources_comment=""
355 fi
356
357 apt_list=
358
359 if include_component software; then
360     apt_list+="
361 # NeuroDebian software repository
362 deb     $ae_mirror_url $ae_release main $apt_flavor
363 ${sources_comment}deb-src $ae_mirror_url $ae_release main $apt_flavor
364 "
365 fi
366
367 if include_component data; then
368     apt_list+="
369 # NeuroDebian data repository
370 deb     $ae_mirror_url data main $apt_flavor
371 ${sources_comment}deb-src $ae_mirror_url data main $apt_flavor
372 "
373 fi
374
375 if include_component devel; then
376     apt_list+="
377 # NeuroDebian -devel repository
378 deb     http://neuro.debian.net/debian-devel $ae_release main $apt_flavor
379 ${sources_comment}deb-src http://neuro.debian.net/debian-devel $ae_release main $apt_flavor
380 "
381 fi
382
383 if [ -e "$ae_output_file" ] && [ -z "$ae_overwrite" ]; then
384     if diff "$ae_output_file" <(echo "$apt_list") | grep -q .; then
385         # error 3
386         print_verbose 1 "File $ae_output_file already exists, containing:\n`cat \"$ae_output_file\"`\n\nI: New configuration is different:\n$apt_list"
387         if get_apt_policy NeuroDebian >/dev/null; then
388             print_verbose 1 "NeuroDebian repositories are already available, thus skipping the rest."
389             print_verbose 1 "Rerun with --overwrite if you would like to reconfigure."
390             exit 0
391         else
392             print_verbose 1 "NeuroDebian configuration is found but not yet available -- continuing with new configuration."
393         fi
394     else
395         print_verbose 1 "New configuration is identical to existing and NeuroDebian repository is already enabled."
396         print_verbose 1 "Skiping the rest. Rerun with --overwrite if you would like to reconfigure."
397         exit 0
398     fi
399 else
400     print_verbose 1 "Generating $ae_output_file"
401     if [ -z "$ae_dry_run" ]; then
402         echo "$apt_list" | $ae_sudo bash -c "cat - >| '$ae_output_file'"
403     else
404         echo "DRY:"
405         echo "$apt_list"
406     fi
407 fi
408
409
410 #
411 # Assure present archive GPG key for APT system
412 #
413
414 # Figure out if key needs to be imported (if ran within package,
415 # should already be there due to neurodebian-keyring package)
416 if LANG=C eval $ae_sudo apt-key export $nd_key_id 2>&1 1>/dev/null | grep -qe "nothing exported"; then
417     print_verbose 1 "Fetching the key from the server"
418     eval_dry apt-key adv --recv-keys --keyserver pgp.mit.edu $nd_key_id
419 fi
420
421 #
422 # Finalizing (apt-get update etc)
423 #
424
425 print_verbose 1 "Updating APT listings, might take a few minutes"
426 if [ -z "$ae_dry_run" ]; then
427     apt_logfile="$ae_tempdir/apt.log"
428     $ae_sudo apt-get update 1>"$apt_logfile" 2>&1 \
429         && rm -f "$apt_logfile" \
430         || {
431              cat "$apt_logfile"
432              error 5 "E: Update failed with exit code $? (above output logged into $apt_logfile)."
433              }
434 else
435     eval_dry apt-get update
436 fi
437
438 if [ "$ae_verbose" -ge 2 ]; then
439     print_verbose 2 "Currently enabled NeuroDebian suites/mirrors:"
440     get_apt_policy NeuroDebian
441 fi