Accepting request 143961 from home:saschpe:branches:openSUSE:Tools
- Use upstream github repository and download via _service file - Add custom git versionformat extensions: + @PARENT_TAG@: Replaced by first tag that is reachable from the current commit (see 'git describe') Future custom additions could follow the @NAME@ scheme OBS-URL: https://build.opensuse.org/request/show/143961 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Tools/obs-service-tar_scm?expand=0&rev=56
This commit is contained in:
parent
64258315e2
commit
9856f5b66c
_servicebzrfixtures.pybzrtests.pycommontests.pyfixtures.pygitfixtures.pygithgtests.pygittests.pyhgfixtures.pyhgtests.pyobs-service-tar_scm-0.2.3.tar.gzobs-service-tar_scm.changesobs-service-tar_scm.specscm-wrapperscmlogs.pysvnfixtures.pysvntests.pytar_scmtar_scm.rctar_scm.servicetest.pytestassertions.pytestenv.pyutils.py
19
_service
Normal file
19
_service
Normal file
@ -0,0 +1,19 @@
|
||||
<services>
|
||||
<service name="tar_scm" mode="disabled">
|
||||
<param name="url">git@github.com:openSUSE/obs-service-tar_scm.git</param>
|
||||
<param name="scm">git</param>
|
||||
<param name="exclude">.git</param>
|
||||
<param name="version">git-master</param>
|
||||
<param name="versionformat">0.2.3</param>
|
||||
<param name="revision">master</param>
|
||||
</service>
|
||||
|
||||
<service name="recompress" mode="disabled">
|
||||
<param name="file">*.tar</param>
|
||||
<param name="compression">gz</param>
|
||||
</service>
|
||||
|
||||
<service name="set_version" mode="disabled">
|
||||
<param name="basename">obs-service-tar_scm</param>
|
||||
</service>
|
||||
</services>
|
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
|
||||
from fixtures import Fixtures
|
||||
from utils import mkfreshdir, run_bzr
|
||||
|
||||
class BzrFixtures(Fixtures):
|
||||
def init(self):
|
||||
self.create_repo()
|
||||
self.create_commits(2)
|
||||
|
||||
def run(self, cmd):
|
||||
return run_bzr(self.repo_path, cmd)
|
||||
|
||||
def create_repo(self):
|
||||
os.makedirs(self.repo_path)
|
||||
os.chdir(self.repo_path)
|
||||
self.run('init')
|
||||
self.run('whoami "%s"' % self.name_and_email)
|
||||
self.wd = self.repo_path
|
||||
print "created repo", self.repo_path
|
||||
|
||||
def do_commit(self, newly_created):
|
||||
self.run('add .')
|
||||
self.run('commit -m%d' % self.next_commit_rev)
|
||||
|
||||
def record_rev(self, rev_num):
|
||||
self.revs[rev_num] = str(rev_num)
|
||||
self.scmlogs.annotate("Recorded rev %d" % rev_num)
|
29
bzrtests.py
29
bzrtests.py
@ -1,29 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from commontests import CommonTests
|
||||
from bzrfixtures import BzrFixtures
|
||||
from utils import run_bzr
|
||||
|
||||
class BzrTests(CommonTests):
|
||||
scm = 'bzr'
|
||||
initial_clone_command = 'bzr checkout'
|
||||
update_cache_command = 'bzr update'
|
||||
fixtures_class = BzrFixtures
|
||||
|
||||
def default_version(self):
|
||||
return self.rev(2)
|
||||
|
||||
def test_versionformat_rev(self):
|
||||
self.tar_scm_std('--versionformat', 'myrev%r.svn')
|
||||
self.assertTarOnly(self.basename(version = 'myrev2.svn'))
|
||||
|
||||
def test_version_versionformat(self):
|
||||
self.tar_scm_std('--version', '3.0', '--versionformat', 'myrev%r.svn')
|
||||
self.assertTarOnly(self.basename(version = 'myrev2.svn'))
|
||||
|
||||
def test_versionformat_revision(self):
|
||||
self.fixtures.create_commits(4)
|
||||
self.tar_scm_std('--versionformat', 'foo%r', '--revision', self.rev(2))
|
||||
basename = self.basename(version = 'foo2')
|
||||
th = self.assertTarOnly(basename)
|
||||
self.assertTarMemberContains(th, basename + '/a', '2')
|
185
commontests.py
185
commontests.py
@ -1,185 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from pprint import pprint, pformat
|
||||
|
||||
from testassertions import TestAssertions
|
||||
from testenv import TestEnvironment
|
||||
from utils import mkfreshdir
|
||||
|
||||
class CommonTests(TestEnvironment, TestAssertions):
|
||||
def basename(self, name='repo', version=None):
|
||||
if version is None:
|
||||
version = self.default_version()
|
||||
return '%s-%s' % (name, version)
|
||||
|
||||
def test_plain(self):
|
||||
self.tar_scm_std()
|
||||
self.assertTarOnly(self.basename())
|
||||
|
||||
def test_exclude(self):
|
||||
self.tar_scm_std('--exclude', '.' + self.scm)
|
||||
self.assertTarOnly(self.basename())
|
||||
|
||||
def test_subdir(self):
|
||||
self.tar_scm_std('--subdir', self.fixtures.subdir)
|
||||
self.assertTarOnly(self.basename(), tarchecker=self.assertSubdirTar)
|
||||
|
||||
def test_history_depth_obsolete(self):
|
||||
(stdout, stderr, ret) = self.tar_scm_std('--history-depth', '1')
|
||||
self.assertRegexpMatches(stdout, 'obsolete')
|
||||
# self.assertTarOnly(self.basename())
|
||||
# self.assertRegexpMatches(self.scmlogs.read()[0], '^%s clone --depth=1')
|
||||
|
||||
# def test_history_depth_full(self):
|
||||
# self.tar_scm_std('--history-depth', 'full')
|
||||
# self.assertTarOnly(self.basename())
|
||||
# self.assertRegexpMatches(self.scmlogs.read()[0], '^git clone --depth=999999+')
|
||||
|
||||
def test_filename(self):
|
||||
name = 'myfilename'
|
||||
self.tar_scm_std('--filename', name)
|
||||
self.assertTarOnly(self.basename(name=name))
|
||||
|
||||
def test_version(self):
|
||||
version = '0.5'
|
||||
self.tar_scm_std('--version', version)
|
||||
self.assertTarOnly(self.basename(version=version))
|
||||
|
||||
def test_filename_version(self):
|
||||
filename = 'myfilename'
|
||||
version = '0.6'
|
||||
self.tar_scm_std('--filename', filename, '--version', version)
|
||||
self.assertTarOnly(self.basename(filename, version))
|
||||
|
||||
def test_revision_nop(self):
|
||||
self.tar_scm_std('--revision', self.rev(2))
|
||||
th = self.assertTarOnly(self.basename())
|
||||
self.assertTarMemberContains(th, self.basename() + '/a', '2')
|
||||
|
||||
def test_revision(self):
|
||||
self._revision()
|
||||
|
||||
def test_revision_no_cache(self):
|
||||
self._revision(use_cache=False)
|
||||
|
||||
def test_revision_subdir(self):
|
||||
self._revision(use_subdir=True)
|
||||
|
||||
def test_revision_subdir_no_cache(self):
|
||||
self._revision(use_cache=False, use_subdir=True)
|
||||
|
||||
def _revision(self, use_cache=True, use_subdir=False):
|
||||
version = '3.0'
|
||||
args_tag2 = [
|
||||
'--version', version,
|
||||
'--revision', self.rev(2),
|
||||
]
|
||||
if use_subdir:
|
||||
args_tag2 += [ '--subdir', self.fixtures.subdir ]
|
||||
self._sequential_calls_with_revision(
|
||||
version,
|
||||
[
|
||||
(0, args_tag2, '2', False),
|
||||
(0, args_tag2, '2', use_cache),
|
||||
(2, args_tag2, '2', use_cache),
|
||||
(0, args_tag2, '2', use_cache),
|
||||
(2, args_tag2, '2', use_cache),
|
||||
(0, args_tag2, '2', use_cache),
|
||||
],
|
||||
use_cache
|
||||
)
|
||||
|
||||
def test_revision_master_alternating(self):
|
||||
self._revision_master_alternating()
|
||||
|
||||
def test_revision_master_alternating_no_cache(self):
|
||||
self._revision_master_alternating(use_cache=False)
|
||||
|
||||
def test_revision_master_alternating_subdir(self):
|
||||
self._revision_master_alternating(use_subdir=True)
|
||||
|
||||
def test_revision_master_alternating_subdir_no_cache(self):
|
||||
self._revision_master_alternating(use_cache=False, use_subdir=True)
|
||||
|
||||
def _revision_master_alternating(self, use_cache=True, use_subdir=False):
|
||||
version = '4.0'
|
||||
args_head = [
|
||||
'--version', version,
|
||||
]
|
||||
if use_subdir:
|
||||
args_head += [ '--subdir', self.fixtures.subdir ]
|
||||
|
||||
args_tag2 = args_head + [ '--revision', self.rev(2) ]
|
||||
self._sequential_calls_with_revision(
|
||||
version,
|
||||
[
|
||||
(0, args_tag2, '2', False),
|
||||
(0, args_head, '2', use_cache),
|
||||
(2, args_tag2, '2', use_cache),
|
||||
(0, args_head, '4', use_cache),
|
||||
(2, args_tag2, '2', use_cache),
|
||||
(0, args_head, '6', use_cache),
|
||||
(0, args_tag2, '2', use_cache),
|
||||
],
|
||||
use_cache
|
||||
)
|
||||
|
||||
def _sequential_calls_with_revision(self, version, calls, use_cache=True):
|
||||
mkfreshdir(self.pkgdir)
|
||||
basename = self.basename(version = version)
|
||||
|
||||
if not use_cache:
|
||||
self.disableCache()
|
||||
|
||||
while calls:
|
||||
new_commits, args, expected, expect_cache_hit = calls.pop(0)
|
||||
if new_commits > 0:
|
||||
self.fixtures.create_commits(new_commits)
|
||||
self.scmlogs.annotate("about to run: " + pformat(args))
|
||||
self.scmlogs.annotate("expecting tar to contain: " + expected)
|
||||
self.tar_scm_std(*args)
|
||||
logpath = self.scmlogs.current_log_path
|
||||
loglines = self.scmlogs.read()
|
||||
if expect_cache_hit:
|
||||
self.assertRanUpdate(logpath, loglines)
|
||||
else:
|
||||
self.assertRanInitialClone(logpath, loglines)
|
||||
|
||||
if self.fixtures.subdir in args:
|
||||
th = self.assertTarOnly(basename, tarchecker=self.assertSubdirTar)
|
||||
tarent = 'b'
|
||||
else:
|
||||
th = self.assertTarOnly(basename)
|
||||
tarent = 'a'
|
||||
self.assertTarMemberContains(th, basename + '/' + tarent, expected)
|
||||
|
||||
self.scmlogs.next()
|
||||
self.postRun()
|
||||
|
||||
def test_switch_revision_and_subdir(self):
|
||||
self._switch_revision_and_subdir()
|
||||
|
||||
def test_switch_revision_and_subdir_no_cache(self):
|
||||
self._switch_revision_and_subdir(use_cache=False)
|
||||
|
||||
def _switch_revision_and_subdir(self, use_cache=True):
|
||||
version = '5.0'
|
||||
args = [
|
||||
'--version', version,
|
||||
]
|
||||
args_subdir = args+ [ '--subdir', self.fixtures.subdir ]
|
||||
|
||||
args_tag2 = args + [ '--revision', self.rev(2) ]
|
||||
self._sequential_calls_with_revision(
|
||||
version,
|
||||
[
|
||||
(0, args_tag2, '2', False),
|
||||
(0, args_subdir, '2', use_cache and self.scm != 'svn'),
|
||||
(2, args_tag2, '2', use_cache),
|
||||
(0, args_subdir, '4', use_cache),
|
||||
(2, args_tag2, '2', use_cache),
|
||||
(0, args_subdir, '6', use_cache),
|
||||
(0, args_tag2, '2', use_cache),
|
||||
],
|
||||
use_cache
|
||||
)
|
78
fixtures.py
78
fixtures.py
@ -1,78 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
class Fixtures:
|
||||
name = 'tar_scm test suite'
|
||||
email = 'root@localhost'
|
||||
name_and_email = '%s <%s>' % (name, email)
|
||||
|
||||
subdir = 'subdir'
|
||||
subdir1 = 'subdir1'
|
||||
subdir2 = 'subdir2'
|
||||
next_commit_rev = 1
|
||||
|
||||
def __init__(self, container_dir, scmlogs):
|
||||
self.container_dir = container_dir
|
||||
self.scmlogs = scmlogs
|
||||
self.repo_path = self.container_dir + '/repo'
|
||||
self.repo_url = 'file://' + self.repo_path
|
||||
|
||||
# Keys are stringified integers representing commit sequence numbers;
|
||||
# values can be passed to --revision
|
||||
self.revs = { }
|
||||
|
||||
def setup(self):
|
||||
print self.__class__.__name__ + ": setting up fixtures"
|
||||
self.init_fixtures_dir()
|
||||
self.init()
|
||||
|
||||
def init_fixtures_dir(self):
|
||||
if os.path.exists(self.repo_path):
|
||||
shutil.rmtree(self.repo_path)
|
||||
|
||||
def init(self):
|
||||
raise NotImplementedError, \
|
||||
self.__class__.__name__ + " didn't implement init()"
|
||||
|
||||
def create_commits(self, num_commits):
|
||||
self.scmlogs.annotate("Creating %d commits ..." % num_commits)
|
||||
if num_commits == 0:
|
||||
return
|
||||
|
||||
for i in xrange(0, num_commits):
|
||||
new_rev = self.create_commit()
|
||||
self.record_rev(new_rev)
|
||||
|
||||
self.scmlogs.annotate("Created %d commits; now at %s" % (num_commits, new_rev))
|
||||
|
||||
def create_commit(self):
|
||||
os.chdir(self.wd)
|
||||
newly_created = self.prep_commit()
|
||||
self.do_commit(newly_created)
|
||||
new_rev = self.next_commit_rev
|
||||
self.next_commit_rev += 1
|
||||
return new_rev
|
||||
|
||||
def prep_commit(self):
|
||||
"""
|
||||
Caller should ensure correct cwd.
|
||||
Returns list of newly created files.
|
||||
"""
|
||||
newly_created = [ ]
|
||||
|
||||
if not os.path.exists('a'):
|
||||
newly_created.append('a')
|
||||
|
||||
if not os.path.exists(self.subdir):
|
||||
os.mkdir(self.subdir)
|
||||
# This will take care of adding subdir/b too
|
||||
newly_created.append(self.subdir)
|
||||
|
||||
for fn in ('a', self.subdir + '/b'):
|
||||
f = open(fn, 'w')
|
||||
f.write(str(self.next_commit_rev))
|
||||
f.close()
|
||||
|
||||
return newly_created
|
@ -1,48 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
|
||||
from fixtures import Fixtures
|
||||
from utils import mkfreshdir, run_git
|
||||
|
||||
class GitFixtures(Fixtures):
|
||||
def init(self):
|
||||
self.create_repo()
|
||||
|
||||
self.timestamps = { }
|
||||
self.sha1s = { }
|
||||
|
||||
self.create_commits(2)
|
||||
|
||||
def run(self, cmd):
|
||||
return run_git(self.repo_path, cmd)
|
||||
|
||||
def create_repo(self):
|
||||
os.makedirs(self.repo_path)
|
||||
os.chdir(self.repo_path)
|
||||
self.run('init')
|
||||
self.run('config user.name test')
|
||||
self.run('config user.email test@test.com')
|
||||
self.wd = self.repo_path
|
||||
print "created repo", self.repo_path
|
||||
|
||||
def do_commit(self, newly_created):
|
||||
self.run('add .')
|
||||
self.run('commit -m%d' % self.next_commit_rev)
|
||||
|
||||
def get_metadata(self, formatstr):
|
||||
return self.run('log -n1 --pretty=format:"%s"' % formatstr)[0]
|
||||
|
||||
def record_rev(self, rev_num):
|
||||
tag = 'tag' + str(rev_num)
|
||||
self.run('tag ' + tag)
|
||||
self.revs[rev_num] = tag
|
||||
self.timestamps[tag] = self.get_metadata('%ct')
|
||||
self.sha1s[tag] = self.get_metadata('%h')
|
||||
self.scmlogs.annotate(
|
||||
"Recorded rev %d: id %s, timestamp %s, SHA1 %s" % \
|
||||
(rev_num,
|
||||
tag,
|
||||
self.timestamps[tag],
|
||||
self.sha1s[tag])
|
||||
)
|
@ -1,38 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
|
||||
from commontests import CommonTests
|
||||
from utils import run_hg
|
||||
|
||||
class GitHgTests(CommonTests):
|
||||
mixed_version_template = '%s.master.%s'
|
||||
|
||||
def test_versionformat_abbrevhash(self):
|
||||
self.tar_scm_std('--versionformat', self.abbrev_hash_format)
|
||||
self.assertTarOnly(self.basename(version = self.sha1s(self.rev(2))))
|
||||
|
||||
def test_versionformat_timestamp(self):
|
||||
self.tar_scm_std('--versionformat', self.timestamp_format)
|
||||
self.assertTarOnly(self.basename(version = self.timestamps(self.rev(2))))
|
||||
|
||||
def _mixed_version_format(self):
|
||||
return self.mixed_version_template % (self.timestamp_format, self.abbrev_hash_format)
|
||||
|
||||
def _mixed_version(self):
|
||||
return self.mixed_version_template % (self.timestamps(self.rev(2)), self.sha1s(self.rev(2)))
|
||||
|
||||
def test_versionformat_mixed(self):
|
||||
self.tar_scm_std('--versionformat', self._mixed_version_format())
|
||||
self.assertTarOnly(self.basename(version = self._mixed_version()))
|
||||
|
||||
def test_version_versionformat(self):
|
||||
self.tar_scm_std('--version', '3.0', '--versionformat', self._mixed_version_format())
|
||||
self.assertTarOnly(self.basename(version = self._mixed_version()))
|
||||
|
||||
def test_versionformat_revision(self):
|
||||
self.fixtures.create_commits(4)
|
||||
self.tar_scm_std('--versionformat', self.abbrev_hash_format, '--revision', self.rev(2))
|
||||
basename = self.basename(version = self.sha1s(self.rev(2)))
|
||||
th = self.assertTarOnly(basename)
|
||||
self.assertTarMemberContains(th, basename + '/a', '2')
|
17
gittests.py
17
gittests.py
@ -1,17 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from githgtests import GitHgTests
|
||||
from gitfixtures import GitFixtures
|
||||
from utils import run_git
|
||||
|
||||
class GitTests(GitHgTests):
|
||||
scm = 'git'
|
||||
initial_clone_command = 'git clone'
|
||||
update_cache_command = 'git fetch'
|
||||
fixtures_class = GitFixtures
|
||||
|
||||
abbrev_hash_format = '%h'
|
||||
timestamp_format = '%ct'
|
||||
|
||||
def default_version(self):
|
||||
return self.timestamps(self.rev(2))
|
@ -1,48 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
|
||||
from fixtures import Fixtures
|
||||
from utils import mkfreshdir, run_hg
|
||||
|
||||
class HgFixtures(Fixtures):
|
||||
def init(self):
|
||||
self.create_repo()
|
||||
|
||||
self.timestamps = { }
|
||||
self.sha1s = { }
|
||||
|
||||
self.create_commits(2)
|
||||
|
||||
def run(self, cmd):
|
||||
return run_hg(self.repo_path, cmd)
|
||||
|
||||
def create_repo(self):
|
||||
os.makedirs(self.repo_path)
|
||||
os.chdir(self.repo_path)
|
||||
self.run('init')
|
||||
c = open('.hg/hgrc', 'w')
|
||||
c.write("[ui]\nusername = %s\n" % self.name_and_email)
|
||||
c.close()
|
||||
self.wd = self.repo_path
|
||||
print "created repo", self.repo_path
|
||||
|
||||
def do_commit(self, newly_created):
|
||||
self.run('add .')
|
||||
self.run('commit -m%d' % self.next_commit_rev)
|
||||
|
||||
def get_metadata(self, formatstr):
|
||||
return self.run('log -l1 --template "%s"' % formatstr)[0]
|
||||
|
||||
def record_rev(self, rev_num):
|
||||
tag = str(rev_num - 1) # hg starts counting changesets at 0
|
||||
self.revs[rev_num] = tag
|
||||
self.timestamps[tag] = self.get_metadata('{date}')
|
||||
self.sha1s[tag] = self.get_metadata('{node|short}')
|
||||
self.scmlogs.annotate(
|
||||
"Recorded rev %d: id %s, timestamp %s, SHA1 %s" % \
|
||||
(rev_num,
|
||||
tag,
|
||||
self.timestamps[tag],
|
||||
self.sha1s[tag])
|
||||
)
|
17
hgtests.py
17
hgtests.py
@ -1,17 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from githgtests import GitHgTests
|
||||
from hgfixtures import HgFixtures
|
||||
from utils import run_hg
|
||||
|
||||
class HgTests(GitHgTests):
|
||||
scm = 'hg'
|
||||
initial_clone_command = 'hg clone'
|
||||
update_cache_command = 'hg pull'
|
||||
fixtures_class = HgFixtures
|
||||
|
||||
abbrev_hash_format = '{node|short}'
|
||||
timestamp_format = '{date}'
|
||||
|
||||
def default_version(self):
|
||||
return self.rev(2)
|
3
obs-service-tar_scm-0.2.3.tar.gz
Normal file
3
obs-service-tar_scm-0.2.3.tar.gz
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:68b8339325bc9bad01d84282d8a3f95728d22351313a1eb672e79cb3d2e0fd15
|
||||
size 14506
|
@ -1,3 +1,16 @@
|
||||
-------------------------------------------------------------------
|
||||
Mon Dec 3 14:46:15 UTC 2012 - saschpe@suse.de
|
||||
|
||||
- Use upstream github repository and download via _service file
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Dec 3 14:11:07 UTC 2012 - saschpe@suse.de
|
||||
|
||||
- Add custom git versionformat extensions:
|
||||
+ @PARENT_TAG@: Replaced by first tag that is reachable from the
|
||||
current commit (see 'git describe')
|
||||
Future custom additions could follow the @NAME@ scheme
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Nov 1 14:53:39 GMT 2012 - aspiers@suse.com
|
||||
|
||||
|
@ -19,44 +19,22 @@
|
||||
%define service tar_scm
|
||||
|
||||
Name: obs-service-%{service}
|
||||
Version: 0.2.3
|
||||
Release: 0
|
||||
Summary: An OBS source service: checkout or update a tar ball from svn/git/hg
|
||||
License: GPL-2.0+
|
||||
Group: Development/Tools/Building
|
||||
Url: https://build.opensuse.org/package/show?package=obs-service-%{service}&project=openSUSE%3ATools
|
||||
Version: 0.2.3
|
||||
Release: 0
|
||||
Source: %{service}
|
||||
Source1: %{service}.service
|
||||
Source2: %{service}.rc
|
||||
|
||||
# test suite files
|
||||
Source100: bzrfixtures.py
|
||||
Source101: bzrtests.py
|
||||
Source102: commontests.py
|
||||
Source103: fixtures.py
|
||||
Source104: gitfixtures.py
|
||||
Source105: githgtests.py
|
||||
Source106: gittests.py
|
||||
Source107: hgfixtures.py
|
||||
Source108: hgtests.py
|
||||
Source109: scmlogs.py
|
||||
Source110: svnfixtures.py
|
||||
Source111: svntests.py
|
||||
Source112: testassertions.py
|
||||
Source113: testenv.py
|
||||
Source114: test.py
|
||||
Source115: utils.py
|
||||
Source116: scm-wrapper
|
||||
|
||||
Requires: bzr
|
||||
Requires: git
|
||||
Requires: mercurial
|
||||
Requires: subversion
|
||||
Url: https://github.com/openSUSE/obs-service-%{service}
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
BuildRequires: bzr
|
||||
BuildRequires: git
|
||||
BuildRequires: mercurial
|
||||
BuildRequires: python >= 2.6
|
||||
BuildRequires: subversion
|
||||
Requires: bzr
|
||||
Requires: git
|
||||
Requires: mercurial
|
||||
Requires: subversion
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||
BuildArch: noarch
|
||||
|
||||
@ -65,31 +43,29 @@ This is a source service for openSUSE Build Service.
|
||||
|
||||
It supports downloading from svn, git, hg and bzr repositories.
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q -D -T 0 -n .
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
|
||||
%install
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/obs/service
|
||||
install -m 0755 %{SOURCE0} $RPM_BUILD_ROOT/usr/lib/obs/service
|
||||
install -m 0644 %{SOURCE1} $RPM_BUILD_ROOT/usr/lib/obs/service
|
||||
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 $RPM_BUILD_ROOT/etc/obs/services
|
||||
install -m 0644 %{SOURCE2} $RPM_BUILD_ROOT/etc/obs/services/%{service}
|
||||
mkdir -p %{buildroot}%{_sysconfdir}/obs/services
|
||||
install -m 0644 tar_scm.rc %{buildroot}%{_sysconfdir}/obs/services/%{service}
|
||||
|
||||
%check
|
||||
chmod +x $RPM_SOURCE_DIR/scm-wrapper
|
||||
: Running the test suite. Please be patient - this takes a few minutes ...
|
||||
python $RPM_SOURCE_DIR/test.py
|
||||
python test.py
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%dir /usr/lib/obs
|
||||
/usr/lib/obs/service
|
||||
%dir /etc/obs
|
||||
%dir /etc/obs/services
|
||||
%config(noreplace) /etc/obs/services/*
|
||||
%dir %{_prefix}/lib/obs
|
||||
%{_prefix}/lib/obs/service
|
||||
%dir %{_sysconfdir}/obs
|
||||
%dir %{_sysconfdir}/obs/services
|
||||
%config(noreplace) %{_sysconfdir}/obs/services/*
|
||||
|
||||
%changelog
|
||||
|
26
scm-wrapper
26
scm-wrapper
@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Wrapper around SCM to enable behaviour verification testing
|
||||
# on tar_scm's repository caching code. This is cleaner than
|
||||
# writing tests which look inside the cache, because then they
|
||||
# become coupled to the cache's implementation, and require
|
||||
# knowledge of where the cache lives etc.
|
||||
|
||||
me=`basename $0`
|
||||
|
||||
if [ -z "$SCM_INVOCATION_LOG" ]; then
|
||||
cat <<EOF >&2
|
||||
\$SCM_INVOCATION_LOG must be set before calling $0.
|
||||
It should be invoked from the test suite, not directly.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$me" = 'scm-wrapper' ]; then
|
||||
echo "$me should not be invoked directly, only via symlink" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$me $*" >> "$SCM_INVOCATION_LOG"
|
||||
|
||||
/usr/bin/$me "$@"
|
85
scmlogs.py
85
scmlogs.py
@ -1,85 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
class ScmInvocationLogs:
|
||||
"""
|
||||
Provides log files which tracks invocations of SCM binaries. The
|
||||
tracking is done via a wrapper around SCM to enable behaviour
|
||||
verification testing on tar_scm's repository caching code. This
|
||||
is cleaner than writing tests which look inside the cache, because
|
||||
then they become coupled to the cache's implementation, and
|
||||
require knowledge of where the cache lives etc.
|
||||
|
||||
One instance should be constructed per unit test. If the test
|
||||
invokes the SCM binary multiple times, invoke next() in between
|
||||
each, so that a separate log file is used for each invocation -
|
||||
this allows more accurate fine-grained assertions on the
|
||||
invocation log.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setup_bin_wrapper(cls, scm, tmp_dir):
|
||||
cls.wrapper_dir = tmp_dir + '/wrappers'
|
||||
|
||||
if not os.path.exists(cls.wrapper_dir):
|
||||
os.makedirs(cls.wrapper_dir)
|
||||
|
||||
wrapper = cls.wrapper_dir + '/' + scm
|
||||
if not os.path.exists(wrapper):
|
||||
os.symlink('../../scm-wrapper', wrapper)
|
||||
|
||||
path = os.getenv('PATH')
|
||||
prepend = cls.wrapper_dir + ':'
|
||||
|
||||
if not path.startswith(prepend):
|
||||
new_path = prepend + path
|
||||
os.environ['PATH'] = new_path
|
||||
|
||||
def __init__(self, scm, test_dir):
|
||||
self.scm = scm
|
||||
self.test_dir = test_dir
|
||||
self.counter = 0
|
||||
self.unlink_existing_logs()
|
||||
|
||||
def get_log_file_template(self):
|
||||
return '%s-invocation-%%s.log' % self.scm
|
||||
|
||||
def get_log_path_template(self):
|
||||
return os.path.join(self.test_dir, self.get_log_file_template())
|
||||
|
||||
def unlink_existing_logs(self):
|
||||
pat = self.get_log_path_template() % '*'
|
||||
for log in glob.glob(pat):
|
||||
os.unlink(log)
|
||||
|
||||
def get_log_file(self, identifier):
|
||||
if identifier:
|
||||
identifier = '-' + identifier
|
||||
return self.get_log_file_template() % ('%02d%s' % (self.counter, identifier))
|
||||
|
||||
def get_log_path(self, identifier):
|
||||
return os.path.join(self.test_dir, self.get_log_file(identifier))
|
||||
|
||||
def next(self, identifier=''):
|
||||
self.counter += 1
|
||||
self.current_log_path = self.get_log_path(identifier)
|
||||
if os.path.exists(self.current_log_path):
|
||||
raise RuntimeError, "%s already existed?!" % self.current_log_path
|
||||
os.putenv('SCM_INVOCATION_LOG', self.current_log_path)
|
||||
|
||||
def annotate(self, msg):
|
||||
log = open(self.current_log_path, 'a')
|
||||
log.write('# ' + msg + "\n")
|
||||
print msg
|
||||
log.close()
|
||||
|
||||
def read(self):
|
||||
if not os.path.exists(self.current_log_path):
|
||||
return '<no %s log>' % self.scm
|
||||
|
||||
log = open(self.current_log_path)
|
||||
loglines = log.readlines()
|
||||
log.close()
|
||||
return loglines
|
@ -1,44 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
|
||||
from fixtures import Fixtures
|
||||
from utils import mkfreshdir, quietrun, run_svn
|
||||
|
||||
class SvnFixtures(Fixtures):
|
||||
def init(self):
|
||||
self.wd_path = self.container_dir + '/wd'
|
||||
|
||||
self.create_repo()
|
||||
self.checkout_repo()
|
||||
|
||||
self.added = { }
|
||||
self.timestamps = { }
|
||||
|
||||
self.create_commits(2)
|
||||
|
||||
def run(self, cmd):
|
||||
return run_svn(self.wd_path, cmd)
|
||||
|
||||
def create_repo(self):
|
||||
quietrun('svnadmin create ' + self.repo_path)
|
||||
print "created repo", self.repo_path
|
||||
|
||||
def checkout_repo(self):
|
||||
mkfreshdir(self.wd_path)
|
||||
quietrun('svn checkout %s %s' % (self.repo_url, self.wd_path))
|
||||
self.wd = self.wd_path
|
||||
|
||||
def do_commit(self, newly_created):
|
||||
for new in newly_created:
|
||||
if not new in self.added:
|
||||
self.run('add ' + new)
|
||||
self.added[new] = True
|
||||
self.run('commit -m%d' % self.next_commit_rev)
|
||||
|
||||
def get_metadata(self, formatstr):
|
||||
return self.run('log -n1' % formatstr)[0]
|
||||
|
||||
def record_rev(self, rev_num):
|
||||
self.revs[rev_num] = str(rev_num)
|
||||
self.scmlogs.annotate("Recorded rev %d" % rev_num)
|
29
svntests.py
29
svntests.py
@ -1,29 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from commontests import CommonTests
|
||||
from svnfixtures import SvnFixtures
|
||||
from utils import run_svn
|
||||
|
||||
class SvnTests(CommonTests):
|
||||
scm = 'svn'
|
||||
initial_clone_command = 'svn (co|checkout) '
|
||||
update_cache_command = 'svn up(date)?'
|
||||
fixtures_class = SvnFixtures
|
||||
|
||||
def default_version(self):
|
||||
return self.rev(2)
|
||||
|
||||
def test_versionformat_rev(self):
|
||||
self.tar_scm_std('--versionformat', 'myrev%r.svn')
|
||||
self.assertTarOnly(self.basename(version = 'myrev2.svn'))
|
||||
|
||||
def test_version_versionformat(self):
|
||||
self.tar_scm_std('--version', '3.0', '--versionformat', 'myrev%r.svn')
|
||||
self.assertTarOnly(self.basename(version = 'myrev2.svn'))
|
||||
|
||||
def test_versionformat_revision(self):
|
||||
self.fixtures.create_commits(4)
|
||||
self.tar_scm_std('--versionformat', 'foo%r', '--revision', self.rev(2))
|
||||
basename = self.basename(version = 'foo2')
|
||||
th = self.assertTarOnly(basename)
|
||||
self.assertTarMemberContains(th, basename + '/a', '2')
|
496
tar_scm
496
tar_scm
@ -1,496 +0,0 @@
|
||||
#!/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] [--submodules disable] --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="%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"
|
||||
}
|
||||
|
||||
get_version () {
|
||||
case "$MYSCM" in
|
||||
git)
|
||||
#version=`safe_run git show --pretty=format:"$MYFORMAT" | head -n 1`
|
||||
version=`safe_run git log -n1 --date=short --pretty=format:"$MYFORMAT"|sed '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`
|
||||
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
|
11
tar_scm.rc
11
tar_scm.rc
@ -1,11 +0,0 @@
|
||||
#
|
||||
# Define a cache directory here to avoid repeating downloads of the
|
||||
# same file. This works on the server and on the client side.
|
||||
#
|
||||
# Note that this file can be overridden with per-user config placed
|
||||
# in ~/.obs/tar_scm
|
||||
#
|
||||
# WARNING: you need to create three directories inside, when changing from default:
|
||||
# mkdir -p repo{,url} incoming
|
||||
#
|
||||
#CACHEDIRECTORY="/var/cache/obs/tar_scm"
|
@ -1,53 +0,0 @@
|
||||
<service name="tar_scm">
|
||||
<summary>Create a tar ball from SCM repository</summary>
|
||||
<description>This service uses a scm client to checkout or update from a given repository. Supported are svn, git, hg and bzr.</description>
|
||||
<parameter name="scm">
|
||||
<description>Used SCM</description>
|
||||
<allowedvalue>svn</allowedvalue>
|
||||
<allowedvalue>git</allowedvalue>
|
||||
<allowedvalue>hg</allowedvalue>
|
||||
<allowedvalue>bzr</allowedvalue>
|
||||
<required/>
|
||||
</parameter>
|
||||
<parameter name="url">
|
||||
<description>Checkout url</description>
|
||||
<required/>
|
||||
</parameter>
|
||||
<parameter name="subdir">
|
||||
<description>package just a sub directory</description>
|
||||
</parameter>
|
||||
<parameter name="version">
|
||||
<description>Specify version to be used in tarball. Defaults to automatically detected value formatted by versionformat parameter.</description>
|
||||
</parameter>
|
||||
<parameter name="versionformat">
|
||||
<description>Auto-generate version from checked out source using this format string. For git, value is passed via git show --pretty=format:... (default '%ct'); for hg, via hg log --template=... (default '{rev}'); for bzr and svn, %r is revision (default '%r'). Overrides tarball name defined by version parameter.</description>
|
||||
</parameter>
|
||||
<parameter name="versionprefix">
|
||||
<description>specify a base version as prefix.</description>
|
||||
</parameter>
|
||||
<parameter name="revision">
|
||||
<description>specify a revision</description>
|
||||
</parameter>
|
||||
<parameter name="filename">
|
||||
<description>name of package - used together with version to determine tarball name</description>
|
||||
</parameter>
|
||||
<parameter name="exclude">
|
||||
<description>for specifying excludes when creating the tar ball</description>
|
||||
</parameter>
|
||||
<parameter name="include">
|
||||
<description>for specifying subset of files/subdirectories to pack in the tar ball</description>
|
||||
</parameter>
|
||||
<parameter name="package-meta">
|
||||
<description>Package the meta data of SCM to allow the user or OBS to update after un-tar</description>
|
||||
<allowedvalue>yes</allowedvalue>
|
||||
</parameter>
|
||||
<parameter name="history-depth">
|
||||
<description>Stored history depth. Special value "full" clones/pulls full history. Only valid if SCM git is used.</description>
|
||||
</parameter>
|
||||
<parameter name="submodules">
|
||||
<description>Whether or not to include git submodules. Default is 'enable'</description>
|
||||
<allowedvalue>enable</allowedvalue>
|
||||
<allowedvalue>disable</allowedvalue>
|
||||
</parameter>
|
||||
</service>
|
||||
|
37
test.py
37
test.py
@ -1,37 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from gittests import GitTests
|
||||
from svntests import SvnTests
|
||||
from hgtests import HgTests
|
||||
from bzrtests import BzrTests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = unittest.TestSuite()
|
||||
testclasses = [
|
||||
SvnTests,
|
||||
GitTests,
|
||||
HgTests,
|
||||
BzrTests,
|
||||
]
|
||||
for testclass in testclasses:
|
||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(testclass))
|
||||
|
||||
runner_args = {
|
||||
#'verbosity' : 2,
|
||||
}
|
||||
major, minor, micro, releaselevel, serial = sys.version_info
|
||||
if major > 2 or (major == 2 and minor >= 7):
|
||||
# New in 2.7
|
||||
runner_args['buffer'] = True
|
||||
#runner_args['failfast'] = True
|
||||
|
||||
runner = unittest.TextTestRunner(**runner_args)
|
||||
result = runner.run(suite)
|
||||
|
||||
if result.wasSuccessful():
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
@ -1,134 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
from pprint import pprint, pformat
|
||||
import re
|
||||
import tarfile
|
||||
import unittest
|
||||
|
||||
line_start = '(^|\n)'
|
||||
|
||||
class TestAssertions(unittest.TestCase):
|
||||
######################################################################
|
||||
# backported from 2.7 just in case we're running on an older Python
|
||||
def assertRegexpMatches(self, text, expected_regexp, msg=None):
|
||||
"""Fail the test unless the text matches the regular expression."""
|
||||
if isinstance(expected_regexp, basestring):
|
||||
expected_regexp = re.compile(expected_regexp)
|
||||
if not expected_regexp.search(text):
|
||||
msg = msg or "Regexp didn't match"
|
||||
msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text)
|
||||
raise self.failureException(msg)
|
||||
|
||||
def assertNotRegexpMatches(self, text, unexpected_regexp, msg=None):
|
||||
"""Fail the test if the text matches the regular expression."""
|
||||
if isinstance(unexpected_regexp, basestring):
|
||||
unexpected_regexp = re.compile(unexpected_regexp)
|
||||
match = unexpected_regexp.search(text)
|
||||
if match:
|
||||
msg = msg or "Regexp matched"
|
||||
msg = '%s: %r matches %r in %r' % (msg,
|
||||
text[match.start():match.end()],
|
||||
unexpected_regexp.pattern,
|
||||
text)
|
||||
raise self.failureException(msg)
|
||||
######################################################################
|
||||
|
||||
def assertNumDirents(self, dir, expected, msg = ''):
|
||||
dirents = os.listdir(dir)
|
||||
got = len(dirents)
|
||||
if len(msg) > 0: msg += "\n"
|
||||
msg += 'expected %d file(s), got %d: %s' % (expected, got, pformat(dirents))
|
||||
self.assertEqual(expected, got, msg)
|
||||
return dirents
|
||||
|
||||
def assertNumTarEnts(self, tar, expected, msg = ''):
|
||||
self.assertTrue(tarfile.is_tarfile(tar))
|
||||
th = tarfile.open(tar)
|
||||
tarents = th.getmembers()
|
||||
got = len(tarents)
|
||||
if len(msg) > 0: msg += "\n"
|
||||
msg += 'expected %s to have %d entries, got %d:\n%s' % \
|
||||
(tar, expected, got, pformat(tarents))
|
||||
self.assertEqual(expected, got, msg)
|
||||
return th, tarents
|
||||
|
||||
def assertStandardTar(self, tar, top):
|
||||
th, entries = self.assertNumTarEnts(tar, 4)
|
||||
entries.sort(lambda x, y: cmp(x.name, y.name))
|
||||
self.assertEqual(entries[0].name, top)
|
||||
self.assertEqual(entries[1].name, top + '/a')
|
||||
self.assertEqual(entries[2].name, top + '/subdir')
|
||||
self.assertEqual(entries[3].name, top + '/subdir/b')
|
||||
return th
|
||||
|
||||
def assertSubdirTar(self, tar, top):
|
||||
th, entries = self.assertNumTarEnts(tar, 2)
|
||||
entries.sort(lambda x, y: cmp(x.name, y.name))
|
||||
self.assertEqual(entries[0].name, top)
|
||||
self.assertEqual(entries[1].name, top + '/b')
|
||||
return th
|
||||
|
||||
def checkTar(self, tar, tarbasename, toptardir=None, tarchecker=None):
|
||||
if not toptardir:
|
||||
toptardir = tarbasename
|
||||
if not tarchecker:
|
||||
tarchecker = self.assertStandardTar
|
||||
|
||||
self.assertEqual(tar, '%s.tar' % tarbasename)
|
||||
tarpath = os.path.join(self.outdir, tar)
|
||||
return tarchecker(tarpath, toptardir)
|
||||
|
||||
def assertTarOnly(self, tarbasename, **kwargs):
|
||||
dirents = self.assertNumDirents(self.outdir, 1)
|
||||
return self.checkTar(dirents[0], tarbasename, **kwargs)
|
||||
|
||||
def assertTarAndDir(self, tarbasename, dirname=None, **kwargs):
|
||||
if not dirname:
|
||||
dirname = tarbasename
|
||||
|
||||
dirents = self.assertNumDirents(self.outdir, 2)
|
||||
pprint(dirents)
|
||||
|
||||
if dirents[0][-4:] == '.tar':
|
||||
tar = dirents[0]
|
||||
wd = dirents[1]
|
||||
elif dirents[1][-4:] == '.tar':
|
||||
tar = dirents[1]
|
||||
wd = dirents[0]
|
||||
else:
|
||||
self.fail('no .tar found in ' + self.outdir)
|
||||
|
||||
self.assertEqual(wd, dirname)
|
||||
self.assertTrue(os.path.isdir(os.path.join(self.outdir, wd)),
|
||||
dirname + ' should be directory')
|
||||
|
||||
return self.checkTar(tar, tarbasename, **kwargs)
|
||||
|
||||
def assertTarMemberContains(self, th, tarmember, contents):
|
||||
f = th.extractfile(tarmember)
|
||||
self.assertEqual(contents, "\n".join(f.readlines()))
|
||||
|
||||
def assertRanInitialClone(self, logpath, loglines):
|
||||
self._find(logpath, loglines, self.initial_clone_command, self.update_cache_command)
|
||||
|
||||
def assertRanUpdate(self, logpath, loglines):
|
||||
self._find(logpath, loglines, self.update_cache_command, self.initial_clone_command)
|
||||
|
||||
def _find(self, logpath, loglines, should_find, should_not_find):
|
||||
print "####", should_find
|
||||
found = False
|
||||
regexp = re.compile('^' + should_find)
|
||||
for line in loglines:
|
||||
msg = \
|
||||
"Shouldn't find /%s/ in %s; log was:\n" \
|
||||
"----\n%s\n----\n" \
|
||||
% (should_not_find, logpath, "".join(loglines))
|
||||
self.assertNotRegexpMatches(line, should_not_find, msg)
|
||||
if regexp.search(line):
|
||||
found = True
|
||||
msg = \
|
||||
"Didn't find /%s/ in %s; log was:\n" \
|
||||
"----\n%s\n----\n" \
|
||||
% (regexp.pattern, logpath, "".join(loglines))
|
||||
self.assertTrue(found, msg)
|
144
testenv.py
144
testenv.py
@ -1,144 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from utils import mkfreshdir, run_cmd
|
||||
from scmlogs import ScmInvocationLogs
|
||||
|
||||
class TestEnvironment:
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__)) # os.getcwd()
|
||||
tmp_dir = tests_dir + '/tmp'
|
||||
is_setup = False
|
||||
|
||||
@classmethod
|
||||
def tar_scm_bin(cls):
|
||||
tar_scm = cls.tests_dir + '/tar_scm'
|
||||
if not os.path.isfile(tar_scm):
|
||||
raise RuntimeError, "Failed to find tar_scm executable at " + tar_scm
|
||||
return tar_scm
|
||||
|
||||
@classmethod
|
||||
def setupClass(cls):
|
||||
# deliberately not setUpClass - we emulate the behaviour
|
||||
# to support Python < 2.7
|
||||
if cls.is_setup:
|
||||
return
|
||||
print "++++++ setupClass ++++++"
|
||||
ScmInvocationLogs.setup_bin_wrapper(cls.scm, cls.tmp_dir)
|
||||
os.putenv('DEBUG_TAR_SCM', 'yes')
|
||||
cls.is_setup = True
|
||||
|
||||
def calcPaths(self):
|
||||
if not self._testMethodName.startswith('test_'):
|
||||
raise RuntimeError, "unexpected test method name: " + self._testMethodName
|
||||
self.test_name = self._testMethodName[5:]
|
||||
self.test_dir = os.path.join(self.tmp_dir, self.scm, self.test_name)
|
||||
self.pkgdir = os.path.join(self.test_dir, 'pkg')
|
||||
self.outdir = os.path.join(self.test_dir, 'out')
|
||||
self.cachedir = os.path.join(self.test_dir, 'cache')
|
||||
|
||||
def setUp(self):
|
||||
self.setupClass()
|
||||
print "++++++ setUp ++++++"
|
||||
|
||||
self.calcPaths()
|
||||
|
||||
self.scmlogs = ScmInvocationLogs(self.scm, self.test_dir)
|
||||
self.scmlogs.next('fixtures')
|
||||
|
||||
self.initDirs()
|
||||
|
||||
self.fixtures = self.fixtures_class(self.test_dir, self.scmlogs)
|
||||
self.fixtures.setup()
|
||||
|
||||
self.scmlogs.next('start-test')
|
||||
self.scmlogs.annotate('Starting %s test' % self.test_name)
|
||||
|
||||
os.putenv('CACHEDIRECTORY', self.cachedir)
|
||||
# osc launches source services with cwd as pkg dir
|
||||
os.chdir(self.pkgdir)
|
||||
|
||||
def initDirs(self):
|
||||
# pkgdir persists between tests to simulate real world use
|
||||
# (although a test can choose to invoke mkfreshdir)
|
||||
if not os.path.exists(self.pkgdir):
|
||||
os.makedirs(self.pkgdir)
|
||||
|
||||
for subdir in ('repo', 'repourl', 'incoming'):
|
||||
mkfreshdir(os.path.join(self.cachedir, subdir))
|
||||
|
||||
def disableCache(self):
|
||||
os.unsetenv('CACHEDIRECTORY')
|
||||
|
||||
def tearDown(self):
|
||||
print "++++++ tearDown ++++++"
|
||||
self.postRun()
|
||||
|
||||
def postRun(self):
|
||||
print "++++++ postRun +++++++"
|
||||
self.service = { 'mode' : 'disabled' }
|
||||
if os.path.exists(self.outdir):
|
||||
self.simulate_osc_postrun()
|
||||
|
||||
def simulate_osc_postrun(self):
|
||||
"""
|
||||
Simulate osc copying files from temporary --outdir back to
|
||||
package source directory, so our tests can catch any
|
||||
potential side-effects due to the persistent nature of the
|
||||
package source directory.
|
||||
"""
|
||||
|
||||
temp_dir = self.outdir
|
||||
dir = self.pkgdir
|
||||
service = self.service
|
||||
|
||||
# This code copied straight out of osc/core.py Serviceinfo.execute():
|
||||
|
||||
if service['mode'] == "disabled" or service['mode'] == "trylocal" or service['mode'] == "localonly" or callmode == "local" or callmode == "trylocal":
|
||||
for filename in os.listdir(temp_dir):
|
||||
shutil.move( os.path.join(temp_dir, filename), os.path.join(dir, filename) )
|
||||
else:
|
||||
for filename in os.listdir(temp_dir):
|
||||
shutil.move( os.path.join(temp_dir, filename), os.path.join(dir, "_service:"+name+":"+filename) )
|
||||
|
||||
def tar_scm_std(self, *args, **kwargs):
|
||||
return self.tar_scm(self.stdargs(*args), **kwargs)
|
||||
|
||||
def tar_scm_std_fail(self, *args):
|
||||
return self.tar_scm(self.stdargs(*args), should_succeed=False)
|
||||
|
||||
def stdargs(self, *args):
|
||||
return [ '--url', self.fixtures.repo_url, '--scm', self.scm ] + list(args)
|
||||
|
||||
def tar_scm(self, args, should_succeed=True):
|
||||
# simulate new temporary outdir for each tar_scm invocation
|
||||
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))
|
||||
print "\n"
|
||||
print "-" * 70
|
||||
print "Running", cmdstr
|
||||
(stdout, stderr, ret) = run_cmd(cmdstr)
|
||||
if stdout:
|
||||
print "STDOUT:"
|
||||
print "------"
|
||||
print stdout,
|
||||
if stderr:
|
||||
print "STDERR:"
|
||||
print "------"
|
||||
print stderr,
|
||||
print "-" * 70
|
||||
succeeded = ret == 0
|
||||
self.assertEqual(succeeded, should_succeed)
|
||||
return (stdout, stderr, ret)
|
||||
|
||||
def rev(self, rev):
|
||||
return self.fixtures.revs[rev]
|
||||
|
||||
def timestamps(self, rev):
|
||||
return self.fixtures.timestamps[rev]
|
||||
|
||||
def sha1s(self, rev):
|
||||
return self.fixtures.sha1s[rev]
|
||||
|
46
utils.py
46
utils.py
@ -1,46 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
def mkfreshdir(path):
|
||||
if not re.search('.{10}/tmp(/|$)', path):
|
||||
raise RuntimeError, 'unsafe call: mkfreshdir(%s)' % path
|
||||
|
||||
cwd = os.getcwd()
|
||||
os.chdir('/')
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
os.makedirs(path)
|
||||
|
||||
def run_cmd(cmd):
|
||||
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(stdout, stderr) = p.communicate()
|
||||
return (stdout, stderr, p.returncode)
|
||||
|
||||
def quietrun(cmd):
|
||||
(stdout, stderr, ret) = run_cmd(cmd)
|
||||
if ret != 0:
|
||||
print cmd, " failed!"
|
||||
print stdout
|
||||
print stderr
|
||||
return (stdout, stderr, ret)
|
||||
|
||||
def run_scm(scm, repo, opts):
|
||||
cmd = 'cd %s && %s %s' % (repo, scm, opts)
|
||||
#return subprocess.check_output(cmd, shell=True)
|
||||
return quietrun(cmd)
|
||||
|
||||
def run_git(repo, opts):
|
||||
return run_scm('git', repo, opts)
|
||||
|
||||
def run_svn(repo, opts):
|
||||
return run_scm('svn', repo, opts)
|
||||
|
||||
def run_hg(repo, opts):
|
||||
return run_scm('hg', repo, opts)
|
||||
|
||||
def run_bzr(repo, opts):
|
||||
return run_scm('bzr', repo, opts)
|
Loading…
Reference in New Issue
Block a user