File tar_scm of Package obs-service-tar_scm

#!/bin/bash

# A simple script to checkout or update a svn or git repo as source service
#
# (C) 2010 by Adrian Schröter <adrian@suse.de>
#  
# 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=""
  USE_SUBMODULES=enable
#  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
      ;;
      *-submodules)
        USE_SUBMODULES="$2"
        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] [--disable-git-submodule] --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-vcs"
  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"
      if [ "$USE_SUBMODULES" == "enable" ]; then
        safe_run cd "$CLONE_TO"
        safe_run git submodule update --init --recursive
        safe_run cd ..
      fi
      ;;
    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='<unknown URL>'
  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)
      # $MYREVISION may refer to any of the following:
      #
      # - explicit SHA1: a1b2c3d4....
      #   - the SHA1 must be reachable from a default clone/fetch (generally, must be
      #     reachable from some branch or tag on the remote).
      #   - set by: git checkout <SHA1>
      #
      # - short branch name: "master", "devel" etc.
      #   - set by: git checkout <branch> && git pull
      #
      # - explicit ref: refs/heads/master, refs/tags/v1.2.3, refs/changes/49/11249/1
      #   - set by: git fetch <url> +<revision>:<revision> && git checkout <revision>
      #
      if ! git checkout "$MYREVISION"; then
        echo "$MYREVISION not accessible by default clone/fetch, attempting explicit fetch"
        safe_run git fetch "$MYURL" "+$MYREVISION:$MYREVISION"
        safe_run git checkout "$MYREVISION"
      fi
      if git branch | grep -q '^\* (no branch)$'; then
        echo "$MYREVISION does not refer to a branch, not attempting git pull"
      else
        safe_run 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 --owner=root --group=root -cf "$TARPATH" $EXCLUDES $MYINCLUDES
  safe_run tar --owner=root --group=root -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
openSUSE Build Service is sponsored by