Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mercurial repository support. #979

Merged
merged 3 commits into from
Oct 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 11 additions & 16 deletions easybuild/tools/repository/gitrepo.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class GitRepository(FileRepository):
def __init__(self, *args):
"""
Initialize git client to None (will be set later)
All the real logic is in the setupRepo and createWorkingCopy methods
All the real logic is in the setup_repo and create_wroking_copy methods
"""
self.client = None
FileRepository.__init__(self, *args)
Expand All @@ -84,10 +84,8 @@ def setup_repo(self):
"""
Set up git repository.
"""
try:
git.GitCommandError
except NameError, err:
self.log.exception("It seems like GitPython is not available: %s" % err)
if not HAVE_GIT:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the catching exception was used to make sure git/svn binary was present on system and I was wrong. What you think about an additional check on binary presence ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say that's the job of the respective Python packages being used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and only git and hglib module are concerned. Just apply a minor fix to catch OSError exception for cloning step.

self.log.error("It seems like GitPython is not available, which is required for Git support.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this point ever be reached? Will the USABLE properties not prevent from ever getting here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the USABLE is only used by EB, but what if someone creates their own GitRepository instance and calls setup_repo? You want to make sure things don't go down hard...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true, then again, the uses of logger makes it hard to use this outside EB (as I noticed in the clean up script).


self.wc = tempfile.mkdtemp(prefix='git-wc-')

Expand All @@ -103,7 +101,7 @@ def create_working_copy(self):
client.clone(self.repo)
reponame = os.listdir(self.wc)[0]
self.log.debug("rep name is %s" % reponame)
except git.GitCommandError, err:
except (git.GitCommandError, OSError), err:
# it might already have existed
self.log.warning("Git local repo initialization failed, it might already exist: %s" % err)

Expand All @@ -120,7 +118,7 @@ def create_working_copy(self):
res = self.client.pull()
self.log.debug("pulled succesfully to %s in %s" % (res, self.wc))
except (git.GitCommandError, OSError), err:
self.log.exception("pull in working copy %s went wrong: %s" % (self.wc, err))
self.log.error("pull in working copy %s went wrong: %s" % (self.wc, err))

def add_easyconfig(self, cfg, name, version, stats, append):
"""
Expand All @@ -139,10 +137,9 @@ def commit(self, msg=None):
Commit working copy to git repository
"""
self.log.debug("committing in git: %s" % msg)
completemsg = "EasyBuild-commit from %s (time: %s, user: %s) \n%s" % (socket.gethostname(),
time.strftime("%Y-%m-%d_%H-%M-%S"),
getpass.getuser(),
msg)
tup = (socket.gethostname(), time.strftime("%Y-%m-%d_%H-%M-%S"), getpass.getuser(), msg)
completemsg = "EasyBuild-commit from %s (time: %s, user: %s) \n%s" % tup

self.log.debug("git status: %s" % self.client.status())
try:
self.client.commit('-am "%s"' % completemsg)
Expand All @@ -153,10 +150,8 @@ def commit(self, msg=None):
info = self.client.push()
self.log.debug("push info: %s " % info)
except GitCommandError, err:
self.log.warning("Push from working copy %s to remote %s (msg: %s) failed: %s" % (self.wc,
self.repo,
msg,
err))
tup = (self.wc, self.repo, msg, err)
self.log.warning("Push from working copy %s to remote %s (msg: %s) failed: %s" % tup)

def cleanup(self):
"""
Expand All @@ -166,4 +161,4 @@ def cleanup(self):
self.wc = os.path.dirname(self.wc)
rmtree2(self.wc)
except IOError, err:
self.log.exception("Can't remove working copy %s: %s" % (self.wc, err))
self.log.error("Can't remove working copy %s: %s" % (self.wc, err))
170 changes: 170 additions & 0 deletions easybuild/tools/repository/hgrepo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# #
# Copyright 2009-2014 Ghent University
#
# This file is part of EasyBuild,
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
# with support of Ghent University (http://ugent.be/hpc),
# the Flemish Supercomputer Centre (VSC) (https://vscentrum.be/nl/en),
# the Hercules foundation (http://www.herculesstichting.be/in_English)
# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en).
#
# http://github.com/hpcugent/easybuild
#
# EasyBuild 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 v2.
#
# EasyBuild is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with EasyBuild. If not, see <http://www.gnu.org/licenses/>.
# #
"""
Repository tools

Mercurial repository

@author: Stijn De Weirdt (Ghent University)
@author: Dries Verdegem (Ghent University)
@author: Kenneth Hoste (Ghent University)
@author: Pieter De Baets (Ghent University)
@author: Jens Timmerman (Ghent University)
@author: Toon Willems (Ghent University)
@author: Ward Poelmans (Ghent University)
@author: Fotis Georgatos (University of Luxembourg)
@author: Cedric Clerget (University of Franche-Comte)
"""
import getpass
import socket
import tempfile
import time
from vsc.utils import fancylogger

from easybuild.tools.filetools import rmtree2
from easybuild.tools.repository.filerepo import FileRepository

_log = fancylogger.getLogger('hgrepo', fname=False)

# optional Python packages, these might be missing
# failing imports are just ignored
# a NameError should be catched where these are used

# python-hglib
try:
import hglib
from hglib.error import CapabilityError as HgCapabilityError
from hglib.error import CommandError as HgCommandError
from hglib.error import ResponseError as HgResponseError
from hglib.error import ServerError as HgServerError
HAVE_HG = True
except ImportError:
_log.debug('Failed to import hglib module')
HAVE_HG = False


class HgRepository(FileRepository):
"""
Class for hg repositories.
"""
DESCRIPTION = ("A non-empty mercurial repository (created with 'hg init' or 'hg clone'). "
"The 1st argument contains the mercurial repository location, which can be a directory or an URL. "
"The 2nd argument is ignored.")

USABLE = HAVE_HG

def __init__(self, *args):
"""
Initialize mercurial client to None (will be set later)
All the real logic is in the setup_repo and create_working_copy methods
"""
self.client = None
FileRepository.__init__(self, *args)

def setup_repo(self):
"""
Set up mercurial repository.
"""
if not HAVE_HG:
self.log.error("The python-hglib Python module is not available, which is required for Mercurial support.")

self.wc = tempfile.mkdtemp(prefix='hg-wc-')

def create_working_copy(self):
"""
Create mercurial working copy.
"""

# try to get a copy of
try:
client = hglib.clone(self.repo, self.wc)
self.log.debug("repo %s cloned in %s" % (self.repo, self.wc))
except (HgCommandError, OSError), err:
# it might already have existed
self.log.warning("Mercurial local repo initialization failed, it might already exist: %s" % err)

# local repo should now exist, let's connect to it again
try:
self.log.debug("connection to mercurial repo in %s" % self.wc)
self.client = hglib.open(self.wc)
except HgServerError, err:
self.log.error("Could not connect to local mercurial repo: %s" % err)
except (HgCapabilityError, HgResponseError), err:
self.log.error("Server response: %s", err)
except (OSError, ValueError), err:
self.log.error("Could not create a local mercurial repo in wc %s: %s" % (self.wc, err))

# try to get the remote data in the local repo
try:
self.client.pull()
self.log.debug("pulled succesfully in %s" % self.wc)
except (HgCommandError, HgServerError, HgResponseError, OSError, ValueError), err:
self.log.error("pull in working copy %s went wrong: %s" % (self.wc, err))

def add_easyconfig(self, cfg, name, version, stats, append):
"""
Add easyconfig to mercurial repository.
"""
dest = FileRepository.add_easyconfig(self, cfg, name, version, stats, append)
# add it to version control
if dest:
try:
self.client.add(dest)
except (HgCommandError, HgServerError, HgResponseError, ValueError), err:
self.log.warning("adding %s to mercurial repository failed: %s" % (dest, err))

def commit(self, msg=None):
"""
Commit working copy to mercurial repository
"""
user = getpass.getuser()
self.log.debug("%s committing in mercurial repository: %s" % (user, msg))
tup = (socket.gethostname(), time.strftime("%Y-%m-%d_%H-%M-%S"), user, msg)
completemsg = "EasyBuild-commit from %s (time: %s, user: %s) \n%s" % tup

self.log.debug("hg status: %s" % self.client.status())
try:
self.client.commit('"%s"' % completemsg, user=user)
self.log.debug("succesfull commit")
except (HgCommandError, HgServerError, HgResponseError, ValueError), err:
self.log.warning("Commit from working copy %s (msg: %s) failed, empty commit?\n%s" % (self.wc, msg, err))
try:
if self.client.push():
info = "pushed"
else:
info = "nothing to push"
self.log.debug("push info: %s " % info)
except (HgCommandError, HgServerError, HgResponseError, ValueError), err:
tup = (self.wc, self.repo, msg, err)
self.log.warning("Push from working copy %s to remote %s (msg: %s) failed: %s" % tup)

def cleanup(self):
"""
Clean up mercurial working copy.
"""
try:
rmtree2(self.wc)
except IOError, err:
self.log.error("Can't remove working copy %s: %s" % (self.wc, err))
2 changes: 1 addition & 1 deletion easybuild/tools/repository/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def __init__(self, repo_path, subdir=''):
"""
Initialize a repository. self.repo and self.subdir will be set.
self.wc will be set to None.
Then, setupRepo and createWorkingCopy will be called (in that order)
Then, setup_repo and create_working_copy will be called (in that order)
"""
self.log = fancylogger.getLogger(self.__class__.__name__, fname=False)
self.subdir = subdir
Expand Down
30 changes: 14 additions & 16 deletions easybuild/tools/repository/svnrepo.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class SvnRepository(FileRepository):

def __init__(self, *args):
"""
Set self.client to None. Real logic is in setupRepo and createWorkingCopy
Set self.client to None. Real logic is in setup_repo and create_working_copy
"""
self.client = None
FileRepository.__init__(self, *args)
Expand All @@ -85,25 +85,23 @@ def setup_repo(self):
Set up SVN repository.
"""
self.repo = os.path.join(self.repo, self.subdir)
try:
pysvn.ClientError # IGNORE:E0611 pysvn fails to recognize ClientError is available
except NameError, err:
self.log.exception("pysvn not available (%s). Make sure it is installed " % err +
"properly. Run 'python -c \"import pysvn\"' to test.")
if not HAVE_PYSVN:
self.log.error("pysvn not available. Make sure it is installed properly. " +
"Run 'python -c \"import pysvn\"' to test.")

# try to connect to the repository
self.log.debug("Try to connect to repository %s" % self.repo)
try:
self.client = pysvn.Client()
self.client.exception_style = 0
except ClientError:
self.log.exception("Svn Client initialization failed.")
self.log.error("Svn Client initialization failed.")

try:
if not self.client.is_url(self.repo):
self.log.error("Provided repository %s is not a valid svn url" % self.repo)
except ClientError:
self.log.exception("Can't connect to svn repository %s" % self.repo)
self.log.error("Can't connect to svn repository %s" % self.repo)

def create_working_copy(self):
"""
Expand All @@ -116,13 +114,13 @@ def create_working_copy(self):
try:
self.client.info2(self.repo, recurse=False)
except ClientError:
self.log.exception("Getting info from %s failed." % self.wc)
self.log.error("Getting info from %s failed." % self.wc)

try:
res = self.client.update(self.wc)
self.log.debug("Updated to revision %s in %s" % (res, self.wc))
except ClientError:
self.log.exception("Update in wc %s went wrong" % self.wc)
self.log.error("Update in wc %s went wrong" % self.wc)

if len(res) == 0:
self.log.error("Update returned empy list (working copy: %s)" % (self.wc))
Expand All @@ -134,7 +132,7 @@ def create_working_copy(self):
res = self.client.checkout(self.repo, self.wc)
self.log.debug("Checked out revision %s in %s" % (res.number, self.wc))
except ClientError, err:
self.log.exception("Checkout of path / in working copy %s went wrong: %s" % (self.wc, err))
self.log.error("Checkout of path / in working copy %s went wrong: %s" % (self.wc, err))

def add_easyconfig(self, cfg, name, version, stats, append):
"""
Expand All @@ -154,13 +152,13 @@ def commit(self, msg=None):
"""
Commit working copy to SVN repository
"""
completemsg = "EasyBuild-commit from %s (time: %s, user: %s) \n%s" % (socket.gethostname(),
time.strftime("%Y-%m-%d_%H-%M-%S"),
getpass.getuser(), msg)
tup = (socket.gethostname(), time.strftime("%Y-%m-%d_%H-%M-%S"), getpass.getuser(), msg)
completemsg = "EasyBuild-commit from %s (time: %s, user: %s) \n%s" % tup

try:
self.client.checkin(self.wc, completemsg, recurse=True)
except ClientError, err:
self.log.exception("Commit from working copy %s (msg: %s) failed: %s" % (self.wc, msg, err))
self.log.error("Commit from working copy %s (msg: %s) failed: %s" % (self.wc, msg, err))

def cleanup(self):
"""
Expand All @@ -169,4 +167,4 @@ def cleanup(self):
try:
rmtree2(self.wc)
except OSError, err:
self.log.exception("Can't remove working copy %s: %s" % (self.wc, err))
self.log.error("Can't remove working copy %s: %s" % (self.wc, err))