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_dry_run=${ND_AE_DRY_RUN:-}
35 ae_defun_only=${ND_AE_DEFUN_ONLY:-} # mode to source this file as a "library"
41 # - apt priority! (so we could avoid automagic upgrades etc)
44 if [ -z "${ND_AE_TEMPDIR:-}" ]; then
45 ae_tempdir=$(mktemp -d)
46 trap "rm -rf \"$ae_tempdir\"" TERM INT EXIT
48 # reuse the same directory/fetched configuration if was specified
49 ae_tempdir="${ND_AE_TEMPDIR:-}"
53 nd_config_file_fresh="$ae_tempdir/neurodebian.cfg"
58 if [ "$ae_verbose" -ge $level ]; then
59 # use stderr for printing within functions stdout of which might be used
61 i=1; while [ $i -lt $level ]; do echo -ne " ">&2; i=$(($i+1)); done
76 nd-aptenable $nd_aptenable_version
78 Copyright (C) 2014 Yaroslav Halchenko <debian@onerussian.com>
80 Licensed under GNU Public License version 3 or later.
81 This is free software; see the source for copying conditions. There is NO
82 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
84 Written by Yaroslav Halchenko for the NeuroDebian project.
91 if [ -z "$ae_dry_run" ]; then
92 if eval "$ae_sudo $@" 1>|"$ae_tempdir/eval.log" 2>&1; then
93 rm "$ae_tempdir/eval.log"
95 error $? "Command $@ failed with exit code $?. Output was: `cat $ae_tempdir/eval.log`"
106 Usage: nd-aptenable [options]
108 Enables NeuroDebian repository for the current Debian or Ubuntu release.
112 -r, --release=RELEASE
113 Name of the Debian/Ubuntu release to be used. If not specified,
114 it is deduced from the apt-cache policy output, by taking repository
115 of Debian or Ubuntu origin with highest priority.
118 Return a list of releases present in NeuroDebian repository.
120 -f, --flavor=full|libre
121 Which flavor of the repository should be enabled:
122 libre -- Only main component, containing only DFSG-compliant content.
123 full -- Includes main, contrib, and non-free.
124 If not specified -- deduced from the output of apt-cache policy
126 -c, --components=c1,c2,c3
127 Comma separated list of components to enable among:
128 software -- primary software repository
129 data -- data packages
130 devel -- "overlay" of development versions (like Debians' "experimental").
131 Not sufficient on its own and available only from the main site
132 If not specified -- "software,data"
134 -m, --mirror=NAME|URL
135 Which mirror to use. Could be a mirror code-name (as specified in
136 /etc/neurodebian/neurodebian.cfg), or a URL.
139 Return a list (with abbreviation) of known NeuroDebian mirrors.
142 If apt file already present, it would not be overriden (by default).
143 Use this option to overwrite.
146 Which suffix to add to the apt file, in case you are trying to enable
147 multiple repositories
149 --sources, --no-sources
150 Either to enable deb-src lines. If none specified -- would be enabled if
151 sources for a core package (apt) are available.
154 Do not perform any changes -- generated configurations and commands will
155 simply be printed to stdout
158 If found absent, all necessary tools (wget, netselect) if available will
162 Enable additional progress messages. Could be used multiple times
165 Make operation quiet -- only error messages would be output
168 Print short description, usage summary and option list.
171 Print version information and exit.
175 non-0 exit status in case of error. Error exit code would depend
176 on which command has failed
180 - Enable software and data components from the optimal (according to
181 netselect) mirror. Some information about progress will be printed
185 - Quietly enable -devel repository for the current release, and place apt
186 configuration into /etc/apt/sources.list.d/neurodebian.sources-devel.list
188 nd-aptenable -q --suffix=-devel -c devel
190 - Force sid distribution, all the components, from the Japan mirror:
192 nd-aptenable -q --suffix=-de-sid-full -c software,data,devel -m jp
197 get_neurodebian_cfg()
199 if [ -s "$nd_config_file_fresh" ]; then
200 print_verbose 3 "Config file $nd_config_file_fresh exists -- not fetching"
201 echo "$nd_config_file_fresh"
204 # First we try to fetch the most recent version from the github
205 print_verbose 3 "Fetching config file from the github repository"
206 assure_command_from_package wget wget 1
207 wget --no-check-certificate -c -q -O$nd_config_file_fresh \
209 && { echo "$nd_config_file_fresh"; } \
210 || { [ -e "$nd_config_file" ] \
211 && echo "$nd_config_file" \
212 || error 10 "Neither could fetch $nd_config_url, nor found $nd_config_file"; }
219 print_verbose 3 "Querying config $config_file section $section"
220 assure_command_from_package python python-minimal 1
221 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')]))"
226 nd_config=`get_neurodebian_cfg`
227 # $exe_dir/nd_querycfg -F" " --config-file="$nd_config" "mirrors" \
229 query_cfg_section "$nd_config" "mirrors" \
230 | while read mirror_name mirror_url; do
231 # verify that url is just a url
232 if echo "$mirror_url" | grep -v -e '^[a-z0-9:+]*://[-+_%.a-z0-9/]*$'; then
233 print_verbose 1 "Mirror $mirror_name has 'illegit' URL: $mirror_url. Skipping"
235 [ -z "$n" ] || echo -ne "${ND_IFS:-\n}"; n+=1
236 echo -n "$mirror_name $mirror_url"
242 nd_config=`get_neurodebian_cfg`
244 query_cfg_section "$nd_config" "release files" \
245 | while read release_name release_url; do
246 # verify that url is just a url
247 if [ "$release_name" = "data" ]; then
251 [ -z "$n" ] || echo -ne "${ND_IFS:-\n}"; n+=1
252 echo -n "$release_name"
256 get_package_version()
258 pkg_version=$(apt-cache policy "$1" | awk '/^ *Installed:/{print $2;}')
259 [ "$pkg_version" != '(none)' ] || pkg_version=''
264 # select "closest" mirror according to netselect.
265 print_verbose 2 "Selecting the 'best' mirror using netselect"
266 assure_command_from_package netselect
267 if ! which netselect >&/dev/null; then
268 print_verbose 1 "netselect (apt-get install netselect) needed to select the 'best' mirror was not found"
269 print_verbose 1 "Selecting the default repository: $nd_mirror_default"
270 echo $nd_mirror_default
272 # squeeze version doesn't have -D yet to force output of the URL not IP, but for our mirrors ATM it shouldn't matter
273 netselect_opts="-s 1"
274 netselect_version="$(get_package_version netselect)"
275 if dpkg --compare-versions "$netselect_version" ge 0.3.ds1-17; then
276 netselect_opts+=" -D"
278 if dpkg --compare-versions "$netselect_version" ge 0.3.ds1-15; then
279 netselect_opts+=" -I"
281 best_mirror=$(get_mirrors | awk '{print $2;}' | eval $ae_sudo xargs netselect $netselect_opts | awk '{print $2;}')
282 print_verbose 2 "Best mirror: $best_mirror"
289 # given mirror alias -- find its url
290 url=$(get_mirrors | awk "/^$1 /{print \$2;}")
291 if [ -z "$url" ]; then
292 error 9 "Cannot resolve mirror $1 to the URL"
299 # Get apt-cache policy output in a single list for matching suites
300 # (could be a separated with \| or , for multiple choices, e.g.
302 # get_apt_policy Debian,Ubuntu
304 # get_apt_policy NeuroDebian
306 $ae_sudo apt-cache policy | grep -B1 -e "o=\(${suites//,/\\|}\)" | tr '\n' ' ' | sed -e 's, -- ,\n,g' | grep -v -e '-\(updates\|security\)' | sort -nr
309 is_component_included()
311 echo "$ae_components" | tr ',' '\n' | grep -q "^$1\$"
316 apt-cache showsrc apt >&/dev/null && echo 1 || echo 0
319 assure_command_from_package()
325 which "$cmd" >&/dev/null && return 0
327 # if absent -- check availability of the package
328 apt_cache=$(LANG=C apt-cache policy "$pkg" 2>&1)
329 if [[ "$apt_cache" =~ "Unable to locate package" ]] || [[ "$apt_cache" =~ "Candidate: (none)" ]]; then
330 print_verbose 1 "Package $pkg providing command $cmd is N/A. Skipping"
333 if echo "$apt_cache" | grep -q '^\s*\*\*\*'; then
334 print_verbose 1 "WARNING -- command $cmd is N/A but package $pkg is claimed to be installed"
335 [ -z "$fail" ] && return 11 || error $fail "Command $cmd is required to proceed"
337 if [ "$ae_install" = "1" ]; then
338 print_verbose 1 "Installing $pkg package to get $cmd command"
339 eval_dry DEBIAN_FRONTEND=noninteractive apt-get install -y "$pkg"
342 print_verbose 1 "Command $cmd (from package $pkg) is N/A."
343 print_verbose 1 "Use with --install to get all necessary packages installed automatically"
344 [ -z "$fail" ] && return 12 || error $fail "Command $cmd is required to proceed"
348 # if it was requested -- return without doing anything
349 [ -z "$ae_defun_only" ] || return
352 # Commandline options handling
355 # Parse commandline options (taken from the getopt examples from the Debian util-linux package)
356 # Note that we use `"$@"' to let each command-line parameter expand to a
357 # separate word. The quotes around `$@' are essential!
358 # We need CLOPTS as the `eval set --' would nuke the return value of getopt.
359 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,print-releases,print-mirrors,print-best-mirror -n 'nd-aptenable' -- "$@"`
361 if [ $? != 0 ] ; then
362 error 2 "Problem with parsing cmdline. Terminating..."
365 # Note the quotes around `$CLOPTS': they are essential!
366 eval set -- "$CLOPTS"
368 if [ `whoami` != "root" ]; then
374 -r|--release) shift; ae_release="$1"; shift;;
375 -f|--flavor) shift; ae_flavor="$1"; shift;;
376 -c|--components) shift; ae_components="$1"; shift;;
377 -m|--mirror) shift; ae_mirror="$1"; shift;;
378 --print-mirrors) get_mirrors; exit 0;;
379 --print-best-mirror) netselect_mirror; exit 0;;
380 --print-releases) get_releases; exit 0;;
381 -n|--dry-run) ae_dry_run=1; shift;;
382 --suffix) shift; ae_suffix="$1"; shift;;
383 --overwrite) ae_overwrite="$1"; shift;;
384 --sources) ae_sources=1; shift;;
385 --no-sources) ae_sources=0; shift;;
386 --install) ae_install=1; shift;;
387 -q|--quiet) ae_verbose=0; shift;;
388 -v|--verbose) ae_verbose=$(($ae_verbose+1)); shift;;
389 -h|--help) print_help; exit 0;;
390 --version) print_version; exit 0;;
392 *) error 1 "Internal error! ($1)";;
397 if [ $# -gt 0 ] ; then
403 [ -z "$ae_sudo" ] || print_verbose 1 "This script requires root access. Since current user is not root, sudo will be used"
406 # Basic system/environment knowledge
409 ae_output_file=/etc/apt/sources.list.d/neurodebian.sources${ae_suffix}.list
411 apt_policy=$(get_apt_policy "Debian,Ubuntu" )
413 if [ -z "$ae_release" ]; then
414 ae_release=$(echo "$apt_policy" | head -1 | sed -e 's/.*,n=\([^,]*\),.*/\1/g')
417 if [ -z "$ae_flavor" ]; then
418 ae_flavor=$(echo "$apt_policy" | grep -e ",n=$ae_release," | grep -qe 'c=\(non-free\|restricted\)' && echo "full" || echo "libre")
422 # Determine which mirror to use
425 # knowing mirror is not necessary for -devel available only from the main site
426 if is_component_included software || is_component_included data; then
427 # for now just use default
428 if [ -z "$ae_mirror" ]; then # none specified
429 ae_mirror_url=$nd_mirror_origin
431 if ! [[ "$ae_mirror" =~ ".*://.*" ]]; then
433 best) ae_mirror_url=$(netselect_mirror);;
434 default) ae_mirror_url=$nd_mirror_default;;
435 origin) ae_mirror_url=$nd_mirror_origin;;
436 *) ae_mirror_url=$(get_mirror_url "$ae_mirror");;
439 ae_mirror_url="$ae_mirror" # it was some kind of a URL already
449 full) apt_flavor="contrib non-free";;
450 libre) apt_flavor="";;
451 *) error 5 "Unknown value of flavor $apt_flavor. Must be full or libre"
454 if [ -z "$ae_sources" ]; then
455 ae_sources=$(is_sources_enabled)
458 if [ $ae_sources -eq 0 ]; then
466 if is_component_included software; then
468 # NeuroDebian software repository
469 deb $ae_mirror_url $ae_release main $apt_flavor
470 ${sources_comment}deb-src $ae_mirror_url $ae_release main $apt_flavor
474 if is_component_included data; then
476 # NeuroDebian data repository
477 deb $ae_mirror_url data main $apt_flavor
478 ${sources_comment}deb-src $ae_mirror_url data main $apt_flavor
482 if is_component_included devel; then
484 # NeuroDebian -devel repository
485 deb http://neuro.debian.net/debian-devel $ae_release main $apt_flavor
486 ${sources_comment}deb-src http://neuro.debian.net/debian-devel $ae_release main $apt_flavor
490 if [ -e "$ae_output_file" ] && [ -z "$ae_overwrite" ]; then
491 if diff "$ae_output_file" <(echo "$apt_list") | grep -q .; then
493 print_verbose 1 "File $ae_output_file already exists, containing:\n`cat \"$ae_output_file\"`\n\nI: New configuration is different:\n$apt_list"
494 if get_apt_policy NeuroDebian >/dev/null; then
495 print_verbose 1 "NeuroDebian repositories are already available, thus skipping the rest."
496 print_verbose 1 "Rerun with --overwrite if you would like to reconfigure."
499 print_verbose 1 "NeuroDebian configuration is found but not yet available -- continuing with new configuration."
502 print_verbose 1 "New configuration is identical to existing and NeuroDebian repository is already enabled."
503 print_verbose 1 "Skiping the rest. Rerun with --overwrite if you would like to reconfigure."
507 print_verbose 1 "Generating $ae_output_file"
508 if [ -z "$ae_dry_run" ]; then
509 echo "$apt_list" | $ae_sudo bash -c "cat - >| '$ae_output_file'"
518 # Assure present archive GPG key for APT system
521 # Figure out if key needs to be imported (if ran within package,
522 # should already be there due to neurodebian-keyring package)
523 if LANG=C eval $ae_sudo apt-key export $nd_key_id 2>&1 1>/dev/null | grep -qe "nothing exported"; then
524 print_verbose 1 "Fetching the key from the server"
525 eval_dry apt-key adv --recv-keys --keyserver pgp.mit.edu $nd_key_id
530 # Finalizing (apt-get update etc)
533 print_verbose 1 "Updating APT listings, might take a few minutes"
534 if [ -z "$ae_dry_run" ]; then
535 apt_logfile="$ae_tempdir/apt.log"
536 $ae_sudo apt-get update 1>"$apt_logfile" 2>&1 \
537 && rm -f "$apt_logfile" \
539 apt_log=$(cat "$apt_logfile")
541 if echo "$apt_log" | grep -q "Malformed line [0-9]* in source list $ae_output_file"; then
542 $ae_sudo mv "${ae_output_file}" "${ae_output_file}-failed.disabled"
543 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"
545 error 5 "Update failed with exit code $? (above output logged into $apt_logfile)."
548 eval_dry apt-get update
551 if [ "$ae_verbose" -ge 2 ]; then
552 print_verbose 2 "Currently enabled NeuroDebian suites/mirrors:"
553 get_apt_policy NeuroDebian