blob: 93833ec1abe1cb275fda418e3af0568e60608d19 [file] [log] [blame] [edit]
#
# Copyright 2007 IBM Corp. Released under the GPL v2
# Authors: Ryan Harper <ryanh@us.ibm.com>
#
"""
This module defines a class for handling building from git repos
"""
__author__ = """
ryanh@us.ibm.com (Ryan Harper)
"""
import os
from autotest_lib.client.common_lib import error
from autotest_lib.server import utils, installable_object
class GitRepo(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.
"""
def __init__(self, repodir, giturl, weburl):
super(installable_object.InstallableObject, self).__init__()
if repodir is None:
e_msg = 'You must provide a directory to hold the git repository'
raise ValueError(e_msg)
self.repodir = utils.sh_escape(repodir)
if giturl is None:
raise ValueError('You must provide a git URL to the repository')
self.giturl = giturl
if weburl is None:
raise ValueError('You must provide a http URL to the repository')
self.weburl = weburl
# path to .git dir
self.gitpath = utils.sh_escape(os.path.join(self.repodir,'.git'))
# base git command , pointing to gitpath git dir
self.gitcmdbase = 'git --git-dir=%s' % 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):
return utils.run(r'%s' % (utils.sh_escape(command)),
timeout, ignore_status)
# base install method
def install(self, host, builddir=None):
# allow override of target remote dir
if builddir:
self.__build = builddir
# push source to host for install
print 'pushing %s to host:%s' %(self.source_material, self.__build)
host.send_file(self.source_material, self.__build)
def gitcmd(self, cmd, ignore_status=False):
return self.run('%s %s'%(self.gitcmdbase, 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.
"""
if not self.is_repo_initialized():
# this is your first time ...
print 'cloning repo...'
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():
print 'updating repo...'
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
def get_local_head(self):
cmd = 'log --max-count=1'
gitlog = self.gitcmd(cmd).stdout
# parsing the commit checksum out of git log 's first entry.
# Output looks like:
#
# commit 1dccba29b4e5bf99fb98c324f952386dda5b097f
# Merge: 031b69b... df6af41...
# Author: Avi Kivity <avi@qumranet.com>
# Date: Tue Oct 23 10:36:11 2007 +0200
#
# Merge home:/home/avi/kvm/linux-2.6
return str(gitlog.split('\n')[0]).split()[1]
def get_remote_head(self):
def __needs_refresh(lines):
tag = '<meta http-equiv="refresh" content="0"/>'
if len(filter(lambda x: x.startswith(tag), lines)) > 0:
return True
return False
# scan git web interface for revision HEAD's commit tag
gitwebaction=';a=commit;h=HEAD'
url = self.weburl+gitwebaction
max_refresh = 4
r = 0
print 'checking %s for changes' %(url)
u = utils.urlopen(url)
lines = u.read().split('\n')
while __needs_refresh(lines) and r < max_refresh:
print 'refreshing url'
r = r+1
u = utils.urlopen(url)
lines = u.read().split('\n')
if r >= max_refresh:
e_msg = 'Failed to get remote repo status, refreshed %s times' % r
raise IndexError(e_msg)
# looking for a line like:
# <tr><td>commit</td><td # class="sha1">aadea67210c8b9e7a57744a1c2845501d2cdbac7</td></tr>
commit_filter = lambda x: x.startswith('<tr><td>commit</td>')
commit_line = filter(commit_filter, lines)
# extract the sha1 sum from the commit line
return str(commit_line).split('>')[4].split('<')[0]
def is_out_of_date(self):
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):
# if we fail to get a rv of 0 out of the git log command
# then the repo is bogus
cmd = 'log --max-count=1'
rv = self.gitcmd(cmd, True)
if rv.exit_status == 0:
return True
return False