|
# This file is part of Buildbot. Buildbot is free software: you can # redistribute it and/or modify it under the terms of the GNU General Public # License as published by the Free Software Foundation, version 2. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Copyright Buildbot Team Members
"""This source will poll a remote git repo for changes and submit them to the change master."""
"pollInterval", "gitbin", "usetimestamps", "category", "project"]
workdir=None, pollInterval=10*60, gitbin='git', usetimestamps=True, category=None, project=None, pollinterval=-2, fetch_refspec=None, encoding='utf-8'): # for backward compatibility; the parameter used to be spelled with 'i'
"'%s'; consider setting workdir=" % self.workdir)
# make our workdir absolute, relative to the master's basedir if not os.path.isabs(self.workdir): self.workdir = os.path.join(self.master.basedir, self.workdir) log.msg("gitpoller: using workdir '%s'" % self.workdir)
# initialize the repository we'll use to get changes; note that # startService is not an event-driven method, so this method will # instead acquire self.initLock immediately when it is called. if not os.path.exists(self.workdir + r'/.git'): d = self.initRepository() d.addErrback(log.err, 'while initializing GitPoller repository') else: log.msg("GitPoller repository already exists")
# call this *after* initRepository, so that the initLock is locked first base.PollingChangeSource.startService(self)
def initRepository(self): d = defer.succeed(None) def make_dir(_): dirpath = os.path.dirname(self.workdir.rstrip(os.sep)) if not os.path.exists(dirpath): log.msg('gitpoller: creating parent directories for workdir') os.makedirs(dirpath) d.addCallback(make_dir)
def git_init(_): log.msg('gitpoller: initializing working dir from %s' % self.repourl) d = utils.getProcessOutputAndValue(self.gitbin, ['init', self.workdir], env=os.environ) d.addCallback(self._convert_nonzero_to_failure) d.addErrback(self._stop_on_failure) return d d.addCallback(git_init)
def git_remote_add(_): d = utils.getProcessOutputAndValue(self.gitbin, ['remote', 'add', 'origin', self.repourl], path=self.workdir, env=os.environ) d.addCallback(self._convert_nonzero_to_failure) d.addErrback(self._stop_on_failure) return d d.addCallback(git_remote_add)
def git_fetch_origin(_): args = ['fetch', 'origin'] self._extend_with_fetch_refspec(args) d = utils.getProcessOutputAndValue(self.gitbin, args, path=self.workdir, env=os.environ) d.addCallback(self._convert_nonzero_to_failure) d.addErrback(self._stop_on_failure) return d d.addCallback(git_fetch_origin)
def set_master(_): log.msg('gitpoller: checking out %s' % self.branch) if self.branch == 'master': # repo is already on branch 'master', so reset d = utils.getProcessOutputAndValue(self.gitbin, ['reset', '--hard', 'origin/%s' % self.branch], path=self.workdir, env=os.environ) else: d = utils.getProcessOutputAndValue(self.gitbin, ['checkout', '-b', self.branch, 'origin/%s' % self.branch], path=self.workdir, env=os.environ) d.addCallback(self._convert_nonzero_to_failure) d.addErrback(self._stop_on_failure) return d d.addCallback(set_master) def get_rev(_): d = utils.getProcessOutputAndValue(self.gitbin, ['rev-parse', self.branch], path=self.workdir, env=os.environ) d.addCallback(self._convert_nonzero_to_failure) d.addErrback(self._stop_on_failure) d.addCallback(lambda (out, err, code) : out.strip()) return d d.addCallback(get_rev) def print_rev(rev): log.msg("gitpoller: finished initializing working dir from %s at rev %s" % (self.repourl, rev)) d.addCallback(print_rev) return d
status = "[STOPPED - check log]" % (self.repourl, self.branch, status)
def poll(self):
# unix timestamp else: return None
# get a deferred object that performs the fetch
# This command always produces data on stderr, but we actually do not care # about the stderr or stdout from this command. We set errortoo=True to # avoid an errback from the deferred. The callback which will be added to this # deferred will not use the response. path=self.workdir, env=os.environ, errortoo=True )
def _process_changes(self, unused_output): # get the change list path=self.workdir, env=os.environ, errortoo=False )
# process oldest change first return
% (self.changeCount, revList, self.workdir) )
self._get_commit_timestamp(rev), self._get_commit_author(rev), self._get_commit_files(rev), self._get_commit_comments(rev), ], consumeErrors=True)
# check for failures # just fail on the first error; they're probably all related! raise failures[0]
author=author, revision=rev, files=files, comments=comments, when_timestamp=epoch2datetime(timestamp), branch=self.branch, category=self.category, project=self.project, repository=self.repourl, src='git')
log.msg('gitpoller: repo poll failed') log.err(f) # eat the failure to continue along the defered chain - we still want to catch up return None
log.msg('gitpoller: no changes, no catch_up') return
log.err(f) log.msg('gitpoller: please resolve issues in local repo: %s' % self.workdir) # this used to stop the service, but this is (a) unfriendly to tests and (b) # likely to leave the error message lost in a sea of other log messages
"utility method to handle the result of getProcessOutputAndValue" raise EnvironmentError('command failed with exit code %d: %s' % (code, stderr))
"utility method to stop the service when a failure occurs" if self.running: d = defer.maybeDeferred(lambda : self.stopService()) d.addErrback(log.err, 'while stopping broken GitPoller service') return f
if type(self.fetch_refspec) in (list,set): args.extend(self.fetch_refspec) else: args.append(self.fetch_refspec) |