One alternative would be to make InstallableGitRepo a subclass of GitRepo,
using inheritance instead of delegation. But I'm not sure that's ideal; the
behavior is a lot more obvious with explicit delegation, instead of using
multiple inheritance.

-- John

On Tue, Sep 14, 2010 at 10:58 AM, Lucas Meneghel Rodrigues
<[email protected]>wrote:

> 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