#!/bin/bash # A simple script to checkout or update a svn or git repo as source service # # (C) 2010 by Adrian Schröter # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # See http://www.gnu.org/licenses/gpl-2.0.html for full license text. SERVICE='tar_scm' set_default_params () { MYSCM="" MYURL="" MYVERSION="_auto_" MYFORMAT="" MYPREFIX="" MYFILENAME="" MYREVISION="" MYPACKAGEMETA="" # MYHISTORYDEPTH="" INCLUDES="" } get_config_options () { # config options for this host ? if [ -f /etc/obs/services/$SERVICE ]; then . /etc/obs/services/$SERVICE fi # config options for this user ? if [ -f "$HOME"/.obs/$SERVICE ]; then . "$HOME"/.obs/$SERVICE fi } parse_params () { while test $# -gt 0; do case $1 in *-scm) MYSCM="$2" shift ;; *-url) MYURL="$2" shift ;; *-subdir) MYSUBDIR="$2" shift ;; *-revision) MYREVISION="$2" shift ;; *-version) MYVERSION="$2" shift ;; *-include) INCLUDES="$INCLUDES $2" shift ;; *-versionformat) MYFORMAT="$2" shift ;; *-versionprefix) MYPREFIX="$2" shift ;; *-exclude) EXCLUDES="$EXCLUDES --exclude=${2#/}" shift ;; *-filename) MYFILENAME="${2#/}" shift ;; *-package-meta) MYPACKAGEMETA="${2#/}" shift ;; *-outdir) MYOUTDIR="$2" shift ;; *-history-depth) echo "history-depth parameter is obsolete and will be ignored" shift ;; *) echo "Unknown parameter: $1" echo 'Usage: $SERVICE --scm $SCM --url $URL [--subdir $SUBDIR] [--revision $REVISION] [--version $VERSION] [--include $INCLUDE]* [--exclude $EXCLUDE]* [--versionformat $FORMAT] [--versionprefix $PREFIX] [--filename $FILENAME] [--package-meta $META] --outdir $OUT' exit 1 ;; esac shift done } error () { echo "ERROR: $*" exit 1 } debug () { [ -n "$DEBUG_TAR_SCM" ] && echo "$*" } safe_run () { if ! "$@"; then error "$* failed; aborting!" fi } sanitise_params () { TAR_VERSION="$MYVERSION" if [ -z "$MYSCM" ]; then error "no scm is given via --scm parameter (git/svn/hg/bzr)!" fi if [ -z "$MYURL" ]; then error "no checkout URL is given via --url parameter!" fi if [ -z "$MYOUTDIR" ]; then error "no output directory is given via --outdir parameter!" fi FILE="$MYFILENAME" WD_VERSION="$MYVERSION" if [ -z "$MYPACKAGEMETA" ]; then EXCLUDES="$EXCLUDES --exclude=.$MYSCM" fi # if [ "$MYHISTORYDEPTH" == "full" ]; then # MYHISTORYDEPTH="999999999" # fi } detect_default_filename_param () { if [ -n "$FILE" ]; then return fi case "$MYSCM" in git) FILE="${MYURL%/}" FILE="${FILE##*/}" FILE="${FILE%.git}" FILE="${FILE#*@*:}" ;; svn|hg|bzr) FILE="${MYURL%/}" FILE="${FILE##*/}" ;; *) error "unknown SCM '$MYSCM'" esac } fetch_upstream () { TOHASH="$MYURL" [ "$MYSCM" = 'svn' ] && TOHASH="$TOHASH/$MYSUBDIR" HASH=`echo "$TOHASH" | sha256sum | cut -d\ -f 1` REPOCACHE= if [ -n "$CACHEDIRECTORY" ]; then REPOCACHEINCOMING="$CACHEDIRECTORY/incoming" REPOCACHEROOT="$CACHEDIRECTORY/repo" REPOCACHE="$REPOCACHEROOT/$HASH" REPOURLCACHE="$CACHEDIRECTORY/repourl/$HASH" fi if [ -z "$MYREVISION" ]; then case "$MYSCM" in git) MYREVISION=master ;; hg) MYREVISION=tip ;; # bzr) # MYREVISION=HEAD # ;; esac if [ -n "$MYREVISION" ]; then debug "no revision specified; defaulting to $MYREVISION" fi fi debug "check local cache if configured" if [ -n "$CACHEDIRECTORY" -a -d "$REPOCACHE/.$MYSCM" ]; then debug "cache hit: $REPOCACHE/.$MYSCM" check_cache echo "Found $TOHASH in $REPOCACHE; updating ..." update_cache REPOPATH="$REPOCACHE" else if [ -n "$CACHEDIRECTORY" ]; then debug "cache miss: $REPOCACHE/.$MYSCM" else debug "cache not enabled" fi calc_dir_to_clone_to debug "new $MYSCM checkout to $CLONE_TO" initial_clone if [ -n "$CACHEDIRECTORY" ]; then cache_repo REPOPATH="$REPOCACHE" else REPOPATH="$MYOUTDIR/$FILE" fi fi safe_run cd "$REPOPATH" switch_to_revision if [ "$TAR_VERSION" == "_auto_" -o -n "$MYFORMAT" ]; then detect_version fi } calc_dir_to_clone_to () { if [ -n "$CACHEDIRECTORY" ]; then safe_run cd "$REPOCACHEINCOMING" # Use dry-run mode because git/hg refuse to clone into # an empty directory on SLES11 debug mktemp -u -d "tmp.XXXXXXXXXX" CLONE_TO=`mktemp -u -d "tmp.XXXXXXXXXX"` else CLONE_TO="$FILE" fi } initial_clone () { echo "Fetching from $MYURL ..." case "$MYSCM" in git) # Clone with full depth; so that the revision can be found if specified safe_run git clone "$MYURL" "$CLONE_TO" ;; svn) args= [ -n "$MYREVISION" ] && args="-r$MYREVISION" if [[ $(svn --version --quiet) > "1.5.99" ]]; then TRUST_SERVER_CERT="--trust-server-cert" fi safe_run svn checkout --non-interactive $TRUST_SERVER_CERT \ $args "$MYURL/$MYSUBDIR" "$CLONE_TO" MYSUBDIR= # repo root is subdir ;; hg) safe_run hg clone "$MYURL" "$CLONE_TO" ;; bzr) args= [ -n "$MYREVISION" ] && args="-r $MYREVISION" safe_run bzr checkout $args "$MYURL" "$CLONE_TO" ;; *) error "unknown SCM '$MYSCM'" esac } cache_repo () { if [ -e "$REPOCACHE" ]; then error "Somebody else beat us to populating the cache for $MYURL ($REPOCACHE)" else # FIXME: small race window here; do source services need to be thread-safe? debug mv2 "$CLONE_TO" "$REPOCACHE" safe_run mv "$CLONE_TO" "$REPOCACHE" echo "$MYURL" > "$REPOURLCACHE" echo "Cached $MYURL at $REPOCACHE" fi } check_cache () { CACHEDURL=`cat "$REPOURLCACHE"` [ -z "$CACHEDURL" ] && CACHEDURL='' if [ "$MYURL" != "$CACHEDURL" ]; then error "Corrupt cache: cache for repo $MYURL was recorded as being from $CACHEDURL" fi } update_cache () { safe_run cd "$REPOCACHE" case "$MYSCM" in git) safe_run git fetch ;; svn) args= [ -n "$MYREVISION" ] && args="-r$MYREVISION" safe_run svn update $args MYSUBDIR= # repo root is subdir ;; hg) if ! out=`hg pull`; then if [[ "$out" == *'no changes found'* ]]; then # Contrary to the docs, hg pull returns exit code 1 when # there are no changes to pull, but we don't want to treat # this as an error. : else error "hg pull failed; aborting!" fi fi ;; bzr) args= [ -n "$MYREVISION" ] && args="-r$MYREVISION" safe_run bzr update $args ;; *) error "unknown SCM '$MYSCM'" esac } switch_to_revision () { case "$MYSCM" in git) safe_run git checkout "$MYREVISION" if git branch | grep -q '^\* (no branch)$'; then echo "$MYREVISION does not refer to a branch, not attempting git pull" else git pull fi ;; svn|bzr) : # should have already happened via checkout or update ;; hg) safe_run hg update "$MYREVISION" ;; # bzr) # safe_run bzr update # if [ -n "$MYREVISION" ]; then # safe_run bzr revert -r "$MYREVISION" # fi # ;; *) error "unknown SCM '$MYSCM'" esac } detect_version () { if [ -z "$MYFORMAT" ]; then case "$MYSCM" in git) MYFORMAT="%at" ;; hg) MYFORMAT="{rev}" ;; svn|bzr) MYFORMAT="%r" ;; *) error "unknown SCM '$MYSCM'" ;; esac fi safe_run cd "$REPOPATH" [ -n "$MYPREFIX" ] && MYPREFIX="$MYPREFIX." get_version TAR_VERSION="$MYPREFIX$version" } get_version () { case "$MYSCM" in git) #version=`safe_run git show --pretty=format:"$MYFORMAT" | head -n 1` version=`safe_run git log -n1 --pretty=format:"$MYFORMAT"` ;; svn) #rev=`LC_ALL=C safe_run svn info | awk '/^Revision:/ { print $2 }'` rev=`LC_ALL=C safe_run svn info | sed -n 's,^Last Changed Rev: \(.*\),\1,p'` version="${MYFORMAT//%r/$rev}" ;; hg) rev=`safe_run hg id -n` version=`safe_run hg log -l1 -r$rev --template "$MYFORMAT"` ;; bzr) #safe_run bzr log -l1 ... rev=`safe_run bzr revno` version="${MYFORMAT//%r/$rev}" ;; *) error "unknown SCM '$MYSCM'" esac } prep_tree_for_tar () { if [ ! -e "$REPOPATH/$MYSUBDIR" ]; then error "directory does not exist: $REPOPATH/$MYSUBDIR" fi if [ -z "$TAR_VERSION" ]; then TAR_BASENAME="$FILE" else TAR_BASENAME="${FILE}-${TAR_VERSION}" fi MYINCLUDES="" for INC in $INCLUDES; do MYINCLUDES="$MYINCLUDES $TAR_BASENAME/$INC" done if [ -z "$MYINCLUDES" ]; then MYINCLUDES="$TAR_BASENAME" fi safe_run cd "$MYOUTDIR" if [ -n "$CACHEDIRECTORY" ]; then debug cp -a "$REPOPATH/$MYSUBDIR" "$TAR_BASENAME" safe_run cp -a "$REPOPATH/$MYSUBDIR" "$TAR_BASENAME" else debug mv3 "$REPOPATH/$MYSUBDIR" "$TAR_BASENAME" safe_run mv "$REPOPATH/$MYSUBDIR" "$TAR_BASENAME" fi } create_tar () { TARFILE="${TAR_BASENAME}.tar" TARPATH="$MYOUTDIR/$TARFILE" debug tar cf "$TARPATH" $EXCLUDES $MYINCLUDES safe_run tar cf "$TARPATH" $EXCLUDES $MYINCLUDES echo "Created $TARFILE" } cleanup () { debug rm -rf "$TAR_BASENAME" "$FILE" rm -rf "$TAR_BASENAME" "$FILE" } main () { set_default_params if [ -z "$DEBUG_TAR_SCM" ]; then get_config_options else # We're in test-mode, so don't let any local site-wide # or per-user config impact the test suite. : fi parse_params "$@" sanitise_params SRCDIR=$(pwd) cd "$MYOUTDIR" detect_default_filename_param fetch_upstream prep_tree_for_tar create_tar cleanup } main "$@" exit 0