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