Hello community,

here is the log from the commit of package obs-service-tar_scm for 
openSUSE:Factory checked in at 2014-09-10 17:02:34
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/obs-service-tar_scm (Old)
 and      /work/SRC/openSUSE:Factory/.obs-service-tar_scm.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "obs-service-tar_scm"

Changes:
--------
--- /work/SRC/openSUSE:Factory/obs-service-tar_scm/obs-service-tar_scm.changes  
2014-07-11 06:47:01.000000000 +0200
+++ 
/work/SRC/openSUSE:Factory/.obs-service-tar_scm.new/obs-service-tar_scm.changes 
    2014-09-10 17:03:14.000000000 +0200
@@ -1,0 +2,14 @@
+Tue Sep  9 19:01:29 UTC 2014 -  [email protected]
+
+- Update to version 0.4.0.1410288598.7f38281:
+  + Python rewrite of tar_scm
+  + Make pep8 happy (except for regex in tar_scm:299)
+  + Address some feedback from pylint
+  + make Python version PEP8-compliant
+  + Let Travis execute pep8
+  + Strip newline ('\n') characters from safe_run output in detect_version()
+  + Make potentially long-running tasks print output in real-time
+  + Improve efficiency of stdout handling in safe_run()
+  + Release obs-service-tar_scm 0.4.0
+
+-------------------------------------------------------------------

Old:
----
  obs-service-tar_scm-0.3.2.1404723797.745a470.tar.gz

New:
----
  obs-service-tar_scm-0.4.0.1410288598.7f38281.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ obs-service-tar_scm.spec ++++++
--- /var/tmp/diff_new_pack.OSYESt/_old  2014-09-10 17:03:15.000000000 +0200
+++ /var/tmp/diff_new_pack.OSYESt/_new  2014-09-10 17:03:15.000000000 +0200
@@ -19,7 +19,7 @@
 %define service tar_scm
 
 Name:           obs-service-%{service}
-Version:        0.3.2.1404723797.745a470
+Version:        0.4.0.1410288598.7f38281
 Release:        0
 Summary:        An OBS source service: checkout or update a tar ball from 
svn/git/hg
 License:        GPL-2.0+
@@ -49,17 +49,11 @@
 %build
 
 %install
-mkdir -p %{buildroot}%{_prefix}/lib/obs/service
-install -m 0755 tar_scm %{buildroot}%{_prefix}/lib/obs/service
-install -m 0644 tar_scm.service %{buildroot}%{_prefix}/lib/obs/service
-
-mkdir -p %{buildroot}%{_sysconfdir}/obs/services
-install -m 0644 tar_scm.rc %{buildroot}%{_sysconfdir}/obs/services/%{service}
+make install DESTDIR="%{buildroot}" PREFIX="%{_prefix}" SYSCFG="%{_sysconfdir}"
 
 %if 0%{?suse_version} >= 1220
 %check
-: Running the test suite.  Please be patient - this takes a few minutes ...
-python tests/test.py
+make check
 %endif
 
 %files

++++++ _service ++++++
--- /var/tmp/diff_new_pack.OSYESt/_old  2014-09-10 17:03:15.000000000 +0200
+++ /var/tmp/diff_new_pack.OSYESt/_new  2014-09-10 17:03:15.000000000 +0200
@@ -4,7 +4,7 @@
     <param name="scm">git</param>
     <param name="exclude">.git</param>
     <param name="version">git-master</param>
-    <param name="versionformat">0.3.2.%ct.%h</param>
+    <param name="versionformat">0.4.0.%ct.%h</param>
     <param name="revision">master</param>
     <param name="changesgenerate">enable</param>
   </service>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.OSYESt/_old  2014-09-10 17:03:15.000000000 +0200
+++ /var/tmp/diff_new_pack.OSYESt/_new  2014-09-10 17:03:15.000000000 +0200
@@ -1,6 +1,6 @@
 <servicedata>
   <service name="tar_scm">
     <param name="url">git://github.com/openSUSE/obs-service-tar_scm.git</param>
-    <param name="changesrevision">745a470cfb</param>
+    <param name="changesrevision">7f382817c6</param>
   </service>
 </servicedata>
\ No newline at end of file

++++++ debian.dsc ++++++
--- /var/tmp/diff_new_pack.OSYESt/_old  2014-09-10 17:03:15.000000000 +0200
+++ /var/tmp/diff_new_pack.OSYESt/_new  2014-09-10 17:03:15.000000000 +0200
@@ -1,6 +1,6 @@
 Format: 1.0
 Source: obs-service-tar-scm
-Version: 0.2.3
+Version: 0.4.0
 Binary: obs-service-tar-scm
 Maintainer: Adrian Schroeter <[email protected]>
 Architecture: all
@@ -9,5 +9,7 @@
  bzr,
  git,
  mercurial,
+ python (>= 2.6),
+ python-argparse | python (>= 2.7),
  subversion,
  python-unittest2

++++++ obs-service-tar_scm-0.3.2.1404723797.745a470.tar.gz -> 
obs-service-tar_scm-0.4.0.1410288598.7f38281.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/obs-service-tar_scm-0.3.2.1404723797.745a470/.travis.yml 
new/obs-service-tar_scm-0.4.0.1410288598.7f38281/.travis.yml
--- old/obs-service-tar_scm-0.3.2.1404723797.745a470/.travis.yml        
2014-07-09 11:15:58.000000000 +0200
+++ new/obs-service-tar_scm-0.4.0.1410288598.7f38281/.travis.yml        
2014-09-09 21:01:29.000000000 +0200
@@ -5,4 +5,6 @@
 before_install:
   - sudo apt-get update -qq
   - sudo apt-get install -qq bzr git mercurial subversion
+install: pip install pep8 --use-mirrors
+before_script: pep8 tar_scm
 script: python tests/test.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/obs-service-tar_scm-0.3.2.1404723797.745a470/GNUmakefile 
new/obs-service-tar_scm-0.4.0.1410288598.7f38281/GNUmakefile
--- old/obs-service-tar_scm-0.3.2.1404723797.745a470/GNUmakefile        
1970-01-01 01:00:00.000000000 +0100
+++ new/obs-service-tar_scm-0.4.0.1410288598.7f38281/GNUmakefile        
2014-09-09 21:01:29.000000000 +0200
@@ -0,0 +1,19 @@
+DESTDIR ?=
+PREFIX   = /usr/local
+SYSCFG   = /etc
+
+mylibdir = $(PREFIX)/lib/obs/service
+mycfgdir = $(SYSCFG)/obs/services
+
+.PHONY: check
+check:
+       : Running the test suite.  Please be patient - this takes a few minutes 
...
+       python tests/test.py
+
+.PHONY: install
+install:
+       mkdir -p $(DESTDIR)$(mylibdir)
+       mkdir -p $(DESTDIR)$(mycfgdir)
+       install -m 0755 tar_scm $(DESTDIR)$(mylibdir)
+       install -m 0644 tar_scm.service $(DESTDIR)$(mylibdir)
+       install -m 0644 tar_scm.rc $(DESTDIR)$(mycfgdir)/tar_scm
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/obs-service-tar_scm-0.3.2.1404723797.745a470/README.md 
new/obs-service-tar_scm-0.4.0.1410288598.7f38281/README.md
--- old/obs-service-tar_scm-0.3.2.1404723797.745a470/README.md  2014-07-09 
11:15:58.000000000 +0200
+++ new/obs-service-tar_scm-0.4.0.1410288598.7f38281/README.md  2014-09-09 
21:01:29.000000000 +0200
@@ -7,11 +7,7 @@
 
 The files in this top-level directory need to be installed using the following:
 
-    mkdir -p /usr/lib/obs/service
-    mkdir -p /etc/obs/services
-    install -m 0755 tar_scm /usr/lib/obs/service
-    install -m 0644 tar_scm.service /usr/lib/obs/service
-    install -m 0644 tar_scm.rc /etc/obs/services/tar_scm
+    make install
 
 ## Test suite
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/obs-service-tar_scm-0.3.2.1404723797.745a470/debian/changelog 
new/obs-service-tar_scm-0.4.0.1410288598.7f38281/debian/changelog
--- old/obs-service-tar_scm-0.3.2.1404723797.745a470/debian/changelog   
2014-07-09 11:15:58.000000000 +0200
+++ new/obs-service-tar_scm-0.4.0.1410288598.7f38281/debian/changelog   
2014-09-09 21:01:29.000000000 +0200
@@ -1,3 +1,17 @@
+obs-service-tar-scm (0.4.0) unstable; urgency=low
+
+  * Python rewrite of tar_scm
+  * Make pep8 happy (except for regex in tar_scm:299)
+  * Address some feedback from pylint
+  * make Python version PEP8-compliant
+  * Let Travis execute pep8
+  * Strip newline ('\n') characters from safe_run output in detect_version()
+  * Make potentially long-running tasks print output in real-time
+  * Improve efficiency of stdout handling in safe_run()
+  * Release obs-service-tar_scm 0.4.0
+
+ -- Jan Blunck <[email protected]>  Tue, 09 Sep 2014 18:37:39 +0200
+
 obs-service-tar-scm (0.2.3) unstable; urgency=low
 
   * Initial release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/obs-service-tar_scm-0.3.2.1404723797.745a470/tar_scm 
new/obs-service-tar_scm-0.4.0.1410288598.7f38281/tar_scm
--- old/obs-service-tar_scm-0.3.2.1404723797.745a470/tar_scm    2014-07-09 
11:15:58.000000000 +0200
+++ new/obs-service-tar_scm-0.4.0.1410288598.7f38281/tar_scm    2014-09-09 
21:01:29.000000000 +0200
@@ -1,707 +1,834 @@
-#!/bin/bash
-
+#!/usr/bin/env python
+#
 # A simple script to checkout or update a svn or git repo as source service
 #
-# (C) 2010 by Adrian Schröter <[email protected]>
-#  
-# 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=""
-  CHANGES_GENERATE=disable
-  CHANGES_REVISION=""
-  CHANGES_AUTHOR=""
-  INCLUDES=""
-}
+# (C) 2010 by Adrian Schroeter <[email protected]>
+# (C) 2014 by Jan Blunck <[email protected]> (Python rewrite)
+#
+# 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.
 
-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
-}
+import argparse
+import datetime
+import os
+import shutil
+import re
+import fnmatch
+import sys
+import tarfile
+import subprocess
+import atexit
+import hashlib
+import tempfile
+import logging
+import glob
+import ConfigParser
+import StringIO
+
+
+def safe_run(cmd, cwd, interactive=False):
+    """Execute the command cmd in the working directory cwd and check return
+    value. If the command returns non-zero raise a SystemExit exception."""
+
+    logging.debug("COMMAND: %s", cmd)
+
+    # Ensure we get predictable results when parsing the output of commands
+    # like 'git branch'
+    env = os.environ.copy()
+    env['LANG'] = 'C'
+
+    proc = subprocess.Popen(cmd,
+                            shell=False,
+                            stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT,
+                            cwd=cwd,
+                            env=env)
+    output = ''
+    if interactive:
+        stdout_lines = []
+        while proc.poll() is None:
+            for line in proc.stdout:
+                print line.rstrip()
+                stdout_lines.append(line.rstrip())
+        output = '\n'.join(stdout_lines)
+    else:
+        output = proc.communicate()[0]
 
-parse_params () {
-  while test $# -gt 0; do
-    if test -n "`echo \"$2\" | sed 's,[0-9a-zA-Z{}|@%:/._-]*,,g'`"; then
-      echo "Argument $1 got an argument with not supported char."
-      exit 1
-    fi
-    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
-      ;;
-      *-changesgenerate)
-        CHANGES_GENERATE="$2"
-        shift
-      ;;
-      *-changesauthor)
-        CHANGES_AUTHOR="$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] [--submodules disable] --outdir $OUT'
-        exit 1
-      ;;
-    esac
-    shift
-  done
-}
+    if proc.returncode:
+        logging.info("ERROR(%d): %s", proc.returncode, repr(output))
+        sys.exit("Command failed(%d): %s" % (proc.returncode, repr(output)))
+    else:
+        logging.debug("RESULT(%d): %s", proc.returncode, repr(output))
+    return (proc.returncode, output)
 
-error () {
-  echo "ERROR: $*"
-  exit 1
-}
 
-debug () {
-  [ -n "$DEBUG_TAR_SCM" ] && echo "$*"
-}
+def fetch_upstream_git(url, clone_dir, revision, cwd):
+    """fetch sources from GIT"""
+
+    safe_run(['git', 'clone', url, clone_dir], cwd=cwd,
+             interactive=sys.stdout.isatty())
+    safe_run(['git', 'submodule', 'update', '--init', '--recursive'],
+             clone_dir)
+
 
-safe_run () {
-  if ! "$@"; then
-    error "$* failed; aborting!"
-  fi
+def fetch_upstream_svn(url, clone_dir, revision, cwd):
+    """fetch sources from SVN"""
+
+    command = ['svn', 'checkout', '--non-interactive', url, clone_dir]
+    if revision:
+        command.insert(4, '-r%s' % revision)
+    safe_run(command, cwd, interactive=sys.stdout.isatty())
+
+
+def fetch_upstream_hg(url, clone_dir, revision, cwd):
+    """fetch sources from HG"""
+
+    safe_run(['hg', 'clone', url, clone_dir], cwd,
+             interactive=sys.stdout.isatty())
+
+
+def fetch_upstream_bzr(url, clone_dir, revision, cwd):
+    """fetch sources from BZR"""
+
+    command = ['bzr', 'checkout', url, clone_dir]
+    if revision:
+        command.insert(3, '-r')
+        command.insert(4, revision)
+    safe_run(command, cwd, interactive=sys.stdout.isatty())
+
+
+FETCH_UPSTREAM_COMMANDS = {
+    'git': fetch_upstream_git,
+    'svn': fetch_upstream_svn,
+    'hg':  fetch_upstream_hg,
+    'bzr': fetch_upstream_bzr,
 }
 
-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
+def update_cache_git(url, clone_dir, revision):
+    """update sources from GIT"""
+
+    safe_run(['git', 'fetch'], cwd=clone_dir, interactive=sys.stdout.isatty())
+
+
+def update_cache_svn(url, clone_dir, revision):
+    """update sources from SVN"""
+
+    command = ['svn', 'update']
+    if revision:
+        command.insert(3, "-r%s" % revision)
+    safe_run(command, cwd=clone_dir, interactive=sys.stdout.isatty())
+
+
+def update_cache_hg(url, clone_dir, revision):
+    """update sources from HG"""
+
+    try:
+        safe_run(['hg', 'pull'], cwd=clone_dir,
+                 interactive=sys.stdout.isatty())
+    except SystemExit, e:
+        # 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.
+        if re.match('.*no changes found.*', e) is None:
+            raise
+
+
+def update_cache_bzr(url, clone_dir, revision):
+    """update sources from BZR"""
+
+    command = ['bzr', 'update']
+    if revision:
+        command.insert(3, '-r')
+        command.insert(4, revision)
+    safe_run(command, cwd=clone_dir, interactive=sys.stdout.isatty())
+
+
+UPDATE_CACHE_COMMANDS = {
+    'git': update_cache_git,
+    'svn': update_cache_svn,
+    'hg':  update_cache_hg,
+    'bzr': update_cache_bzr,
 }
 
-detect_default_filename_param () {
-  if [ -n "$FILE" ]; then
+
+def switch_revision_git(clone_dir, revision):
+    """Switch sources to revision. The GIT revision 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).
+    - short branch name: "master", "devel" etc.
+    - explicit ref: refs/heads/master, refs/tags/v1.2.3,
+      refs/changes/49/11249/1
+    """
+
+    if revision is None:
+        revision = 'master'
+
+    revs = [x + revision for x in ['origin/', '']]
+    for rev in revs:
+        try:
+            safe_run(['git', 'rev-parse', '--verify', '--quiet', rev],
+                     cwd=clone_dir)
+            text = safe_run(['git', 'reset', '--hard', rev], cwd=clone_dir)[1]
+            print text.rstrip()
+            break
+        except SystemExit:
+            continue
+    else:
+        sys.exit('%s: No such revision' % revision)
+
+    safe_run(['git', 'submodule', 'update', '--recursive'], cwd=clone_dir)
+
+
+def switch_revision_hg(clone_dir, revision):
+    """Switch sources to revision."""
+
+    if revision is None:
+        revision = 'tip'
+
+    try:
+        safe_run(['hg', 'update', revision], cwd=clone_dir,
+                 interactive=sys.stdout.isatty())
+    except SystemExit:
+        sys.exit('%s: No such revision' % revision)
+
+
+def switch_revision_none(clone_dir, revision):
+    """Switch sources to revision. Dummy implementation for version control
+    systems that change revision during fetch/update."""
+
     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
+
+SWITCH_REVISION_COMMANDS = {
+    'git': switch_revision_git,
+    'svn': switch_revision_none,
+    'hg':  switch_revision_hg,
+    'bzr': switch_revision_none,
 }
 
-detect_changes () {
-  # Try to load from _servicedata. We have to change $PWD, ET.parse() seems to 
be relative...
-  CHANGES_REVISION=$(python <<-EOF
-import os, shutil
-try:
-    # If lxml is available, we can use a parser that doesnt destroy comments
-    import lxml.etree as ET
-    xml_parser = ET.XMLParser(remove_comments=False)
-except ImportError:
-    import xml.etree.ElementTree as ET
-    xml_parser = None
-create_servicedata, tar_scm_service = False, None
-tar_scm_xmlstring = "  <service name=\"tar_scm\">\n    <param 
name=\"url\">${MYURL}</param>\n  </service>\n"
-root=None
-try:
-    tree = ET.parse(os.path.join("$SRCDIR", "_servicedata"), parser=xml_parser)
+
+def fetch_upstream(scm, url, revision, out_dir):
+    """Fetch sources from repository and checkout given revision."""
+
+    # calc_dir_to_clone_to
+    basename = os.path.basename(re.sub(r'/.git$', '', url))
+    clone_dir = os.path.abspath(os.path.join(out_dir, basename))
+
+    if not os.path.isdir(clone_dir):
+        # initial clone
+        os.mkdir(clone_dir)
+        FETCH_UPSTREAM_COMMANDS[scm](url, clone_dir, revision, cwd=out_dir)
+    else:
+        logging.info("Detected cached repository...")
+        UPDATE_CACHE_COMMANDS[scm](url, clone_dir, revision)
+
+    # switch_to_revision
+    SWITCH_REVISION_COMMANDS[scm](clone_dir, revision)
+
+    return clone_dir
+
+
+def prep_tree_for_tar(repodir, subdir, outdir, dstname):
+    """Prepare directory tree for creation of the tarball by copying the
+    requested sub-directory to the top-level destination directory."""
+
+    src = os.path.join(repodir, subdir)
+    if not os.path.exists(src):
+        sys.exit("%s: No such file or directory" % src)
+
+    dst = os.path.join(outdir, dstname)
+    if os.path.exists(dst) and \
+        (os.path.samefile(src, dst) or
+         os.path.samefile(os.path.dirname(src), dst)):
+        sys.exit("%s: src and dst refer to same file" % src)
+
+    shutil.copytree(src, dst)
+
+    return dst
+
+
+def create_tar(repodir, outdir, dstname, extension='tar',
+               exclude=[], include=[], package_metadata=False):
+    """Create a tarball of repodir in destination directory."""
+
+    (workdir, topdir) = os.path.split(repodir)
+
+    incl_patterns = []
+    excl_patterns = []
+
+    for i in include:
+        incl_patterns.append(re.compile(fnmatch.translate(i)))
+
+    # skip vcs files base on this pattern
+    if not package_metadata:
+        excl_patterns.append(re.compile(r".*/\.bzr.*"))
+        excl_patterns.append(re.compile(r".*/\.git.*"))
+        excl_patterns.append(re.compile(r".*/\.hg.*"))
+        excl_patterns.append(re.compile(r".*/\.svn.*"))
+
+    for e in exclude:
+        excl_patterns.append(re.compile(fnmatch.translate(e)))
+
+    def tar_exclude(filename):
+        '''Exclude (return True) or add (return False) file to tar achive'''
+
+        if incl_patterns:
+            for pat in incl_patterns:
+                if pat.match(filename):
+                    return False
+            return True
+
+        for pat in excl_patterns:
+            if pat.match(filename):
+                return True
+        return False
+
+    def tar_filter(tarinfo):
+        '''Python 2.7 only: reset uid/gid to 0/0 (root)'''
+
+        tarinfo.uid = tarinfo.gid = 0
+        tarinfo.uname = tarinfo.gname = "root"
+
+        if tar_exclude(tarinfo.name):
+            return None
+
+        return tarinfo
+
+    os.chdir(workdir)
+
+    tar = tarfile.open(os.path.join(outdir, dstname + '.' + extension), "w")
+    try:
+        tar.add(topdir, filter=tar_filter)
+    except TypeError:
+        # Python 2.6 compatibility
+        tar.add(topdir, exclude=tar_exclude)
+    tar.close()
+
+
+CLEANUP_DIRS = []
+
+
+def cleanup(dirs):
+    '''Cleaning temporary directories.'''
+
+    logging.info("Cleaning: %s", ' '.join(dirs))
+
+    for d in dirs:
+        if not os.path.exists(d):
+            continue
+        for root, dirs, files in os.walk(d, topdown=False):
+            for name in files:
+                os.remove(os.path.join(root, name))
+            for name in dirs:
+                os.rmdir(os.path.join(root, name))
+        os.rmdir(d)
+
+
+def version_iso_cleanup(version):
+    '''Reformat timestamp value.'''
+
+    version = re.sub(r'([0-9]{4})-([0-9]{2})-([0-9]{2}) +'
+                     r'([0-9]{2})([:]([0-9]{2})([:]([0-9]{2}))?)?'
+                     r'( +[-+][0-9]{3,4})', r'\1\2\3T\4\6\8', version)
+    version = re.sub(r'[-:]', '', version)
+    return version
+
+
+def detect_version_git(repodir, versionformat):
+    '''Automatic detection of version number for checked-out GIT repository.'''
+
+    if versionformat is None:
+        versionformat = '%ct'
+
+    if re.match('.*@PARENT_TAG@.*', versionformat):
+        try:
+            text = safe_run(['git', 'describe', '--tags', '--abbrev=0'],
+                            repodir)[1]
+            versionformat = re.sub('@PARENT_TAG@', text, versionformat)
+        except SystemExit:
+            sys.exit(r'\e[0;31mThe git repository has no tags,'
+                     r' thus @PARENT_TAG@ can not be expanded\e[0m')
+
+    version = safe_run(['git', 'log', '-n1', '--date=short',
+                        "--pretty=format:%s" % versionformat], repodir)[1]
+    return version_iso_cleanup(version)
+
+
+def detect_version_svn(repodir, versionformat):
+    '''Automatic detection of version number for checked-out SVN repository.'''
+
+    if versionformat is None:
+        versionformat = '%r'
+
+    svn_info = safe_run(['svn', 'info'], repodir)[1]
+
+    version = ''
+    match = re.search('Last Changed Rev: (.*)', svn_info, re.MULTILINE)
+    if match:
+        version = match.group(1).strip()
+    return re.sub('%r', version, versionformat)
+
+
+def detect_version_hg(repodir, versionformat):
+    '''Automatic detection of version number for checked-out HG repository.'''
+
+    if versionformat is None:
+        versionformat = '{rev}'
+
+    version = safe_run(['hg', 'id', '-n'], repodir)[1]
+
+    # Mercurial internally stores commit dates in its changelog
+    # context objects as (epoch_secs, tz_delta_to_utc) tuples (see
+    # mercurial/util.py).  For example, if the commit was created
+    # whilst the timezone was BST (+0100) then tz_delta_to_utc is
+    # -3600.  In this case,
+    #
+    #     hg log -l1 -r$rev --template '{date}\n'
+    #
+    # will result in something like '1375437706.0-3600' where the
+    # first number is timezone-agnostic.  However, hyphens are not
+    # permitted in rpm version numbers, so tar_scm removes them via
+    # sed.  This is required for this template format for any time
+    # zone "numerically east" of UTC.
+    #
+    # N.B. since the extraction of the timestamp as a version number
+    # is generally done in order to provide chronological sorting,
+    # ideally we would ditch the second number.  However the
+    # template format string is left up to the author of the
+    # _service file, so we can't do it here because we don't know
+    # what it will expand to.  Mercurial provides template filters
+    # for dates (e.g. 'hgdate') which _service authors could
+    # potentially use, but unfortunately none of them can easily
+    # extract only the first value from the tuple, except for maybe
+    # 'sub(...)' which is only available since 2.4 (first introduced
+    # in openSUSE 12.3).
+
+    version = safe_run(['hg', 'log', '-l1', "-r%s" % version.strip(),
+                        '--template', versionformat], repodir)[1]
+    return version_iso_cleanup(version)
+
+
+def detect_version_bzr(repodir, versionformat):
+    '''Automatic detection of version number for checked-out BZR repository.'''
+
+    if versionformat is None:
+        versionformat = '%r'
+
+    version = safe_run(['bzr', 'revno'], repodir)[1]
+    return re.sub('%r', version.strip(), versionformat)
+
+
+def detect_version(scm, repodir, versionformat=None):
+    '''Automatic detection of version number for checked-out repository.'''
+
+    detect_version_commands = {
+        'git': detect_version_git,
+        'svn': detect_version_svn,
+        'hg':  detect_version_hg,
+        'bzr': detect_version_bzr,
+    }
+
+    version = detect_version_commands[scm](repodir, versionformat).strip()
+    logging.debug("VERSION(auto): %s", version)
+    return version
+
+
+def get_repocache_hash(scm, url, subdir):
+    '''Calculate hash fingerprint for repository cache.'''
+
+    digest = hashlib.new('sha256')
+    digest.update(url)
+    if scm == 'svn':
+        digest.update('/' + subdir)
+    return digest.hexdigest()
+
+
+def read_changes_revision(url, srcdir, outdir):
+    """Reads the _servicedata file and returns a dictionary with 'revision' on
+    success. As a side-effect it creates the _servicedata file if it doesn't
+    exist. 'revision' is None in that case.
+    """
+
+    change = {
+        'revision': None,
+        'url': url,
+    }
+
+    try:
+        # If lxml is available, we can use a parser that doesn't
+        # destroy comments
+        import lxml.etree as ET
+        xml_parser = ET.XMLParser(remove_comments=False)
+    except ImportError:
+        import xml.etree.ElementTree as ET
+        xml_parser = None
+
+    create_servicedata, tar_scm_service = False, None
+    tar_scm_xmlstring = """  <service name=\"tar_scm\">
+    <param name=\"url\">%s</param>
+  </service>
+""" % url
+    root = None
+    try:
+        tree = ET.parse(os.path.join(srcdir, "_servicedata"),
+                        parser=xml_parser)
+        root = tree.getroot()
+        for service in root.findall("service[@name='tar_scm']"):
+            for param in service.findall("param[@name='url']"):
+                if param.text == url:
+                    tar_scm_service = service
+                    break
+        if tar_scm_service is not None:
+            params = tar_scm_service.findall("param[@name='changesrevision']")
+            if len(params) == 1:
+                # Found what we searched for!
+                change['revision'] = params[0].text
+        else:
+            # File exists, is well-formed but does not contain the service
+            # we search
+            root.append(ET.fromstring(tar_scm_xmlstring))
+            create_servicedata = True
+    except IOError as e:
+        # File doesnt exist
+        root = ET.fromstring("<servicedata>\n%s</servicedata>\n" %
+                             tar_scm_xmlstring)
+        create_servicedata = True
+    except ET.ParseError as e:
+        if e.message.startswith("Document is empty"):
+            # File is empty
+            root = ET.fromstring("<servicedata>\n%s</servicedata>\n" %
+                                 tar_scm_xmlstring)
+            create_servicedata = True
+        else:
+            # File is corrupt
+            raise
+
+    if create_servicedata:
+        ET.ElementTree(root).write(os.path.join(outdir, "_servicedata"))
+    else:
+        if not os.path.samefile(os.path.join(srcdir, "_servicedata"),
+                                os.path.join(outdir, "_servicedata")):
+            shutil.copy(os.path.join(srcdir, "_servicedata"),
+                        os.path.join(outdir, "_servicedata"))
+
+    return change
+
+
+def write_changes_revision(url, outdir, revision):
+    '''Updates the changesrevision in the _servicedata file. '''
+
+    logging.debug("Updating %s", os.path.join(outdir, '_servicedata'))
+
+    try:
+        # If lxml is available, we can use a parser that doesn't
+        # destroy comments
+        import lxml.etree as ET
+        xml_parser = ET.XMLParser(remove_comments=False)
+    except ImportError:
+        import xml.etree.ElementTree as ET
+        xml_parser = None
+
+    tree = ET.parse(os.path.join(outdir, "_servicedata"), parser=xml_parser)
     root = tree.getroot()
+    changed, tar_scm_service = False, None
     for service in root.findall("service[@name='tar_scm']"):
         for param in service.findall("param[@name='url']"):
-            if param.text == "${MYURL}":
+            if param.text == url:
                 tar_scm_service = service
                 break
     if tar_scm_service is not None:
-        changerev_params = 
tar_scm_service.findall("param[@name='changesrevision']")
-        if len(changerev_params) == 1:
-            print(changerev_params[0].text)  # Found what we searched for!
-    else:
-        # File exists, is well-formed but does not contain the service we 
search
-        root.append(ET.fromstring(tar_scm_xmlstring))
-        create_servicedata = True
-except IOError as e:
-    root = ET.fromstring("<servicedata>\n%s</servicedata>\n" % 
tar_scm_xmlstring)
-    create_servicedata = True  # File doesnt exist
-except ET.ParseError as e:
-    if e.message.startswith("Document is empty"):
-        root = ET.fromstring("<servicedata>\n%s</servicedata>\n" % 
tar_scm_xmlstring)
-        create_servicedata = True  # File is empty
+        changerev_params = \
+            tar_scm_service.findall("param[@name='changesrevision']")
+        if len(changerev_params) == 1:  # already present, just update
+            if changerev_params[0].text != revision:
+                changerev_params[0].text = revision
+                changed = True
+        else:  # not present, add changesrevision element
+            tree = ET.fromstring(
+                "    <param name=\"changesrevision\">%s</param>\n"
+                % revision)
+            tar_scm_service.append(tree)
+            changed = True
+        if changed:
+            tree.write(os.path.join(outdir, "_servicedata"))
     else:
-        print("error: %s" % e) # File is mal-formed, bail out.
-except Exception as e:
-    print("error: %s" % e)  # Catch-all, since we are in a here-document
-
-if create_servicedata:
-    ET.ElementTree(root).write(os.path.join("$MYOUTDIR", "_servicedata"))
-else:
-    shutil.copy(os.path.join("$SRCDIR", "_servicedata"), 
os.path.join("$MYOUTDIR", "_servicedata"))
-EOF
-)
-  if [[ $CHANGES_REVISION == error* ]] ; then
-    echo $CHANGES_REVISION  # All we can do here, really.
-    exit 1
-  fi
-
-  safe_run cd $REPOPATH
-
-  case "$MYSCM" in
-    git)
-      if [ -z "$CHANGES_REVISION" ]; then
-        # Ok, first run. Let's ask git for a range...
-        CHANGES_REVISION=`safe_run git log -n10 --pretty=format:%H | tail -n 1`
-      fi
-      CURRENT_REVISION=`safe_run git log -n1 --pretty=format:%H`
-      CURRENT_REVISION=${CURRENT_REVISION:0:10} # shorten SHA1 hash
-      # Use pattern-matching to check if either revision is a prefix of the 
other. It's pretty common at least
-      # for git users to abbreviate commit hashes but people disagree on the 
exact length, thus:
-      if [[ ${CURRENT_REVISION} == ${CHANGES_REVISION}* || ${CHANGES_REVISION} 
== ${CURRENT_REVISION}* ]]; then
-        debug "No new commits, skipping changes file generation"
+        sys.exit("File _servicedata is missing tar_scm with URL '%s'" % url)
+
+
+def write_changes(changes_filename, changes, version, author):
+    '''Add changes to given *.changes file.'''
+
+    if changes is None:
         return
-      fi
-      debug "Generate changes between $CHANGES_REVISION and $CURRENT_REVISION"
-      lines=`safe_run git log --pretty=tformat:%s --no-merges 
${CHANGES_REVISION}..${CURRENT_REVISION} | tac`
-      ;;
-    svn|hg|bzr)
-      debug "Unable to generate changes for subversion, mercurial or bazaar, 
skipping changes file generation"
-      return
-    ;;
-    *)
-      error "Unknown SCM '$MYSCM'"
-  esac
-  OLD_IFS="$IFS"
-  IFS=$'\n' CHANGES_LINES=( $lines )
-  IFS="$OLD_IFS"
-  CHANGES_REVISION=$CURRENT_REVISION
-}
 
-write_changes () {
-  # Replace or add changesrevision in _servicedata file and do it in Python, 
otherwise sth. like
-  # https://gist.github.com/mralexgray/1209534 would be needed. The stdlib xml 
module's XPath
-  # support is quite basic, thus there are some for-loops in the code:
-  python <<-EOF
-import os
-try:
-    # If lxml is available, we can use a parser that doesn't destroy comments
-    import lxml.etree as ET
-    xml_parser = ET.XMLParser(remove_comments=False)
-except ImportError:
-    import xml.etree.ElementTree as ET
-    xml_parser = None
-tree = ET.parse(os.path.join("$MYOUTDIR", "_servicedata"), parser=xml_parser)
-root = tree.getroot()
-changed, tar_scm_service = False, None
-for service in root.findall("service[@name='tar_scm']"):
-    for param in service.findall("param[@name='url']"):
-        if param.text == "${MYURL}":
-            tar_scm_service = service
-            break
-if tar_scm_service is not None:
-    changerev_params = 
tar_scm_service.findall("param[@name='changesrevision']")
-    if len(changerev_params) == 1:  # already present, just update
-        if changerev_params[0].text != "${CHANGES_REVISION}":
-            changerev_params[0].text = "${CHANGES_REVISION}"
-            changed = True
-    else:  # not present, add changesrevision element
-        tar_scm_service.append(ET.fromstring("    <param 
name=\"changesrevision\">${CHANGES_REVISION}</param>\n"))
-        changed = True
-    if changed:
-        tree.write(os.path.join("$MYOUTDIR", "_servicedata"))
-else:
-    print("File _servicedata is missing tar_scm with URL '${MYURL}'")
-EOF
+    logging.debug("Writing changes file %s", changes_filename)
 
-  if [ ${#CHANGES_LINES[@]} -eq 0 ] ; then
-    echo "No changes since $CHANGES_REVISION, skipping changes file generation"
-    return
-  fi
+    tmp_fp = tempfile.NamedTemporaryFile(delete=False)
+    tmp_fp.write('-' * 66 + '\n')
+    tmp_fp.write("%s - %s\n" % (
+        datetime.datetime.utcnow().strftime('%a %b %d %H:%M:%S UTC %Y'),
+        author))
+    tmp_fp.write('\n')
+    tmp_fp.write("- Update to version %s:\n" % version)
+    for line in changes.split(os.linesep):
+        tmp_fp.write(" + %s\n" % line)
+    tmp_fp.write('\n')
 
-  if [ -z "$CHANGES_AUTHOR" ] ; then
-    OSCRC="$HOME/.oscrc"
-    if [ -f $OSCRC ] ; then
-      CHANGES_AUTHOR=$(grep -e '^email.*=' $OSCRC | head -n1 | cut -d"=" -f2)
-    else
-      CHANGES_AUTHOR="[email protected]"
-    fi
-  fi
-
-  
change_entry="-------------------------------------------------------------------
-$(LC_ALL=POSIX TZ=UTC date) - ${CHANGES_AUTHOR}
-
-- Update to version ${TAR_VERSION}:"
-  for commit in "${CHANGES_LINES[@]}" ; do
-      change_entry="$change_entry
-  + $commit"
-  done
-  change_entry="$change_entry
-"
-
-  # Prepend change entry to changes files
-  for changes_file in $SRCDIR/*.changes ; do
-      tmpfile=$(mktemp)
-      echo "$change_entry" | cat - $changes_file > $tmpfile && mv $tmpfile 
$MYOUTDIR/$(basename $changes_file)
-  done
-}
+    old_fp = open(changes_filename, 'r')
+    tmp_fp.write(old_fp.read())
+    old_fp.close()
 
-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
-  if [ "$CHANGES_GENERATE" == "enable" ]; then
-    detect_changes
-  fi
-}
+    tmp_fp.close()
 
-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
-}
+    os.rename(tmp_fp.name, changes_filename)
 
-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
-}
+def detect_changes_commands_git(repodir, changes):
+    '''Detect changes between GIT revisions.'''
 
-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 "mv #1: $CLONE_TO -> $REPOCACHE"
-    safe_run mv "$CLONE_TO" "$REPOCACHE"
-    echo "$MYURL" > "$REPOURLCACHE"
-    echo "Cached $MYURL at $REPOCACHE"
-  fi
-}
+    last_rev = changes['revision']
 
-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
-}
+    if last_rev is None:
+        last_rev = safe_run(['git', 'log', '-n1', '--pretty=format:%H',
+                             '--skip=10'], cwd=repodir)[1]
+    current_rev = safe_run(['git', 'log', '-n1', '--pretty=format:%H'],
+                           cwd=repodir)[1]
 
-update_cache () {
-  safe_run cd "$REPOCACHE"
+    if last_rev == current_rev:
+        logging.debug("No new commits, skipping changes file generation")
+        return
 
-  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
-}
+    logging.debug("Generating changes between %s and %s", last_rev,
+                  current_rev)
 
-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).
-      # - short branch name: "master", "devel" etc.
-      # - explicit ref: refs/heads/master, refs/tags/v1.2.3, 
refs/changes/49/11249/1
-      #
-      if ! git rev-parse --verify --quiet tar_scm_tmp >/dev/null; then
-        safe_run git checkout -b tar_scm_tmp
-      fi
-      if git rev-parse --verify --quiet "origin/$MYREVISION" >/dev/null; then
-          safe_run git reset --hard "origin/$MYREVISION"
-      else
-          safe_run git reset --hard "$MYREVISION"
-      fi
-      if [ "$USE_SUBMODULES" == "enable" ]; then
-          safe_run git submodule update --recursive
-      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
-}
+    lines = safe_run(['git', 'log', '--no-merges', '--pretty=tformat:%s',
+                      "%s..%s" % (last_rev, current_rev)], repodir)[1]
 
-detect_version () {
-  if [ -z "$MYFORMAT" ]; then
-    case "$MYSCM" in
-      git)
-        MYFORMAT="%ct"
-        ;;
-      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"
-}
+    changes['revision'] = current_rev
+    changes['lines'] = '\n'.join(reversed(lines.split('\n')))
+    return changes
 
-ISO_CLEANUP_RE='s@([0-9]{4})-([0-9]{2})-([0-9]{2}) 
+([0-9]{2})([:]([0-9]{2})([:]([0-9]{2}))?)?( +[-+][0-9]{3,4})?@\1\2\3T\4\6\8@g'
 
-get_version () {
-  case "$MYSCM" in
-    git)
-      #version=`safe_run git show --pretty=format:"$MYFORMAT" | head -n 1`
-      if [[ "$MYFORMAT" =~ .*@PARENT_TAG@.*  ]] ; then
-          PARENT_TAG=$(git describe --tags --abbrev=0)
-          PARENT_TAG=${PARENT_TAG/-/.}
-          MYFORMAT=${MYFORMAT/@PARENT_TAG@/$PARENT_TAG}
-          echo "MYFORMAT: $MYFORMAT"
-          if [ $? -gt 0 ] ; then
-              echo -e "\e[0;31mThe git repository has no tags, thus 
@PARENT_TAG@ can not be expanded\e[0m"
-              exit 1
-          fi
-      fi
-      version=`safe_run git log -n1 --date=short --pretty=format:"$MYFORMAT" | 
sed -r -e "$ISO_CLEANUP_RE" -e 's@[-:]@@g'`
-      ;;
-    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`
-      # Mercurial internally stores commit dates in its changelog
-      # context objects as (epoch_secs, tz_delta_to_utc) tuples (see
-      # mercurial/util.py).  For example, if the commit was created
-      # whilst the timezone was BST (+0100) then tz_delta_to_utc is
-      # -3600.  In this case,
-      #
-      #     hg log -l1 -r$rev --template '{date}\n'
-      #
-      # will result in something like '1375437706.0-3600' where the
-      # first number is timezone-agnostic.  However, hyphens are not
-      # permitted in rpm version numbers, so tar_scm removes them via
-      # sed.  This is required for this template format for any time
-      # zone "numerically east" of UTC.
-      #
-      # N.B. since the extraction of the timestamp as a version number
-      # is generally done in order to provide chronological sorting,
-      # ideally we would ditch the second number.  However the
-      # template format string is left up to the author of the
-      # _service file, so we can't do it here because we don't know
-      # what it will expand to.  Mercurial provides template filters
-      # for dates (e.g. 'hgdate') which _service authors could
-      # potentially use, but unfortunately none of them can easily
-      # extract only the first value from the tuple, except for maybe
-      # 'sub(...)' which is only available since 2.4 (first introduced
-      # in openSUSE 12.3).
-      version=`safe_run hg log -l1 -r$rev --template "$MYFORMAT" | sed -r -e 
"$ISO_CLEANUP_RE" -e 's@[-:]@@g'`
-      ;;
-    bzr)
-      #safe_run bzr log -l1 ...
-      rev=`safe_run bzr revno`
-      version="${MYFORMAT//%r/$rev}"
-      ;;
-    *)
-      error "unknown SCM '$MYSCM'"
-  esac
-}
+def detect_changes(scm, url, repodir, outdir):
+    '''Detect changes between revisions.'''
 
-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
-}
+    try:
+        changes = read_changes_revision(url, os.getcwd(), outdir)
+    except Exception, e:
+        sys.exit("_servicedata: Failed to parse (%s)" % e)
 
-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"
-}
+    detect_changes_commands = {
+        'git': detect_changes_commands_git,
+    }
 
-cleanup () {
-  debug rm -rf "$TAR_BASENAME" "$FILE"
-  rm -rf "$TAR_BASENAME" "$FILE"
-}
+    return detect_changes_commands[scm](repodir, changes)
+
+
+def get_config_options():
+    '''Read user-specific and system-wide service configuration files, if not
+    in test-mode. This function returns an instance of ConfigParser.'''
+
+    config = ConfigParser.RawConfigParser()
+    config.optionxform = str
 
-main () {
-  # Ensure we get predictable results when parsing the output of commands
-  # like 'git branch'
-  LANG=C
-
-  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
-  if [ "$CHANGES_GENERATE" == "enable" ]; then
-    write_changes
-  fi
+    if os.getenv('DEBUG_TAR_SCM'):
+        logging.info("Ignoring config files: test-mode detected")
+        return config
+
+    # fake a section header for configuration files
+    for fname in ['/etc/obs/services/tar_scm',
+                  os.path.expanduser('~/.obs/tar_scm')]:
+        try:
+            tmp_fp = StringIO.StringIO()
+            tmp_fp.write('[tar_scm]\n')
+            tmp_fp.write(open(fname, 'r').read())
+            tmp_fp.seek(0, os.SEEK_SET)
+            config.readfp(tmp_fp)
+        except (OSError, IOError):
+            continue
+
+    # strip quotes from pathname
+    for opt in config.options('tar_scm'):
+        config.set('tar_scm', opt, re.sub(r'"(.*)"', r'\1',
+                                          config.get('tar_scm', opt)))
+
+    return config
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description='Git Tarballs')
+    parser.add_argument('--scm', required=True,
+                        help='Used SCM')
+    parser.add_argument('--url', required=True,
+                        help='upstream tarball URL to download')
+    parser.add_argument('--outdir', required=True,
+                        help='osc service parameter that does nothing')
+    parser.add_argument('--verbose', '-v', action='store_true', default=False,
+                        help='enable verbose output')
+    parser.add_argument('--version', default='_auto_',
+                        help='Specify version to be used in tarball. '
+                             'Defaults to automatically detected value '
+                             'formatted by versionformat parameter.')
+    parser.add_argument('--versionformat',
+                        help='Auto-generate version from checked out source '
+                             'using this format string.  This parameter is '
+                             'used if the \'version\' parameter is not '
+                             'specified.')
+    parser.add_argument('--versionprefix',
+                        help='specify a base version as prefix.')
+    parser.add_argument('--changesgenerate', choices=['enable', 'disable'],
+                        default='disable',
+                        help='Whether or not to generate changes file entries '
+                             'from SCM commit log since a given parent '
+                             'revision (see changesrevision).')
+    parser.add_argument('--changesauthor',
+                        help='The author of the changes file entry to be '
+                             'written, defaults to first email entry in '
+                             '~/.oscrc or "[email protected]" '
+                             'if there is no ~/.oscrc found.')
+    parser.add_argument('--filename',
+                        help='name of package - used together with version '
+                             'to determine tarball name')
+    parser.add_argument('--extension', default='tar',
+                        help='suffix name of package - used together with '
+                             'filename to determine tarball name')
+    parser.add_argument('--revision',
+                        help='revision to package')
+    parser.add_argument('--subdir', default='',
+                        help='package just a sub directory')
+    group = parser.add_mutually_exclusive_group()
+    group.add_argument('--include', action='append', default=[],
+                       help='for specifying subset of files/subdirectories to '
+                            'pack in the tar ball')
+    group.add_argument('--exclude', action='append', default=[],
+                       help='for specifying excludes when creating the '
+                            'tar ball')
+    parser.add_argument('--package-meta', choices=['yes', 'no'], default='no',
+                        help='Package the meta data of SCM to allow the user '
+                             'or OBS to update after un-tar')
+    parser.add_argument('--history-depth',
+                        help='osc service parameter that does nothing')
+    parser.add_argument('--submodules',
+                        help='osc service parameter that does nothing')
+    args = parser.parse_args()
+
+    # basic argument validation
+    if not os.path.isdir(args.outdir):
+        sys.exit("%s: No such directory" % args.outdir)
+
+    if args.history_depth:
+        print "history-depth parameter is obsolete and will be ignored"
+
+    # booleanize non-standard parameters
+    if args.changesgenerate == 'enable':
+        args.changesgenerate = True
+    else:
+        args.changesgenerate = False
 
-  cleanup
-}
+    if args.package_meta == 'yes':
+        args.package_meta = True
+    else:
+        args.package_meta = False
 
-main "$@"
+    # force verbose mode in test-mode
+    if os.getenv('DEBUG_TAR_SCM'):
+        args.verbose = True
+
+    FORMAT = "%(message)s"
+    logging.basicConfig(format=FORMAT, stream=sys.stderr, level=logging.INFO)
+    if args.verbose:
+        logging.getLogger().setLevel(logging.DEBUG)
+
+    # force cleaning of our workspace on exit
+    atexit.register(cleanup, CLEANUP_DIRS)
+
+    # check for enabled caches (1. environment, 2. user confog, 3. system wide)
+    repocachedir = os.getenv('CACHEDIRECTORY')
+    if repocachedir is None:
+        config = get_config_options()
+        try:
+            repocachedir = config.get('tar_scm', 'CACHEDIRECTORY')
+        except ConfigParser.Error:
+            pass
+
+    if repocachedir:
+        logging.debug("REPOCACHE: %s", repocachedir)
+
+    # construct repodir (the parent directory of the checkout)
+    repodir = None
+    if repocachedir and os.path.isdir(os.path.join(repocachedir, 'repo')):
+        repohash = get_repocache_hash(args.scm, args.url, args.subdir)
+        logging.debug("HASH: %s", repohash)
+        repodir = os.path.join(repocachedir, 'repo')
+        repodir = os.path.join(repodir, repohash)
+
+    # if caching is enabled but we haven't cached something yet
+    if repodir and not os.path.isdir(repodir):
+        repodir = tempfile.mkdtemp(dir=os.path.join(repocachedir, 'incoming'))
+
+    if repodir is None:
+        repodir = tempfile.mkdtemp(dir=args.outdir)
+        CLEANUP_DIRS.append(repodir)
+
+    clone_dir = fetch_upstream(args.scm, args.url, args.revision, repodir)
+
+    if args.filename:
+        dstname = args.filename
+    else:
+        dstname = os.path.basename(clone_dir)
 
-exit 0
+    version = args.version
+    if version == '_auto_' or args.versionformat:
+        version = detect_version(args.scm, clone_dir, args.versionformat)
+    if args.versionprefix:
+        version = "%s.%s" % (args.versionprefix, version)
+    if version:
+        dstname = dstname + '-' + version
+
+    logging.debug("DST: %s", dstname)
+
+    changes = None
+    if args.changesgenerate:
+        changes = detect_changes(args.scm, args.url, clone_dir, args.outdir)
+
+    tar_dir = prep_tree_for_tar(clone_dir, args.subdir, args.outdir,
+                                dstname=dstname)
+    CLEANUP_DIRS.append(tar_dir)
+
+    create_tar(tar_dir, args.outdir,
+               dstname=dstname, extension=args.extension,
+               exclude=args.exclude, include=args.include,
+               package_metadata=args.package_meta)
+
+    if changes:
+        changesauthor = args.changesauthor
+        if changesauthor is None:
+            config = ConfigParser.RawConfigParser({
+                'email': '[email protected]',
+            })
+            config.read(os.path.expanduser('~/.oscrc'))
+            changesauthor = config.get('https://api.opensuse.org', 'email')
+
+        logging.debug("AUTHOR: %s", changesauthor)
+
+        for filename in glob.glob(os.path.join(args.outdir, '*.changes')):
+            write_changes(filename, changes['lines'], version, changesauthor)
+        write_changes_revision(changes['url'], args.outdir,
+                               changes['revision'])
+
+    # Populate cache
+    if repocachedir and os.path.isdir(os.path.join(repocachedir, 'repo')):
+        repodir2 = os.path.join(repocachedir, 'repo')
+        repodir2 = os.path.join(repodir2, repohash)
+        if repodir2 and not os.path.isdir(repodir2):
+            os.rename(repodir, repodir2)
+        elif not os.path.samefile(repodir, repodir2):
+            CLEANUP_DIRS.append(repodir)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/obs-service-tar_scm-0.3.2.1404723797.745a470/tests/testenv.py 
new/obs-service-tar_scm-0.4.0.1410288598.7f38281/tests/testenv.py
--- old/obs-service-tar_scm-0.3.2.1404723797.745a470/tests/testenv.py   
2014-07-09 11:15:58.000000000 +0200
+++ new/obs-service-tar_scm-0.4.0.1410288598.7f38281/tests/testenv.py   
2014-09-09 21:01:29.000000000 +0200
@@ -139,7 +139,7 @@
         mkfreshdir(self.outdir)
         cmdargs = args + [ '--outdir', self.outdir ]
         quotedargs = [ "'%s'" % arg for arg in cmdargs ]
-        cmdstr = 'bash %s %s 2>&1' % (self.tar_scm_bin(), " ".join(quotedargs))
+        cmdstr = 'python %s %s 2>&1' % (self.tar_scm_bin(), " 
".join(quotedargs))
         print
         print ">>>>>>>>>>>"
         print "Running", cmdstr

-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to