#!/bin/bash # # Mirror script for use as a dgit-repos-server mirror hook # # In addition to updated-hook (invoked by dgit-repos-server), # this script also supports the following ACTIONs: # MIRROR-HOOK-SCRIPT ... setup [...] create queue dir etc. # MIRROR-HOOK-SCRIPT ... backlog [...] do all packages which need it # MIRROR-HOOK-SCRIPT ... all [...] do all packages # MIRROR-HOOK-SCRIPT ... mirror PACKAGE [...] do just that, longer timeout # # DISTRO-DIR must contain a file `mirror-settings' which is a bash # script fragment assigning the following variables: # remoterepos for rsync, in form user@host:/dir # and optionally # hooktimeout default 30 [sec] # rsynctimeout default 900 [sec] # rsyncssh default 'ssh -o batchmode=yes' # rsync array, default (rsync -rltH --safe-links --delete) # repos default DISTRO-DIR/repos # (optional settings are all set before mirror-settings is included, # so you can modify them with += or some such) set -e set -o pipefail shopt -s nullglob case "$DGIT_DRS_DEBUG" in ''|0|1) ;; *) set -x ;; esac fail () { echo >&2 "dgit-mirror-rsync: $*"; exit 127 } if [ $# -lt 2 ]; then fail "too few arguments"; fi self=$0 case "$self" in /*) ;; */*) self="$PWD/$self" ;; *) ;; esac distrodir=$1; shift action=$1; shift package=$1 repos=$distrodir/repos rsync=(rsync -rltH --safe-links --delete) hooktimeout=30 rsynctimeout=900 rsyncssh='ssh -o batchmode=yes' mirror_gc_cmd='git gc --auto' . $distrodir/mirror-settings # contents of $queue # $queue/$package.n - mirror needed # $queue/$package.a - being attempted, or attempt failed # $queue/$package.lock - lock (with-lock-ex) # $queue/$package.err - stderr from failed (or current) run # $queue/$package.log - stderr from last successful run cd $repos queue=_mirror-queue case "$remoterepos" in *:/*|/*) ;; '') fail "remoterepos config not set" ;; *) fail "remoterepos config does not match *:/* or /*" ;; esac actually () { if [ "x$mirror_gc_cmd" != x ]; then ( cd "$repos/$package.git" $mirror_gc_cmd ) fi "${rsync[@]}" \ --timeout=$rsynctimeout \ -e "$rsyncssh" \ "$repos/$package.git"/. \ "$remoterepos/$package.git" } reinvoke () { newaction="$1"; shift exec \ "$@" \ "$self" "$distrodir" "reinvoke$newaction" "$package" } check-package-mirrorable () { local repo=$repos/$package.git local mode; mode=$(stat -c%a "$repo") case $mode in *5) return 0 ;; *0) return 1 ;; *) echo >&2 "unexpected mode $mode for $repo"; return 1 ;; esac } lock-and-process () { check-package-mirrorable || return 0 reinvoke -locked with-lock-ex -w "$queue/$package.lock" } attempt () { exec 3>&2 >"$queue/$package.err" 2>&1 if actually; then rm -f "$queue/$package.a" exec 2>&3 2>&1 mv -f "$queue/$package.err" "$queue/$package.log" if ! [ -s "$queue/$package.log" ]; then rm "$queue/$package.log" fi rm "$queue/$package.lock" else cat >&3 "$queue/$package.err" exit 127 fi } lock-and-process-baseof-f () { package=${f##*/} package=${package%.*} lock-and-process } case "$action" in updated-hook) check-package-mirrorable || exit 0 touch "$queue/$package.n" reinvoke -timed timeout --foreground $hooktimeout ;; reinvoke-timed) (lock-and-process) >/dev/null 2>&1 ;; mirror) lock-and-process ;; reinvoke-locked) touch "$queue/$package.a" rm -f "$queue/$package.n" attempt ;; backlog) for f in $queue/*.[na]; do (lock-and-process-baseof-f ||:) done ;; all) for f in [a-z0-9]*.git; do (lock-and-process-baseof-f) done ;; setup) test -d "$queue" || mkdir "$queue" ;; *) fail "bad action $action" ;; esac