bhearsum%mozilla.com 780035b40b bug 393424: Create package structure for custom buildbot steps - add mising imports, actually start UploadTryBuild. r=robcee, patch=me
git-svn-id: svn://10.0.0.236/trunk@242328 18797224-902f-48f8-a5cc-f745e15eee43
2008-01-03 21:52:50 +00:00

405 lines
15 KiB
Python

# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla-specific Buildbot steps.
#
# The Initial Developer of the Original Code is
# Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Hearsum <bhearsum@mozilla.com>
# ***** END LICENSE BLOCK *****
import os
from os import path, chmod
from time import localtime, strftime
import re
from twisted.python import log
from buildbot.steps.shell import ShellCommand
from buildbot.steps.source import Mercurial
from buildbot.steps.transfer import FileDownload
from buildbot.process.buildstep import BuildStep
from buildbot.sourcestamp import SourceStamp
from buildbot.buildset import BuildSet
from buildbot.status.builder import SUCCESS, SKIPPED, WARNINGS
def parseSendchangeArguments(args):
"""This function parses the arguments that the Buildbot patch uploader
sends to Buildbot via the "changed files". It takes an argument of a
list of files and returns a dictionary with key/value pairs
"""
parsedArgs = {}
for arg in args:
try:
(key, value) = arg.split(":", 1)
value = value.lstrip().rstrip()
parsedArgs[key] = value
except:
pass
return parsedArgs
class MozillaTryProcessing(BuildStep):
warnOnFailure = True
name = "try server pre-processing"
"""This step does some preprocessing that the try server needs.
1) Resubmits any extra changes attached to this Build.
2) Sets all of the sendchange arguments as build properties
3) Provides a short header to tho build to help easily identify it
"""
def start(self):
changes = self.step_status.build.getChanges()
# 1) Resubmit extra changes
if len(changes) > 1:
builderName = self.step_status.build.builder.name
remainingChanges = changes[1:] # everything but the first
# get rid of the rest of the changes in the Build and BuildStatus
changes = changes[:1] # only the first one
self.step_status.build.changes = changes
bs = BuildSet([builderName], SourceStamp(changes=remainingChanges))
# submit the buildset back to the BuildMaster
self.build.builder.botmaster.parent.submitBuildSet(bs)
# 2) Set sendchange arguments to build properties
args = parseSendchangeArguments(changes[0].files)
for arg in args:
self.setProperty(arg, args[arg])
# 3) Add a header
buildNum = self.step_status.build.getNumber()
who = changes[0].who
comments = changes[0].comments
msg = "TinderboxPrint: %s\n" % who
if 'identifier' in args:
msg += "TinderboxPrint: %s\n" % args['identifier']
msg += "Comments: %s\n\n" % comments
self.addCompleteLog("header", msg)
self.finished(SUCCESS)
return SUCCESS
class MozillaDownloadMozconfig(FileDownload):
haltOnFailure = False
flunkOnFailure = False
warnOnFailure = False
def __init__(self, mastersrc=None, patchDir=".", **kwargs):
"""arguments:
@type patchDir: string
@param patchDir: The directory on the master that holds the mozconfig
This directory is relative to the base buildmaster
directory.
ie. /home/buildmaster/project
Defaults to '.'
"""
self.workdir = "mozilla/"
kwargs['workdir'] = "mozilla/"
self.patchDir = patchDir
# masterscr and slavedest get overridden in start()
FileDownload.__init__(self, mastersrc=mastersrc, slavedest=".mozconfig",
**kwargs)
def start(self):
changes = self.step_status.build.getChanges()
args = parseSendchangeArguments(changes[0].files)
# if we were passed in a mozconfig and also have an uploaded one
# they need to be combined, with the uploaded one overwriting any
# settings set by the passed in one
try:
uploadedFile = path.join(self.patchDir, args['mozconfig'])
os.stat(uploadedFile)
oldMasterSrc = self.mastersrc
self.mastersrc = uploadedFile
try:
os.stat(oldMasterSrc)
# we have both a passed in and uploaded mozconfig
self.mastersrc = "%s-%s" % (uploadedFile,
self.getProperty("slavename"))
# read in both configs
initialConfig = open(oldMasterSrc)
newConfig = initialConfig.read()
initialConfig.close()
uploadedConfig = open(uploadedFile)
newConfig += "\n"
newConfig += uploadedConfig.read()
uploadedConfig.close()
# now write out the whole new thing
mozconfig = open(self.mastersrc, "w")
mozconfig.write(newConfig)
mozconfig.close()
except (OSError, TypeError, KeyError):
# no passed in mozconfig, mastersrc set above
try:
os.stat(self.mastersrc)
except (OSError, TypeError, KeyError):
return SKIPPED
except (OSError, TypeError, KeyError):
# no uploaded mozconfig
try:
os.stat(self.mastersrc)
# if this succeeds, the passed in mastersrc is valid
except (OSError, TypeError, KeyError):
# nothing to transfer, skip
return SKIPPED
# everything is set up, download the file
FileDownload.start(self)
class MozillaPatchDownload(FileDownload):
"""This step reads a Change for a filename and downloads it to the slave.
It is typically used in conjunction with the MozillaCustomPatch step.
"""
haltOnFailure = True
def __init__(self, patchDir=".", **kwargs):
"""arguments:
@type patchDir: string
@param patchDir: The directory on the master that holds the patches
This directory is relative to the base buildmaster
directory.
ie. /home/buildmaster/project
Defaults to '.'
'workdir' is assumed to be 'build' and should be passed if it is
anything else.
"""
self.patchDir = patchDir
# mastersrc and slavedest get overridden in start()
if not 'workdir' in kwargs:
kwargs['workdir'] = "build"
FileDownload.__init__(self, mastersrc=".", slavedest=".", **kwargs)
def start(self):
changes = self.step_status.build.getChanges()
if len(changes) < 1:
return
args = parseSendchangeArguments(changes[0].files)
self.mastersrc = "%s/%s" % (self.patchDir, args['patchFile'])
self.slavedest = "%s" % (args['patchFile'])
# now that everything is set-up, download the file
FileDownload.start(self)
class MozillaUploadTryBuild(ShellCommand):
warnOnFailure = True
def __init__(self, slavedir, baseFilename, scpString, **kwargs):
"""
@type slavedir: string
@param slavedir: The directory that contains the file that will be
transferred (on the BuildSlave)
@type baseFilename: string
@param baseFilename: The filename (without the identifier) of the file
that will be transferred
@type scpString: string
@param scpString: The scp user@host:/dir string to upload the file to.
For example,
foo@some.server.com:/var/www.
This user should have passwordless access to the
host.
"""
self.slavedir = slavedir
self.baseFilename = baseFilename
self.scpString = scpString
ShellCommand.__init__(self, **kwargs)
def start(self):
# we need to append some additional information to the package name
# to make sure we don't overwrite any existing packages
changes = self.step_status.build.getChanges()
args = parseSendchangeArguments(changes[0].files)
# the REMOTE_USER from the submission form
changer = changes[0].who
# the time the change was processed, this should be the same for every
# build in a set
when = strftime("%Y-%m-%d_%H:%M", localtime(changes[0].when))
dir = "%s-%s-%s" % (when, changer, args['identifier'])
# this is the filename of the package built on the slave
filename = "%s-%s" % (args['identifier'], self.baseFilename)
# the path to the package + the filename
slavesrc = path.join(self.slavedir, filename)
# the full path + full filename to the package
# the filename is prepended with the submission time and submitter so
# it can be sorted by the server on the other end
self.scpString = path.join(self.scpString, dir, "%s-%s"
% (changer, filename))
self.setCommand(["scp", slavesrc, self.scpString])
ShellCommand.start(self)
class MozillaTryServerHgClone(Mercurial):
haltOnFailure = True
flunkOnFailure = True
def __init__(self, repourl="http://hg.mozilla.org/mozilla-central",
**kwargs):
timeout = 3600
if 'timeout' in kwargs:
timeout = kwargs['timeout']
# repourl overridden in startVC
Mercurial.__init__(self, repourl=repourl, timeout=timeout, **kwargs)
def startVC(self, branch, revision, patch):
changes = self.step_status.build.getChanges()
args = parseSendchangeArguments(changes[0].files)
self.repourl = args['mozillaRepoPath']
Mercurial.startVC(self, None, revision, patch)
class MozillaClientMk(ShellCommand):
haltOnFailure = True
flunkOnFailure = True
name = "checkout client.mk"
description = ["fetching client.mk"]
descriptionDone = ["client.mk"]
def __init__(self, cvsroot, **kwargs):
self.cvsroot = cvsroot
self.workdir = "."
kwargs['workdir'] = "."
# command may be overridden in start()
kwargs['command'] = ["cvs", "-d", cvsroot, "co", "mozilla/client.mk"]
ShellCommand.__init__(self, **kwargs)
def start(self):
changes = self.step_status.build.getChanges()
args = parseSendchangeArguments(changes[0].files)
self.command = ["cvs", "-d", self.cvsroot, "co"]
if 'branch' in args:
self.command.extend(["-r", args['branch']])
self.command.append("mozilla/client.mk")
ShellCommand.start(self)
class MozillaTryServerHgCheckout(ShellCommand):
haltOnFailure = True
flunkOnFailure = True
timeout = 3600
def __init__(self, mozillaRepoPath="http://hg.mozilla.org/mozilla-central",
tamarinRepoPath="http://hg.mozilla.org/tamarin-central",
cvsroot=":pserver:anonymous@cvs.mozilla.org:/cvsroot",
**kwargs):
self.mozillaRepoPath = mozillaRepoPath
self.tamarinRepoPath = tamarinRepoPath
self.cvsroot = cvsroot
timeout = 3600
if 'timeout' in kwargs:
timeout = kwargs['timeout']
# repo paths overridden in start()
kwargs['command'] = ["python", "client.py", "-m" "$MOZ_REPO",
"-t", "$TAMARIN_REPO",
"--cvsroot=%s" % self.cvsroot,
"checkout"]
ShellCommand.__init__(self, timeout=timeout, **kwargs)
def start(self):
changes = self.step_status.build.getChanges()
args = parseSendchangeArguments(changes[0].files)
if 'mozillaRepoPath' in args:
self.mozillaRepoPath = args['mozillaRepoPath']
if 'tamarinRepoPath' in args:
self.tamarinRepoPath = args['tamarinRepoPath']
self.command = ["python", "client.py", "-m", self.mozillaRepoPath,
"-t", self.tamarinRepoPath,
"--cvsroot=%s" % self.cvsroot, "checkout"]
ShellCommand.start(self)
class MozillaCustomPatch(ShellCommand):
"""This step looks at a Change to find the name of a diff.
The diff is applied to the current tree.
This step is typically used in conjunction with the MozillaPatchDownload
step.
"""
haltOnFailure = True
def __init__(self, **kwargs):
"""
'workdir' is assumed to be 'build' and should be passed if it is
anything else.
"""
if not 'workdir' in kwargs:
kwargs['workdir'] = "build"
ShellCommand.__init__(self, **kwargs)
def start(self):
changes = self.step_status.build.getChanges()
if len(changes) < 1:
log.msg("No changes, not doing anything")
self.step_status.setColor("yellow")
self.step_status.setText(["Skipped patch step:", "no patch"])
self.finished(WARNINGS)
return
if len(changes) > 1:
log.msg("Ignoring all but the first change...")
args = parseSendchangeArguments(changes[0].files)
self.setCommand(["patch", "-f", "-p%d" % int(args['patchLevel']), "-i",
args['patchFile']])
ShellCommand.start(self)
class MozillaCreateUploadDirectory(ShellCommand):
def __init__(self, scpString, **kwargs):
(self.sshHost, self.sshDir) = scpString.split(":")
ShellCommand.__init__(self, **kwargs)
def start(self):
changes = self.step_status.build.getChanges()
when = strftime("%Y-%m-%d_%H:%M", localtime(changes[0].when))
changer = changes[0].who
args = parseSendchangeArguments(changes[0].files)
dir = "%s-%s-%s" % (when, changer, args['identifier'])
fullDir = path.join(self.sshDir, dir)
self.setCommand(['ssh', self.sshHost, 'mkdir', fullDir])
ShellCommand.start(self)
def evaluateCommand(self, cmd):
result = ShellCommand.evaluateCommand(self, cmd)
if None != re.search('File exists', cmd.logs['stdio'].getText()):
result = SUCCESS
return result