blob: ee34980f122464ab641250e1c4f6b518751a7c55 [file] [log] [blame]
mbligh798d6512007-12-04 22:45:42 +00001#
2# Copyright 2007 IBM Corp. Released under the GPL v2
3# Authors: Ryan Harper <ryanh@us.ibm.com>
4#
5
6"""
7This module defines a class for handling building from git repos
8"""
9
10__author__ = """
11ryanh@us.ibm.com (Ryan Harper)
12"""
13
14
mbligh02ff2d52008-06-03 15:00:21 +000015import os
mbligh313f12c2008-05-15 23:33:50 +000016from autotest_lib.client.common_lib import error
mblighccb9e182008-04-17 15:42:10 +000017from autotest_lib.server import utils, installable_object
mblighccb9e182008-04-17 15:42:10 +000018
mbligh798d6512007-12-04 22:45:42 +000019
20class GitRepo(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000021 """
22 This class represents a git repo.
mbligh798d6512007-12-04 22:45:42 +000023
jadmanski0afbb632008-06-06 21:10:57 +000024 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.
mbligh798d6512007-12-04 22:45:42 +000027
jadmanski0afbb632008-06-06 21:10:57 +000028 """
mbligh798d6512007-12-04 22:45:42 +000029
jadmanski0afbb632008-06-06 21:10:57 +000030 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)
jadmanskif7ca76d2008-06-12 23:22:56 +000035 self.repodir = utils.sh_escape(repodir)
jadmanski0afbb632008-06-06 21:10:57 +000036 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
mbligh798d6512007-12-04 22:45:42 +000042
jadmanski0afbb632008-06-06 21:10:57 +000043 # path to .git dir
44 self.gitpath = utils.sh_escape(os.path.join(self.repodir,'.git'))
mbligh798d6512007-12-04 22:45:42 +000045
jadmanski0afbb632008-06-06 21:10:57 +000046 # base git command , pointing to gitpath git dir
47 self.gitcmdbase = 'git --git-dir=%s' % self.gitpath
mbligh798d6512007-12-04 22:45:42 +000048
jadmanski0afbb632008-06-06 21:10:57 +000049 # default to same remote path as local
50 self.__build = os.path.dirname(self.repodir)
mbligh798d6512007-12-04 22:45:42 +000051
52
jadmanski0afbb632008-06-06 21:10:57 +000053 def run(self, command, timeout=None, ignore_status=False):
54 return utils.run(r'%s' % (utils.sh_escape(command)),
55 timeout, ignore_status)
mbligh798d6512007-12-04 22:45:42 +000056
57
jadmanski0afbb632008-06-06 21:10:57 +000058 # base install method
59 def install(self, host, builddir=None):
60 # allow override of target remote dir
61 if builddir:
62 self.__build = builddir
mbligh798d6512007-12-04 22:45:42 +000063
jadmanski0afbb632008-06-06 21:10:57 +000064 # 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)
mbligh798d6512007-12-04 22:45:42 +000067
68
jadmanski0afbb632008-06-06 21:10:57 +000069 def gitcmd(self, cmd, ignore_status=False):
70 return self.run('%s %s'%(self.gitcmdbase, cmd),
71 ignore_status=ignore_status)
mbligh798d6512007-12-04 22:45:42 +000072
73
jadmanski0afbb632008-06-06 21:10:57 +000074 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 """
mbligh798d6512007-12-04 22:45:42 +000082
jadmanski0afbb632008-06-06 21:10:57 +000083 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
mbligh798d6512007-12-04 22:45:42 +000093
jadmanski0afbb632008-06-06 21:10:57 +000094 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'
mbligh798d6512007-12-04 22:45:42 +0000105
106
jadmanski0afbb632008-06-06 21:10:57 +0000107 # remember where the source is
108 self.source_material = self.repodir
mbligh798d6512007-12-04 22:45:42 +0000109
110
jadmanski0afbb632008-06-06 21:10:57 +0000111 def get_local_head(self):
112 cmd = 'log --max-count=1'
113 gitlog = self.gitcmd(cmd).stdout
mbligh798d6512007-12-04 22:45:42 +0000114
jadmanski0afbb632008-06-06 21:10:57 +0000115 # 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]
mbligh798d6512007-12-04 22:45:42 +0000125
126
jadmanski0afbb632008-06-06 21:10:57 +0000127 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
mbligh798d6512007-12-04 22:45:42 +0000132
jadmanski0afbb632008-06-06 21:10:57 +0000133 return False
mbligh798d6512007-12-04 22:45:42 +0000134
135
jadmanski0afbb632008-06-06 21:10:57 +0000136 # 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
mbligh798d6512007-12-04 22:45:42 +0000141
jadmanski0afbb632008-06-06 21:10:57 +0000142 print 'checking %s for changes' %(url)
143 u = utils.urlopen(url)
144 lines = u.read().split('\n')
mbligh798d6512007-12-04 22:45:42 +0000145
jadmanski0afbb632008-06-06 21:10:57 +0000146 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')
mbligh798d6512007-12-04 22:45:42 +0000151
jadmanski0afbb632008-06-06 21:10:57 +0000152 if r >= max_refresh:
153 e_msg = 'Failed to get remote repo status, refreshed %s times' % r
154 raise IndexError(e_msg)
mbligh798d6512007-12-04 22:45:42 +0000155
jadmanski0afbb632008-06-06 21:10:57 +0000156 # 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)
mbligh798d6512007-12-04 22:45:42 +0000160
jadmanski0afbb632008-06-06 21:10:57 +0000161 # extract the sha1 sum from the commit line
162 return str(commit_line).split('>')[4].split('<')[0]
mbligh798d6512007-12-04 22:45:42 +0000163
164
jadmanski0afbb632008-06-06 21:10:57 +0000165 def is_out_of_date(self):
166 local_head = self.get_local_head()
167 remote_head = self.get_remote_head()
mbligh798d6512007-12-04 22:45:42 +0000168
jadmanski0afbb632008-06-06 21:10:57 +0000169 # local is out-of-date, pull
170 if local_head != remote_head:
171 return True
mbligh798d6512007-12-04 22:45:42 +0000172
jadmanski0afbb632008-06-06 21:10:57 +0000173 return False
mbligh798d6512007-12-04 22:45:42 +0000174
175
jadmanski0afbb632008-06-06 21:10:57 +0000176 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
mbligh798d6512007-12-04 22:45:42 +0000179
jadmanski0afbb632008-06-06 21:10:57 +0000180 cmd = 'log --max-count=1'
181 rv = self.gitcmd(cmd, True)
182 if rv.exit_status == 0:
183 return True
mbligh798d6512007-12-04 22:45:42 +0000184
jadmanski0afbb632008-06-06 21:10:57 +0000185 return False