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
