Introduce a library with classes to abstract interaction
with version control system repositories. Right now, there is
only GitRepo implemented. Made server/git.py to use the
common_lib implementation (the server git class represents
the install of a git repo in a host).

Now, test writers and can use a shared API to check out and
update git code conveniently.

Signed-off-by: Lucas Meneghel Rodrigues <[email protected]>
---
 client/common_lib/revision_control.py |  232 +++++++++++++++++++++++++++++++++
 server/git.py                         |  150 +++-------------------
 server/git_kernel.py                  |    5 +-
 3 files changed, 252 insertions(+), 135 deletions(-)
 create mode 100644 client/common_lib/revision_control.py

diff --git a/client/common_lib/revision_control.py 
b/client/common_lib/revision_control.py
new file mode 100644
index 0000000..79adfff
--- /dev/null
+++ b/client/common_lib/revision_control.py
@@ -0,0 +1,232 @@
+"""
+Module with abstraction layers to revision control systems.
+
+With this library, autotest developers can handle source code checkouts and
+updates on both client as well as server code.
+"""
+
+import os, warnings, logging
+import error, utils
+from autotest_lib.client.bin import os_dep
+
+
+class GitRepo(object):
+    """
+    This class represents a git repo.
+
+    It is used to pull down a local copy of a git repo, check if the local
+    repo is up-to-date, if not update.  It delegates the install to
+    implementation classes.
+    """
+
+    def __init__(self, repodir, giturl, weburl=None):
+        if repodir is None:
+            raise ValueError('You must provide a path that will hold the'
+                             'git repository')
+        self.repodir = utils.sh_escape(repodir)
+        if giturl is None:
+            raise ValueError('You must provide a git URL repository')
+        self.giturl = giturl
+        if weburl is not None:
+            warnings.warn("Param weburl: You are no longer required to provide 
"
+                          "a web URL for your git repos", DeprecationWarning)
+
+        # path to .git dir
+        self.gitpath = utils.sh_escape(os.path.join(self.repodir,'.git'))
+
+        # Find git base command. If not found, this will throw an exception
+        git_base_cmd = os_dep.command('git')
+
+        # base git command , pointing to gitpath git dir
+        self.gitcmdbase = '%s --git-dir=%s' % (git_base_cmd, self.gitpath)
+
+        # default to same remote path as local
+        self._build = os.path.dirname(self.repodir)
+
+
+    def _run(self, command, timeout=None, ignore_status=False):
+        """
+        Auxiliary function to run a command, with proper shell escaping.
+
+        @param timeout: Timeout to run the command.
+        @param ignore_status: Whether we should supress error.CmdError
+                exceptions if the command did return exit code !=0 (True), or
+                not supress them (False).
+        """
+        return utils.run(r'%s' % (utils.sh_escape(command)),
+                         timeout, ignore_status)
+
+
+    def gitcmd(self, cmd, ignore_status=False):
+        """
+        Wrapper for a git command.
+
+        @param cmd: Git subcommand (ex 'clone').
+        @param ignore_status: Whether we should supress error.CmdError
+                exceptions if the command did return exit code !=0 (True), or
+                not supress them (False).
+        """
+        cmd = '%s %s' % (self.gitcmdbase, cmd)
+        return self._run(cmd, ignore_status=ignore_status)
+
+
+    def get(self, **kwargs):
+        """
+        This method overrides baseclass get so we can do proper git
+        clone/pulls, and check for updated versions.  The result of
+        this method will leave an up-to-date version of git repo at
+        'giturl' in 'repodir' directory to be used by build/install
+        methods.
+
+        @param **kwargs: Dictionary of parameters to the method get.
+        """
+        if not self.is_repo_initialized():
+            # this is your first time ...
+            logging.info('Cloning git repo %s', self.giturl)
+            cmd = 'clone %s %s ' % (self.giturl, self.repodir)
+            rv = self.gitcmd(cmd, True)
+            if rv.exit_status != 0:
+                logging.error(rv.stderr)
+                raise error.CmdError('Failed to clone git url', rv)
+            else:
+                logging.info(rv.stdout)
+
+        else:
+            # exiting repo, check if we're up-to-date
+            if self.is_out_of_date():
+                logging.info('Updating git repo %s', self.giturl)
+                rv = self.gitcmd('pull', True)
+                if rv.exit_status != 0:
+                    logging.error(rv.stderr)
+                    e_msg = 'Failed to pull git repo data'
+                    raise error.CmdError(e_msg, rv)
+            else:
+                logging.info('repo up-to-date')
+
+        # remember where the source is
+        self.source_material = self.repodir
+
+
+    def get_local_head(self):
+        """
+        Get the top commit hash of the current local git branch.
+
+        @return: Top commit hash of local git branch
+        """
+        cmd = 'log --pretty=format:"%H" -1'
+        l_head_cmd = self.gitcmd(cmd)
+        return l_head_cmd.stdout
+
+
+    def get_remote_head(self):
+        """
+        Get the top commit hash of the current remote git branch.
+
+        @return: Top commit hash of remote git branch
+        """
+        cmd1 = 'remote show'
+        origin_name_cmd = self.gitcmd(cmd1)
+        cmd2 = 'log --pretty=format:"%H" -1 ' + origin_name_cmd.stdout
+        r_head_cmd = self.gitcmd(cmd2)
+        return r_head_cmd.stdout
+
+
+    def is_out_of_date(self):
+        """
+        Return whether this branch is out of date with regards to remote 
branch.
+
+        @return: False, if the branch is outdated, True if it is current.
+        """
+        local_head = self.get_local_head()
+        remote_head = self.get_remote_head()
+
+        # local is out-of-date, pull
+        if local_head != remote_head:
+            return True
+
+        return False
+
+
+    def is_repo_initialized(self):
+        """
+        Return whether the git repo was already initialized (has a top commit).
+
+        @return: False, if the repo was initialized, True if it was not.
+        """
+        cmd = 'log --max-count=1'
+        rv = self.gitcmd(cmd, True)
+        if rv.exit_status == 0:
+            return True
+
+        return False
+
+
+    def get_revision(self):
+        """
+        Return current HEAD commit id
+        """
+        if not self.is_repo_initialized():
+            self.get()
+
+        cmd = 'rev-parse --verify HEAD'
+        gitlog = self.gitcmd(cmd, True)
+        if gitlog.exit_status != 0:
+            logging.error(gitlog.stderr)
+            raise error.CmdError('Failed to find git sha1 revision', gitlog)
+        else:
+            return gitlog.stdout.strip('\n')
+
+
+    def checkout(self, remote, local=None):
+        """
+        Check out the git commit id, branch, or tag given by remote.
+
+        Optional give the local branch name as local.
+
+        @param remote: Remote commit hash
+        @param local: Local commit hash
+        @note: For git checkout tag git version >= 1.5.0 is required
+        """
+        if not self.is_repo_initialized():
+            self.get()
+
+        assert(isinstance(remote, basestring))
+        if local:
+            cmd = 'checkout -b %s %s' % (local, remote)
+        else:
+            cmd = 'checkout %s' % (remote)
+        gitlog = self.gitcmd(cmd, True)
+        if gitlog.exit_status != 0:
+            logging.error(gitlog.stderr)
+            raise error.CmdError('Failed to checkout git branch', gitlog)
+        else:
+            logging.info(gitlog.stdout)
+
+
+    def get_branch(self, all=False, remote_tracking=False):
+        """
+        Show the branches.
+
+        @param all: List both remote-tracking branches and local branches 
(True)
+                or only the local ones (False).
+        @param remote_tracking: Lists the remote-tracking branches.
+        """
+        if not self.is_repo_initialized():
+            self.get()
+
+        cmd = 'branch --no-color'
+        if all:
+            cmd = " ".join([cmd, "-a"])
+        if remote_tracking:
+            cmd = " ".join([cmd, "-r"])
+
+        gitlog = self.gitcmd(cmd, True)
+        if gitlog.exit_status != 0:
+            logging.error(gitlog.stderr)
+            raise error.CmdError('Failed to get git branch', gitlog)
+        elif all or remote_tracking:
+            return gitlog.stdout.strip('\n')
+        else:
+            branch = [b[2:] for b in gitlog.stdout.split('\n')
+                      if b.startswith('*')][0]
+            return branch
diff --git a/server/git.py b/server/git.py
index b974668..0428b80 100644
--- a/server/git.py
+++ b/server/git.py
@@ -6,63 +6,29 @@ This module defines a class for handling building from git 
repos
 """
 
 import os, warnings, logging
-from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib import error, revision_control
 from autotest_lib.client.bin import os_dep
 from autotest_lib.server import utils, installable_object
 
 
-class GitRepo(installable_object.InstallableObject):
+class InstallableGitRepo(installable_object.InstallableObject):
     """
-    This class represents a git repo.
-
-    It is used to pull down a local copy of a git repo, check if the local
-    repo is up-to-date, if not update.  It delegates the install to
-    implementation classes.
+    This class helps to pick a git repo and install it in a host.
     """
-
     def __init__(self, repodir, giturl, weburl=None):
-        super(installable_object.InstallableObject, self).__init__()
-        if repodir is None:
-            raise ValueError('You must provide a path that will hold the'
-                             'git repository')
-        self.repodir = utils.sh_escape(repodir)
-        if giturl is None:
-            raise ValueError('You must provide a git URL repository')
+        self.repodir = repodir
         self.giturl = giturl
-        if weburl is not None:
-            warnings.warn("Param weburl: You are no longer required to provide 
"
-                          "a web URL for your git repos", DeprecationWarning)
-
-        # path to .git dir
-        self.gitpath = utils.sh_escape(os.path.join(self.repodir,'.git'))
-
-        # Find git base command. If not found, this will throw an exception
-        git_base_cmd = os_dep.command('git')
-
-        # base git command , pointing to gitpath git dir
-        self.gitcmdbase = '%s --git-dir=%s' % (git_base_cmd, self.gitpath)
-
+        self.weburl = weburl
+        self.git_repo = revision_control.GitRepo(self.repodir, self.giturl,
+                                                 self.weburl)
         # default to same remote path as local
         self._build = os.path.dirname(self.repodir)
 
 
-    def _run(self, command, timeout=None, ignore_status=False):
-        """
-        Auxiliary function to run a command, with proper shell escaping.
-
-        @param timeout: Timeout to run the command.
-        @param ignore_status: Whether we should supress error.CmdError
-                exceptions if the command did return exit code !=0 (True), or
-                not supress them (False).
-        """
-        return utils.run(r'%s' % (utils.sh_escape(command)),
-                         timeout, ignore_status)
-
-
     # base install method
     def install(self, host, builddir=None):
         """
-        Install a git repo on a host. It works by pushing the downloaded source
+        Install a git repo in a host. It works by pushing the downloaded source
         code to the host.
 
         @param host: Host object.
@@ -88,8 +54,7 @@ class GitRepo(installable_object.InstallableObject):
                 exceptions if the command did return exit code !=0 (True), or
                 not supress them (False).
         """
-        cmd = '%s %s' % (self.gitcmdbase, cmd)
-        return self._run(cmd, ignore_status=ignore_status)
+        return self.git_repo.gitcmd(cmd, ignore_status)
 
 
     def get(self, **kwargs):
@@ -102,31 +67,8 @@ class GitRepo(installable_object.InstallableObject):
 
         @param **kwargs: Dictionary of parameters to the method get.
         """
-        if not self.is_repo_initialized():
-            # this is your first time ...
-            logging.info('Cloning git repo %s', self.giturl)
-            cmd = 'clone %s %s ' % (self.giturl, self.repodir)
-            rv = self.gitcmd(cmd, True)
-            if rv.exit_status != 0:
-                print rv.stderr
-                raise error.CmdError('Failed to clone git url', rv)
-            else:
-                print rv.stdout
-
-        else:
-            # exiting repo, check if we're up-to-date
-            if self.is_out_of_date():
-                logging.info('Updating git repo %s', self.giturl)
-                rv = self.gitcmd('pull', True)
-                if rv.exit_status != 0:
-                    print rv.stderr
-                    e_msg = 'Failed to pull git repo data'
-                    raise error.CmdError(e_msg, rv)
-            else:
-                print 'repo up-to-date'
-
-        # remember where the source is
         self.source_material = self.repodir
+        return self.git_repo.get(**kwargs)
 
 
     def get_local_head(self):
@@ -135,9 +77,7 @@ class GitRepo(installable_object.InstallableObject):
 
         @return: Top commit hash of local git branch
         """
-        cmd = 'log --pretty=format:"%H" -1'
-        l_head_cmd = self.gitcmd(cmd)
-        return l_head_cmd.stdout
+        return self.git_repo.get_local_head()
 
 
     def get_remote_head(self):
@@ -146,11 +86,7 @@ class GitRepo(installable_object.InstallableObject):
 
         @return: Top commit hash of remote git branch
         """
-        cmd1 = 'remote show'
-        origin_name_cmd = self.gitcmd(cmd1)
-        cmd2 = 'log --pretty=format:"%H" -1 ' + origin_name_cmd.stdout
-        r_head_cmd = self.gitcmd(cmd2)
-        return r_head_cmd.stdout
+        return self.git_repo.get_remote_head()
 
 
     def is_out_of_date(self):
@@ -159,14 +95,7 @@ class GitRepo(installable_object.InstallableObject):
 
         @return: False, if the branch is outdated, True if it is current.
         """
-        local_head = self.get_local_head()
-        remote_head = self.get_remote_head()
-
-        # local is out-of-date, pull
-        if local_head != remote_head:
-            return True
-
-        return False
+        return self.git_repo.is_out_of_date()
 
 
     def is_repo_initialized(self):
@@ -175,28 +104,14 @@ class GitRepo(installable_object.InstallableObject):
 
         @return: False, if the repo was initialized, True if it was not.
         """
-        cmd = 'log --max-count=1'
-        rv = self.gitcmd(cmd, True)
-        if rv.exit_status == 0:
-            return True
-
-        return False
+        return self.git_repo.is_repo_initialized()
 
 
     def get_revision(self):
         """
         Return current HEAD commit id
         """
-        if not self.is_repo_initialized():
-            self.get()
-
-        cmd = 'rev-parse --verify HEAD'
-        gitlog = self.gitcmd(cmd, True)
-        if gitlog.exit_status != 0:
-            print gitlog.stderr
-            raise error.CmdError('Failed to find git sha1 revision', gitlog)
-        else:
-            return gitlog.stdout.strip('\n')
+        return self.git_repo.get_revision()
 
 
     def checkout(self, remote, local=None):
@@ -209,20 +124,7 @@ class GitRepo(installable_object.InstallableObject):
         @param local: Local commit hash
         @note: For git checkout tag git version >= 1.5.0 is required
         """
-        if not self.is_repo_initialized():
-            self.get()
-
-        assert(isinstance(remote, basestring))
-        if local:
-            cmd = 'checkout -b %s %s' % (local, remote)
-        else:
-            cmd = 'checkout %s' % (remote)
-        gitlog = self.gitcmd(cmd, True)
-        if gitlog.exit_status != 0:
-            print gitlog.stderr
-            raise error.CmdError('Failed to checkout git branch', gitlog)
-        else:
-            print gitlog.stdout
+        return self.git_repo.checkout(remote, local)
 
 
     def get_branch(self, all=False, remote_tracking=False):
@@ -233,22 +135,4 @@ class GitRepo(installable_object.InstallableObject):
                 or only the local ones (False).
         @param remote_tracking: Lists the remote-tracking branches.
         """
-        if not self.is_repo_initialized():
-            self.get()
-
-        cmd = 'branch --no-color'
-        if all:
-            cmd = " ".join([cmd, "-a"])
-        if remote_tracking:
-            cmd = " ".join([cmd, "-r"])
-
-        gitlog = self.gitcmd(cmd, True)
-        if gitlog.exit_status != 0:
-            print gitlog.stderr
-            raise error.CmdError('Failed to get git branch', gitlog)
-        elif all or remote_tracking:
-            return gitlog.stdout.strip('\n')
-        else:
-            branch = [b[2:] for b in gitlog.stdout.split('\n')
-                      if b.startswith('*')][0]
-            return branch
+        return self.git_repo.get_branch(all, remote_tracking)
diff --git a/server/git_kernel.py b/server/git_kernel.py
index 8a3ea8f..015e3c0 100644
--- a/server/git_kernel.py
+++ b/server/git_kernel.py
@@ -9,9 +9,9 @@ import os, logging
 import git, source_kernel
 
 
-class GitKernel(git.GitRepo):
+class GitKernel(git.InstallableGitRepo):
     """
-    This class represents a git kernel repo.
+    This class represents an installable git kernel repo.
 
     It is used to pull down a local copy of a git repo, check if the local repo
     is up-to-date, if not update and then build the kernel from the git repo.
@@ -47,6 +47,7 @@ class GitKernel(git.GitRepo):
         logging.info('Checked out %s on branch %s', self._revision,
                      self._branch)
 
+
     def show_branch(self):
         """
         Print the current local branch name.
-- 
1.7.2.2

_______________________________________________
Autotest mailing list
[email protected]
http://test.kernel.org/cgi-bin/mailman/listinfo/autotest

Reply via email to