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:
5 # Depends: apt (assumed to be present), python, wget
6 # Recommends: netselect
16 nd_aptenable_version=0.1
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?
24 # To be set by cmdline args or via env variables with prefix ND_AE_
25 ae_release=${ND_AE_RELEASE:-}
26 ae_components=${ND_AE_COMPONENTS:-software,data}
27 ae_flavor=${ND_AE_FLAVOR:-}
28 ae_mirror=${ND_AE_MIRROR:-best}
29 ae_suffix=${ND_AE_SUFFIX:-}
30 ae_verbose=${ND_AE_VERBOSE:-1}
31 ae_overwrite=${ND_AE_OVERWRITE:-}
32 ae_sources=${ND_AE_SOURCES:-}
33 ae_install=${ND_AE_INSTALL:-}
34 ae_update=${ND_AE_UPDATE:-1}
35 ae_dry_run=${ND_AE_DRY_RUN:-}
36 ae_defun_only=${ND_AE_DEFUN_ONLY:-} # mode to source this file as a "library"
42 # - apt priority! (so we could avoid automagic upgrades etc)
45 if [ -z "${ND_AE_TEMPDIR:-}" ]; then
46 ae_tempdir=$(mktemp -d)
47 trap "rm -rf \"$ae_tempdir\"" TERM INT EXIT
49 # reuse the same directory/fetched configuration if was specified
50 ae_tempdir="${ND_AE_TEMPDIR:-}"
54 nd_config_file_fresh="$ae_tempdir/neurodebian.cfg"
59 if [ "$ae_verbose" -ge $level ]; then
60 # use stderr for printing within functions stdout of which might be used
62 i=1; while [ $i -lt $level ]; do echo -ne " ">&2; i=$(($i+1)); done
77 nd-aptenable $nd_aptenable_version
79 Copyright (C) 2014 Yaroslav Halchenko <debian@onerussian.com>
81 Licensed under GNU Public License version 3 or later.
82 This is free software; see the source for copying conditions. There is NO
83 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
85 Written by Yaroslav Halchenko for the NeuroDebian project.
92 if [ -z "$ae_dry_run" ]; then
93 if eval "$ae_sudo $@" 1>|"$ae_tempdir/eval.log" 2>&1; then
94 rm "$ae_tempdir/eval.log"
96 error $? "Command $@ failed with exit code $?. Output was: `cat $ae_tempdir/eval.log`"
107 Usage: nd-aptenable [options]
109 Enables NeuroDebian repository for the current Debian or Ubuntu release.
113 -r, --release=RELEASE
114 Name of the Debian/Ubuntu release to be used. If not specified,
115 it is deduced from the apt-cache policy output, by taking repository
116 of Debian or Ubuntu origin with highest priority.
119 Return a list of releases present in NeuroDebian repository.
121 -f, --flavor=full|libre
122 Which flavor of the repository should be enabled:
123 libre -- Only main component, containing only DFSG-compliant content.
124 full -- Includes main, contrib, and non-free.
125 If not specified -- deduced from the output of apt-cache policy
127 -c, --components=c1,c2,c3
128 Comma separated list of components to enable among:
129 software -- primary software repository
130 data -- data packages
131 devel -- "overlay" of development versions (like Debians' "experimental").
132 Not sufficient on its own and available only from the main site
133 If not specified -- "software,data"
135 -m, --mirror=NAME|URL
136 Which mirror to use. Could be a mirror code-name (as specified in
137 /etc/neurodebian/neurodebian.cfg), or a URL.
140 Return a list (with abbreviation) of known NeuroDebian mirrors.
143 If apt file already present, it would not be overriden (by default).
144 Use this option to overwrite.
147 Which suffix to add to the apt file, in case you are trying to enable
148 multiple repositories
150 --sources, --no-sources
151 Either to enable deb-src lines. If none specified -- would be enabled if
152 sources for a core package (apt) are available.
155 Do not perform any changes -- generated configurations and commands will
156 simply be printed to stdout
159 If found absent, all necessary tools (wget, netselect) if available will
163 Enable additional progress messages. Could be used multiple times
166 Make operation quiet -- only error messages would be output
169 Print short description, usage summary and option list.
172 Print version information and exit.
176 non-0 exit status in case of error. Error exit code would depend
177 on which command has failed
181 - Enable software and data components from the optimal (according to
182 netselect) mirror. Some information about progress will be printed
186 - Quietly enable -devel repository for the current release, and place apt
187 configuration into /etc/apt/sources.list.d/neurodebian.sources-devel.list
189 nd-aptenable -q --suffix=-devel -c devel
191 - Force sid distribution, all the components, from the Japan mirror:
193 nd-aptenable -q --suffix=-de-sid-full -c software,data,devel -m jp
198 get_neurodebian_cfg()
200 if [ -s "$nd_config_file_fresh" ]; then
201 print_verbose 3 "Config file $nd_config_file_fresh exists -- not fetching"
202 echo "$nd_config_file_fresh"
205 # First we try to fetch the most recent version from the github
206 print_verbose 3 "Fetching config file from the github repository"
207 assure_command_from_package wget wget 1
208 wget --no-check-certificate -c -q -O$nd_config_file_fresh \
210 && { echo "$nd_config_file_fresh"; } \
211 || { [ -e "$nd_config_file" ] \
212 && echo "$nd_config_file" \
213 || error 10 "Neither could fetch $nd_config_url, nor found $nd_config_file"; }
220 print_verbose 3 "Querying config $config_file section $section"
221 assure_command_from_package python python-minimal 1
222 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')]))"
227 nd_config=`get_neurodebian_cfg`
228 # $exe_dir/nd_querycfg -F" " --config-file="$nd_config" "mirrors" \
230 query_cfg_section "$nd_config" "mirrors" \
231 | while read mirror_name mirror_url; do
232 # verify that url is just a url
233 if echo "$mirror_url" | grep -v -e '^[a-z0-9:+]*://[-+_%.a-z0-9/]*$'; then
234 print_verbose 1 "Mirror $mirror_name has 'illegit' URL: $mirror_url. Skipping"
236 [ -z "$n" ] || echo -ne "${ND_IFS:-\n}"; n+=1
237 echo -n "$mirror_name $mirror_url"
243 nd_config=`get_neurodebian_cfg`
245 query_cfg_section "$nd_config" "release files" \
246 | while read release_name release_url; do
247 # verify that url is just a url
248 if [ "$release_name" = "data" ]; then
252 [ -z "$n" ] || echo -ne "${ND_IFS:-\n}"; n+=1
253 echo -n "$release_name"
257 get_package_version()
259 pkg_version=$(apt-cache policy "$1" | awk '/^ *Installed:/{print $2;}')
260 [ "$pkg_version" != '(none)' ] || pkg_version=''
265 # select "closest" mirror according to netselect.
266 print_verbose 2 "Selecting the 'best' mirror using netselect"
267 assure_command_from_package netselect
268 if ! which netselect >&/dev/null; then
269 print_verbose 1 "netselect (apt-get install netselect) needed to select the 'best' mirror was not found"
270 print_verbose 1 "Selecting the default repository: $nd_mirror_default"
271 echo $nd_mirror_default
273 # squeeze version doesn't have -D yet to force output of the URL not IP, but for our mirrors ATM it shouldn't matter
274 netselect_opts="-s 1"
275 netselect_version="$(get_package_version netselect)"
276 if dpkg --compare-versions "$netselect_version" ge 0.3.ds1-17; then
277 netselect_opts+=" -D"
279 if dpkg --compare-versions "$netselect_version" ge 0.3.ds1-15; then
280 netselect_opts+=" -I"
282 best_mirror=$(get_mirrors | awk '{print $2;}' | eval $ae_sudo xargs netselect $netselect_opts | awk '{print $2;}')
283 if [ -z "$best_mirror" ]; then
284 print_verbose 1 "Failed to select mirror using netselect. Selecting default one ($nd_mirror_default)"
285 echo "$nd_mirror_default"
287 print_verbose 2 "Best mirror: $best_mirror"
295 # given mirror alias -- find its url
296 url=$(get_mirrors | awk "/^$1 /{print \$2;}")
297 if [ -z "$url" ]; then
298 error 9 "Cannot resolve mirror $1 to the URL"
305 # Get apt-cache policy output in a single list for matching suites
306 # (could be a separated with \| or , for multiple choices, e.g.
308 # get_apt_policy Debian,Ubuntu
310 # get_apt_policy NeuroDebian
312 $ae_sudo apt-cache policy | grep -B1 -e "o=\(${suites//,/\\|}\)" | tr '\n' ' ' | sed -e 's, -- ,\n,g' | grep -v -e '-\(updates\|security\)' | sort -nr
315 is_component_included()
317 echo "$ae_components" | tr ',' '\n' | grep -q "^$1\$"
322 apt-cache showsrc apt >&/dev/null && echo 1 || echo 0
325 assure_command_from_package()
331 which "$cmd" >&/dev/null && return 0
333 # if absent -- check availability of the package
334 apt_cache=$(LANG=C apt-cache policy "$pkg" 2>&1)
335 if [[ "$apt_cache" =~ Unable\ to\ locate\ package ]] || [[ "$apt_cache" =~ Candidate:\ (none) ]]; then
336 print_verbose 1 "Package $pkg providing command $cmd is N/A. Skipping"
339 if echo "$apt_cache" | grep -q '^\s*\*\*\*'; then
340 print_verbose 1 "WARNING -- command $cmd is N/A but package $pkg is claimed to be installed"
341 [ -z "$fail" ] && return 11 || error $fail "Command $cmd is required to proceed"
343 if [ "$ae_install" = "1" ]; then
344 print_verbose 1 "Installing $pkg package to get $cmd command"
345 eval_dry DEBIAN_FRONTEND=noninteractive apt-get install -y "$pkg"
348 print_verbose 1 "Command $cmd (from package $pkg) is N/A."
349 print_verbose 1 "Use with --install to get all necessary packages installed automatically"
350 [ -z "$fail" ] && return 12 || error $fail "Command $cmd is required to proceed"
354 # if it was requested -- return without doing anything
355 [ -z "$ae_defun_only" ] || return
358 # Commandline options handling
361 # Parse commandline options (taken from the getopt examples from the Debian util-linux package)
362 # Note that we use `"$@"' to let each command-line parameter expand to a
363 # separate word. The quotes around `$@' are essential!
364 # We need CLOPTS as the `eval set --' would nuke the return value of getopt.
365 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,install,dry-run,do-not-update,print-releases,print-mirrors,print-best-mirror -n 'nd-aptenable' -- "$@"`
367 if [ $? != 0 ] ; then
368 error 2 "Problem with parsing cmdline. Terminating..."
371 # Note the quotes around `$CLOPTS': they are essential!
372 eval set -- "$CLOPTS"
374 if [ `whoami` != "root" ]; then
380 -r|--release) shift; ae_release="$1"; shift;;
381 -f|--flavor) shift; ae_flavor="$1"; shift;;
382 -c|--components) shift; ae_components="$1"; shift;;
383 -m|--mirror) shift; ae_mirror="$1"; shift;;
384 --print-mirrors) get_mirrors; exit 0;;
385 --print-best-mirror) netselect_mirror; exit 0;;
386 --print-releases) get_releases; exit 0;;
387 -n|--dry-run) ae_dry_run=1; shift;;
388 --suffix) shift; ae_suffix="$1"; shift;;
389 --overwrite) ae_overwrite="$1"; shift;;
390 --do-not-update) ae_update=""; shift;;
391 --sources) ae_sources=1; shift;;
392 --no-sources) ae_sources=0; shift;;
393 --install) ae_install=1; shift;;
394 -q|--quiet) ae_verbose=0; shift;;
395 -v|--verbose) ae_verbose=$(($ae_verbose+1)); shift;;
396 -h|--help) print_help; exit 0;;
397 --version) print_version; exit 0;;
399 *) error 1 "Internal error! ($1)";;
404 if [ $# -gt 0 ] ; then
410 [ -z "$ae_sudo" ] || print_verbose 1 "This script requires root access. Since current user is not root, sudo will be used"
413 # Basic system/environment knowledge
416 ae_output_file=/etc/apt/sources.list.d/neurodebian.sources${ae_suffix}.list
418 apt_policy=$(get_apt_policy "Debian,Ubuntu" )
420 if [ -z "$ae_release" ]; then
421 ae_release=$(echo "$apt_policy" | head -1 | sed -e 's/.*,n=\([^,]*\),.*/\1/g')
424 if [ -z "$ae_flavor" ]; then
425 ae_flavor=$(echo "$apt_policy" | grep -e ",n=$ae_release," | grep -qe 'c=\(non-free\|multiverse\)' && echo "full" || echo "libre")
429 # Determine which mirror to use
432 # knowing mirror is not necessary for -devel available only from the main site
433 if is_component_included software || is_component_included data; then
434 # for now just use default
435 if [ -z "$ae_mirror" ]; then # none specified
436 ae_mirror_url=$nd_mirror_origin
438 if ! [[ "$ae_mirror" =~ .*://.* ]]; then
440 best) ae_mirror_url=$(netselect_mirror);;
441 default) ae_mirror_url=$nd_mirror_default;;
442 origin) ae_mirror_url=$nd_mirror_origin;;
443 *) ae_mirror_url=$(get_mirror_url "$ae_mirror");;
446 ae_mirror_url="$ae_mirror" # it was some kind of a URL already
456 full) apt_flavor="contrib non-free";;
457 libre) apt_flavor="";;
458 *) error 5 "Unknown value of flavor $apt_flavor. Must be full or libre"
461 if [ -z "$ae_sources" ]; then
462 ae_sources=$(is_sources_enabled)
465 if [ $ae_sources -eq 0 ]; then
473 if is_component_included software; then
475 # NeuroDebian software repository
476 deb $ae_mirror_url $ae_release main $apt_flavor
477 ${sources_comment}deb-src $ae_mirror_url $ae_release main $apt_flavor
481 if is_component_included data; then
483 # NeuroDebian data repository
484 deb $ae_mirror_url data main $apt_flavor
485 ${sources_comment}deb-src $ae_mirror_url data main $apt_flavor
489 if is_component_included devel; then
491 # NeuroDebian -devel repository
492 deb http://neuro.debian.net/debian-devel $ae_release main $apt_flavor
493 ${sources_comment}deb-src http://neuro.debian.net/debian-devel $ae_release main $apt_flavor
497 if [ -e "$ae_output_file" ] && [ -z "$ae_overwrite" ]; then
498 if diff "$ae_output_file" <(echo "$apt_list") | grep -q .; then
500 print_verbose 1 "File $ae_output_file already exists, containing:\n`cat \"$ae_output_file\"`\n\nI: New configuration is different:\n$apt_list"
501 if get_apt_policy NeuroDebian >/dev/null; then
502 print_verbose 1 "NeuroDebian repositories are already available, thus skipping the rest."
503 print_verbose 1 "Rerun with --overwrite if you would like to reconfigure."
506 print_verbose 1 "NeuroDebian configuration is found but not yet available -- continuing with new configuration."
509 print_verbose 1 "New configuration is identical to existing and NeuroDebian repository is already enabled."
510 print_verbose 1 "Skiping the rest. Rerun with --overwrite if you would like to reconfigure."
514 print_verbose 1 "Generating $ae_output_file"
515 if [ -z "$ae_dry_run" ]; then
516 echo "$apt_list" | $ae_sudo bash -c "cat - >| '$ae_output_file'"
525 # Assure present archive GPG key for APT system
528 # Figure out if key needs to be imported (if ran within package,
529 # should already be there due to neurodebian-keyring package)
530 if LANG=C eval $ae_sudo apt-key export $nd_key_id 2>&1 1>/dev/null | grep -qe "nothing exported"; then
531 print_verbose 1 "Fetching the key from the server"
532 eval_dry apt-key adv --recv-keys --keyserver pgp.mit.edu $nd_key_id
537 # Finalizing (apt-get update etc)
540 if [ ! -z "$ae_update" ]; then
541 print_verbose 1 "Updating APT listings, might take a few minutes"
542 if [ -z "$ae_dry_run" ]; then
543 apt_logfile="$ae_tempdir/apt.log"
544 $ae_sudo apt-get update 1>"$apt_logfile" 2>&1 \
545 && rm -f "$apt_logfile" \
547 apt_log=$(cat "$apt_logfile")
549 if echo "$apt_log" | grep -q "Malformed line [0-9]* in source list $ae_output_file"; then
550 $ae_sudo mv "${ae_output_file}" "${ae_output_file}-failed.disabled"
551 error 6 "Update failed to possible errorneous APT listing file produced by this script. Generated $ae_output_file renamed to ${ae_output_file}-failed.disabled to not interfer"
553 error 5 "Update failed with exit code $? (above output logged into $apt_logfile)."
556 eval_dry apt-get update
559 print_verbose 1 "apt-get update was not run. Please run to take an effect of changes"
562 if [ "$ae_verbose" -ge 2 ]; then
563 print_verbose 2 "Currently enabled NeuroDebian suites/mirrors:"
564 get_apt_policy NeuroDebian