mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 1 | # |
| 2 | # Copyright 2007 IBM Corp. Released under the GPL v2 |
| 3 | # Authors: Ryan Harper <ryanh@us.ibm.com> |
| 4 | # |
| 5 | |
| 6 | """ |
| 7 | This module defines a class for handling building from git repos |
| 8 | """ |
| 9 | |
| 10 | __author__ = """ |
| 11 | ryanh@us.ibm.com (Ryan Harper) |
| 12 | """ |
| 13 | |
| 14 | |
mbligh | 02ff2d5 | 2008-06-03 15:00:21 +0000 | [diff] [blame] | 15 | import os |
mbligh | 313f12c | 2008-05-15 23:33:50 +0000 | [diff] [blame] | 16 | from autotest_lib.client.common_lib import error |
mbligh | ccb9e18 | 2008-04-17 15:42:10 +0000 | [diff] [blame] | 17 | from autotest_lib.server import utils, installable_object |
mbligh | ccb9e18 | 2008-04-17 15:42:10 +0000 | [diff] [blame] | 18 | |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 19 | |
| 20 | class GitRepo(installable_object.InstallableObject): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 21 | """ |
| 22 | This class represents a git repo. |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 23 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 24 | It is used to pull down a local copy of a git repo, check if the local |
| 25 | repo is up-to-date, if not update. It delegates the install to |
| 26 | implementation classes. |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 27 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 28 | """ |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 29 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 30 | def __init__(self, repodir, giturl, weburl): |
| 31 | super(installable_object.InstallableObject, self).__init__() |
| 32 | if repodir == None: |
| 33 | e_msg = 'You must provide a directory to hold the git repository' |
| 34 | raise ValueError(e_msg) |
jadmanski | f7ca76d | 2008-06-12 23:22:56 +0000 | [diff] [blame] | 35 | self.repodir = utils.sh_escape(repodir) |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 36 | if giturl == None: |
| 37 | raise ValueError('You must provide a git URL to the repository') |
| 38 | self.giturl = giturl |
| 39 | if weburl == None: |
| 40 | raise ValueError('You must provide a http URL to the repository') |
| 41 | self.weburl = weburl |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 42 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 43 | # path to .git dir |
| 44 | self.gitpath = utils.sh_escape(os.path.join(self.repodir,'.git')) |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 45 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 46 | # base git command , pointing to gitpath git dir |
| 47 | self.gitcmdbase = 'git --git-dir=%s' % self.gitpath |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 48 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 49 | # default to same remote path as local |
| 50 | self.__build = os.path.dirname(self.repodir) |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 51 | |
| 52 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 53 | def run(self, command, timeout=None, ignore_status=False): |
| 54 | return utils.run(r'%s' % (utils.sh_escape(command)), |
| 55 | timeout, ignore_status) |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 56 | |
| 57 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 58 | # base install method |
| 59 | def install(self, host, builddir=None): |
| 60 | # allow override of target remote dir |
| 61 | if builddir: |
| 62 | self.__build = builddir |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 63 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 64 | # push source to host for install |
| 65 | print 'pushing %s to host:%s' %(self.source_material, self.__build) |
| 66 | host.send_file(self.source_material, self.__build) |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 67 | |
| 68 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 69 | def gitcmd(self, cmd, ignore_status=False): |
| 70 | return self.run('%s %s'%(self.gitcmdbase, cmd), |
| 71 | ignore_status=ignore_status) |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 72 | |
| 73 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 74 | def get(self, **kwargs): |
| 75 | """ |
| 76 | This method overrides baseclass get so we can do proper git |
| 77 | clone/pulls, and check for updated versions. The result of |
| 78 | this method will leave an up-to-date version of git repo at |
| 79 | 'giturl' in 'repodir' directory to be used by build/install |
| 80 | methods. |
| 81 | """ |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 82 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 83 | if not self.is_repo_initialized(): |
| 84 | # this is your first time ... |
| 85 | print 'cloning repo...' |
| 86 | cmd = 'clone %s %s ' %(self.giturl, self.repodir) |
| 87 | rv = self.gitcmd(cmd, True) |
| 88 | if rv.exit_status != 0: |
| 89 | print rv.stderr |
| 90 | raise error.CmdError('Failed to clone git url', rv) |
| 91 | else: |
| 92 | print rv.stdout |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 93 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 94 | else: |
| 95 | # exiting repo, check if we're up-to-date |
| 96 | if self.is_out_of_date(): |
| 97 | print 'updating repo...' |
| 98 | rv = self.gitcmd('pull', True) |
| 99 | if rv.exit_status != 0: |
| 100 | print rv.stderr |
| 101 | e_msg = 'Failed to pull git repo data' |
| 102 | raise error.CmdError(e_msg, rv) |
| 103 | else: |
| 104 | print 'repo up-to-date' |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 105 | |
| 106 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 107 | # remember where the source is |
| 108 | self.source_material = self.repodir |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 109 | |
| 110 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 111 | def get_local_head(self): |
| 112 | cmd = 'log --max-count=1' |
| 113 | gitlog = self.gitcmd(cmd).stdout |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 114 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 115 | # parsing the commit checksum out of git log 's first entry. |
| 116 | # Output looks like: |
| 117 | # |
| 118 | # commit 1dccba29b4e5bf99fb98c324f952386dda5b097f |
| 119 | # Merge: 031b69b... df6af41... |
| 120 | # Author: Avi Kivity <avi@qumranet.com> |
| 121 | # Date: Tue Oct 23 10:36:11 2007 +0200 |
| 122 | # |
| 123 | # Merge home:/home/avi/kvm/linux-2.6 |
| 124 | return str(gitlog.split('\n')[0]).split()[1] |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 125 | |
| 126 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 127 | def get_remote_head(self): |
| 128 | def __needs_refresh(lines): |
| 129 | tag = '<meta http-equiv="refresh" content="0"/>' |
| 130 | if len(filter(lambda x: x.startswith(tag), lines)) > 0: |
| 131 | return True |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 132 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 133 | return False |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 134 | |
| 135 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 136 | # scan git web interface for revision HEAD's commit tag |
| 137 | gitwebaction=';a=commit;h=HEAD' |
| 138 | url = self.weburl+gitwebaction |
| 139 | max_refresh = 4 |
| 140 | r = 0 |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 141 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 142 | print 'checking %s for changes' %(url) |
| 143 | u = utils.urlopen(url) |
| 144 | lines = u.read().split('\n') |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 145 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 146 | while __needs_refresh(lines) and r < max_refresh: |
| 147 | print 'refreshing url' |
| 148 | r = r+1 |
| 149 | u = utils.urlopen(url) |
| 150 | lines = u.read().split('\n') |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 151 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 152 | if r >= max_refresh: |
| 153 | e_msg = 'Failed to get remote repo status, refreshed %s times' % r |
| 154 | raise IndexError(e_msg) |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 155 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 156 | # looking for a line like: |
| 157 | # <tr><td>commit</td><td # class="sha1">aadea67210c8b9e7a57744a1c2845501d2cdbac7</td></tr> |
| 158 | commit_filter = lambda x: x.startswith('<tr><td>commit</td>') |
| 159 | commit_line = filter(commit_filter, lines) |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 160 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 161 | # extract the sha1 sum from the commit line |
| 162 | return str(commit_line).split('>')[4].split('<')[0] |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 163 | |
| 164 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 165 | def is_out_of_date(self): |
| 166 | local_head = self.get_local_head() |
| 167 | remote_head = self.get_remote_head() |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 168 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 169 | # local is out-of-date, pull |
| 170 | if local_head != remote_head: |
| 171 | return True |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 172 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 173 | return False |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 174 | |
| 175 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 176 | def is_repo_initialized(self): |
| 177 | # if we fail to get a rv of 0 out of the git log command |
| 178 | # then the repo is bogus |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 179 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 180 | cmd = 'log --max-count=1' |
| 181 | rv = self.gitcmd(cmd, True) |
| 182 | if rv.exit_status == 0: |
| 183 | return True |
mbligh | 798d651 | 2007-12-04 22:45:42 +0000 | [diff] [blame] | 184 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 185 | return False |