From warner at users.sourceforge.net Sun Jun 17 21:10:25 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Sun, 17 Jun 2007 21:10:25 +0000 Subject: [Buildbot-commits] buildbot CREDITS,1.8,1.9 ChangeLog,1.851,1.852 Message-ID: Update of /cvsroot/buildbot/buildbot In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv20957 Modified Files: CREDITS ChangeLog Log Message: [project @ update CREDITS] Original author: warner at lothar.com Date: 2007-04-17 06:38:12+00:00 Index: CREDITS =================================================================== RCS file: /cvsroot/buildbot/buildbot/CREDITS,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- CREDITS 5 Nov 2006 05:13:14 -0000 1.8 +++ CREDITS 17 Jun 2007 21:10:22 -0000 1.9 @@ -49,3 +49,13 @@ Mark Rowe Ben Hearsum Dave Liebreich +Phil Thompson +Benoit Sigoure +Mark Pauley +Rob Helmer +Grig Gheorghiu +Christian Unger +Mateusz Loskot +Jose Dapena Paz +Riccardo Magliocchetti +Alexander Lorenz Index: ChangeLog =================================================================== RCS file: /cvsroot/buildbot/buildbot/ChangeLog,v retrieving revision 1.851 retrieving revision 1.852 diff -u -d -r1.851 -r1.852 --- ChangeLog 17 Apr 2007 06:38:49 -0000 1.851 +++ ChangeLog 17 Jun 2007 21:10:22 -0000 1.852 @@ -1,3 +1,7 @@ +2007-04-16 Brian Warner + + * CREDITS: update list of contributors. Thank you all! + 2007-04-13 Brian Warner * buildbot/status/mail.py (MailNotifier): add the project name to From warner at users.sourceforge.net Sun Jun 17 21:10:30 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Sun, 17 Jun 2007 21:10:30 +0000 Subject: [Buildbot-commits] buildbot/buildbot/status words.py,1.48,1.49 Message-ID: Update of /cvsroot/buildbot/buildbot/buildbot/status In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv21025/buildbot/status Modified Files: words.py Log Message: [project @ refactor the irc bot, in preparation for other IM status backends] Original author: warner at lothar.com Date: 2007-05-17 22:41:10+00:00 Index: words.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/status/words.py,v retrieving revision 1.48 retrieving revision 1.49 diff -u -d -r1.48 -r1.49 --- words.py 7 Feb 2007 04:25:29 -0000 1.48 +++ words.py 17 Jun 2007 21:10:27 -0000 1.49 @@ -5,6 +5,7 @@ import re, shlex +from zope.interface import Interface, implements from twisted.internet import protocol, reactor from twisted.words.protocols import irc from twisted.python import log, failure @@ -26,17 +27,15 @@ hasStarted = False timer = None - def __init__(self, parent, reply): + def __init__(self, parent): self.parent = parent - self.reply = reply self.timer = reactor.callLater(5, self.soon) def soon(self): del self.timer if not self.hasStarted: - self.parent.reply(self.reply, - "The build has been queued, I'll give a shout" - " when it starts") + self.parent.send("The build has been queued, I'll give a shout" + " when it starts") def started(self, c): self.hasStarted = True @@ -48,14 +47,28 @@ response = "build #%d forced" % s.getNumber() if eta is not None: response = "build forced [ETA %s]" % self.parent.convertTime(eta) - self.parent.reply(self.reply, response) - self.parent.reply(self.reply, - "I'll give a shout when the build finishes") + self.parent.send(response) + self.parent.send("I'll give a shout when the build finishes") d = s.waitUntilFinished() - d.addCallback(self.parent.buildFinished, self.reply) + d.addCallback(self.parent.buildFinished) -class IrcStatusBot(irc.IRCClient): +class Contact: + """I hold the state for a single user's interaction with the buildbot. + + This base class provides all the basic behavior (the queries and + responses). Subclasses for each channel type (IRC, different IM + protocols) are expected to provide the lower-level send/receive methods. + + There will be one instance of me for each user who interacts personally + with the buildbot. There will be an additional instance for each + 'broadcast contact' (chat rooms, IRC channels as a whole). + """ + + def __init__(self, channel, username): + self.channel = channel + self.username = username + silly = { "What happen ?": "Somebody set up us the bomb.", "It's You !!": ["How are you gentlemen !!", @@ -64,91 +77,6 @@ "What you say !!": ["You have no chance to survive make your time.", "HA HA HA HA ...."], } - def __init__(self, nickname, password, channels, status, categories): - """ - @type nickname: string - @param nickname: the nickname by which this bot should be known - @type password: string - @param password: the password to use for identifying with Nickserv - @type channels: list of strings - @param channels: the bot will maintain a presence in these channels - @type status: L{buildbot.status.builder.Status} - @param status: the build master's Status object, through which the - bot retrieves all status information - """ - self.nickname = nickname - self.channels = channels - self.password = password - self.status = status - self.categories = categories - self.counter = 0 - self.hasQuit = 0 - - def signedOn(self): - if self.password: - self.msg("Nickserv", "IDENTIFY " + self.password) - for c in self.channels: - self.join(c) - def joined(self, channel): - log.msg("I have joined", channel) - def left(self, channel): - log.msg("I have left", channel) - def kickedFrom(self, channel, kicker, message): - log.msg("I have been kicked from %s by %s: %s" % (channel, - kicker, - message)) - - # input - def privmsg(self, user, channel, message): - user = user.split('!', 1)[0] # rest is ~user at hostname - # channel is '#twisted' or 'buildbot' (for private messages) - channel = channel.lower() - #print "privmsg:", user, channel, message - if channel == self.nickname: - # private message - message = "%s: %s" % (self.nickname, message) - reply = user - else: - reply = channel - if message.startswith("%s:" % self.nickname): - message = message[len("%s:" % self.nickname):] - - message = message.lstrip() - if self.silly.has_key(message): - return self.doSilly(user, reply, message) - - parts = message.split(' ', 1) - if len(parts) == 1: - parts = parts + [''] - cmd, args = parts - log.msg("irc command", cmd) - - meth = self.getCommandMethod(cmd) - if not meth and message[-1] == '!': - meth = self.command_EXCITED - - error = None - try: - if meth: - meth(user, reply, args.strip()) - except UsageError, e: - self.reply(reply, str(e)) - except: - f = failure.Failure() - log.err(f) - error = "Something bad happened (see logs): %s" % f.type - - if error: - try: - self.reply(reply, error) - except: - log.err() - - #self.say(channel, "count %d" % self.counter) - self.counter += 1 - def reply(self, dest, message): - # maybe self.notice(dest, message) instead? - self.msg(dest, message) def getCommandMethod(self, command): meth = getattr(self, 'command_' + command.upper(), None) @@ -156,16 +84,16 @@ def getBuilder(self, which): try: - b = self.status.getBuilder(which) + b = self.channel.status.getBuilder(which) except KeyError: raise UsageError, "no such builder '%s'" % which return b def getControl(self, which): - if not self.control: + if not self.channel.control: raise UsageError("builder control is not enabled") try: - bc = self.control.getBuilder(which) + bc = self.channel.control.getBuilder(which) except KeyError: raise UsageError("no such builder '%s'" % which) return bc @@ -174,9 +102,9 @@ """ @rtype: list of L{buildbot.process.builder.Builder} """ - names = self.status.getBuilderNames(categories=self.categories) + names = self.channel.status.getBuilderNames(categories=self.categories) names.sort() - builders = [self.status.getBuilder(n) for n in names] + builders = [self.channel.status.getBuilder(n) for n in names] return builders def convertTime(self, seconds): @@ -190,22 +118,22 @@ minutes = minutes - 60*hours return "%dh%02dm%02ds" % (hours, minutes, seconds) - def doSilly(self, user, reply, message): + def doSilly(self, message): response = self.silly[message] if type(response) != type([]): response = [response] when = 0.5 for r in response: - reactor.callLater(when, self.reply, reply, r) + reactor.callLater(when, self.send, r) when += 2.5 - def command_HELLO(self, user, reply, args): - self.reply(reply, "yes?") + def command_HELLO(self, args): + self.send("yes?") - def command_VERSION(self, user, reply, args): - self.reply(reply, "buildbot-%s at your service" % version) + def command_VERSION(self, args): + self.send("buildbot-%s at your service" % version) - def command_LIST(self, user, reply, args): + def command_LIST(self, args): args = args.split() if len(args) == 0: raise UsageError, "try 'list builders'" @@ -219,11 +147,11 @@ str += "[offline]" str += " " str.rstrip() - self.reply(reply, str) + self.send(str) return command_LIST.usage = "list builders - List configured builders" - def command_STATUS(self, user, reply, args): + def command_STATUS(self, args): args = args.split() if len(args) == 0: which = "all" @@ -234,12 +162,12 @@ if which == "all": builders = self.getAllBuilders() for b in builders: - self.emit_status(reply, b.name) + self.emit_status(b.name) return - self.emit_status(reply, which) + self.emit_status(which) command_STATUS.usage = "status [] - List status of a builder (or all builders)" - def command_WATCH(self, user, reply, args): + def command_WATCH(self, args): args = args.split() if len(args) != 1: raise UsageError("try 'watch '") @@ -247,22 +175,22 @@ b = self.getBuilder(which) builds = b.getCurrentBuilds() if not builds: - self.reply(reply, "there are no builds currently running") + self.send("there are no builds currently running") return for build in builds: assert not build.isFinished() d = build.waitUntilFinished() - d.addCallback(self.buildFinished, reply) + d.addCallback(self.buildFinished) r = "watching build %s #%d until it finishes" \ % (which, build.getNumber()) eta = build.getETA() if eta is not None: r += " [%s]" % self.convertTime(eta) r += ".." - self.reply(reply, r) + self.send(r) command_WATCH.usage = "watch - announce the completion of an active build" - def buildFinished(self, b, reply): + def buildFinished(self, b): results = {SUCCESS: "Success", WARNINGS: "Warnings", FAILURE: "Failure", @@ -282,12 +210,12 @@ b.getNumber(), results.get(b.getResults(), "??")) r += " [%s]" % " ".join(b.getText()) - self.reply(reply, r) - buildurl = self.status.getURLForThing(b) + self.send(r) + buildurl = self.channel.status.getURLForThing(b) if buildurl: - self.reply(reply, "Build details are at %s" % buildurl) + self.send("Build details are at %s" % buildurl) - def command_FORCE(self, user, reply, args): + def command_FORCE(self, args): args = shlex.split(args) # TODO: this requires python2.3 or newer if args.pop(0) != "build": raise UsageError("try 'force build WHICH '") @@ -303,11 +231,11 @@ # centralize this somewhere. if branch and not re.match(r'^[\w\.\-\/]*$', branch): log.msg("bad branch '%s'" % branch) - self.reply(reply, "sorry, bad branch '%s'" % branch) + self.send("sorry, bad branch '%s'" % branch) return if revision and not re.match(r'^[\w\.\-\/]*$', revision): log.msg("bad revision '%s'" % revision) - self.reply(reply, "sorry, bad revision '%s'" % revision) + self.send("sorry, bad revision '%s'" % revision) return bc = self.getControl(which) @@ -315,9 +243,8 @@ who = None # TODO: if we can authenticate that a particular User # asked for this, use User Name instead of None so they'll # be informed of the results. - # TODO: or, monitor this build and announce the results through the - # 'reply' argument. - r = "forced: by IRC user <%s>: %s" % (user, reason) + # TODO: or, monitor this build and announce the results + r = "forced: by IRC user <%s>: %s" % (self.username, reason) # TODO: maybe give certain users the ability to request builds of # certain branches s = SourceStamp(branch=branch, revision=revision) @@ -325,16 +252,15 @@ try: bc.requestBuildSoon(req) except interfaces.NoSlaveError: - self.reply(reply, - "sorry, I can't force a build: all slaves are offline") + self.send("sorry, I can't force a build: all slaves are offline") return - ireq = IrcBuildRequest(self, reply) + ireq = IrcBuildRequest(self) req.subscribe(ireq.started) command_FORCE.usage = "force build - Force a build" - def command_STOP(self, user, reply, args): + def command_STOP(self, args): args = args.split(None, 2) if len(args) < 3 or args[0] != 'build': raise UsageError, "try 'stop build WHICH '" @@ -344,13 +270,13 @@ buildercontrol = self.getControl(which) who = None - r = "stopped: by IRC user <%s>: %s" % (user, reason) + r = "stopped: by IRC user <%s>: %s" % (self.username, reason) # find an in-progress build builderstatus = self.getBuilder(which) builds = builderstatus.getCurrentBuilds() if not builds: - self.reply(reply, "sorry, no build is currently running") + self.send("sorry, no build is currently running") return for build in builds: num = build.getNumber() @@ -361,11 +287,11 @@ # make it stop buildcontrol.stopBuild(r) - self.reply(reply, "build %d interrupted" % num) + self.send("build %d interrupted" % num) command_STOP.usage = "stop build - Stop a running build" - def emit_status(self, reply, which): + def emit_status(self, which): b = self.getBuilder(which) str = "%s: " % which state, builds = b.getState() @@ -386,9 +312,9 @@ s += " [ETA %s]" % self.convertTime(ETA) t.append(s) str += ", ".join(t) - self.reply(reply, str) + self.send(str) - def emit_last(self, reply, which): + def emit_last(self, which): last = self.getBuilder(which).getLastFinishedBuild() if not last: str = "(no builds run since last restart)" @@ -396,9 +322,9 @@ start,finish = last.getTimes() str = "%s secs ago: " % (int(util.now() - finish)) str += " ".join(last.getText()) - self.reply(reply, "last build [%s]: %s" % (which, str)) + self.send("last build [%s]: %s" % (which, str)) - def command_LAST(self, user, reply, args): + def command_LAST(self, args): args = args.split() if len(args) == 0: which = "all" @@ -409,9 +335,9 @@ if which == "all": builders = self.getAllBuilders() for b in builders: - self.emit_last(reply, b.name) + self.emit_last(b.name) return - self.emit_last(reply, which) + self.emit_last(which) command_LAST.usage = "last - list last build status for builder " def build_commands(self): @@ -422,10 +348,10 @@ commands.sort() return commands - def command_HELP(self, user, reply, args): + def command_HELP(self, args): args = args.split() if len(args) == 0: - self.reply(reply, "Get help on what? (try 'help ', or 'commands' for a command list)") + self.send("Get help on what? (try 'help ', or 'commands' for a command list)") return command = args[0] meth = self.getCommandMethod(command) @@ -433,56 +359,208 @@ raise UsageError, "no such command '%s'" % command usage = getattr(meth, 'usage', None) if usage: - self.reply(reply, "Usage: %s" % usage) + self.send("Usage: %s" % usage) else: - self.reply(reply, "No usage info for '%s'" % command) + self.send("No usage info for '%s'" % command) command_HELP.usage = "help - Give help for " - def command_SOURCE(self, user, reply, args): + def command_SOURCE(self, args): banner = "My source can be found at http://buildbot.sourceforge.net/" - self.reply(reply, banner) + self.send(banner) - def command_COMMANDS(self, user, reply, args): + def command_COMMANDS(self, args): commands = self.build_commands() str = "buildbot commands: " + ", ".join(commands) - self.reply(reply, str) + self.send(str) command_COMMANDS.usage = "commands - List available commands" - def command_DESTROY(self, user, reply, args): - self.me(reply, "readies phasers") + def command_DESTROY(self, args): + self.act("readies phasers") - def command_DANCE(self, user, reply, args): - reactor.callLater(1.0, self.reply, reply, "0-<") - reactor.callLater(3.0, self.reply, reply, "0-/") - reactor.callLater(3.5, self.reply, reply, "0-\\") + def command_DANCE(self, args): + reactor.callLater(1.0, self.send, "0-<") + reactor.callLater(3.0, self.send, "0-/") + reactor.callLater(3.5, self.send, "0-\\") - def command_EXCITED(self, user, reply, args): + def command_EXCITED(self, args): # like 'buildbot: destroy the sun!' - self.reply(reply, "What you say!") + self.send("What you say!") + + def handleAction(self, data, user): + # this is sent when somebody performs an action that mentions the + # buildbot (like '/me kicks buildbot'). 'user' is the name/nick/id of + # the person who performed the action, so if their action provokes a + # response, they can be named. + if not data.endswith("s buildbot"): + return + words = data.split() + verb = words[-2] + timeout = 4 + if verb == "kicks": + response = "%s back" % verb + timeout = 1 + else: + response = "%s %s too" % (verb, user) + reactor.callLater(timeout, self.act, response) + +class IRCContact(Contact): + # this is the IRC-specific subclass of Contact + + def __init__(self, channel, dest): + Contact.__init__(self, channel) + self.dest = dest + + # userJoined(self, user, channel) + + def send(self, message): + self.channel.msg(self.dest, message) + def act(self, action): + self.channel.me(self.dest, action) + + + def handleMessage(self, message): + message = message.lstrip() + if self.silly.has_key(message): + return self.doSilly(message) + + parts = message.split(' ', 1) + if len(parts) == 1: + parts = parts + [''] + cmd, args = parts + log.msg("irc command", cmd) + + meth = self.getCommandMethod(cmd) + if not meth and message[-1] == '!': + meth = self.command_EXCITED + + error = None + try: + if meth: + meth(args.strip()) + except UsageError, e: + self.send(str(e)) + except: + f = failure.Failure() + log.err(f) + error = "Something bad happened (see logs): %s" % f.type + + if error: + try: + self.send(error) + except: + log.err() + + #self.say(channel, "count %d" % self.counter) + self.channel.counter += 1 + +class IChannel(Interface): + """I represent the buildbot's presence in a particular IM scheme. + + This provides the connection to the IRC server, or represents the + buildbot's account with an IM service. Each Channel will have zero or + more Contacts associated with it. + """ + +class IrcStatusBot(irc.IRCClient): + """I represent the buildbot to an IRC server. + """ + implements(IChannel) + + def __init__(self, nickname, password, channels, status, categories): + """ + @type nickname: string + @param nickname: the nickname by which this bot should be known + @type password: string + @param password: the password to use for identifying with Nickserv + @type channels: list of strings + @param channels: the bot will maintain a presence in these channels + @type status: L{buildbot.status.builder.Status} + @param status: the build master's Status object, through which the + bot retrieves all status information + """ + self.nickname = nickname + self.channels = channels + self.password = password + self.status = status + self.categories = categories + self.counter = 0 + self.hasQuit = 0 + self.contacts = {} + + def addContact(self, name, contact): + self.contacts[name] = contact + + def getContact(self, name): + if name in self.contacts: + return self.contacts[name] + new_contact = IRCContact(self, name) + self.contacts[name] = new_contact + return new_contact + + def deleteContact(self, contact): + name = contact.getName() + if name in self.contacts: + assert self.contacts[name] == contact + del self.contacts[name] + + def log(self, msg): + log.msg("%s: %s" % (self, msg)) + + + # the following irc.IRCClient methods are called when we have input + + def privmsg(self, user, channel, message): + user = user.split('!', 1)[0] # rest is ~user at hostname + # channel is '#twisted' or 'buildbot' (for private messages) + channel = channel.lower() + #print "privmsg:", user, channel, message + if channel == self.nickname: + # private message + contact = self.getContact(user) + contact.handleMessage(message) + return + # else it's a broadcast message, maybe for us, maybe not. 'channel' + # is '#twisted' or the like. + contact = self.getContact(channel) + if message.startswith("%s:" % self.nickname): + message = message[len("%s:" % self.nickname):] + contact.handleMessage(message) + # to track users comings and goings, add code here def action(self, user, channel, data): #log.msg("action: %s,%s,%s" % (user, channel, data)) user = user.split('!', 1)[0] # rest is ~user at hostname - # somebody did an action (/me actions) - if data.endswith("s buildbot"): - words = data.split() - verb = words[-2] - timeout = 4 - if verb == "kicks": - response = "%s back" % verb - timeout = 1 - else: - response = "%s %s too" % (verb, user) - reactor.callLater(timeout, self.me, channel, response) - # userJoined(self, user, channel) - - # output + # somebody did an action (/me actions) in the broadcast channel + contact = self.getContact(channel) + if "buildbot" in data: + contact.handleAction(data, user) + + + + def signedOn(self): + if self.password: + self.msg("Nickserv", "IDENTIFY " + self.password) + for c in self.channels: + self.join(c) + + def joined(self, channel): + self.log("I have joined %s" % (channel,)) + def left(self, channel): + self.log("I have left %s" % (channel,)) + def kickedFrom(self, channel, kicker, message): + self.log("I have been kicked from %s by %s: %s" % (channel, + kicker, + message)) + + # we can using the following irc.IRCClient methods to send output. Most + # of these are used by the IRCContact class. + # # self.say(channel, message) # broadcast # self.msg(user, message) # unicast # self.me(channel, action) # send action # self.away(message='') # self.quit(message='') - + class ThrottledClientFactory(protocol.ClientFactory): lostDelay = 2 failedDelay = 60 @@ -586,20 +664,6 @@ return base.StatusReceiverMultiService.stopService(self) -def main(): - from twisted.internet import app - a = app.Application("irctest") - f = IrcStatusFactory() - host = "localhost" - port = 6667 - f.addNetwork((host, port), ["private", "other"]) - a.connectTCP(host, port, f) - a.run(save=0) - - -if __name__ == '__main__': - main() - ## buildbot: list builders # buildbot: watch quick # print notification when current build in 'quick' finishes From warner at users.sourceforge.net Sun Jun 17 21:10:34 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Sun, 17 Jun 2007 21:10:34 +0000 Subject: [Buildbot-commits] buildbot ChangeLog,1.852,1.853 Message-ID: Update of /cvsroot/buildbot/buildbot In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv21038 Modified Files: ChangeLog Log Message: [project @ words.py: refactor into Contact/IChannel classes, to add more IM protocols in the future] Original author: warner at lothar.com Date: 2007-05-18 00:56:48+00:00 Index: ChangeLog =================================================================== RCS file: /cvsroot/buildbot/buildbot/ChangeLog,v retrieving revision 1.852 retrieving revision 1.853 diff -u -d -r1.852 -r1.853 --- ChangeLog 17 Jun 2007 21:10:22 -0000 1.852 +++ ChangeLog 17 Jun 2007 21:10:32 -0000 1.853 @@ -1,3 +1,12 @@ +2007-05-17 Brian Warner + + * buildbot/status/words.py: refactor the IRC status bot into + separate 'Contact' and 'Channel' classes. The base Contact class + contains the interaction code: commands and responses. The + IRCContact subclass (and IrcStatusBot 'Channel') handle the + IRC-specific aspects. The plan is to write other subclasses for + other IM protocols like AIM and Jabber. + 2007-04-16 Brian Warner * CREDITS: update list of contributors. Thank you all! From warner at users.sourceforge.net Sun Jun 17 21:10:35 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Sun, 17 Jun 2007 21:10:35 +0000 Subject: [Buildbot-commits] buildbot/buildbot/status words.py,1.49,1.50 Message-ID: Update of /cvsroot/buildbot/buildbot/buildbot/status In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv21038/buildbot/status Modified Files: words.py Log Message: [project @ words.py: refactor into Contact/IChannel classes, to add more IM protocols in the future] Original author: warner at lothar.com Date: 2007-05-18 00:56:48+00:00 Index: words.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/status/words.py,v retrieving revision 1.49 retrieving revision 1.50 diff -u -d -r1.49 -r1.50 --- words.py 17 Jun 2007 21:10:27 -0000 1.49 +++ words.py 17 Jun 2007 21:10:32 -0000 1.50 @@ -65,9 +65,8 @@ 'broadcast contact' (chat rooms, IRC channels as a whole). """ - def __init__(self, channel, username): + def __init__(self, channel): self.channel = channel - self.username = username silly = { "What happen ?": "Somebody set up us the bomb.", @@ -102,7 +101,7 @@ """ @rtype: list of L{buildbot.process.builder.Builder} """ - names = self.channel.status.getBuilderNames(categories=self.categories) + names = self.channel.status.getBuilderNames(categories=self.channel.categories) names.sort() builders = [self.channel.status.getBuilder(n) for n in names] return builders @@ -127,13 +126,13 @@ reactor.callLater(when, self.send, r) when += 2.5 - def command_HELLO(self, args): + def command_HELLO(self, args, who): self.send("yes?") - def command_VERSION(self, args): + def command_VERSION(self, args, who): self.send("buildbot-%s at your service" % version) - def command_LIST(self, args): + def command_LIST(self, args, who): args = args.split() if len(args) == 0: raise UsageError, "try 'list builders'" @@ -151,7 +150,7 @@ return command_LIST.usage = "list builders - List configured builders" - def command_STATUS(self, args): + def command_STATUS(self, args, who): args = args.split() if len(args) == 0: which = "all" @@ -167,7 +166,7 @@ self.emit_status(which) command_STATUS.usage = "status [] - List status of a builder (or all builders)" - def command_WATCH(self, args): + def command_WATCH(self, args, who): args = args.split() if len(args) != 1: raise UsageError("try 'watch '") @@ -201,8 +200,8 @@ builder = b.getBuilder() log.msg('builder %r in category %s finished' % (builder, builder.category)) - if (self.categories != None and - builder.category not in self.categories): + if (self.channel.categories != None and + builder.category not in self.channel.categories): return r = "Hey! build %s #%d is complete: %s" % \ @@ -215,9 +214,12 @@ if buildurl: self.send("Build details are at %s" % buildurl) - def command_FORCE(self, args): + def command_FORCE(self, args, who): args = shlex.split(args) # TODO: this requires python2.3 or newer - if args.pop(0) != "build": + if not args: + raise UsageError("try 'force build WHICH '") + what = args.pop(0) + if what != "build": raise UsageError("try 'force build WHICH '") opts = ForceOptions() opts.parseOptions(args) @@ -227,6 +229,10 @@ revision = opts['revision'] reason = opts['reason'] + if which is None: + raise UsageError("you must provide a Builder, " + "try 'force build WHICH '") + # keep weird stuff out of the branch and revision strings. TODO: # centralize this somewhere. if branch and not re.match(r'^[\w\.\-\/]*$', branch): @@ -240,11 +246,7 @@ bc = self.getControl(which) - who = None # TODO: if we can authenticate that a particular User - # asked for this, use User Name instead of None so they'll - # be informed of the results. - # TODO: or, monitor this build and announce the results - r = "forced: by IRC user <%s>: %s" % (self.username, reason) + r = "forced: by %s: %s" % (self.describeUser(who), reason) # TODO: maybe give certain users the ability to request builds of # certain branches s = SourceStamp(branch=branch, revision=revision) @@ -260,7 +262,7 @@ command_FORCE.usage = "force build - Force a build" - def command_STOP(self, args): + def command_STOP(self, args, who): args = args.split(None, 2) if len(args) < 3 or args[0] != 'build': raise UsageError, "try 'stop build WHICH '" @@ -269,8 +271,7 @@ buildercontrol = self.getControl(which) - who = None - r = "stopped: by IRC user <%s>: %s" % (self.username, reason) + r = "stopped: by %s: %s" % (self.describeUser(who), reason) # find an in-progress build builderstatus = self.getBuilder(which) @@ -324,7 +325,7 @@ str += " ".join(last.getText()) self.send("last build [%s]: %s" % (which, str)) - def command_LAST(self, args): + def command_LAST(self, args, who): args = args.split() if len(args) == 0: which = "all" @@ -342,13 +343,13 @@ def build_commands(self): commands = [] - for k in self.__class__.__dict__.keys(): + for k in dir(self): if k.startswith('command_'): commands.append(k[8:].lower()) commands.sort() return commands - def command_HELP(self, args): + def command_HELP(self, args, who): args = args.split() if len(args) == 0: self.send("Get help on what? (try 'help ', or 'commands' for a command list)") @@ -364,25 +365,25 @@ self.send("No usage info for '%s'" % command) command_HELP.usage = "help - Give help for " - def command_SOURCE(self, args): - banner = "My source can be found at http://buildbot.sourceforge.net/" + def command_SOURCE(self, args, who): + banner = "My source can be found at http://buildbot.net/" self.send(banner) - def command_COMMANDS(self, args): + def command_COMMANDS(self, args, who): commands = self.build_commands() str = "buildbot commands: " + ", ".join(commands) self.send(str) command_COMMANDS.usage = "commands - List available commands" - def command_DESTROY(self, args): + def command_DESTROY(self, args, who): self.act("readies phasers") - def command_DANCE(self, args): + def command_DANCE(self, args, who): reactor.callLater(1.0, self.send, "0-<") reactor.callLater(3.0, self.send, "0-/") reactor.callLater(3.5, self.send, "0-\\") - def command_EXCITED(self, args): + def command_EXCITED(self, args, who): # like 'buildbot: destroy the sun!' self.send("What you say!") @@ -408,8 +409,17 @@ def __init__(self, channel, dest): Contact.__init__(self, channel) + # when people send us public messages ("buildbot: command"), + # self.dest is the name of the channel ("#twisted"). When they send + # us private messages (/msg buildbot command), self.dest is their + # username. self.dest = dest + def describeUser(self, user): + if self.dest[0] == "#": + return "IRC user <%s> on channel %s" % (user, self.dest) + return "IRC user <%s> (privmsg)" % user + # userJoined(self, user, channel) def send(self, message): @@ -418,7 +428,14 @@ self.channel.me(self.dest, action) - def handleMessage(self, message): + def handleMessage(self, message, who): + # a message has arrived from 'who'. For broadcast contacts (i.e. when + # people do an irc 'buildbot: command'), this will be a string + # describing the sender of the message in some useful-to-log way, and + # a single Contact may see messages from a variety of users. For + # unicast contacts (i.e. when people do an irc '/msg buildbot + # command'), a single Contact will only ever see messages from a + # single user. message = message.lstrip() if self.silly.has_key(message): return self.doSilly(message) @@ -436,7 +453,7 @@ error = None try: if meth: - meth(args.strip()) + meth(args.strip(), who) except UsageError, e: self.send(str(e)) except: @@ -517,14 +534,14 @@ if channel == self.nickname: # private message contact = self.getContact(user) - contact.handleMessage(message) + contact.handleMessage(message, user) return # else it's a broadcast message, maybe for us, maybe not. 'channel' # is '#twisted' or the like. contact = self.getContact(channel) if message.startswith("%s:" % self.nickname): message = message[len("%s:" % self.nickname):] - contact.handleMessage(message) + contact.handleMessage(message, user) # to track users comings and goings, add code here def action(self, user, channel, data): From warner at users.sourceforge.net Sun Jun 17 21:10:41 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Sun, 17 Jun 2007 21:10:41 +0000 Subject: [Buildbot-commits] buildbot CREDITS, 1.9, 1.10 ChangeLog, 1.853, 1.854 Message-ID: Update of /cvsroot/buildbot/buildbot In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv21056 Modified Files: CREDITS ChangeLog Log Message: [project @ svnpoller: tolerate SVN failures and allow the poll to continue. Closes #34.] Original author: warner at lothar.com Date: 2007-06-17 05:53:09+00:00 Index: CREDITS =================================================================== RCS file: /cvsroot/buildbot/buildbot/CREDITS,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- CREDITS 17 Jun 2007 21:10:22 -0000 1.9 +++ CREDITS 17 Jun 2007 21:10:39 -0000 1.10 @@ -59,3 +59,4 @@ Jose Dapena Paz Riccardo Magliocchetti Alexander Lorenz +Dustin Mitchell Index: ChangeLog =================================================================== RCS file: /cvsroot/buildbot/buildbot/ChangeLog,v retrieving revision 1.853 retrieving revision 1.854 diff -u -d -r1.853 -r1.854 --- ChangeLog 17 Jun 2007 21:10:32 -0000 1.853 +++ ChangeLog 17 Jun 2007 21:10:39 -0000 1.854 @@ -1,3 +1,11 @@ +2007-06-16 Brian Warner + + * buildbot/changes/svnpoller.py: when the poll fails, don't kill + the LoopingCall, just eat the failure so that we'll poll again + next time. This should allow us to tolerate (e.g.) sf.net SVN + failures more gracefully. Many thanks to Dustin Mitchell for the + patch. Closes #34. + 2007-05-17 Brian Warner * buildbot/status/words.py: refactor the IRC status bot into From warner at users.sourceforge.net Sun Jun 17 21:10:41 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Sun, 17 Jun 2007 21:10:41 +0000 Subject: [Buildbot-commits] buildbot/buildbot/changes svnpoller.py,1.5,1.6 Message-ID: Update of /cvsroot/buildbot/buildbot/buildbot/changes In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv21056/buildbot/changes Modified Files: svnpoller.py Log Message: [project @ svnpoller: tolerate SVN failures and allow the poll to continue. Closes #34.] Original author: warner at lothar.com Date: 2007-06-17 05:53:09+00:00 Index: svnpoller.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/changes/svnpoller.py,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- svnpoller.py 12 Dec 2006 03:24:03 -0000 1.5 +++ svnpoller.py 17 Jun 2007 21:10:39 -0000 1.6 @@ -255,7 +255,7 @@ d.addCallback(self.get_new_logentries) d.addCallback(self.create_changes) d.addCallback(self.submit_changes) - d.addBoth(self.finished) + d.addCallbacks(self.finished_ok, self.finished_failure) return d def getProcessOutput(self, args): @@ -436,9 +436,16 @@ for c in changes: self.parent.addChange(c) - def finished(self, res): + def finished_ok(self, res): log.msg("SVNPoller finished polling") dbgMsg('_finished : %s' % res) assert self.working self.working = False return res + + def finished_failure(self, f): + log.msg("SVNPoller failed") + dbgMsg('_finished : %s' % f) + assert self.working + self.working = False + return None # eat the failure From warner at users.sourceforge.net Mon Jun 18 02:52:51 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 02:52:51 +0000 Subject: [Buildbot-commits] buildbot ChangeLog,1.854,1.855 Message-ID: Update of /cvsroot/buildbot/buildbot In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv23618 Modified Files: ChangeLog Log Message: [project @ use instances instead of class/kwarg tuples when putting BuildSteps in factories. Closes: #11.] Original author: warner at lothar.com Date: 2007-06-18 02:51:00+00:00 Index: ChangeLog =================================================================== RCS file: /cvsroot/buildbot/buildbot/ChangeLog,v retrieving revision 1.854 retrieving revision 1.855 diff -u -d -r1.854 -r1.855 --- ChangeLog 17 Jun 2007 21:10:39 -0000 1.854 +++ ChangeLog 18 Jun 2007 02:52:48 -0000 1.855 @@ -1,3 +1,51 @@ +2007-06-17 Brian Warner + + * buildbot/process/factory.py (BuildFactory.addStep): To simplify + the config file, we're moving to using actual instances instead of + the (class, kwargs) 'step specification' tuples. BuildFactory + still keeps a list of tuples internally, but when real instances + are passed in to addStep(), they are asked for their class and + kwargs using the new BuildStep.getStepFactory method. BuildFactory + accepts both instances and the tuple form, and converts instances + to the tuple form, but the instance form is preferred because it + gives the Steps a chance to do argument validation. Closes: #11. + + * buildbot/process/buildstep.py (BuildStep.__init__): record the + factory information necessary to implement getStepFactory. The + addFactoryArguments() method can be used to include arguments that + aren't passed to the BuildStep base class constructor. + (BuildStep.setBuild): + (BuildStep.setDefaultWorkdir): new methods to take parameters that + are needed for live BuildSteps but not to construct the specification + tuples that are stashed in the factory. + + * buildbot/process/base.py (Build.setupBuild): pass 'build' and + 'workdir' into new BuildSteps by using methods instead of + arguments. This makes the constructor for BuildSteps a lot + simpler. + + * buildbot/steps/*.py: update to match this change. Basically this + means adding calls to addFactoryArguments() in the __init__ + methods to capture the arguments that aren't passed through to the + base class. + * buildbot/steps/shell.py (ShellCommand.setDefaultWorkdir): copy + the new workdir (if any) into the RemoteShellCommands arguments. + * buildbot/steps/source.py: allow workdir= to be optional, + implement setDefaultWorkdir() since we don't inherit from + (CVS): finally remove old clobber=/export=/copydir= arguments, + in favor of the mode= argument that's been around forever now. + * buildbot/steps/transfer.py: remove build= argument + + * buildbot/test/*.py: update to match, generally by turning all + build= arguments into subsequent calls to s.setBuild() + * buildbot/test/test_config.py (Factories): verify that we can + use either BuildStep instances or class/kwarg tuples in both + BuildFactory.addStep and BuildFactory.__init__ + + * docs/buildbot.texinfo (Build Steps): document the new approach, + mention compatibility with the old approach, update all examples + to use the new style. + 2007-06-16 Brian Warner * buildbot/changes/svnpoller.py: when the poll fails, don't kill From warner at users.sourceforge.net Mon Jun 18 02:52:51 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 02:52:51 +0000 Subject: [Buildbot-commits] buildbot/buildbot/process base.py, 1.73, 1.74 buildstep.py, 1.5, 1.6 factory.py, 1.15, 1.16 Message-ID: Update of /cvsroot/buildbot/buildbot/buildbot/process In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv23618/buildbot/process Modified Files: base.py buildstep.py factory.py Log Message: [project @ use instances instead of class/kwarg tuples when putting BuildSteps in factories. Closes: #11.] Original author: warner at lothar.com Date: 2007-06-18 02:51:00+00:00 Index: base.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/process/base.py,v retrieving revision 1.73 retrieving revision 1.74 diff -u -d -r1.73 -r1.74 --- base.py 11 Dec 2006 09:06:34 -0000 1.73 +++ base.py 18 Jun 2007 02:52:49 -0000 1.74 @@ -237,16 +237,14 @@ # consider sorting these by number return changetext - def setSteps(self, steps): - """Set a list of StepFactories, which are generally just class - objects which derive from step.BuildStep . These are used to create - the Steps themselves when the Build starts (as opposed to when it is - first created). By creating the steps later, their __init__ method - will have access to things like build.allFiles() .""" - self.stepFactories = steps # tuples of (factory, kwargs) - for s in steps: - pass - + def setStepFactories(self, step_factories): + """Set a list of 'step factories', which are tuples of (class, + kwargs), where 'class' is generally a subclass of step.BuildStep . + These are used to create the Steps themselves when the Build starts + (as opposed to when it is first created). By creating the steps + later, their __init__ method will have access to things like + build.allFiles() .""" + self.stepFactories = list(step_factories) @@ -349,14 +347,14 @@ for factory, args in self.stepFactories: args = args.copy() - if not args.has_key("workdir"): - args['workdir'] = self.workdir try: - step = factory(build=self, **args) + step = factory(**args) except: log.msg("error while creating step, factory=%s, args=%s" % (factory, args)) raise + step.setBuild(self) + step.setDefaultWorkdir(self.workdir) name = step.name count = 1 while name in stepnames and count < 100: Index: buildstep.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/process/buildstep.py,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- buildstep.py 12 Dec 2006 03:24:03 -0000 1.5 +++ buildstep.py 18 Jun 2007 02:52:49 -0000 1.6 @@ -546,7 +546,7 @@ # arguments to the RemoteShellCommand that it creates). Such delegating # subclasses will use this list to figure out which arguments are meant # for us and which should be given to someone else. - parms = ['build', 'name', 'locks', + parms = ['name', 'locks', 'haltOnFailure', 'flunkOnWarnings', 'flunkOnFailure', @@ -563,23 +563,40 @@ step_status = None progress = None - def __init__(self, build, **kwargs): - self.build = build + def __init__(self, **kwargs): + self.factory = (self.__class__, dict(kwargs)) for p in self.__class__.parms: if kwargs.has_key(p): setattr(self, p, kwargs[p]) del kwargs[p] - # we want to encourage all steps to get a workdir, so tolerate its - # presence here. It really only matters for non-ShellCommand steps - # like Dummy - if kwargs.has_key('workdir'): - del kwargs['workdir'] if kwargs: why = "%s.__init__ got unexpected keyword argument(s) %s" \ % (self, kwargs.keys()) raise TypeError(why) self._pendingLogObservers = [] + def setBuild(self, build): + # subclasses which wish to base their behavior upon qualities of the + # Build (e.g. use the list of changed files to run unit tests only on + # code which has been modified) should do so here. The Build is not + # available during __init__, but setBuild() will be called just + # afterwards. + self.build = build + + def setDefaultWorkdir(self, workdir): + # the Build calls this just after __init__ and setDefaultWorkdir. + # ShellCommand and variants use a slave-side workdir, but some other + # steps do not. Subclasses which use a workdir should use the value + # set by this method unless they were constructed with something more + # specific. + pass + + def addFactoryArguments(self, **kwargs): + self.factory[1].update(kwargs) + + def getStepFactory(self): + return self.factory + def setStepStatus(self, step_status): self.step_status = step_status @@ -895,6 +912,7 @@ def __init__(self, logfiles={}, *args, **kwargs): BuildStep.__init__(self, *args, **kwargs) + self.addFactoryArguments(logfiles=logfiles) # merge a class-level 'logfiles' attribute with one passed in as an # argument self.logfiles = self.logfiles.copy() Index: factory.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/process/factory.py,v retrieving revision 1.15 retrieving revision 1.16 diff -u -d -r1.15 -r1.16 --- factory.py 17 Sep 2006 20:35:49 -0000 1.15 +++ factory.py 18 Jun 2007 02:52:49 -0000 1.16 @@ -24,7 +24,12 @@ def __init__(self, steps=None): if steps is None: steps = [] - self.steps = steps + self.steps = [self._makeStepFactory(s) for s in steps] + + def _makeStepFactory(self, step_or_factory): + if isinstance(step_or_factory, BuildStep): + return step_or_factory.getStepFactory() + return step_or_factory def newBuild(self, request): """Create a new Build instance. @@ -32,11 +37,15 @@ """ b = self.buildClass(request) b.useProgress = self.useProgress - b.setSteps(self.steps) + b.setStepFactories(self.steps) return b - def addStep(self, steptype, **kwargs): - self.steps.append((steptype, kwargs)) + def addStep(self, step_or_factory, **kwargs): + if isinstance(step_or_factory, BuildStep): + s = step_or_factory.getStepFactory() + else: + s = (step_or_factory, dict(kwargs)) + self.steps.append(s) # BuildFactory subclasses for common build tools From warner at users.sourceforge.net Mon Jun 18 02:52:51 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 02:52:51 +0000 Subject: [Buildbot-commits] buildbot/buildbot/steps dummy.py, 1.4, 1.5 maxq.py, 1.1, 1.2 python_twisted.py, 1.3, 1.4 shell.py, 1.4, 1.5 source.py, 1.5, 1.6 transfer.py, 1.10, 1.11 Message-ID: Update of /cvsroot/buildbot/buildbot/buildbot/steps In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv23618/buildbot/steps Modified Files: dummy.py maxq.py python_twisted.py shell.py source.py transfer.py Log Message: [project @ use instances instead of class/kwarg tuples when putting BuildSteps in factories. Closes: #11.] Original author: warner at lothar.com Date: 2007-06-18 02:51:00+00:00 Index: dummy.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/steps/dummy.py,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- dummy.py 24 Nov 2006 07:16:35 -0000 1.4 +++ dummy.py 18 Jun 2007 02:52:49 -0000 1.5 @@ -20,6 +20,7 @@ @param timeout: the number of seconds to delay before completing """ BuildStep.__init__(self, **kwargs) + self.addFactoryArguments(timeout=timeout) self.timeout = timeout self.timer = None @@ -70,6 +71,7 @@ @param timeout: the number of seconds to delay """ LoggingBuildStep.__init__(self, **kwargs) + self.addFactoryArguments(timeout=timeout) self.timeout = timeout self.description = ["remote", "delay", "%s secs" % timeout] @@ -89,6 +91,7 @@ name = "wait" def __init__(self, handle, **kwargs): LoggingBuildStep.__init__(self, **kwargs) + self.addFactoryArguments(handle=handle) self.handle = handle def describe(self, done=False): Index: maxq.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/steps/maxq.py,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- maxq.py 15 Sep 2006 14:47:03 -0000 1.1 +++ maxq.py 18 Jun 2007 02:52:49 -0000 1.2 @@ -10,6 +10,7 @@ raise TypeError("please pass testdir") command = 'run_maxq.py %s' % (testdir,) ShellCommand.__init__(self, command=command, **kwargs) + self.addFactoryArguments(testdir=testdir) def startStatus(self): evt = event.Event("yellow", ['running', 'maxq', 'tests'], Index: python_twisted.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/steps/python_twisted.py,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- python_twisted.py 12 Dec 2006 03:24:03 -0000 1.3 +++ python_twisted.py 18 Jun 2007 02:52:49 -0000 1.4 @@ -34,6 +34,7 @@ def __init__(self, python=None, **kwargs): ShellCommand.__init__(self, **kwargs) + self.addFactoryArguments(python=python) self.python = python def start(self): @@ -332,6 +333,17 @@ timeout. """ ShellCommand.__init__(self, **kwargs) + self.addFactoryArguments(reactor=reactor, + python=python, + trial=trial, + testpath=testpath, + tests=tests, + testChanges=testChanges, + recurse=recurse, + randomly=randomly, + trialMode=trialMode, + trialArgs=trialArgs, + ) if python: self.python = python Index: shell.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/steps/shell.py,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- shell.py 26 Nov 2006 08:31:37 -0000 1.4 +++ shell.py 18 Jun 2007 02:52:49 -0000 1.5 @@ -81,7 +81,7 @@ # without dooming the entire build to a status of FAILURE flunkOnFailure = True - def __init__(self, workdir, + def __init__(self, workdir=None, description=None, descriptionDone=None, command=None, **kwargs): @@ -89,7 +89,7 @@ # that we create, but first strip out the ones that we pass to # BuildStep (like haltOnFailure and friends), and a couple that we # consume ourselves. - self.workdir = workdir # required by RemoteShellCommand + if description: self.description = description if isinstance(self.description, str): @@ -108,11 +108,18 @@ buildstep_kwargs[k] = kwargs[k] del kwargs[k] LoggingBuildStep.__init__(self, **buildstep_kwargs) + self.addFactoryArguments(workdir=workdir, + description=description, + descriptionDone=descriptionDone, + command=command) # everything left over goes to the RemoteShellCommand kwargs['workdir'] = workdir # including a copy of 'workdir' self.remote_kwargs = kwargs + def setDefaultWorkdir(self, workdir): + rkw = self.remote_kwargs + rkw['workdir'] = rkw['workdir'] or workdir def setCommand(self, command): self.command = command Index: source.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/steps/source.py,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- source.py 6 Feb 2007 20:36:16 -0000 1.5 +++ source.py 18 Jun 2007 02:52:49 -0000 1.6 @@ -22,7 +22,7 @@ branch = None # the default branch, should be set in __init__ - def __init__(self, workdir, mode='update', alwaysUseLatest=False, + def __init__(self, workdir=None, mode='update', alwaysUseLatest=False, timeout=20*60, retry=None, **kwargs): """ @type workdir: string @@ -105,6 +105,12 @@ """ LoggingBuildStep.__init__(self, **kwargs) + self.addFactoryArguments(workdir=workdir, + mode=mode, + alwaysUseLatest=alwaysUseLatest, + timeout=timeout, + retry=retry, + ) assert mode in ("update", "copy", "clobber", "export") if retry: @@ -132,6 +138,9 @@ self.description = description self.descriptionDone = descriptionDone + def setDefaultWorkdir(self, workdir): + self.args['workdir'] = self.args['workdir'] or workdir + def describe(self, done=False): if done: return self.descriptionDone @@ -206,7 +215,6 @@ def __init__(self, cvsroot, cvsmodule, global_options=[], branch=None, checkoutDelay=None, login=None, - clobber=0, export=0, copydir=None, **kwargs): """ @@ -263,20 +271,14 @@ self.checkoutDelay = checkoutDelay self.branch = branch - if not kwargs.has_key('mode') and (clobber or export or copydir): - # deal with old configs - warnings.warn("Please use mode=, not clobber/export/copydir", - DeprecationWarning) - if export: - kwargs['mode'] = "export" - elif clobber: - kwargs['mode'] = "clobber" - elif copydir: - kwargs['mode'] = "copy" - else: - kwargs['mode'] = "update" - Source.__init__(self, **kwargs) + self.addFactoryArguments(cvsroot=cvsroot, + cvsmodule=cvsmodule, + global_options=global_options, + branch=branch, + checkoutDelay=checkoutDelay, + login=login, + ) self.args.update({'cvsroot': cvsroot, 'cvsmodule': cvsmodule, @@ -383,6 +385,11 @@ self.branch = defaultBranch Source.__init__(self, **kwargs) + self.addFactoryArguments(svnurl=svnurl, + baseURL=baseURL, + defaultBranch=defaultBranch, + directory=directory, + ) if not svnurl and not baseURL: raise ValueError("you must use exactly one of svnurl and baseURL") @@ -502,7 +509,11 @@ self.baseURL = baseURL self.branch = defaultBranch Source.__init__(self, **kwargs) - assert kwargs['mode'] != "export", \ + self.addFactoryArguments(repourl=repourl, + baseURL=baseURL, + defaultBranch=defaultBranch, + ) + assert self.args['mode'] != "export", \ "Darcs does not have an 'export' mode" if (not repourl and not baseURL) or (repourl and baseURL): raise ValueError("you must provide exactly one of repourl and" @@ -565,6 +576,7 @@ """ self.branch = None # TODO Source.__init__(self, **kwargs) + self.addFactoryArguments(repourl=repourl) self.args['repourl'] = repourl def startVC(self, branch, revision, patch): @@ -609,6 +621,10 @@ """ self.branch = version Source.__init__(self, **kwargs) + self.addFactoryArguments(url=url, + version=version, + archive=archive, + ) self.args.update({'url': url, 'archive': archive, }) @@ -716,6 +732,10 @@ """ self.branch = version Source.__init__(self, **kwargs) + self.addFactoryArguments(url=url, + version=version, + archive=archive, + ) self.args.update({'url': url, 'archive': archive, }) @@ -769,6 +789,10 @@ self.baseURL = baseURL self.branch = defaultBranch Source.__init__(self, **kwargs) + self.addFactoryArguments(repourl=repourl, + baseURL=baseURL, + defaultBranch=defaultBranch, + ) if (not repourl and not baseURL) or (repourl and baseURL): raise ValueError("you must provide exactly one of repourl and" " baseURL") @@ -833,6 +857,10 @@ self.baseURL = baseURL self.branch = defaultBranch Source.__init__(self, **kwargs) + self.addFactoryArguments(repourl=repourl, + baseURL=baseURL, + defaultBranch=defaultBranch, + ) if (not repourl and not baseURL) or (repourl and baseURL): raise ValueError("you must provide exactly one of repourl and" " baseURL") @@ -900,14 +928,26 @@ self.branch = defaultBranch Source.__init__(self, **kwargs) + self.addFactoryArguments(p4base=p4base, + defaultBranch=defaultBranch, + p4port=p4port, + p4user=p4user, + p4passwd=p4passwd, + p4extra_views=p4extra_views, + p4client=p4client, + ) self.args['p4port'] = p4port self.args['p4user'] = p4user self.args['p4passwd'] = p4passwd self.args['p4base'] = p4base self.args['p4extra_views'] = p4extra_views - self.args['p4client'] = p4client % { - 'slave': self.build.slavename, - 'builder': self.build.builder.name, + self.p4client = p4client + + def setBuild(self, build): + Source.setBuild(self, build) + self.args['p4client'] = self.p4client % { + 'slave': build.slavename, + 'builder': build.builder.name, } def computeSourceRevision(self, changes): @@ -950,6 +990,11 @@ assert kwargs['mode'] == "copy", "P4Sync can only be used in mode=copy" self.branch = None Source.__init__(self, **kwargs) + self.addFactoryArguments(p4port=p4port, + p4user=p4user, + p4passwd=p4passwd, + p4client=p4client, + ) self.args['p4port'] = p4port self.args['p4user'] = p4user self.args['p4passwd'] = p4passwd @@ -982,6 +1027,11 @@ monotone="monotone", **kwargs): Source.__init__(self, **kwargs) + self.addFactoryArguments(server_addr=server_addr, + branch=branch, + db_path=db_path, + monotone=monotone, + ) self.args.update({"server_addr": server_addr, "branch": branch, "db_path": db_path, Index: transfer.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/steps/transfer.py,v retrieving revision 1.10 retrieving revision 1.11 diff -u -d -r1.10 -r1.11 --- transfer.py 17 Apr 2007 06:38:51 -0000 1.10 +++ transfer.py 18 Jun 2007 02:52:49 -0000 1.11 @@ -88,10 +88,17 @@ name = 'upload' - def __init__(self, build, slavesrc, masterdest, + def __init__(self, slavesrc, masterdest, workdir="build", maxsize=None, blocksize=16*1024, mode=None, **buildstep_kwargs): - BuildStep.__init__(self, build, **buildstep_kwargs) + BuildStep.__init__(self, **buildstep_kwargs) + self.addFactoryArguments(slavesrc=slavesrc, + masterdest=masterdest, + workdir=workdir, + maxsize=maxsize, + blocksize=blocksize, + mode=mode, + ) self.slavesrc = slavesrc self.masterdest = masterdest @@ -208,10 +215,17 @@ name = 'download' - def __init__(self, build, mastersrc, slavedest, + def __init__(self, mastersrc, slavedest, workdir="build", maxsize=None, blocksize=16*1024, mode=None, **buildstep_kwargs): - BuildStep.__init__(self, build, **buildstep_kwargs) + BuildStep.__init__(self, **buildstep_kwargs) + self.addFactoryArguments(mastersrc=mastersrc, + slavedest=slavedest, + workdir=workdir, + maxsize=maxsize, + blocksize=blocksize, + mode=mode, + ) self.mastersrc = mastersrc self.slavedest = slavedest From warner at users.sourceforge.net Mon Jun 18 02:52:52 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 02:52:52 +0000 Subject: [Buildbot-commits] buildbot/docs buildbot.texinfo,1.98,1.99 Message-ID: Update of /cvsroot/buildbot/buildbot/docs In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv23618/docs Modified Files: buildbot.texinfo Log Message: [project @ use instances instead of class/kwarg tuples when putting BuildSteps in factories. Closes: #11.] Original author: warner at lothar.com Date: 2007-06-18 02:51:00+00:00 Index: buildbot.texinfo =================================================================== RCS file: /cvsroot/buildbot/buildbot/docs/buildbot.texinfo,v retrieving revision 1.98 retrieving revision 1.99 diff -u -d -r1.98 -r1.99 --- buildbot.texinfo 7 Feb 2007 06:13:23 -0000 1.98 +++ buildbot.texinfo 18 Jun 2007 02:52:49 -0000 1.99 @@ -3175,25 +3175,31 @@ @section Build Steps @code{BuildStep}s are usually specified in the buildmaster's -configuration file, in a list of ``step specifications'' that is used -to create the @code{BuildFactory}. These ``step specifications'' are -not actual steps, but rather a tuple of the @code{BuildStep} subclass -to be created and a dictionary of arguments. (the actual - at code{BuildStep} instances are not created until the Build is started, -so that each Build gets an independent copy of each BuildStep). The -preferred way to create these step specifications is with the - at code{BuildFactory}'s @code{addStep} method: +configuration file, in a list that goes into the @code{BuildFactory}. +The @code{BuildStep} instances in this list are used as templates to +construct new independent copies for each build (so that state can be +kept on the @code{BuildStep} in one build without affecting a later +build). Each @code{BuildFactory} can be created with a list of steps, +or the factory can be created empty and then steps added to it using +the @code{addStep} method: @example from buildbot.steps import source, shell from buildbot.process import factory f = factory.BuildFactory() -f.addStep(source.SVN, svnurl="http://svn.example.org/Trunk/") -f.addStep(shell.ShellCommand, command=["make", "all"]) -f.addStep(shell.ShellCommand, command=["make", "test"]) +f.addStep(source.SVN(svnurl="http://svn.example.org/Trunk/")) +f.addStep(shell.ShellCommand(command=["make", "all"])) +f.addStep(shell.ShellCommand(command=["make", "test"])) @end example +In earlier versions (0.7.5 and older), these steps were specified with +a tuple of (step_class, keyword_arguments). Steps can still be +specified this way, but the preferred form is to pass actual + at code{BuildStep} instances to @code{addStep}, because that gives the + at code{BuildStep} class a chance to do some validation on the +arguments. + The rest of this section lists all the standard BuildStep objects available for use in a Build, and the parameters which can be used to control each. @@ -3380,7 +3386,6 @@ @node CVS, SVN, Source Checkout, Source Checkout @subsubsection CVS - @cindex CVS Checkout @bsindex buildbot.steps.source.CVS @@ -3531,11 +3536,11 @@ c['schedulers'] = [s1] f = factory.BuildFactory() -f.addStep(source.SVN, mode='update', - baseURL='svn://svn.example.org/MyProject/', - defaultBranch='trunk') -f.addStep(shell.Compile, command="make all") -f.addStep(shell.Test, command="make test") +f.addStep(source.SVN(mode='update', + baseURL='svn://svn.example.org/MyProject/', + defaultBranch='trunk')) +f.addStep(shell.Compile(command="make all")) +f.addStep(shell.Test(command="make test")) c['builders'] = [ @{'name':'test-i386', 'slavename':'bot-i386', 'builddir':'test-i386', @@ -4520,8 +4525,8 @@ @example f = BuildFactory() -f.addStep(SVN, svnurl="stuff") -f.addStep(Framboozle) +f.addStep(SVN(svnurl="stuff")) +f.addStep(Framboozle()) @end example Remember that master.cfg is secretly just a python program with one @@ -4563,8 +4568,8 @@ @example from framboozle import Framboozle f = BuildFactory() -f.addStep(SVN, svnurl="stuff") -f.addStep(Framboozle) +f.addStep(SVN(svnurl="stuff")) +f.addStep(Framboozle()) @end example or: @@ -4572,8 +4577,8 @@ @example import framboozle f = BuildFactory() -f.addStep(SVN, svnurl="stuff") -f.addStep(framboozle.Framboozle) +f.addStep(SVN(svnurl="stuff")) +f.addStep(framboozle.Framboozle()) @end example (check out the python docs for details about how "import" and "from A @@ -4630,8 +4635,8 @@ @example from buildbot.steps import framboozle f = BuildFactory() -f.addStep(SVN, svnurl="stuff") -f.addStep(framboozle.Framboozle) +f.addStep(SVN(svnurl="stuff")) +f.addStep(framboozle.Framboozle()) @end example And then you don't even have to install framboozle.py anywhere on your @@ -4809,9 +4814,9 @@ db_lock = locks.MasterLock("database") f = factory.BuildFactory() -f.addStep(source.SVN, svnurl="http://example.org/svn/Trunk") -f.addStep(shell.ShellCommand, command="make all") -f.addStep(shell.ShellCommand, command="make test", locks=[db_lock]) +f.addStep(source.SVN(svnurl="http://example.org/svn/Trunk")) +f.addStep(shell.ShellCommand(command="make all")) +f.addStep(shell.ShellCommand(command="make test", locks=[db_lock])) b1 = @{'name': 'full1', 'slavename': 'bot-1', builddir='f1', 'factory': f@} b2 = @{'name': 'full2', 'slavename': 'bot-2', builddir='f2', 'factory': f@} b3 = @{'name': 'full3', 'slavename': 'bot-3', builddir='f3', 'factory': f@} @@ -4869,11 +4874,10 @@ slavecounts = @{"bot-slow": 1, "bot-fast": 100@} cpu_lock = locks.SlaveLock("cpu", maxCountForSlave=slavecounts) f = factory.BuildFactory() -f.addStep(source.SVN, svnurl="http://example.org/svn/Trunk") -f.addStep(shell.ShellCommand, command="make all", locks=[cpu_lock]) -f.addStep(shell.ShellCommand, command="make test", locks=[cpu_lock]) -f.addStep(shell.ShellCommand, command="make db-test", - locks=[db_lock, cpu_lock]) +f.addStep(source.SVN(svnurl="http://example.org/svn/Trunk")) +f.addStep(shell.ShellCommand(command="make all", locks=[cpu_lock])) +f.addStep(shell.ShellCommand(command="make test", locks=[cpu_lock])) +f.addStep(shell.ShellCommand(command="make db-test", locks=[db_lock, cpu_lock])) b1 = @{'name': 'full1', 'slavename': 'bot-slow', builddir='full1', 'factory': f@} @@ -4979,40 +4983,49 @@ @bfindex buildbot.process.factory.BasicSVN The default @code{BuildFactory}, provided in the - at code{buildbot.process.factory} module, contains a list of ``BuildStep -specifications'': a list of @code{(step_class, kwargs)} tuples for -each. When asked to create a Build, it loads the list of steps into -the new Build object. When the Build is actually started, these step -specifications are used to create the actual set of BuildSteps, which -are then executed one at a time. For example, a build which consists -of a CVS checkout followed by a @code{make build} would be constructed -as follows: + at code{buildbot.process.factory} module, contains an internal list of +``BuildStep specifications'': a list of @code{(step_class, kwargs)} +tuples for each. These specification tuples are constructed when the +config file is read, by asking the instances passed to @code{addStep} +for their subclass and arguments. + +When asked to create a Build, the @code{BuildFactory} puts a copy of +the list of step specifications into the new Build object. When the +Build is actually started, these step specifications are used to +create the actual set of BuildSteps, which are then executed one at a +time. This serves to give each Build an independent copy of each step. +For example, a build which consists of a CVS checkout followed by a + at code{make build} would be constructed as follows: @example from buildbot.steps import source, shell from buildbot.process import factory f = factory.BuildFactory() -f.addStep(source.CVS, cvsroot=CVSROOT, cvsmodule="project", mode="update") -f.addStep(shell.Compile, command=["make", "build"]) +f.addStep(source.CVS(cvsroot=CVSROOT, cvsmodule="project", mode="update")) +f.addStep(shell.Compile(command=["make", "build"])) @end example -It is also possible to pass a list of step specifications into the +(To support config files from buildbot-0.7.5 and earlier, + at code{addStep} also accepts the @code{f.addStep(shell.Compile, +command=["make","build"])} form, although its use is discouraged +because then the @code{Compile} step doesn't get to validate or +complain about its arguments until build time. The modern +pass-by-instance approach allows this validation to occur while the +config file is being loaded, where the admin has a better chance of +noticing problems). + +It is also possible to pass a list of steps into the @code{BuildFactory} when it is created. Using @code{addStep} is usually simpler, but there are cases where is is more convenient to -create the list of steps ahead of time. To make this approach easier, -a convenience function named @code{s} is available: +create the list of steps ahead of time.: @example from buildbot.steps import source, shell from buildbot.process import factory -from buildbot.factory import s -# s is a convenience function, defined with: -# def s(steptype, **kwargs): return (steptype, kwargs) -all_steps = [s(source.CVS, cvsroot=CVSROOT, cvsmodule="project", - mode="update"), - s(shell.Compile, command=["make", "build"]), +all_steps = [source.CVS(cvsroot=CVSROOT, cvsmodule="project", mode="update"), + shell.Compile(command=["make", "build"]), ] f = factory.BuildFactory(all_steps) @end example @@ -5057,7 +5070,7 @@ reasonably appropriate flags set on them already. For example, without a source tree there is no point in continuing the build, so the @code{CVS} class has the @code{haltOnFailure} flag set to True. Look -in @file{buildbot/process/step.py} to see how the other Steps are +in @file{buildbot/steps/*.py} to see how the other Steps are marked. Each Step is created with an additional @code{workdir} argument that From warner at users.sourceforge.net Mon Jun 18 02:52:51 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 02:52:51 +0000 Subject: [Buildbot-commits] buildbot/buildbot/test runutils.py, 1.18, 1.19 test_config.py, 1.42, 1.43 test_properties.py, 1.6, 1.7 test_steps.py, 1.38, 1.39 test_vc.py, 1.73, 1.74 test_web.py, 1.43, 1.44 Message-ID: Update of /cvsroot/buildbot/buildbot/buildbot/test In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv23618/buildbot/test Modified Files: runutils.py test_config.py test_properties.py test_steps.py test_vc.py test_web.py Log Message: [project @ use instances instead of class/kwarg tuples when putting BuildSteps in factories. Closes: #11.] Original author: warner at lothar.com Date: 2007-06-18 02:51:00+00:00 Index: runutils.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/test/runutils.py,v retrieving revision 1.18 retrieving revision 1.19 diff -u -d -r1.18 -r1.19 --- runutils.py 11 Dec 2006 08:23:29 -0000 1.18 +++ runutils.py 18 Jun 2007 02:52:49 -0000 1.19 @@ -242,7 +242,8 @@ br = BuildRequest("reason", ss) b = Build([br]) b.setBuilder(b0) - s = step_class(build=b, **kwargs) + s = step_class(**kwargs) + s.setBuild(b) s.setStepStatus(bss) b.setupStatus(bss.getBuild()) s.slaveVersion = fake_slaveVersion @@ -398,9 +399,8 @@ workdir = "build" def makeStep(self, factory, **kwargs): - if not kwargs.has_key("workdir"): - kwargs['workdir'] = self.workdir step = makeBuildStep(self.masterbase, factory, **kwargs) + step.setDefaultWorkdir(self.workdir) return step def runStep(self, step): Index: test_config.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/test/test_config.py,v retrieving revision 1.42 retrieving revision 1.43 diff -u -d -r1.42 -r1.43 --- test_config.py 11 Dec 2006 08:40:17 -0000 1.42 +++ test_config.py 18 Jun 2007 02:52:49 -0000 1.43 @@ -22,8 +22,8 @@ from twisted.web.distrib import ResourcePublisher from buildbot.process.builder import Builder from buildbot.process.factory import BasicBuildFactory -from buildbot.steps.source import CVS -from buildbot.steps.shell import Compile, Test +from buildbot.steps.source import CVS, Darcs +from buildbot.steps.shell import Compile, Test, ShellCommand from buildbot.status import base words = None try: @@ -1099,3 +1099,81 @@ def _testStartService_4(self, res): self.failUnlessEqual(self.master.targetevents, [('stop', 'b'), ('start', 'a')]) + +cfg1 = \ +""" +from buildbot.process.factory import BuildFactory, s +from buildbot.steps.shell import ShellCommand +from buildbot.steps.source import Darcs +BuildmasterConfig = c = {} +c['bots'] = [('bot1', 'pw1')] +c['sources'] = [] +c['schedulers'] = [] +c['slavePortnum'] = 9999 +f1 = BuildFactory([ShellCommand(command='echo yes'), + s(ShellCommand, command='old-style'), + ]) +f1.addStep(Darcs(repourl='http://buildbot.net/repos/trunk')) +f1.addStep(ShellCommand, command='echo old-style') +c['builders'] = [{'name':'builder1', 'slavename':'bot1', + 'builddir':'workdir', 'factory':f1}] +""" + +class Factories(unittest.TestCase): + + def failUnlessExpectedShell(self, factory, defaults=True, **kwargs): + shell_args = {} + if defaults: + shell_args.update({'descriptionDone': None, + 'description': None, + 'workdir': None, + 'logfiles': {}, + }) + shell_args.update(kwargs) + self.failUnlessIdentical(factory[0], ShellCommand) + if factory[1] != shell_args: + print + print "factory had:" + for k in sorted(factory[1].keys()): + print k + print "but we were expecting:" + for k in sorted(shell_args.keys()): + print k + self.failUnlessEqual(factory[1], shell_args) + + def failUnlessExpectedDarcs(self, factory, **kwargs): + darcs_args = {'workdir': None, + 'alwaysUseLatest': False, + 'mode': 'update', + 'timeout': 1200, + 'retry': None, + 'baseURL': None, + 'defaultBranch': None, + 'logfiles': {}, + } + darcs_args.update(kwargs) + self.failUnlessIdentical(factory[0], Darcs) + if factory[1] != darcs_args: + print + print "factory had:" + for k in sorted(factory[1].keys()): + print k + print "but we were expecting:" + for k in sorted(darcs_args.keys()): + print k + self.failUnlessEqual(factory[1], darcs_args) + + def testSteps(self): + m = BuildMaster(".") + m.loadConfig(cfg1) + b = m.botmaster.builders["builder1"] + steps = b.buildFactory.steps + self.failUnlessEqual(len(steps), 4) + + self.failUnlessExpectedShell(steps[0], command="echo yes") + self.failUnlessExpectedShell(steps[1], defaults=False, + command="old-style") + self.failUnlessExpectedDarcs(steps[2], + repourl="http://buildbot.net/repos/trunk") + self.failUnlessExpectedShell(steps[3], defaults=False, + command="echo old-style") Index: test_properties.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/test/test_properties.py,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- test_properties.py 11 Dec 2006 08:23:29 -0000 1.6 +++ test_properties.py 18 Jun 2007 02:52:49 -0000 1.7 @@ -50,11 +50,12 @@ def testWithProperties(self): self.build.setProperty("revision", 47) self.failUnlessEqual(self.build_status.getProperty("revision"), 47) - c = ShellCommand(workdir=dir, build=self.build, + c = ShellCommand(workdir=dir, command=["tar", "czf", WithProperties("build-%s.tar.gz", "revision"), "source"]) + c.setBuild(self.build) cmd = c._interpolateProperties(c.command) self.failUnlessEqual(cmd, ["tar", "czf", "build-47.tar.gz", "source"]) @@ -62,55 +63,61 @@ def testWithPropertiesDict(self): self.build.setProperty("other", "foo") self.build.setProperty("missing", None) - c = ShellCommand(workdir=dir, build=self.build, + c = ShellCommand(workdir=dir, command=["tar", "czf", WithProperties("build-%(other)s.tar.gz"), "source"]) + c.setBuild(self.build) cmd = c._interpolateProperties(c.command) self.failUnlessEqual(cmd, ["tar", "czf", "build-foo.tar.gz", "source"]) def testWithPropertiesEmpty(self): self.build.setProperty("empty", None) - c = ShellCommand(workdir=dir, build=self.build, + c = ShellCommand(workdir=dir, command=["tar", "czf", WithProperties("build-%(empty)s.tar.gz"), "source"]) + c.setBuild(self.build) cmd = c._interpolateProperties(c.command) self.failUnlessEqual(cmd, ["tar", "czf", "build-.tar.gz", "source"]) def testCustomBuildStep(self): - c = MyBuildStep(workdir=dir, build=self.build) + c = MyBuildStep(workdir=dir) + c.setBuild(self.build) cmd = c._interpolateProperties(c.command) self.failUnlessEqual(cmd, ["tar", "czf", "build-1234.tar.gz", "source"]) def testSourceStamp(self): - c = ShellCommand(workdir=dir, build=self.build, + c = ShellCommand(workdir=dir, command=["touch", WithProperties("%s-dir", "branch"), WithProperties("%s-rev", "revision"), ]) + c.setBuild(self.build) cmd = c._interpolateProperties(c.command) self.failUnlessEqual(cmd, ["touch", "branch2-dir", "1234-rev"]) def testSlaveName(self): - c = ShellCommand(workdir=dir, build=self.build, + c = ShellCommand(workdir=dir, command=["touch", WithProperties("%s-slave", "slavename"), ]) + c.setBuild(self.build) cmd = c._interpolateProperties(c.command) self.failUnlessEqual(cmd, ["touch", "bot12-slave"]) def testBuildNumber(self): - c = ShellCommand(workdir=dir, build=self.build, + c = ShellCommand(workdir=dir, command=["touch", WithProperties("build-%d", "buildnumber"), WithProperties("builder-%s", "buildername"), ]) + c.setBuild(self.build) cmd = c._interpolateProperties(c.command) self.failUnlessEqual(cmd, ["touch", "build-5", "builder-fakebuilder"]) Index: test_steps.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/test/test_steps.py,v retrieving revision 1.38 retrieving revision 1.39 diff -u -d -r1.38 -r1.39 --- test_steps.py 7 Feb 2007 04:25:29 -0000 1.38 +++ test_steps.py 18 Jun 2007 02:52:49 -0000 1.39 @@ -98,8 +98,8 @@ dir = "murkle" self.expectedEvents = [] buildstep.RemoteCommand.commandCounter[0] = 3 - c = MyShellCommand(workdir=dir, command=cmd, build=self.build, - timeout=10) + c = MyShellCommand(workdir=dir, command=cmd, timeout=10) + c.setBuild(self.build) self.assertEqual(self.remote.events, self.expectedEvents) c.step_status = self.build_status.addStepWithName("myshellcommand") d = c.startStep(self.remote) Index: test_vc.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/test/test_vc.py,v retrieving revision 1.73 retrieving revision 1.74 diff -u -d -r1.73 -r1.74 --- test_vc.py 7 Feb 2007 04:25:29 -0000 1.73 +++ test_vc.py 18 Jun 2007 02:52:49 -0000 1.74 @@ -2484,7 +2484,8 @@ def testCVS1(self): r = base.BuildRequest("forced build", SourceStamp()) b = base.Build([r]) - s = source.CVS(cvsroot=None, cvsmodule=None, workdir=None, build=b) + s = source.CVS(cvsroot=None, cvsmodule=None) + s.setBuild(b) self.failUnlessEqual(s.computeSourceRevision(b.allChanges()), None) def testCVS2(self): @@ -2496,7 +2497,8 @@ submitted = "Wed, 08 Sep 2004 09:04:00 -0700" r.submittedAt = mktime_tz(parsedate_tz(submitted)) b = base.Build([r]) - s = source.CVS(cvsroot=None, cvsmodule=None, workdir=None, build=b) + s = source.CVS(cvsroot=None, cvsmodule=None) + s.setBuild(b) self.failUnlessEqual(s.computeSourceRevision(b.allChanges()), "Wed, 08 Sep 2004 16:03:00 -0000") @@ -2509,8 +2511,8 @@ submitted = "Wed, 08 Sep 2004 09:04:00 -0700" r.submittedAt = mktime_tz(parsedate_tz(submitted)) b = base.Build([r]) - s = source.CVS(cvsroot=None, cvsmodule=None, workdir=None, build=b, - checkoutDelay=10) + s = source.CVS(cvsroot=None, cvsmodule=None, checkoutDelay=10) + s.setBuild(b) self.failUnlessEqual(s.computeSourceRevision(b.allChanges()), "Wed, 08 Sep 2004 16:02:10 -0000") @@ -2530,14 +2532,16 @@ r2.submittedAt = mktime_tz(parsedate_tz(submitted)) b = base.Build([r1, r2]) - s = source.CVS(cvsroot=None, cvsmodule=None, workdir=None, build=b) + s = source.CVS(cvsroot=None, cvsmodule=None) + s.setBuild(b) self.failUnlessEqual(s.computeSourceRevision(b.allChanges()), "Wed, 08 Sep 2004 16:06:00 -0000") def testSVN1(self): r = base.BuildRequest("forced", SourceStamp()) b = base.Build([r]) - s = source.SVN(svnurl="dummy", workdir=None, build=b) + s = source.SVN(svnurl="dummy") + s.setBuild(b) self.failUnlessEqual(s.computeSourceRevision(b.allChanges()), None) def testSVN2(self): @@ -2547,7 +2551,8 @@ c.append(self.makeChange(revision=67)) r = base.BuildRequest("forced", SourceStamp(changes=c)) b = base.Build([r]) - s = source.SVN(svnurl="dummy", workdir=None, build=b) + s = source.SVN(svnurl="dummy") + s.setBuild(b) self.failUnlessEqual(s.computeSourceRevision(b.allChanges()), 67) class Patch(VCBase, unittest.TestCase): Index: test_web.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/test/test_web.py,v retrieving revision 1.43 retrieving revision 1.44 diff -u -d -r1.43 -r1.44 --- test_web.py 17 Apr 2007 06:38:51 -0000 1.43 +++ test_web.py 18 Jun 2007 02:52:49 -0000 1.44 @@ -412,7 +412,8 @@ bs.setReason("reason") bs.buildStarted(build1) - step1 = BuildStep(build=build1, name="setup") + step1 = BuildStep(name="setup") + step1.setBuild(build1) bss = bs.addStepWithName("setup") step1.setStepStatus(bss) bss.stepStarted() From warner at users.sourceforge.net Mon Jun 18 03:58:19 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 03:58:19 +0000 Subject: [Buildbot-commits] buildbot ChangeLog,1.855,1.856 Message-ID: Update of /cvsroot/buildbot/buildbot In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv19929 Modified Files: ChangeLog Log Message: [project @ steps/maxq.py: fix import errors] Original author: warner at lothar.com Date: 2007-06-18 03:33:33+00:00 Index: ChangeLog =================================================================== RCS file: /cvsroot/buildbot/buildbot/ChangeLog,v retrieving revision 1.855 retrieving revision 1.856 diff -u -d -r1.855 -r1.856 --- ChangeLog 18 Jun 2007 02:52:48 -0000 1.855 +++ ChangeLog 18 Jun 2007 03:58:17 -0000 1.856 @@ -1,5 +1,8 @@ 2007-06-17 Brian Warner + * buildbot/steps/maxq.py: fix import errors. Guess this hasn't been + used in a while.. + * buildbot/process/factory.py (BuildFactory.addStep): To simplify the config file, we're moving to using actual instances instead of the (class, kwargs) 'step specification' tuples. BuildFactory From warner at users.sourceforge.net Mon Jun 18 03:58:20 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 03:58:20 +0000 Subject: [Buildbot-commits] buildbot/buildbot/steps maxq.py,1.2,1.3 Message-ID: Update of /cvsroot/buildbot/buildbot/buildbot/steps In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv19929/buildbot/steps Modified Files: maxq.py Log Message: [project @ steps/maxq.py: fix import errors] Original author: warner at lothar.com Date: 2007-06-18 03:33:33+00:00 Index: maxq.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/steps/maxq.py,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- maxq.py 18 Jun 2007 02:52:49 -0000 1.2 +++ maxq.py 18 Jun 2007 03:58:17 -0000 1.3 @@ -1,5 +1,5 @@ from buildbot.steps.shell import ShellCommand -from buildbot.status import event, builder +from buildbot.status.builder import Event, SUCCESS, FAILURE class MaxQ(ShellCommand): flunkOnFailure = True @@ -13,8 +13,8 @@ self.addFactoryArguments(testdir=testdir) def startStatus(self): - evt = event.Event("yellow", ['running', 'maxq', 'tests'], - files={'log': self.log}) + evt = Event("yellow", ['running', 'maxq', 'tests'], + files={'log': self.log}) self.setCurrentActivity(evt) @@ -25,11 +25,10 @@ output = self.log.getAll() self.failures += output.count('\nTEST FAILURE:') - result = (builder.SUCCESS, ['maxq']) + result = (SUCCESS, ['maxq']) if self.failures: - result = (builder.FAILURE, - [str(self.failures), 'maxq', 'failures']) + result = (FAILURE, [str(self.failures), 'maxq', 'failures']) return self.stepComplete(result) From warner at users.sourceforge.net Mon Jun 18 03:58:25 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 03:58:25 +0000 Subject: [Buildbot-commits] buildbot/buildbot/steps maxq.py,1.3,1.4 Message-ID: Update of /cvsroot/buildbot/buildbot/buildbot/steps In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv19948/buildbot/steps Modified Files: maxq.py Log Message: [project @ test_config.py: make sure we can round-trip all of our current step classes] Original author: warner at lothar.com Date: 2007-06-18 03:51:32+00:00 Index: maxq.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/steps/maxq.py,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- maxq.py 18 Jun 2007 03:58:17 -0000 1.3 +++ maxq.py 18 Jun 2007 03:58:23 -0000 1.4 @@ -8,8 +8,8 @@ def __init__(self, testdir=None, **kwargs): if not testdir: raise TypeError("please pass testdir") - command = 'run_maxq.py %s' % (testdir,) - ShellCommand.__init__(self, command=command, **kwargs) + kwargs['command'] = 'run_maxq.py %s' % (testdir,) + ShellCommand.__init__(self, **kwargs) self.addFactoryArguments(testdir=testdir) def startStatus(self): From warner at users.sourceforge.net Mon Jun 18 03:58:25 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 03:58:25 +0000 Subject: [Buildbot-commits] buildbot ChangeLog,1.856,1.857 Message-ID: Update of /cvsroot/buildbot/buildbot In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv19948 Modified Files: ChangeLog Log Message: [project @ test_config.py: make sure we can round-trip all of our current step classes] Original author: warner at lothar.com Date: 2007-06-18 03:51:32+00:00 Index: ChangeLog =================================================================== RCS file: /cvsroot/buildbot/buildbot/ChangeLog,v retrieving revision 1.856 retrieving revision 1.857 diff -u -d -r1.856 -r1.857 --- ChangeLog 18 Jun 2007 03:58:17 -0000 1.856 +++ ChangeLog 18 Jun 2007 03:58:22 -0000 1.857 @@ -1,5 +1,10 @@ 2007-06-17 Brian Warner + * buildbot/test/test_config.py (Factories.testAllSteps): make sure + we can round-trip all of our current step classes by calling + getStepFactory() and using the results to make a clone of the + original step. + * buildbot/steps/maxq.py: fix import errors. Guess this hasn't been used in a while.. From warner at users.sourceforge.net Mon Jun 18 03:58:25 2007 From: warner at users.sourceforge.net (Brian Warner) Date: Mon, 18 Jun 2007 03:58:25 +0000 Subject: [Buildbot-commits] buildbot/buildbot/test test_config.py, 1.43, 1.44 Message-ID: Update of /cvsroot/buildbot/buildbot/buildbot/test In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv19948/buildbot/test Modified Files: test_config.py Log Message: [project @ test_config.py: make sure we can round-trip all of our current step classes] Original author: warner at lothar.com Date: 2007-06-18 03:51:32+00:00 Index: test_config.py =================================================================== RCS file: /cvsroot/buildbot/buildbot/buildbot/test/test_config.py,v retrieving revision 1.43 retrieving revision 1.44 diff -u -d -r1.43 -r1.44 --- test_config.py 18 Jun 2007 02:52:49 -0000 1.43 +++ test_config.py 18 Jun 2007 03:58:23 -0000 1.44 @@ -25,6 +25,8 @@ from buildbot.steps.source import CVS, Darcs from buildbot.steps.shell import Compile, Test, ShellCommand from buildbot.status import base +from buildbot.steps import dummy, maxq, python, python_twisted, shell, \ + source, transfer words = None try: from buildbot.status import words @@ -1177,3 +1179,41 @@ repourl="http://buildbot.net/repos/trunk") self.failUnlessExpectedShell(steps[3], defaults=False, command="echo old-style") + + def _loop(self, orig): + step_class, kwargs = orig.getStepFactory() + newstep = step_class(**kwargs) + return newstep + + def testAllSteps(self): + # make sure that steps can be created from the factories that they + # return + for s in ( dummy.Dummy(), dummy.FailingDummy(), dummy.RemoteDummy(), + maxq.MaxQ("testdir"), + python.BuildEPYDoc(), python.PyFlakes(), + python_twisted.HLint(), + python_twisted.Trial(testpath=None, tests="tests"), + python_twisted.ProcessDocs(), python_twisted.BuildDebs(), + python_twisted.RemovePYCs(), + shell.ShellCommand(), shell.TreeSize(), + shell.Configure(), shell.Compile(), shell.Test(), + source.CVS("cvsroot", "module"), + source.SVN("svnurl"), source.Darcs("repourl"), + source.Git("repourl"), + source.Arch("url", "version"), + source.Bazaar("url", "version", "archive"), + source.Bzr("repourl"), + source.Mercurial("repourl"), + source.P4("p4base"), + source.P4Sync(1234, "p4user", "passwd", "client", + mode="copy"), + source.Monotone("server", "branch"), + transfer.FileUpload("src", "dest"), + transfer.FileDownload("src", "dest"), + ): + try: + self._loop(s) + except: + print "error checking %s" % s + raise +