Ticket #87: 124.patch
| File 124.patch, 127.4 KB (added by dustin, 2 years ago) |
|---|
-
buildbot/buildset.py
Mon Apr 14 14:53:43 EDT 2008 dustin@v.igoro.us * #124:docs.patch Add properties to the documentation, along with a nontrivial reorganization of the scheduler section of the documentation. Mon Apr 14 12:06:01 EDT 2008 dustin@v.igoro.us * wrap-long-doc-line-2.patch Wrap another long line Mon Apr 14 12:03:44 EDT 2008 dustin@v.igoro.us * wrap-long-doc-line.patch Wrap an overly long line in the documentation. Sun Apr 13 16:26:53 EDT 2008 dustin@v.igoro.us * #124:display-properties-web-status.patch Display build properties in the build status page. Sun Apr 13 15:55:26 EDT 2008 dustin@v.igoro.us * #124:remove-custom-props.patch Remove custom properties, which are now largely redundant, and on which the properties interface was modeled. Sun Apr 13 14:42:31 EDT 2008 dustin@v.igoro.us * #124:schedulers-provide-properties.patch Make the scheduler classes actually provide properties to the buildsets they submit. Triggerable schedulers also do a nice job of configurably propagating properties from the triggering build. Sat Apr 12 20:32:42 EDT 2008 dustin@v.igoro.us * #124:global-properties.patch Support global properties, defined with c['properties'] = {} in master.cfg Sat Apr 12 19:46:58 EDT 2008 dustin@v.igoro.us * #124:scheduler-properties.patch Arrange for properties to come down from schedulers, via BuildStep and BuildRequest objects. custom_props are still present, in parallel, but will go eventually. Triggered builds no longer propagate custom props. Sat Apr 12 18:11:03 EDT 2008 dustin@v.igoro.us * #124:getProperty-returns-property-only.patch Change Property.getProperty to just return the property value, since having getProperty return different things on different objects is confusing. Sat Apr 12 16:58:59 EDT 2008 dustin@v.igoro.us * #124:properties-class.patch Add and use a Properties class, refactor the way properties are rendered, and update unit tests accordingly. Sat Apr 12 01:50:01 EDT 2008 dustin@v.igoro.us * #124:remove-customBuildProperties-unused.patch Remove unused and undocumented customBuildProperties config. This did not allow users to specify global properties; rather, it specified descriptions for properties that were not used anywhere in the codebase. diff -rN -u old-124/buildbot/buildset.py new-124/buildbot/buildset.pyold new 1 2 1 from buildbot.process import base 3 2 from buildbot.status import builder 3 from buildbot.process.properties import Properties 4 4 5 5 6 6 class BuildSet: … … 11 11 (source.changes=list).""" 12 12 13 13 def __init__(self, builderNames, source, reason=None, bsid=None, 14 scheduler=None, custom_props=None):14 properties=None): 15 15 """ 16 16 @param source: a L{buildbot.sourcestamp.SourceStamp} 17 17 """ … … 19 19 self.source = source 20 20 self.reason = reason 21 21 22 if not custom_props: custom_props = {}23 self.custom_props = custom_props22 self.properties = Properties() 23 if properties: self.properties.updateFromProperties(properties) 24 24 25 25 self.stillHopeful = True 26 26 self.status = bss = builder.BuildSetStatus(source, reason, 27 27 builderNames, bsid) 28 self.scheduler = scheduler29 28 30 29 def waitUntilSuccess(self): 31 30 return self.status.waitUntilSuccess() … … 41 40 # create the requests 42 41 for b in builders: 43 42 req = base.BuildRequest(self.reason, self.source, b.name, 44 scheduler=self.scheduler, 45 custom_props=self.custom_props) 43 properties=self.properties) 46 44 reqs.append((b, req)) 47 45 self.requests.append(req) 48 46 d = req.waitUntilFinished() -
buildbot/buildslave.py
diff -rN -u old-124/buildbot/buildslave.py new-124/buildbot/buildslave.py
old new 11 11 from buildbot.status.builder import SlaveStatus 12 12 from buildbot.status.mail import MailNotifier 13 13 from buildbot.interfaces import IBuildSlave 14 from buildbot.process.properties import Properties 14 15 15 16 class BuildSlave(NewCredPerspective, service.MultiService): 16 17 """This is the master-side representative for a remote buildbot slave. … … 26 27 implements(IBuildSlave) 27 28 28 29 def __init__(self, name, password, max_builds=None, 29 notify_on_missing=[], missing_timeout=3600): 30 notify_on_missing=[], missing_timeout=3600, 31 properties={}): 30 32 """ 31 33 @param name: botname this machine will supply when it connects 32 34 @param password: password this machine will supply when … … 34 36 @param max_builds: maximum number of simultaneous builds that will 35 37 be run concurrently on this buildslave (the 36 38 default is None for no limit) 39 @param properties: properties that will be applied to builds run on 40 this slave 41 @type properties: dictionary 37 42 """ 38 43 service.MultiService.__init__(self) 39 44 self.slavename = name … … 44 49 self.slave_commands = None 45 50 self.slavebuilders = [] 46 51 self.max_builds = max_builds 52 53 self.properties = Properties() 54 self.properties.update(properties, "BuildSlave") 55 self.properties.setProperty("slavename", name, "BuildSlave") 56 47 57 self.lastMessageReceived = 0 48 58 if isinstance(notify_on_missing, str): 49 59 notify_on_missing = [notify_on_missing] -
buildbot/clients/debug.py
diff -rN -u old-124/buildbot/clients/debug.py new-124/buildbot/clients/debug.py
old new 105 105 if revision == '': 106 106 revision = None 107 107 reason = "debugclient 'Request Build' button pushed" 108 custom_props = {}108 properties = {} 109 109 d = self.remote.callRemote("requestBuild", 110 name, reason, branch, revision, custom_props)110 name, reason, branch, revision, properties) 111 111 d.addErrback(self.err) 112 112 113 113 def do_ping(self, widget): -
buildbot/interfaces.py
diff -rN -u old-124/buildbot/interfaces.py new-124/buildbot/interfaces.py
old new 38 38 class IScheduler(Interface): 39 39 """I watch for Changes in the source tree and decide when to trigger 40 40 Builds. I create BuildSet objects and submit them to the BuildMaster. I 41 am a service, and the BuildMaster is always my parent.""" 41 am a service, and the BuildMaster is always my parent. 42 43 @ivar properties: properties to be applied to all builds started by this 44 scheduler 45 @type properties: L<buildbot.process.properties.Properties> 46 """ 42 47 43 48 def addChange(change): 44 49 """A Change has just been dispatched by one of the ChangeSources. -
buildbot/master.py
diff -rN -u old-124/buildbot/master.py new-124/buildbot/master.py
old new 27 27 from buildbot.sourcestamp import SourceStamp 28 28 from buildbot.buildslave import BuildSlave 29 29 from buildbot import interfaces 30 from buildbot.process.properties import Properties 30 31 31 32 ######################################## 32 33 … … 227 228 def detached(self, mind): 228 229 pass 229 230 230 def perspective_requestBuild(self, buildername, reason, branch, revision, custom_props): 231 assert isinstance(custom_props, dict), \ 232 "custom_props must be a dict (not %r)" % (custom_props,) 233 234 # Provide default values for any custom build properties the 235 # client did not send. 236 for propertyDict in (self.master.customBuildProperties or []): 237 custom_props.setdefault(propertyDict['propertyName'], "") 238 231 def perspective_requestBuild(self, buildername, reason, branch, revision, properties={}): 239 232 c = interfaces.IControl(self.master) 240 233 bc = c.getBuilder(buildername) 241 234 ss = SourceStamp(branch, revision) 242 br = BuildRequest(reason, ss, builderName=buildername, custom_props=custom_props) 235 properties = Properties() 236 properties.update(properties, "remote requestBuild") 237 br = BuildRequest(reason, ss, builderName=buildername, properties=properties) 243 238 bc.requestBuild(br) 244 239 245 240 def perspective_pingBuilder(self, buildername): … … 347 342 projectURL = None 348 343 buildbotURL = None 349 344 change_svc = None 350 customBuildProperties = None345 properties = Properties() 351 346 352 347 def __init__(self, basedir, configFileName="master.cfg"): 353 348 service.MultiService.__init__(self) … … 503 498 "schedulers", "builders", 504 499 "slavePortnum", "debugPassword", "manhole", 505 500 "status", "projectName", "projectURL", "buildbotURL", 506 " customBuildProperties"501 "properties" 507 502 ) 508 503 for k in config.keys(): 509 504 if k not in known_keys: … … 531 526 projectName = config.get('projectName') 532 527 projectURL = config.get('projectURL') 533 528 buildbotURL = config.get('buildbotURL') 534 customBuildProperties = config.get('customBuildProperties')529 properties = config.get('properties', {}) 535 530 536 531 except KeyError, e: 537 532 log.msg("config dictionary is missing a required parameter") … … 663 658 else: 664 659 locks[l.name] = l 665 660 661 if not isinstance(properties, dict): 662 raise ValueError("c['properties'] must be a dictionary") 663 666 664 # slavePortnum supposed to be a strports specification 667 665 if type(slavePortnum) is int: 668 666 slavePortnum = "tcp:%d" % slavePortnum … … 677 675 self.projectName = projectName 678 676 self.projectURL = projectURL 679 677 self.buildbotURL = buildbotURL 680 self.customBuildProperties = customBuildProperties 678 679 self.properties = Properties() 680 self.properties.update(properties, self.configFileName) 681 681 682 682 # self.slaves: Disconnect any that were attached and removed from the 683 683 # list. Update self.checker with the new list of passwords, including -
buildbot/process/base.py
diff -rN -u old-124/buildbot/process/base.py new-124/buildbot/process/base.py
old new 11 11 from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION 12 12 from buildbot.status.builder import Results, BuildRequestStatus 13 13 from buildbot.status.progress import BuildProgress 14 from buildbot.process.properties import Properties 14 15 15 16 class BuildRequest: 16 17 """I represent a request to a specific Builder to run a single build. … … 39 40 provide this, but for forced builds the user requesting the 40 41 build will provide a string. 41 42 42 @type custom_props: dictionary.43 @ivar custom_props: custom user properties.43 @type properties: Properties object 44 @ivar properties: properties that should be applied to this build 44 45 45 46 @ivar status: the IBuildStatus object which tracks our status 46 47 … … 52 53 source = None 53 54 builder = None 54 55 startCount = 0 # how many times we have tried to start this build 55 custom_props = {}56 56 57 57 implements(interfaces.IBuildRequestControl) 58 58 59 def __init__(self, reason, source, builderName=None, scheduler=None, custom_props=None):59 def __init__(self, reason, source, builderName=None, properties=None): 60 60 # TODO: remove the =None on builderName, it is there so I don't have 61 61 # to change a lot of tests that create BuildRequest objects 62 62 assert interfaces.ISourceStamp(source, None) 63 63 self.reason = reason 64 64 self.source = source 65 self.scheduler = scheduler66 65 67 if not custom_props: custom_props = {} 68 self.custom_props = custom_props 69 assert isinstance(self.custom_props, dict), \ 70 "custom_props must be a dict (not %r)" % (self.custom_props,) 66 self.properties = Properties() 67 if properties: 68 self.properties.updateFromProperties(properties) 71 69 72 70 self.start_watchers = [] 73 71 self.finish_watchers = [] … … 95 93 self.finish_watchers.append(d) 96 94 return d 97 95 98 def customProps(self):99 return self.custom_props100 101 96 # these are called by the Builder 102 97 103 98 def requestSubmitted(self, builder): … … 182 177 # build a source stamp 183 178 self.source = requests[0].mergeWith(requests[1:]) 184 179 self.reason = requests[0].mergeReasons(requests[1:]) 185 self.scheduler = requests[0].scheduler186 187 # Set custom properties.188 self.custom_properties = requests[0].customProps()189 190 #self.abandoned = False191 180 192 181 self.progress = None 193 182 self.currentStep = None … … 207 196 def getSourceStamp(self): 208 197 return self.source 209 198 210 def setProperty(self, propname, value ):199 def setProperty(self, propname, value, source): 211 200 """Set a property on this build. This may only be called after the 212 201 build has started, so that it has a BuildStatus object where the 213 202 properties can live.""" 214 self.build_status.setProperty(propname, value )203 self.build_status.setProperty(propname, value, source) 215 204 216 def get CustomProperties(self):217 return self. custom_properties205 def getProperties(self): 206 return self.build_status.getProperties() 218 207 219 208 def getProperty(self, propname): 220 return self.build_status. properties[propname]209 return self.build_status.getProperty(propname) 221 210 222 211 def allChanges(self): 223 212 return self.source.changes … … 275 264 def getSlaveName(self): 276 265 return self.slavebuilder.slave.slavename 277 266 278 def setupStatus(self, build_status): 279 self.build_status = build_status 280 self.setProperty("buildername", self.builder.name) 281 self.setProperty("buildnumber", self.build_status.number) 282 self.setProperty("branch", self.source.branch) 283 self.setProperty("revision", self.source.revision) 284 if self.scheduler is None: 285 self.setProperty("scheduler", "none") 286 else: 287 self.setProperty("scheduler", self.scheduler.name) 288 for key, userProp in self.custom_properties.items(): 289 self.setProperty(key, userProp) 267 def setupProperties(self): 268 props = self.getProperties() 269 270 # start with global properties from the configuration 271 buildmaster = self.builder.botmaster.parent 272 props.updateFromProperties(buildmaster.properties) 273 274 # get any properties from requests (this is the path through 275 # which schedulers will send us properties) 276 for rq in self.requests: 277 props.updateFromProperties(rq.properties) 278 279 # now set some properties of our own, corresponding to the 280 # build itself 281 props.setProperty("buildername", self.builder.name, "Build") 282 props.setProperty("buildnumber", self.build_status.number, "Build") 283 props.setProperty("branch", self.source.branch, "Build") 284 props.setProperty("revision", self.source.revision, "Build") 290 285 291 286 def setupSlaveBuilder(self, slavebuilder): 292 287 self.slavebuilder = slavebuilder 288 289 # navigate our way back to the L{buildbot.buildslave.BuildSlave} 290 # object that came from the config, and get its properties 291 buildslave_properties = slavebuilder.slave.properties 292 self.getProperties().updateFromProperties(buildslave_properties) 293 293 294 self.slavename = slavebuilder.slave.slavename 294 295 self.build_status.setSlavename(self.slavename) 295 self.setProperty("slavename", self.slavename)296 296 297 297 def startBuild(self, build_status, expectations, slavebuilder): 298 298 """This method sets up the build, then starts it by invoking the … … 305 305 # the Deferred returned by this method. 306 306 307 307 log.msg("%s.startBuild" % self) 308 self. setupStatus(build_status)308 self.build_status = build_status 309 309 # now that we have a build_status, we can set properties 310 self.setupProperties() 310 311 self.setupSlaveBuilder(slavebuilder) 311 312 312 313 # convert all locks into their real forms -
buildbot/process/builder.py
diff -rN -u old-124/buildbot/process/builder.py new-124/buildbot/process/builder.py
old new 253 253 @type building: list of L{buildbot.process.base.Build} 254 254 @ivar building: Builds that are actively running 255 255 256 @type slaves: list of L{buildbot.buildslave.BuildSlave} objects 257 @ivar slaves: the slaves currently available for building 256 258 """ 257 259 258 260 expectations = None # this is created the first time we get a good build … … 445 447 """This is invoked by the BuildSlave when the self.slavename bot 446 448 registers their builder. 447 449 448 @type slave: L{buildbot. master.BuildSlave}450 @type slave: L{buildbot.buildslave.BuildSlave} 449 451 @param slave: the BuildSlave that represents the buildslave as a whole 450 452 @type remote: L{twisted.spread.pb.RemoteReference} 451 453 @param remote: a reference to the L{buildbot.slave.bot.SlaveBuilder} -
buildbot/process/buildstep.py
diff -rN -u old-124/buildbot/process/buildstep.py new-124/buildbot/process/buildstep.py
old new 632 632 def getProperty(self, propname): 633 633 return self.build.getProperty(propname) 634 634 635 def setProperty(self, propname, value ):636 self.build.setProperty(propname, value )635 def setProperty(self, propname, value, source): 636 self.build.setProperty(propname, value, source) 637 637 638 638 def startStep(self, remote): 639 639 """Begin the step. This returns a Deferred that will fire when the … … 1092 1092 self.step_status.setText(self.getText(cmd, results)) 1093 1093 self.step_status.setText2(self.maybeGetText2(cmd, results)) 1094 1094 1095 class _BuildPropertyMapping: 1096 def __init__(self, build): 1097 self.build = build 1098 def __getitem__(self, name): 1099 p = self.build.getProperty(name) 1100 if p is None: 1101 p = "" 1102 return p 1103 1104 class WithProperties(util.ComparableMixin): 1105 """This is a marker class, used in ShellCommand's command= argument to 1106 indicate that we want to interpolate a build property. 1107 """ 1108 1109 compare_attrs = ('fmtstring', 'args') 1110 1111 def __init__(self, fmtstring, *args): 1112 self.fmtstring = fmtstring 1113 self.args = args 1114 1115 def render(self, build): 1116 pmap = _BuildPropertyMapping(build) 1117 if self.args: 1118 strings = [] 1119 for name in self.args: 1120 strings.append(pmap[name]) 1121 s = self.fmtstring % tuple(strings) 1122 else: 1123 s = self.fmtstring % pmap 1124 return s 1125 1126 def render_properties(s, build): 1127 """Return a string based on s and build that is suitable for use 1128 in a running BuildStep. If s is a string, return s. If s is a 1129 WithProperties object, return the result of s.render(build). 1130 Otherwise, return str(s). 1131 """ 1132 if isinstance(s, (str, unicode)): 1133 return s 1134 elif isinstance(s, WithProperties): 1135 return s.render(build) 1136 else: 1137 return str(s) 1095 # (WithProeprties used to be available in this module) 1096 from buildbot.process.properties import WithProperties -
buildbot/process/properties.py
diff -rN -u old-124/buildbot/process/properties.py new-124/buildbot/process/properties.py
old new 1 from zope.interface import implements 2 from buildbot import util 3 from twisted.python import log 4 from twisted.python.failure import Failure 5 6 class Properties(util.ComparableMixin): 7 """ 8 I represent a set of properties that can be interpolated into various 9 strings in buildsteps. 10 11 @ivar properties: dictionary mapping property values to tuples 12 (value, source), where source is a string identifing the source 13 of the property. 14 15 Objects of this class can be read like a dictionary -- in this case, 16 only the property value is returned. 17 18 As a special case, a property value of None is returned as an empty 19 string when used as a mapping. 20 """ 21 22 compare_attrs = ('properties') 23 24 def __init__(self, **kwargs): 25 """ 26 @param kwargs: initial property values (for testing) 27 """ 28 self.properties = {} 29 if kwargs: self.update(kwargs, "TEST") 30 31 def __getitem__(self, name): 32 """Just get the value for this property, special-casing None -> ''""" 33 rv = self.properties[name][0] 34 if rv is None: rv = '' 35 return rv 36 37 def has_key(self, name): 38 return self.properties.has_key(name) 39 40 def getProperty(self, name, default=None): 41 """Get the value for the given property, with no None -> '' special case""" 42 return self.properties.get(name, (default,))[0] 43 44 def getPropertySource(self, name): 45 return self.properties[name][1] 46 47 def asList(self): 48 """Return the properties as a sorted list of (name, value, source)""" 49 l = [ (k, v[0], v[1]) for k,v in self.properties.items() ] 50 l.sort() 51 return l 52 53 def __repr__(self): 54 return repr(dict([ (k,v[0]) for k,v in self.properties.iteritems() ])) 55 56 def setProperty(self, name, value, source): 57 self.properties[name] = (value, source) 58 59 def update(self, dict, source): 60 """Update this object from a dictionary, with an explicit source specified.""" 61 for k, v in dict.items(): 62 self.properties[k] = (v, source) 63 64 def updateFromProperties(self, other): 65 """Update this object based on another object; the other object's """ 66 self.properties.update(other.properties) 67 68 def render(self, value): 69 """ 70 Return a variant of value that has any WithProperties objects 71 substituted. This recurses into Python's compound data types. 72 """ 73 if isinstance(value, (str, unicode)): 74 return value 75 elif isinstance(value, WithProperties): 76 return value.render(self) 77 elif isinstance(value, list): 78 return [ self.render(e) for e in value ] 79 elif isinstance(value, tuple): 80 return tuple([ self.render(e) for e in value ]) 81 elif isinstance(value, dict): 82 return dict([ (self.render(k), self.render(v)) for k,v in value.iteritems() ]) 83 else: 84 return value 85 86 class WithProperties(util.ComparableMixin): 87 """This is a marker class, used in ShellCommand's command= argument to 88 indicate that we want to interpolate a build property. 89 """ 90 91 compare_attrs = ('fmtstring', 'args') 92 93 def __init__(self, fmtstring, *args): 94 self.fmtstring = fmtstring 95 self.args = args 96 97 def render(self, properties): 98 if self.args: 99 strings = [] 100 for name in self.args: 101 strings.append(properties[name]) 102 s = self.fmtstring % tuple(strings) 103 else: 104 s = self.fmtstring % properties 105 return s -
buildbot/scheduler.py
diff -rN -u old-124/buildbot/scheduler.py new-124/buildbot/scheduler.py
old new 14 14 from buildbot.status import builder 15 15 from buildbot.sourcestamp import SourceStamp 16 16 from buildbot.changes.maildir import MaildirService 17 from buildbot.process.properties import Properties 17 18 18 19 19 20 class BaseScheduler(service.MultiService, util.ComparableMixin): 21 """ 22 A Schduler creates BuildSets and submits them to the BuildMaster. 23 24 @ivar name: name of the scheduler 25 26 @ivar properties: additional properties specified in this 27 scheduler's configuration 28 @type properties: Properties object 29 """ 20 30 implements(interfaces.IScheduler) 21 31 22 def __init__(self, name): 32 def __init__(self, name, properties={}): 33 """ 34 @param name: name for this scheduler 35 36 @param properties: properties to be propagated from this scheduler 37 @type properties: dict 38 """ 23 39 service.MultiService.__init__(self) 24 40 self.name = name 41 self.properties = Properties() 42 self.properties.update(properties, "Scheduler") 43 self.properties.setProperty("scheduler", name, "Scheduler") 25 44 26 45 def __repr__(self): 27 46 # TODO: why can't id() return a positive number? %d is ugly. 28 47 return "<Scheduler '%s' at %d>" % (self.name, id(self)) 29 48 30 def submit (self, bs):49 def submitBuildSet(self, bs): 31 50 self.parent.submitBuildSet(bs) 32 51 33 52 def addChange(self, change): … … 36 55 class BaseUpstreamScheduler(BaseScheduler): 37 56 implements(interfaces.IUpstreamScheduler) 38 57 39 def __init__(self, name ):40 BaseScheduler.__init__(self, name )58 def __init__(self, name, properties={}): 59 BaseScheduler.__init__(self, name, properties) 41 60 self.successWatchers = [] 42 61 43 62 def subscribeToSuccessfulBuilds(self, watcher): … … 45 64 def unsubscribeToSuccessfulBuilds(self, watcher): 46 65 self.successWatchers.remove(watcher) 47 66 48 def submit (self, bs):67 def submitBuildSet(self, bs): 49 68 d = bs.waitUntilFinished() 50 69 d.addCallback(self.buildSetFinished) 51 self.parent.submitBuildSet(bs)70 BaseScheduler.submitBuildSet(self, bs) 52 71 53 72 def buildSetFinished(self, bss): 54 73 if not self.running: … … 69 88 70 89 fileIsImportant = None 71 90 compare_attrs = ('name', 'treeStableTimer', 'builderNames', 'branch', 72 'fileIsImportant' )91 'fileIsImportant', 'properties') 73 92 74 93 def __init__(self, name, branch, treeStableTimer, builderNames, 75 fileIsImportant=None ):94 fileIsImportant=None, properties={}): 76 95 """ 77 96 @param name: the name of this Scheduler 78 97 @param branch: The branch name that the Scheduler should pay … … 94 113 build is triggered by an important change. 95 114 The default value of None means that all 96 115 Changes are important. 116 117 @param properties: properties to apply to all builds started from this 118 scheduler 97 119 """ 98 120 99 BaseUpstreamScheduler.__init__(self, name )121 BaseUpstreamScheduler.__init__(self, name, properties) 100 122 self.treeStableTimer = treeStableTimer 101 123 errmsg = ("The builderNames= argument to Scheduler must be a list " 102 124 "of Builder description names (i.e. the 'name' key of the " … … 171 193 # create a BuildSet, submit it to the BuildMaster 172 194 bs = buildset.BuildSet(self.builderNames, 173 195 SourceStamp(changes=changes), 174 scheduler=self)175 self.submit (bs)196 properties=self.properties) 197 self.submitBuildSet(bs) 176 198 177 199 def stopService(self): 178 200 self.stopTimer() … … 188 210 fileIsImportant = None 189 211 190 212 compare_attrs = ('name', 'branches', 'treeStableTimer', 'builderNames', 191 'fileIsImportant' )213 'fileIsImportant', 'properties') 192 214 193 215 def __init__(self, name, branches, treeStableTimer, builderNames, 194 fileIsImportant=None ):216 fileIsImportant=None, properties={}): 195 217 """ 196 218 @param name: the name of this Scheduler 197 219 @param branches: The branch names that the Scheduler should pay … … 216 238 build is triggered by an important change. 217 239 The default value of None means that all 218 240 Changes are important. 241 242 @param properties: properties to apply to all builds started from this 243 scheduler 219 244 """ 220 245 221 BaseUpstreamScheduler.__init__(self, name )246 BaseUpstreamScheduler.__init__(self, name, properties) 222 247 self.treeStableTimer = treeStableTimer 223 248 for b in builderNames: 224 249 assert isinstance(b, str) … … 272 297 self.schedulers[branch] = s 273 298 s.addChange(change) 274 299 275 def submitBuildSet(self, bs):276 self.parent.submitBuildSet(bs)277 278 300 279 301 class Dependent(BaseUpstreamScheduler): 280 302 """This scheduler runs some set of 'downstream' builds when the 281 303 'upstream' scheduler has completed successfully.""" 282 304 283 compare_attrs = ('name', 'upstream', 'builders' )305 compare_attrs = ('name', 'upstream', 'builders', 'properties') 284 306 285 def __init__(self, name, upstream, builderNames ):307 def __init__(self, name, upstream, builderNames, properties={}): 286 308 assert interfaces.IUpstreamScheduler.providedBy(upstream) 287 BaseUpstreamScheduler.__init__(self, name )309 BaseUpstreamScheduler.__init__(self, name, properties) 288 310 self.upstream = upstream 289 311 self.builderNames = builderNames 290 312 … … 305 327 return d 306 328 307 329 def upstreamBuilt(self, ss): 308 bs = buildset.BuildSet(self.builderNames, ss, scheduler=self) 309 self.submit(bs) 330 bs = buildset.BuildSet(self.builderNames, ss, 331 properties=self.properties) 332 self.submitBuildSet(bs) 310 333 311 334 312 335 … … 319 342 # TODO: consider having this watch another (changed-based) scheduler and 320 343 # merely enforce a minimum time between builds. 321 344 322 compare_attrs = ('name', 'builderNames', 'periodicBuildTimer', 'branch' )345 compare_attrs = ('name', 'builderNames', 'periodicBuildTimer', 'branch', 'properties') 323 346 324 347 def __init__(self, name, builderNames, periodicBuildTimer, 325 branch=None ):326 BaseUpstreamScheduler.__init__(self, name )348 branch=None, properties={}): 349 BaseUpstreamScheduler.__init__(self, name, properties) 327 350 self.builderNames = builderNames 328 351 self.periodicBuildTimer = periodicBuildTimer 329 352 self.branch = branch … … 344 367 def doPeriodicBuild(self): 345 368 bs = buildset.BuildSet(self.builderNames, 346 369 SourceStamp(branch=self.branch), 347 self.reason, scheduler=self) 348 self.submit(bs) 370 self.reason, 371 properties=self.properties) 372 self.submitBuildSet(bs) 349 373 350 374 351 375 … … 389 413 390 414 compare_attrs = ('name', 'builderNames', 391 415 'minute', 'hour', 'dayOfMonth', 'month', 392 'dayOfWeek', 'branch' )416 'dayOfWeek', 'branch', 'properties') 393 417 394 418 def __init__(self, name, builderNames, minute=0, hour='*', 395 419 dayOfMonth='*', month='*', dayOfWeek='*', 396 branch=None ):420 branch=None, properties={}): 397 421 # Setting minute=0 really makes this an 'Hourly' scheduler. This 398 422 # seemed like a better default than minute='*', which would result in 399 423 # a build every 60 seconds. 400 BaseUpstreamScheduler.__init__(self, name )424 BaseUpstreamScheduler.__init__(self, name, properties) 401 425 self.builderNames = builderNames 402 426 self.minute = minute 403 427 self.hour = hour … … 502 526 # And trigger a build 503 527 bs = buildset.BuildSet(self.builderNames, 504 528 SourceStamp(branch=self.branch), 505 self.reason, scheduler=self) 506 self.submit(bs) 529 self.reason, 530 properties=self.properties) 531 self.submitBuildSet(bs) 507 532 508 533 def addChange(self, change): 509 534 pass 510 535 511 536 512 537 513 class TryBase(service.MultiService, util.ComparableMixin): 514 implements(interfaces.IScheduler) 515 516 def __init__(self, name, builderNames): 517 service.MultiService.__init__(self) 518 self.name = name 538 class TryBase(BaseScheduler): 539 def __init__(self, name, builderNames, properties={}): 540 BaseScheduler.__init__(self, name, properties) 519 541 self.builderNames = builderNames 520 542 521 543 def listBuilderNames(self): … … 546 568 self.error = True 547 569 548 570 class Try_Jobdir(TryBase): 549 compare_attrs = ["name", "builderNames", "jobdir"]571 compare_attrs = ( 'name', 'builderNames', 'jobdir', 'properties' ) 550 572 551 def __init__(self, name, builderNames, jobdir ):552 TryBase.__init__(self, name, builderNames )573 def __init__(self, name, builderNames, jobdir, properties={}): 574 TryBase.__init__(self, name, builderNames, properties) 553 575 self.jobdir = jobdir 554 576 self.watcher = MaildirService() 555 577 self.watcher.setServiceParent(self) … … 624 646 return 625 647 626 648 reason = "'try' job" 627 bs = buildset.BuildSet(builderNames, ss, reason=reason, bsid=bsid, scheduler=self) 628 self.parent.submitBuildSet(bs) 649 bs = buildset.BuildSet(builderNames, ss, reason=reason, 650 bsid=bsid, properties=self.properties) 651 self.submitBuildSet(bs) 629 652 630 653 class Try_Userpass(TryBase): 631 compare_attrs = ["name", "builderNames", "port", "userpass"]654 compare_attrs = ( 'name', 'builderNames', 'port', 'userpass', 'properties' ) 632 655 implements(portal.IRealm) 633 656 634 def __init__(self, name, builderNames, port, userpass ):635 TryBase.__init__(self, name, builderNames )657 def __init__(self, name, builderNames, port, userpass, properties={}): 658 TryBase.__init__(self, name, builderNames, properties) 636 659 if type(port) is int: 637 660 port = "tcp:%d" % port 638 661 self.port = port … … 657 680 p = Try_Userpass_Perspective(self, avatarID) 658 681 return (pb.IPerspective, p, lambda: None) 659 682 660 def submitBuildSet(self, bs):661 return self.parent.submitBuildSet(bs)662 663 683 class Try_Userpass_Perspective(pbutil.NewCredPerspective): 664 684 def __init__(self, parent, username): 665 685 self.parent = parent 666 686 self.username = username 667 687 668 def perspective_try(self, branch, revision, patch, builderNames, 669 custom_props): 688 def perspective_try(self, branch, revision, patch, builderNames, properties={}): 670 689 log.msg("user %s requesting build on builders %s" % (self.username, 671 690 builderNames)) 672 691 for b in builderNames: … … 678 697 ss = SourceStamp(branch, revision, patch) 679 698 reason = "'try' job from user %s" % self.username 680 699 700 # roll the specified props in with our inherited props 701 combined_props = Properties() 702 combined_props.updateFromProperties(self.parent.properties) 703 combined_props.update(properties, "try build") 704 681 705 bs = buildset.BuildSet(builderNames, 682 706 ss, 683 707 reason=reason, 684 scheduler=self, 685 custom_props=custom_props) 708 properties=combined_props) 686 709 687 710 self.parent.submitBuildSet(bs) 688 711 … … 696 719 the builds that I fire have finished. 697 720 """ 698 721 699 def __init__(self, name, builderNames): 700 BaseUpstreamScheduler.__init__(self, name) 722 compare_attrs = ('name', 'builderNames', 'properties') 723 724 def __init__(self, name, builderNames, properties={}): 725 BaseUpstreamScheduler.__init__(self, name, properties) 701 726 self.builderNames = builderNames 702 727 703 728 def listBuilderNames(self): … … 706 731 def getPendingBuildTimes(self): 707 732 return [] 708 733 709 def trigger(self, ss, custom_props={}):734 def trigger(self, ss, set_props=None): 710 735 """Trigger this scheduler. Returns a deferred that will fire when the 711 736 buildset is finished. 712 737 """ 713 bs = buildset.BuildSet(self.builderNames, ss, scheduler=self, custom_props=custom_props) 738 739 # properties for this buildset are composed of our own properties, 740 # potentially overridden by anything from the triggering build 741 props = Properties() 742 props.updateFromProperties(self.properties) 743 if set_props: props.updateFromProperties(set_props) 744 745 bs = buildset.BuildSet(self.builderNames, ss, properties=props) 714 746 d = bs.waitUntilFinished() 715 self.submit (bs)747 self.submitBuildSet(bs) 716 748 return d -
buildbot/scripts/runner.py
diff -rN -u old-124/buildbot/scripts/runner.py new-124/buildbot/scripts/runner.py
old new 763 763 764 764 ["builder", "b", None, 765 765 "Run the trial build on this Builder. Can be used multiple times."], 766 [" customproperties", None, None,767 "A set of customproperties made available in the build environment, format:prop=value,propb=valueb..."],766 ["properties", None, None, 767 "A set of properties made available in the build environment, format:prop=value,propb=valueb..."], 768 768 ] 769 769 770 770 optFlags = [ … … 774 774 def __init__(self): 775 775 super(TryOptions, self).__init__() 776 776 self['builders'] = [] 777 self[' custom_props'] = {}777 self['properties'] = {} 778 778 779 779 def opt_builder(self, option): 780 780 self['builders'].append(option) 781 781 782 def opt_customproperties(self, option): 783 # We need to split the value of this option into a dictionary of custom 784 # properties 785 custom_props = {} 782 def opt_properties(self, option): 783 # We need to split the value of this option into a dictionary of properties 784 properties = {} 786 785 propertylist = option.split(",") 787 786 for i in range(0,len(propertylist)): 788 787 print propertylist[i] 789 788 splitproperty = propertylist[i].split("=") 790 custom_props[splitproperty[0]] = splitproperty[1]791 self[' custom_props'] = custom_props789 properties[splitproperty[0]] = splitproperty[1] 790 self['properties'] = properties 792 791 793 792 def opt_patchlevel(self, option): 794 793 self['patchlevel'] = int(option) -
buildbot/scripts/tryclient.py
diff -rN -u old-124/buildbot/scripts/tryclient.py new-124/buildbot/scripts/tryclient.py
old new 448 448 ss.revision, 449 449 ss.patch, 450 450 self.builderNames, 451 self.config.get(' custom_props', {}))451 self.config.get('properties', {})) 452 452 d.addCallback(self._deliverJob_pb2) 453 453 return d 454 454 def _deliverJob_pb2(self, status): -
buildbot/status/builder.py
diff -rN -u old-124/buildbot/status/builder.py new-124/buildbot/status/builder.py
old new 5 5 from twisted.persisted import styles 6 6 from twisted.internet import reactor, defer 7 7 from twisted.protocols import basic 8 from buildbot.process.properties import Properties 8 9 9 10 import os, shutil, sys, re, urllib, itertools 10 11 from cPickle import load, dump … … 888 889 889 890 class BuildStatus(styles.Versioned): 890 891 implements(interfaces.IBuildStatus, interfaces.IStatusEvent) 891 persistenceVersion = 2892 persistenceVersion = 3 892 893 893 894 source = None 894 895 reason = None … … 924 925 self.finishedWatchers = [] 925 926 self.steps = [] 926 927 self.testResults = {} 927 self.properties = {}928 self.properties = Properties() 928 929 929 930 # IBuildStatus 930 931 … … 937 938 def getProperty(self, propname): 938 939 return self.properties[propname] 939 940 941 def getProperties(self): 942 return self.properties 943 940 944 def getNumber(self): 941 945 return self.number 942 946 … … 1074 1078 self.steps.append(s) 1075 1079 return s 1076 1080 1077 def setProperty(self, propname, value ):1078 self.properties [propname] = value1081 def setProperty(self, propname, value, source): 1082 self.properties.setProperty(propname, value, source) 1079 1083 1080 1084 def addTestResult(self, result): 1081 1085 self.testResults[result.getName()] = result … … 1229 1233 def upgradeToVersion2(self): 1230 1234 self.properties = {} 1231 1235 1236 def upgradeToVersion3(self): 1237 # in version 3, self.properties became a Properties object 1238 propdict = self.properties 1239 self.properties = Properties() 1240 self.properties.update(propdict) 1241 1232 1242 def upgradeLogfiles(self): 1233 1243 # upgrade any LogFiles that need it. This must occur after we've been 1234 1244 # attached to our Builder, and after we know about all LogFiles of … … 1789 1799 return self.botmaster.parent.projectURL 1790 1800 def getBuildbotURL(self): 1791 1801 return self.botmaster.parent.buildbotURL 1792 def getCustomBuildProperties(self):1793 return self.botmaster.parent.customBuildProperties1794 1802 1795 1803 def getURLForThing(self, thing): 1796 1804 prefix = self.getBuildbotURL() -
buildbot/status/web/build.py
diff -rN -u old-124/buildbot/status/web/build.py new-124/buildbot/status/web/build.py
old new 121 121 data += " </li>\n" 122 122 data += "</ol>\n" 123 123 124 data += "<h2>Build Properties:</h2>\n" 125 data += "<table><tr><th valign=\"left\">Name</th><th valign=\"left\">Value</th><th valign=\"left\">Source</th></tr>\n" 126 for name, value, source in b.getProperties().asList(): 127 data += "<tr><td>%s</td><td>%s</td><td>%s</td></tr>\n" % (name, value, source) 128 data += "</table>" 129 124 130 data += "<h2>Blamelist:</h2>\n" 125 131 if list(b.getResponsibleUsers()): 126 132 data += " <ol>\n" -
buildbot/status/web/builder.py
diff -rN -u old-124/buildbot/status/web/builder.py new-124/buildbot/status/web/builder.py
old new 152 152 153 153 return data 154 154 155 def force(self, req , custom_props={}):155 def force(self, req): 156 156 """ 157 157 158 158 Custom properties can be passed from the web form. To do … … 161 161 by inspecting req.args), then pass them to this superclass 162 162 force method. 163 163 164 @param custom_props: Custom properties to set on build165 166 164 """ 167 165 name = req.args.get("username", ["<unknown>"])[0] 168 166 reason = req.args.get("comments", ["<no reason specified>"])[0] … … 196 194 # button, use their name instead of None, so they'll be informed of 197 195 # the results. 198 196 s = SourceStamp(branch=branch, revision=revision) 199 req = BuildRequest(r, s, builderName=self.builder_status.getName() , custom_props=custom_props)197 req = BuildRequest(r, s, builderName=self.builder_status.getName()) 200 198 try: 201 199 self.builder_control.requestBuildSoon(req) 202 200 except interfaces.NoSlaveError: -
buildbot/steps/python.py
diff -rN -u old-124/buildbot/steps/python.py new-124/buildbot/steps/python.py
old new 96 96 if counts[m]: 97 97 self.descriptionDone.append("%s=%d" % (m, counts[m])) 98 98 self.addCompleteLog(m, "".join(summaries[m])) 99 self.setProperty("pyflakes-%s" % m, counts[m] )100 self.setProperty("pyflakes-total", sum(counts.values()) )99 self.setProperty("pyflakes-%s" % m, counts[m], "pyflakes") 100 self.setProperty("pyflakes-total", sum(counts.values()), "pyflakes") 101 101 102 102 103 103 def evaluateCommand(self, cmd): -
buildbot/steps/shell.py
diff -rN -u old-124/buildbot/steps/shell.py new-124/buildbot/steps/shell.py
old new 2 2 3 3 import re 4 4 from twisted.python import log 5 from buildbot.process.buildstep import LoggingBuildStep, RemoteShellCommand, \ 6 render_properties 5 from buildbot.process.buildstep import LoggingBuildStep, RemoteShellCommand 7 6 from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE 8 7 9 # for existing configurations that import WithProperties from here 10 from buildbot.process.buildstep import WithProperties 8 # for existing configurations that import WithProperties from here. We like 9 # to move this class around just to keep our readers guessing. 10 from buildbot.process.properties import WithProperties 11 11 12 12 class ShellCommand(LoggingBuildStep): 13 13 """I run a single shell command on the buildslave. I return FAILURE if … … 118 118 if self.description is not None: 119 119 return self.description 120 120 121 properties = self.build.getProperties() 121 122 words = self.command 122 123 if isinstance(words, (str, unicode)): 123 124 words = words.split() 124 125 # render() each word to handle WithProperties objects 125 words = [render_properties(word, self.build) for word in words]126 words = properties.render(words) 126 127 if len(words) < 1: 127 128 return ["???"] 128 129 if len(words) == 1: … … 131 132 return ["'%s" % words[0], "%s'" % words[1]] 132 133 return ["'%s" % words[0], "%s" % words[1], "...'"] 133 134 134 def _interpolateProperties(self, value):135 """136 Expand the L{WithProperties} objects in L{value}137 """138 if isinstance(value, (str, unicode, bool, int, float, type(None))):139 return value140 141 if isinstance(value, list):142 return [self._interpolateProperties(val) for val in value]143 144 if isinstance(value, tuple):145 return tuple([self._interpolateProperties(val) for val in value])146 147 if isinstance(value, dict):148 new_dict = { }149 for key, val in value.iteritems():150 new_key = self._interpolateProperties(key)151 new_dict[new_key] = self._interpolateProperties(val)152 return new_dict153 154 # To make sure we catch anything we forgot155 assert isinstance(value, WithProperties), \156 "%s (%s) is not a WithProperties" % (value, type(value))157 158 return value.render(self.build)159 160 def _interpolateWorkdir(self, workdir):161 return render_properties(workdir, self.build)162 163 135 def setupEnvironment(self, cmd): 136 # XXX is this used? documented? replaced by properties? 164 137 # merge in anything from Build.slaveEnvironment . Earlier steps 165 138 # (perhaps ones which compile libraries or sub-projects that need to 166 139 # be referenced by later steps) can add keys to 167 140 # self.build.slaveEnvironment to affect later steps. 141 properties = self.build.getProperties() 168 142 slaveEnv = self.build.slaveEnvironment 169 143 if slaveEnv: 170 144 if cmd.args['env'] is None: 171 145 cmd.args['env'] = {} 172 cmd.args['env'].update( self._interpolateProperties(slaveEnv))146 cmd.args['env'].update(properties.render(slaveEnv)) 173 147 # note that each RemoteShellCommand gets its own copy of the 174 148 # dictionary, so we shouldn't be affecting anyone but ourselves. 175 149 … … 199 173 # this block is specific to ShellCommands. subclasses that don't need 200 174 # to set up an argv array, an environment, or extra logfiles= (like 201 175 # the Source subclasses) can just skip straight to startCommand() 202 command = self._interpolateProperties(self.command)203 assert isinstance(command, (list, tuple, str)) 176 properties = self.build.getProperties() 177 204 178 # create the actual RemoteShellCommand instance now 205 kwargs = self._interpolateProperties(self.remote_kwargs) 206 kwargs['workdir'] = self._interpolateWorkdir(kwargs['workdir']) 207 kwargs['command'] = command 179 kwargs = properties.render(self.remote_kwargs) 180 kwargs['command'] = properties.render(self.command) 208 181 kwargs['logfiles'] = self.logfiles 209 182 cmd = RemoteShellCommand(**kwargs) 210 183 self.setupEnvironment(cmd) … … 224 197 m = re.search(r'^(\d+)', out) 225 198 if m: 226 199 self.kib = int(m.group(1)) 227 self.setProperty("tree-size-KiB", self.kib )200 self.setProperty("tree-size-KiB", self.kib, "treesize") 228 201 229 202 def evaluateCommand(self, cmd): 230 203 if cmd.rc != 0: … … 298 271 old_count = self.getProperty("warnings-count") 299 272 except KeyError: 300 273 old_count = 0 301 self.setProperty("warnings-count", old_count + self.warnCount )274 self.setProperty("warnings-count", old_count + self.warnCount, "WarningCountingShellCommand") 302 275 303 276 304 277 def evaluateCommand(self, cmd): -
buildbot/steps/source.py
diff -rN -u old-124/buildbot/steps/source.py new-124/buildbot/steps/source.py
old new 187 187 got_revision = None 188 188 if cmd.updates.has_key("got_revision"): 189 189 got_revision = str(cmd.updates["got_revision"][-1]) 190 self.setProperty("got_revision", got_revision )190 self.setProperty("got_revision", got_revision, "Source") 191 191 192 192 193 193 -
buildbot/steps/transfer.py
diff -rN -u old-124/buildbot/steps/transfer.py new-124/buildbot/steps/transfer.py
old new 4 4 from twisted.internet import reactor 5 5 from twisted.spread import pb 6 6 from twisted.python import log 7 from buildbot.process.buildstep import RemoteCommand, BuildStep, \ 8 render_properties 7 from buildbot.process.buildstep import RemoteCommand, BuildStep 9 8 from buildbot.process.buildstep import SUCCESS, FAILURE 10 9 from buildbot.interfaces import BuildSlaveTooOldError 11 10 … … 111 110 112 111 def start(self): 113 112 version = self.slaveVersion("uploadFile") 113 properties = self.build.getProperties() 114 114 115 if not version: 115 116 m = "slave is too old, does not know about uploadFile" 116 117 raise BuildSlaveTooOldError(m) 117 118 118 source = render_properties(self.slavesrc, self.build)119 masterdest = render_properties(self.masterdest, self.build)119 source = properties.render(self.slavesrc) 120 masterdest = properties.render(self.masterdest) 120 121 # we rely upon the fact that the buildmaster runs chdir'ed into its 121 122 # basedir to make sure that relative paths in masterdest are expanded 122 123 # properly. TODO: maybe pass the master's basedir all the way down … … 237 238 self.mode = mode 238 239 239 240 def start(self): 241 properties = self.build.getProperties() 242 240 243 version = self.slaveVersion("downloadFile") 241 244 if not version: 242 245 m = "slave is too old, does not know about downloadFile" … … 244 247 245 248 # we are currently in the buildmaster's basedir, so any non-absolute 246 249 # paths will be interpreted relative to that 247 source = os.path.expanduser(render_properties(self.mastersrc, 248 self.build)) 249 slavedest = render_properties(self.slavedest, self.build) 250 source = os.path.expanduser(properties.render(self.mastersrc)) 251 slavedest = properties.render(self.slavedest) 250 252 log.msg("FileDownload started, from master %r to slave %r" % 251 253 (source, slavedest)) 252 254 -
buildbot/steps/trigger.py
diff -rN -u old-124/buildbot/steps/trigger.py new-124/buildbot/steps/trigger.py
old new 1 1 from buildbot.process.buildstep import LoggingBuildStep, SUCCESS, FAILURE, EXCEPTION 2 from buildbot. steps.shell import WithProperties2 from buildbot.process.properties import WithProperties, Properties 3 3 from buildbot.scheduler import Triggerable 4 4 from twisted.internet import defer 5 5 … … 12 12 flunkOnFailure = True 13 13 14 14 def __init__(self, schedulerNames=[], updateSourceStamp=True, 15 waitForFinish=False, **kwargs):15 waitForFinish=False, set_properties={}, **kwargs): 16 16 """ 17 17 Trigger the given schedulers when this step is executed. 18 18 … … 33 33 schedulers. If True, I will wait until all of 34 34 the triggered schedulers have finished their 35 35 builds. 36 37 @param set_properties: A dictionary of properties to set for any 38 builds resulting from this trigger. To copy 39 existing properties, use WithProperties. These 40 properties will override properties set in the 41 Triggered scheduler's constructor. 42 36 43 """ 37 44 assert schedulerNames, "You must specify a scheduler to trigger" 38 45 self.schedulerNames = schedulerNames 39 46 self.updateSourceStamp = updateSourceStamp 40 47 self.waitForFinish = waitForFinish 48 self.set_properties = set_properties 41 49 self.running = False 42 50 LoggingBuildStep.__init__(self, **kwargs) 43 51 self.addFactoryArguments(schedulerNames=schedulerNames, … … 51 59 self.step_status.setText(["interrupted"]) 52 60 53 61 def start(self): 54 custom_props = {} 62 properties = self.build.getProperties() 63 64 # make a new properties object from a dict rendered by the old 65 # properties object 66 props_to_set = Properties() 67 props_to_set.update(properties.render(self.set_properties), "Trigger") 68 55 69 self.running = True 56 70 ss = self.build.getSourceStamp() 57 71 if self.updateSourceStamp: 58 got = None 59 try: 60 got = self.build.getProperty('got_revision') 61 except KeyError: 62 pass 72 got = properties.getProperty('got_revision') 63 73 if got: 64 74 ss = ss.getAbsoluteSourceStamp(got) 65 custom_props = self.build.getCustomProperties() 75 66 76 # (is there an easier way to find the BuildMaster?) 67 77 all_schedulers = self.build.builder.botmaster.parent.allSchedulers() 68 78 all_schedulers = dict([(sch.name, sch) for sch in all_schedulers]) … … 72 82 # TODO: don't fire any schedulers if we discover an unknown one 73 83 dl = [] 74 84 for scheduler in self.schedulerNames: 75 if isinstance(scheduler, WithProperties): 76 scheduler = scheduler.render(self.build) 85 scheduler = properties.render(scheduler) 77 86 if all_schedulers.has_key(scheduler): 78 87 sch = all_schedulers[scheduler] 79 88 if isinstance(sch, Triggerable): 80 dl.append(sch.trigger(ss, custom_props))89 dl.append(sch.trigger(ss, set_props=props_to_set)) 81 90 triggered_schedulers.append(scheduler) 82 91 else: 83 92 unknown_schedulers.append(scheduler) … … 101 110 else: 102 111 d = defer.succeed([]) 103 112 104 # TODO: review this shadowed 'rc' value: can the callback modify the105 # one that was defined above?106 113 def cb(rclist): 107 rc = SUCCESS 114 rc = SUCCESS # (this rc is not the same variable as that above) 108 115 for was_cb, buildsetstatus in rclist: 109 116 # TODO: make this algo more configurable 110 117 if not was_cb: -
buildbot/test/runutils.py
diff -rN -u old-124/buildbot/test/runutils.py new-124/buildbot/test/runutils.py
old new 13 13 from buildbot.process.buildstep import BuildStep 14 14 from buildbot.sourcestamp import SourceStamp 15 15 from buildbot.status import builder 16 from buildbot.process.properties import Properties 16 17 17 18 18 19 … … 152 153 if bs.getResults() != builder.SUCCESS: 153 154 log.msg("failUnlessBuildSucceeded noticed that the build failed") 154 155 self.logBuildResults(bs) 155 self.failUnless (bs.getResults() ==builder.SUCCESS)156 self.failUnlessEqual(bs.getResults(), builder.SUCCESS) 156 157 return bs # useful for chaining 157 158 158 159 def logBuildResults(self, bs): … … 275 276 from buildbot.slave.registry import commandRegistry 276 277 return commandRegistry[command] 277 278 279 class FakeBuildMaster: 280 properties = Properties(masterprop="master") 281 282 class FakeBotMaster: 283 parent = FakeBuildMaster() 284 278 285 def makeBuildStep(basedir, step_class=BuildStep, **kwargs): 279 286 bss = setupBuildStepStatus(basedir) 280 287 … … 282 289 setup = {'name': "builder1", "slavename": "bot1", 283 290 'builddir': "builddir", 'factory': None} 284 291 b0 = Builder(setup, bss.getBuild().getBuilder()) 292 b0.botmaster = FakeBotMaster() 285 293 br = BuildRequest("reason", ss) 286 294 b = Build([br]) 287 295 b.setBuilder(b0) 288 296 s = step_class(**kwargs) 289 297 s.setBuild(b) 290 298 s.setStepStatus(bss) 291 b.setupStatus(bss.getBuild()) 299 b.build_status = bss.getBuild() 300 b.setupProperties() 292 301 s.slaveVersion = fake_slaveVersion 293 302 return s 294 303 … … 480 489 self.value = value 481 490 482 491 def start(self): 483 _flags[self.flagname] = self.value 492 properties = self.build.getProperties() 493 _flags[self.flagname] = properties.render(self.value) 484 494 self.finished(builder.SUCCESS) 485 495 486 496 class TestFlagMixin: -
buildbot/test/test_properties.py
diff -rN -u old-124/buildbot/test/test_properties.py new-124/buildbot/test/test_properties.py
old new 6 6 7 7 from buildbot.sourcestamp import SourceStamp 8 8 from buildbot.process import base 9 from buildbot.steps.shell import ShellCommand, WithProperties 9 from buildbot.process.properties import WithProperties, Properties 10 from buildbot.steps.shell import ShellCommand 10 11 from buildbot.status import builder 11 12 from buildbot.slave.commands import rmdirRecursive 12 13 from buildbot.test.runutils import RunMixin … … 14 15 15 16 class FakeBuild: 16 17 pass 18 class FakeBuildMaster: 19 properties = Properties(masterprop="master") 20 class FakeBotMaster: 21 parent = FakeBuildMaster() 17 22 class FakeBuilder: 18 23 statusbag = None 19 24 name = "fakebuilder" 25 botmaster = FakeBotMaster() 20 26 class FakeSlave: 21 27 slavename = "bot12" 28 properties = Properties(slavename="bot12") 22 29 class FakeSlaveBuilder: 23 30 slave = FakeSlave() 24 31 def getSlaveCommandVersion(self, command, oldversion=None): … … 26 33 class FakeScheduler: 27 34 name = "fakescheduler" 28 35 29 class Interpolate(unittest.TestCase):36 class TestProperties(unittest.TestCase): 30 37 def setUp(self): 31 self.builder = FakeBuilder() 32 self.builder_status = builder.BuilderStatus("fakebuilder") 33 self.builder_status.basedir = "test_properties" 34 self.builder_status.nextBuildNumber = 5 35 rmdirRecursive(self.builder_status.basedir) 36 os.mkdir(self.builder_status.basedir) 37 self.build_status = self.builder_status.newBuild() 38 req = base.BuildRequest("reason", SourceStamp(branch="branch2", 39 revision=1234)) 40 self.build = base.Build([req]) 41 self.build.setBuilder(self.builder) 42 self.build.setupStatus(self.build_status) 43 self.build.setupSlaveBuilder(FakeSlaveBuilder()) 38 self.props = Properties() 39 40 def testDictBehavior(self): 41 self.props.setProperty("do-tests", 1, "scheduler") 42 self.props.setProperty("do-install", 2, "scheduler") 43 44 self.assert_(self.props.has_key('do-tests')) 45 self.failUnlessEqual(self.props['do-tests'], 1) 46 self.failUnlessEqual(self.props['do-install'], 2) 47 self.assertRaises(KeyError, lambda : self.props['do-nothing']) 48 self.failUnlessEqual(self.props.getProperty('do-install'), 2) 49 50 def testEmpty(self): 51 # test the special case for Null 52 self.props.setProperty("x", None, "hi") 53 self.failUnlessEqual(self.props.getProperty('x'), None) 54 self.failUnlessEqual(self.props['x'], '') 55 56 def testUpdate(self): 57 self.props.setProperty("x", 24, "old") 58 newprops = { 'a' : 1, 'b' : 2 } 59 self.props.update(newprops, "new") 60 61 self.failUnlessEqual(self.props.getProperty('x'), 24) 62 self.failUnlessEqual(self.props.getPropertySource('x'), 'old') 63 self.failUnlessEqual(self.props.getProperty('a'), 1) 64 self.failUnlessEqual(self.props.getPropertySource('a'), 'new') 65 66 def testUpdateFromProperties(self): 67 self.props.setProperty("x", 24, "old") 68 newprops = Properties() 69 newprops.setProperty('a', 1, "new") 70 newprops.setProperty('b', 2, "new") 71 self.props.updateFromProperties(newprops) 72 73 self.failUnlessEqual(self.props.getProperty('x'), 24) 74 self.failUnlessEqual(self.props.getPropertySource('x'), 'old') 75 self.failUnlessEqual(self.props.getProperty('a'), 1) 76 self.failUnlessEqual(self.props.getPropertySource('a'), 'new') 77 78 # render() is pretty well tested by TestWithProperties 79 80 class TestWithProperties(unittest.TestCase): 81 def setUp(self): 82 self.props = Properties() 44 83 45 def testWithProperties(self): 46 self.build.setProperty("revision", 47) 47 self.failUnlessEqual(self.build_status.getProperty("revision"), 47) 48 c = ShellCommand(workdir=dir, 49 command=["tar", "czf", 50 WithProperties("build-%s.tar.gz", 51 "revision"), 52 "source"]) 53 c.setBuild(self.build) 54 cmd = c._interpolateProperties(c.command) 55 self.failUnlessEqual(cmd, 56 ["tar", "czf", "build-47.tar.gz", "source"]) 57 self.failUnlessEqual(self.build.getProperty("scheduler"), "none") 58 59 def testWorkdir(self): 60 self.build.setProperty("revision", 47) 61 self.failUnlessEqual(self.build_status.getProperty("revision"), 47) 62 c = ShellCommand(command=["tar", "czf", "foo.tar.gz", "source"]) 63 c.setBuild(self.build) 64 workdir = WithProperties("workdir-%d", "revision") 65 workdir = c._interpolateWorkdir(workdir) 66 self.failUnlessEqual(workdir, "workdir-47") 67 68 def testWithPropertiesDict(self): 69 self.build.setProperty("other", "foo") 70 self.build.setProperty("missing", None) 71 c = ShellCommand(workdir=dir, 72 command=["tar", "czf", 73 WithProperties("build-%(other)s.tar.gz"), 74 "source"]) 75 c.setBuild(self.build) 76 cmd = c._interpolateProperties(c.command) 77 self.failUnlessEqual(cmd, 78 ["tar", "czf", "build-foo.tar.gz", "source"]) 79 80 def testWithPropertiesEmpty(self): 81 self.build.setProperty("empty", None) 82 c = ShellCommand(workdir=dir, 83 command=["tar", "czf", 84 WithProperties("build-%(empty)s.tar.gz"), 85 "source"]) 86 c.setBuild(self.build) 87 cmd = c._interpolateProperties(c.command) 88 self.failUnlessEqual(cmd, 89 ["tar", "czf", "build-.tar.gz", "source"]) 90 91 def testSourceStamp(self): 92 c = ShellCommand(workdir=dir, 93 command=["touch", 94 WithProperties("%s-dir", "branch"), 95 WithProperties("%s-rev", "revision"), 96 ]) 97 c.setBuild(self.build) 98 cmd = c._interpolateProperties(c.command) 99 self.failUnlessEqual(cmd, 100 ["touch", "branch2-dir", "1234-rev"]) 101 102 def testSlaveName(self): 103 c = ShellCommand(workdir=dir, 104 command=["touch", 105 WithProperties("%s-slave", "slavename"), 106 ]) 107 c.setBuild(self.build) 108 cmd = c._interpolateProperties(c.command) 109 self.failUnlessEqual(cmd, 110 ["touch", "bot12-slave"]) 111 112 def testBuildNumber(self): 113 c = ShellCommand(workdir=dir, 114 command=["touch", 115 WithProperties("build-%d", "buildnumber"), 116 WithProperties("builder-%s", "buildername"), 117 ]) 118 c.setBuild(self.build) 119 cmd = c._interpolateProperties(c.command) 120 self.failUnlessEqual(cmd, 121 ["touch", "build-5", "builder-fakebuilder"]) 84 def testBasic(self): 85 # test basic substitution with WithProperties 86 self.props.setProperty("revision", "47", "test") 87 command = WithProperties("build-%s.tar.gz", "revision") 88 self.failUnlessEqual(self.props.render(command), 89 "build-47.tar.gz") 90 91 def testDict(self): 92 # test dict-style substitution with WithProperties 93 self.props.setProperty("other", "foo", "test") 94 command = WithProperties("build-%(other)s.tar.gz") 95 self.failUnlessEqual(self.props.render(command), 96 "build-foo.tar.gz") 97 98 def testEmpty(self): 99 # None should render as '' 100 self.props.setProperty("empty", None, "test") 101 command = WithProperties("build-%(empty)s.tar.gz") 102 self.failUnlessEqual(self.props.render(command), 103 "build-.tar.gz") 104 105 def testRecursiveList(self): 106 self.props.setProperty("x", 10, "test") 107 self.props.setProperty("y", 20, "test") 108 command = [ WithProperties("%(x)s %(y)s"), "and", 109 WithProperties("%(y)s %(x)s") ] 110 self.failUnlessEqual(self.props.render(command), 111 ["10 20", "and", "20 10"]) 112 113 def testRecursiveTuple(self): 114 self.props.setProperty("x", 10, "test") 115 self.props.setProperty("y", 20, "test") 116 command = ( WithProperties("%(x)s %(y)s"), "and", 117 WithProperties("%(y)s %(x)s") ) 118 self.failUnlessEqual(self.props.render(command), 119 ("10 20", "and", "20 10")) 120 121 def testRecursiveDict(self): 122 self.props.setProperty("x", 10, "test") 123 self.props.setProperty("y", 20, "test") 124 command = { WithProperties("%(x)s %(y)s") : 125 WithProperties("%(y)s %(x)s") } 126 self.failUnlessEqual(self.props.render(command), 127 {"10 20" : "20 10"}) 122 128 123 class SchedulerTest(unittest.TestCase): 129 class BuildProperties(unittest.TestCase): 130 """Test the properties that a build should have.""" 124 131 def setUp(self): 125 132 self.builder = FakeBuilder() 126 133 self.builder_status = builder.BuilderStatus("fakebuilder") … … 129 136 rmdirRecursive(self.builder_status.basedir) 130 137 os.mkdir(self.builder_status.basedir) 131 138 self.build_status = self.builder_status.newBuild() 132 req = base.BuildRequest("reason", SourceStamp(branch="branch2", 133 revision=1234), scheduler=FakeScheduler()) 139 req = base.BuildRequest("reason", 140 SourceStamp(branch="branch2", revision="1234"), 141 properties=Properties(scheduler="fakescheduler")) 134 142 self.build = base.Build([req]) 143 self.build.build_status = self.build_status 135 144 self.build.setBuilder(self.builder) 136 self.build.setup Status(self.build_status)145 self.build.setupProperties() 137 146 self.build.setupSlaveBuilder(FakeSlaveBuilder()) 138 147 139 def testWithScheduler(self): 140 self.failUnlessEqual(self.build.getProperty("scheduler"), 141 "fakescheduler") 148 def testProperties(self): 149 self.failUnlessEqual(self.build.getProperty("scheduler"), "fakescheduler") 150 self.failUnlessEqual(self.build.getProperty("branch"), "branch2") 151 self.failUnlessEqual(self.build.getProperty("revision"), "1234") 152 self.failUnlessEqual(self.build.getProperty("slavename"), "bot12") 153 self.failUnlessEqual(self.build.getProperty("buildnumber"), 5) 154 self.failUnlessEqual(self.build.getProperty("buildername"), "fakebuilder") 155 self.failUnlessEqual(self.build.getProperty("masterprop"), "master") 142 156 143 157 run_config = """ 144 158 from buildbot.process import factory … … 147 161 s = factory.s 148 162 149 163 BuildmasterConfig = c = {} 150 c['slaves'] = [BuildSlave('bot1', 'sekrit' )]164 c['slaves'] = [BuildSlave('bot1', 'sekrit', properties={'slprop':'slprop'})] 151 165 c['schedulers'] = [] 152 166 c['slavePortnum'] = 0 167 c['properties'] = { 'global' : 'global' } 153 168 154 169 # Note: when run against twisted-1.3.0, this locks up about 5% of the time. I 155 170 # suspect that a command with no output that finishes quickly triggers a race … … 160 175 f1 = factory.BuildFactory([s(ShellCommand, 161 176 flunkOnFailure=True, 162 177 command=['touch', 163 WithProperties('%s-slave', 'slavename'), 178 WithProperties('%s-%s-%s', 179 'slavename', 'global', 'slprop'), 164 180 ], 165 181 workdir='.', 166 182 timeout=10, … … 180 196 d.addCallback(lambda res: self.requestBuild("full1")) 181 197 d.addCallback(self.failUnlessBuildSucceeded) 182 198 def _check_touch(res): 183 f = os.path.join("slavebase-bot1", "bd1", "bot1- slave")199 f = os.path.join("slavebase-bot1", "bd1", "bot1-global-slprop") 184 200 self.failUnless(os.path.exists(f)) 185 201 return res 186 202 d.addCallback(_check_touch) -
buildbot/test/test_run.py
diff -rN -u old-124/buildbot/test/test_run.py new-124/buildbot/test/test_run.py
old new 699 699 self.failIfFlagSet('triggeree_finished') 700 700 self.failIfFlagSet('triggerer_finished') 701 701 702 class PropertyPropagation(RunMixin, TestFlagMixin, unittest.TestCase): 703 def setupTest(self, config, builders, checkFn): 704 self.clearFlags() 705 m = self.master 706 m.loadConfig(config) 707 m.readConfig = True 708 m.startService() 709 710 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff") 711 m.change_svc.addChange(c) 712 713 d = self.connectSlave(builders=builders) 714 d.addCallback(self.startTimer, 0.5, checkFn) 715 return d 716 717 def startTimer(self, res, time, next_fn): 718 d = defer.Deferred() 719 reactor.callLater(time, d.callback, None) 720 d.addCallback(next_fn) 721 return d 722 723 config_schprop = config_base + """ 724 from buildbot.scheduler import Scheduler 725 from buildbot.steps.dummy import Dummy 726 from buildbot.test.runutils import SetTestFlagStep 727 from buildbot.process.properties import WithProperties 728 c['schedulers'] = [ 729 Scheduler('mysched', None, 0.1, ['flagcolor'], properties={'color':'red'}), 730 ] 731 factory = factory.BuildFactory([ 732 s(SetTestFlagStep, flagname='testresult', 733 value=WithProperties('color=%(color)s sched=%(scheduler)s')), 734 ]) 735 c['builders'] = [{'name': 'flagcolor', 'slavename': 'bot1', 736 'builddir': 'test', 'factory': factory}, 737 ] 738 """ 739 740 def testScheduler(self): 741 def _check(res): 742 self.failUnlessEqual(self.getFlag('testresult'), 743 'color=red sched=mysched') 744 return self.setupTest(self.config_schprop, ['flagcolor'], _check) 745 746 config_slaveprop = config_base + """ 747 from buildbot.scheduler import Scheduler 748 from buildbot.steps.dummy import Dummy 749 from buildbot.test.runutils import SetTestFlagStep 750 from buildbot.process.properties import WithProperties 751 c['schedulers'] = [ 752 Scheduler('mysched', None, 0.1, ['flagcolor']) 753 ] 754 c['slaves'] = [BuildSlave('bot1', 'sekrit', properties={'color':'orange'})] 755 factory = factory.BuildFactory([ 756 s(SetTestFlagStep, flagname='testresult', 757 value=WithProperties('color=%(color)s slavename=%(slavename)s')), 758 ]) 759 c['builders'] = [{'name': 'flagcolor', 'slavename': 'bot1', 760 'builddir': 'test', 'factory': factory}, 761 ] 762 """ 763 def testSlave(self): 764 def _check(res): 765 self.failUnlessEqual(self.getFlag('testresult'), 766 'color=orange slavename=bot1') 767 return self.setupTest(self.config_slaveprop, ['flagcolor'], _check) 768 769 config_trigger = config_base + """ 770 from buildbot.scheduler import Triggerable, Scheduler 771 from buildbot.steps.trigger import Trigger 772 from buildbot.steps.dummy import Dummy 773 from buildbot.test.runutils import SetTestFlagStep 774 from buildbot.process.properties import WithProperties 775 c['schedulers'] = [ 776 Scheduler('triggerer', None, 0.1, ['triggerer'], 777 properties={'color':'mauve', 'pls_trigger':'triggeree'}), 778 Triggerable('triggeree', ['triggeree'], properties={'color':'invisible'}) 779 ] 780 triggerer = factory.BuildFactory([ 781 s(SetTestFlagStep, flagname='testresult', value='wrongone'), 782 s(Trigger, flunkOnFailure=True, 783 schedulerNames=[WithProperties('%(pls_trigger)s')], 784 set_properties={'color' : WithProperties('%(color)s')}), 785 s(SetTestFlagStep, flagname='testresult', value='triggered'), 786 ]) 787 triggeree = factory.BuildFactory([ 788 s(SetTestFlagStep, flagname='testresult', 789 value=WithProperties('sched=%(scheduler)s color=%(color)s')), 790 ]) 791 c['builders'] = [{'name': 'triggerer', 'slavename': 'bot1', 792 'builddir': 'triggerer', 'factory': triggerer}, 793 {'name': 'triggeree', 'slavename': 'bot1', 794 'builddir': 'triggeree', 'factory': triggeree}] 795 """ 796 def testTrigger(self): 797 def _check(res): 798 self.failUnlessEqual(self.getFlag('testresult'), 799 'sched=triggeree color=mauve') 800 return self.setupTest(self.config_trigger, 801 ['triggerer', 'triggeree'], _check) 802 803 702 804 config_test_flag = config_base + """ 703 805 from buildbot.scheduler import Scheduler 704 806 c['schedulers'] = [Scheduler('quick', None, 0.1, ['dummy'])] -
buildbot/test/test_steps.py
diff -rN -u old-124/buildbot/test/test_steps.py new-124/buildbot/test/test_steps.py
old new 236 236 s = makeBuildStep("test_steps.Steps.test_getProperty") 237 237 bs = s.step_status.getBuild() 238 238 239 s.setProperty("prop1", "value1" )240 s.setProperty("prop2", "value2" )239 s.setProperty("prop1", "value1", "test") 240 s.setProperty("prop2", "value2", "test") 241 241 self.failUnlessEqual(s.getProperty("prop1"), "value1") 242 242 self.failUnlessEqual(bs.getProperty("prop1"), "value1") 243 243 self.failUnlessEqual(s.getProperty("prop2"), "value2") 244 244 self.failUnlessEqual(bs.getProperty("prop2"), "value2") 245 s.setProperty("prop1", "value1a" )245 s.setProperty("prop1", "value1a", "test") 246 246 self.failUnlessEqual(s.getProperty("prop1"), "value1a") 247 247 self.failUnlessEqual(bs.getProperty("prop1"), "value1a") 248 248 … … 601 601 line 23: warning: we are now on line 23 602 602 ending line 603 603 """ 604 step.setProperty("warnings-count", 10 )604 step.setProperty("warnings-count", 10, "test") 605 605 log = step.addLog("stdio") 606 606 log.addStdout(output) 607 607 log.finish() -
buildbot/test/test_vc.py
diff -rN -u old-124/buildbot/test/test_vc.py new-124/buildbot/test/test_vc.py
old new 541 541 self.shouldExist(self.workdir, "subdir", "subdir.c") 542 542 if self.metadir: 543 543 self.shouldExist(self.workdir, self.metadir) 544 self.failUnlessEqual(bs.getProperty("revision"), None)545 self.failUnlessEqual(bs.getProperty("branch"), None)544 self.failUnlessEqual(bs.getProperty("revision"), '') 545 self.failUnlessEqual(bs.getProperty("branch"), '') 546 546 self.checkGotRevisionIsLatest(bs) 547 547 548 548 self.touch(self.workdir, "newfile") … … 565 565 self.shouldExist(self.workdir, "subdir", "subdir.c") 566 566 if self.metadir: 567 567 self.shouldExist(self.workdir, self.metadir) 568 self.failUnlessEqual(bs.getProperty("revision"), self.helper.trunk[0] )569 self.failUnlessEqual(bs.getProperty("branch"), None)568 self.failUnlessEqual(bs.getProperty("revision"), self.helper.trunk[0] or '') 569 self.failUnlessEqual(bs.getProperty("branch"), '') 570 570 self.checkGotRevision(bs, self.helper.trunk[0]) 571 571 # leave the tree at HEAD 572 572 return self.doBuild() … … 585 585 "version=%d" % self.helper.version) 586 586 if self.metadir: 587 587 self.shouldExist(self.workdir, self.metadir) 588 self.failUnlessEqual(bs.getProperty("revision"), None)588 self.failUnlessEqual(bs.getProperty("revision"), '') 589 589 self.checkGotRevisionIsLatest(bs) 590 590 591 591 self.touch(self.workdir, "newfile") … … 609 609 self.shouldContain(self.workdir, "version.c", 610 610 "version=%d" % self.helper.version) 611 611 self.shouldExist(self.workdir, "newfile") 612 self.failUnlessEqual(bs.getProperty("revision"), None)612 self.failUnlessEqual(bs.getProperty("revision"), '') 613 613 self.checkGotRevisionIsLatest(bs) 614 614 615 615 # now "update" to an older revision … … 623 623 self.shouldContain(self.workdir, "version.c", 624 624 "version=%d" % (self.helper.version-1)) 625 625 self.failUnlessEqual(bs.getProperty("revision"), 626 self.helper.trunk[-2] )626 self.helper.trunk[-2] or '') 627 627 self.checkGotRevision(bs, self.helper.trunk[-2]) 628 628 629 629 # now update to the newer revision … … 637 637 self.shouldContain(self.workdir, "version.c", 638 638 "version=%d" % self.helper.version) 639 639 self.failUnlessEqual(bs.getProperty("revision"), 640 self.helper.trunk[-1] )640 self.helper.trunk[-1] or '') 641 641 self.checkGotRevision(bs, self.helper.trunk[-1]) 642 642 643 643 … … 674 674 self.shouldNotExist(self.workdir, "newfile") 675 675 self.touch(self.workdir, "newfile") 676 676 self.touch(self.vcdir, "newvcfile") 677 self.failUnlessEqual(bs.getProperty("revision"), None)677 self.failUnlessEqual(bs.getProperty("revision"), '') 678 678 self.checkGotRevisionIsLatest(bs) 679 679 680 680 d = self.doBuild() # copy rebuild clobbers new files … … 687 687 self.shouldNotExist(self.workdir, "newfile") 688 688 self.shouldExist(self.vcdir, "newvcfile") 689 689 self.shouldExist(self.workdir, "newvcfile") 690 self.failUnlessEqual(bs.getProperty("revision"), None)690 self.failUnlessEqual(bs.getProperty("revision"), '') 691 691 self.checkGotRevisionIsLatest(bs) 692 692 self.touch(self.workdir, "newfile") 693 693 … … 698 698 def _do_vctest_export_1(self, bs): 699 699 self.shouldNotExist(self.workdir, self.metadir) 700 700 self.shouldNotExist(self.workdir, "newfile") 701 self.failUnlessEqual(bs.getProperty("revision"), None)701 self.failUnlessEqual(bs.getProperty("revision"), '') 702 702 #self.checkGotRevisionIsLatest(bs) 703 703 # VC 'export' is not required to have a got_revision 704 704 self.touch(self.workdir, "newfile") … … 709 709 def _do_vctest_export_2(self, bs): 710 710 self.shouldNotExist(self.workdir, self.metadir) 711 711 self.shouldNotExist(self.workdir, "newfile") 712 self.failUnlessEqual(bs.getProperty("revision"), None)712 self.failUnlessEqual(bs.getProperty("revision"), '') 713 713 #self.checkGotRevisionIsLatest(bs) 714 714 # VC 'export' is not required to have a got_revision 715 715 … … 744 744 data = open(subdir_c, "r").read() 745 745 self.failUnlessIn("Hello patched subdir.\\n", data) 746 746 self.failUnlessEqual(bs.getProperty("revision"), 747 self.helper.trunk[-1] )747 self.helper.trunk[-1] or '') 748 748 self.checkGotRevision(bs, self.helper.trunk[-1]) 749 749 750 750 # make sure that a rebuild does not use the leftover patched workdir … … 758 758 "subdir", "subdir.c") 759 759 data = open(subdir_c, "r").read() 760 760 self.failUnlessIn("Hello subdir.\\n", data) 761 self.failUnlessEqual(bs.getProperty("revision"), None)761 self.failUnlessEqual(bs.getProperty("revision"), '') 762 762 self.checkGotRevisionIsLatest(bs) 763 763 764 764 # now make sure we can patch an older revision. We need at least two … … 783 783 data = open(subdir_c, "r").read() 784 784 self.failUnlessIn("Hello patched subdir.\\n", data) 785 785 self.failUnlessEqual(bs.getProperty("revision"), 786 self.helper.trunk[-2] )786 self.helper.trunk[-2] or '') 787 787 self.checkGotRevision(bs, self.helper.trunk[-2]) 788 788 789 789 # now check that we can patch a branch … … 802 802 data = open(subdir_c, "r").read() 803 803 self.failUnlessIn("Hello patched subdir.\\n", data) 804 804 self.failUnlessEqual(bs.getProperty("revision"), 805 self.helper.branch[-1] )806 self.failUnlessEqual(bs.getProperty("branch"), self.helper.branchname )805 self.helper.branch[-1] or '') 806 self.failUnlessEqual(bs.getProperty("branch"), self.helper.branchname or '') 807 807 self.checkGotRevision(bs, self.helper.branch[-1]) 808 808 809 809 -
docs/Makefile
diff -rN -u old-124/docs/Makefile new-124/docs/Makefile
old new 2 2 buildbot.info: buildbot.texinfo 3 3 makeinfo --fill-column=70 $< 4 4 5 buildbot.html: buildbot.texinfo images-png 5 #images-png 6 buildbot.html: buildbot.texinfo 6 7 makeinfo --no-split --html $< 7 8 8 buildbot.ps: buildbot.texinfo images-eps 9 #images-eps 10 buildbot.ps: buildbot.texinfo 9 11 texi2dvi $< 10 12 dvips buildbot.dvi 11 13 rm buildbot.aux buildbot.cp buildbot.cps buildbot.fn buildbot.ky buildbot.log buildbot.pg buildbot.toc buildbot.tp buildbot.vr -
docs/buildbot.texinfo
diff -rN -u old-124/docs/buildbot.texinfo new-124/docs/buildbot.texinfo
old new 109 109 * BuildRequest:: 110 110 * Builder:: 111 111 * Users:: 112 * Build Properties:: 112 113 113 114 Version Control Systems 114 115 … … 130 131 * Loading the Config File:: 131 132 * Testing the Config File:: 132 133 * Defining the Project:: 133 * ListingChange Sources and Schedulers::134 * Change Sources and Schedulers:: 134 135 * Setting the slaveport:: 135 136 * Buildslave Specifiers:: 137 * Defining Global Properties:: 136 138 * Defining Builders:: 137 139 * Defining Status Targets:: 138 140 * Debug options:: 139 141 140 ListingChange Sources and Schedulers142 Change Sources and Schedulers 141 143 142 * Scheduler Types:: 143 * Build Dependencies:: 144 * Scheduler Scheduler:: 145 * AnyBranchScheduler:: 146 * Dependent Scheduler:: 147 * Periodic Scheduler:: 148 * Nightly Scheduler:: 149 * Try Schedulers:: 150 * Triggerable Scheduler:: 144 151 145 152 Buildslave Specifiers 146 153 … … 180 187 Build Steps 181 188 182 189 * Common Parameters:: 190 * Using Build Properties:: 183 191 * Source Checkout:: 184 192 * ShellCommand:: 185 193 * Simple ShellCommand Subclasses:: … … 206 214 * Compile:: 207 215 * Test:: 208 216 * TreeSize:: 209 * Build Properties::210 217 211 218 Python BuildSteps 212 219 … … 1231 1238 * BuildRequest:: 1232 1239 * Builder:: 1233 1240 * Users:: 1241 * Build Properties:: 1234 1242 @end menu 1235 1243 1236 1244 @node Version Control Systems, Schedulers, Concepts, Concepts … … 1756 1764 OS-X-based buildslave. 1757 1765 1758 1766 1759 @node Users, , Builder, Concepts1767 @node Users, Build Properties, Builder, Concepts 1760 1768 @section Users 1761 1769 1762 1770 @cindex Users … … 1897 1905 alternative way to deliver low-latency high-interruption messages to the 1898 1906 developer (like ``hey, you broke the build''). 1899 1907 1908 @node Build Properties, , Users, Concepts 1909 @section Build Properties 1910 @cindex Properties 1911 1912 Each build has a set of ``Build Properties'', which can be used by its 1913 BuildStep to modify their actions. These properties, in the form of 1914 key-value pairs, provide a general framework for dynamically altering 1915 the behavior of a build based on its circumstances. 1916 1917 Properties come from a number of places: 1918 @itemize 1919 @item global configuration -- 1920 These properties apply to all builds. 1921 @item schedulers -- 1922 A scheduler can specify properties available to all the builds it 1923 starts. 1924 @item buildslaves -- 1925 A buildslave can pass properties on to the builds it performs. 1926 @item builds -- 1927 A build automatically sets a number of properties on itself. 1928 @item steps -- 1929 Steps of a build can set properties that are available to subsequent 1930 steps. In particular, source steps set a number of properties. 1931 @end itemize 1932 1933 Properties are very flexible, and can be used to implement all manner 1934 of functionality. Here are some examples: 1935 1936 Most Source steps record the revision that they checked out in 1937 the @code{got_revision} property. A later step could use this 1938 property to specify the name of a fully-built tarball, dropped in an 1939 easily-acessible directory for later testing. 1940 1941 Some projects want to perform nightly builds as well as in response 1942 to committed changes. Such a project would run two schedulers, 1943 both pointing to the same set of builders, but could provide an 1944 @code{is_nightly} property so that steps can distinguish the nightly 1945 builds, perhaps to run more resource-intensive tests. 1946 1947 Some projects have different build processes on different systems. 1948 Rather than create a build factory for each slave, the steps can use 1949 buildslave properties to identify the unique aspects of each slave 1950 and adapt the build process dynamically. 1900 1951 1901 1952 @node Configuration, Getting Source Code Changes, Concepts, Top 1902 1953 @chapter Configuration … … 1925 1976 * Loading the Config File:: 1926 1977 * Testing the Config File:: 1927 1978 * Defining the Project:: 1928 * ListingChange Sources and Schedulers::1979 * Change Sources and Schedulers:: 1929 1980 * Setting the slaveport:: 1930 1981 * Buildslave Specifiers:: 1982 * Defining Global Properties:: 1931 1983 * Defining Builders:: 1932 1984 * Defining Status Targets:: 1933 1985 * Debug options:: … … 2049 2101 2050 2102 @example 2051 2103 % buildbot checkconfig master.cfg 2052 /usr/lib/python2.4/site-packages/buildbot/master.py:559: DeprecationWarning: c['sources'] is deprecated as of 0.7.6 and will be removed by 0.8.0 . Please use c['change_source'] instead. 2104 /usr/lib/python2.4/site-packages/buildbot/master.py:559: DeprecationWarning: c['sources'] is 2105 deprecated as of 0.7.6 and will be removed by 0.8.0 . Please use c['change_source'] instead. 2053 2106 warnings.warn(m, DeprecationWarning) 2054 2107 Config file is good! 2055 2108 @end example … … 2071 2124 @end example 2072 2125 2073 2126 2074 @node Defining the Project, ListingChange Sources and Schedulers, Testing the Config File, Configuration2127 @node Defining the Project, Change Sources and Schedulers, Testing the Config File, Configuration 2075 2128 @section Defining the Project 2076 2129 2077 2130 There are a couple of basic settings that you use to tell the buildbot … … 2110 2163 more information about this buildbot. 2111 2164 2112 2165 2113 @node ListingChange Sources and Schedulers, Setting the slaveport, Defining the Project, Configuration2114 @section ListingChange Sources and Schedulers2166 @node Change Sources and Schedulers, Setting the slaveport, Defining the Project, Configuration 2167 @section Change Sources and Schedulers 2115 2168 2116 2169 @bcindex c['sources'] 2117 2170 @bcindex c['change_source'] … … 2129 2182 from buildbot.changes.pb import PBChangeSource 2130 2183 c['change_source'] = PBChangeSource() 2131 2184 @end example 2185 @bcindex c['schedulers'] 2132 2186 2133 2187 (note: in buildbot-0.7.5 and earlier, this key was named 2134 2188 @code{c['sources']}, and required a list. @code{c['sources']} is 2135 2189 deprecated as of buildbot-0.7.6 and is scheduled to be removed in a 2136 2190 future release). 2137 2191 2138 @bcindex c['schedulers'] 2139 @code{c['schedulers']} is a list of Scheduler instances, each of which 2140 causes builds to be started on a particular set of Builders. The two 2141 basic Scheduler classes you are likely to start with are 2142 @code{Scheduler} and @code{Periodic}, but you can write a customized 2143 subclass to implement more complicated build scheduling. 2144 2145 The docstring for @code{buildbot.scheduler.Scheduler} is the best 2146 place to see all the options that can be used. Type @code{pydoc 2147 buildbot.scheduler.Scheduler} to see it, or look in 2148 @file{buildbot/scheduler.py} directly. 2192 @code{c['schedulers']} is a list of Scheduler instances, each 2193 of which causes builds to be started on a particular set of 2194 Builders. The two basic Scheduler classes you are likely to start 2195 with are @code{Scheduler} and @code{Periodic}, but you can write a 2196 customized subclass to implement more complicated build scheduling. 2149 2197 2150 The basic Scheduler takes four arguments: 2198 Scheduler arguments 2199 should always be specified by name (as keyword arguments), to allow 2200 for future expansion: 2201 2202 @example 2203 sched = Scheduler(name="quick", builderNames=['lin', 'win']) 2204 @end example 2205 2206 All schedulers have several arguments in common: 2151 2207 2152 2208 @table @code 2153 2209 @item name 2154 Each Scheduler must have a unique name. This is only used in status 2155 displays. 2210 2211 Each Scheduler must have a unique name. This is used in status 2212 displays, and is also available in the build property @code{scheduler}. 2213 2214 @item builderNames 2215 2216 This is the set of builders which this scheduler should trigger, specified 2217 as a list of names (strings). 2218 2219 @item properties 2220 @cindex Properties 2221 2222 This is a dictionary specifying properties that will be transmitted 2223 to all builds started by this scheduler. 2224 2225 @end table 2226 2227 Here is a brief catalog of the available Scheduler types. All these 2228 Schedulers are classes in @code{buildbot.scheduler}, and the 2229 docstrings there are the best source of documentation on the arguments 2230 taken by each one. 2231 2232 @menu 2233 * Scheduler Scheduler:: 2234 * AnyBranchScheduler:: 2235 * Dependent Scheduler:: 2236 * Periodic Scheduler:: 2237 * Nightly Scheduler:: 2238 * Try Schedulers:: 2239 * Triggerable Scheduler:: 2240 @end menu 2241 2242 @node Scheduler Scheduler 2243 @subsection Scheduler Scheduler 2244 @slindex buildbot.scheduler.Scheduler 2245 2246 This is the original and still most popular Scheduler class. It follows 2247 exactly one branch, and starts a configurable tree-stable-timer after 2248 each change on that branch. When the timer expires, it starts a build 2249 on some set of Builders. The Scheduler accepts a @code{fileIsImportant} 2250 function which can be used to ignore some Changes if they do not 2251 affect any ``important'' files. 2252 2253 The arguments to this scheduler are: 2254 2255 @table @code 2256 @item name 2257 2258 @item builderNames 2259 2260 @item properties 2156 2261 2157 2262 @item branch 2158 2263 This Scheduler will pay attention to a single branch, ignoring Changes 2159 2264 that occur on other branches. Setting @code{branch} equal to the 2160 special value of @code{None} means it should only pay attention to the2161 default branch. Note that @code{None} is a keyword, not a string, so 2162 you want to use @code{None} and not @code{"None"}.2265 special value of @code{None} means it should only pay attention to 2266 the default branch. Note that @code{None} is a keyword, not a string, 2267 so you want to use @code{None} and not @code{"None"}. 2163 2268 2164 2269 @item treeStableTimer 2165 2270 The Scheduler will wait for this many seconds before starting the … … 2167 2272 restarted, so really the build will be started after a change and then 2168 2273 after this many seconds of inactivity. 2169 2274 2170 @item builderNames 2171 When the tree-stable-timer finally expires, builds will be started on 2172 these Builders. Each Builder gets a unique name: these strings must 2173 match. 2174 2275 @item fileIsImportant 2276 A callable which takes one argument, a Change instance, and returns 2277 @code{True} if the change is worth building, and @code{False} if 2278 it is not. Unimportant Changes are accumulated until the build is 2279 triggered by an important change. The default value of None means 2280 that all Changes are important. 2175 2281 @end table 2176 2282 2283 Example: 2284 2177 2285 @example 2178 2286 from buildbot import scheduler 2179 quick = scheduler.Scheduler("quick", None, 60, 2180 ["quick-linux", "quick-netbsd"]) 2181 full = scheduler.Scheduler("full", None, 5*60, 2182 ["full-linux", "full-netbsd", "full-OSX"]) 2183 nightly = scheduler.Periodic("nightly", ["full-solaris"], 24*60*60) 2184 c['schedulers'] = [quick, full, nightly] 2287 quick = scheduler.Scheduler(name="quick", 2288 branch=None, 2289 treeStableTimer=60, 2290 builderNames=["quick-linux", "quick-netbsd"]) 2291 full = scheduler.Scheduler(name="full", 2292 branch=None, 2293 treeStableTimer=5*60, 2294 builderNames=["full-linux", "full-netbsd", "full-OSX"]) 2295 c['schedulers'] = [quick, full] 2185 2296 @end example 2186 2297 2187 In this example, the two ``quick'' build s are triggered 60 seconds2298 In this example, the two ``quick'' builders are triggered 60 seconds 2188 2299 after the tree has been changed. The ``full'' builds do not run quite 2189 2300 so quickly (they wait 5 minutes), so hopefully if the quick builds 2190 2301 fail due to a missing file or really simple typo, the developer can 2191 2302 discover and fix the problem before the full builds are started. Both 2192 Schedulers only pay attention to the default branch: any changes on2193 o ther branches are ignored by these Schedulers. Each Scheduler2303 Schedulers only pay attention to the default branch: any changes 2304 on other branches are ignored by these Schedulers. Each Scheduler 2194 2305 triggers a different set of Builders, referenced by name. 2195 2306 2196 The third Scheduler in this example just runs the full solaris build 2197 once per day. (note that this Scheduler only lets you control the time 2198 between builds, not the absolute time-of-day of each Build, so this 2199 could easily wind up a ``daily'' or ``every afternoon'' scheduler 2200 depending upon when it was first activated). 2201 2202 @menu 2203 * Scheduler Types:: 2204 * Build Dependencies:: 2205 @end menu 2206 2207 @node Scheduler Types, Build Dependencies, Listing Change Sources and Schedulers, Listing Change Sources and Schedulers 2208 @subsection Scheduler Types 2209 2210 @slindex buildbot.scheduler.Scheduler 2307 @node AnyBranchScheduler 2308 @subsection AnyBranchScheduler 2211 2309 @slindex buildbot.scheduler.AnyBranchScheduler 2212 @slindex buildbot.scheduler.Periodic2213 @slindex buildbot.scheduler.Nightly2214 2310 2215 Here is a brief catalog of the available Scheduler types. All these2216 Schedulers are classes in @code{buildbot.scheduler}, and the2217 docstrings there are the best source of documentation on the arguments2218 taken by each one.2219 2220 @table @code2221 @item Scheduler2222 This is the default Scheduler class. It follows exactly one branch,2223 and starts a configurable tree-stable-timer after each change on that2224 branch. When the timer expires, it starts a build on some set of2225 Builders. The Scheduler accepts a @code{fileIsImportant} function2226 which can be used to ignore some Changes if they do not affect any2227 ``important'' files.2228 2229 @item AnyBranchScheduler2230 2311 This scheduler uses a tree-stable-timer like the default one, but 2231 2312 follows multiple branches at once. Each branch gets a separate timer. 2232 2313 2233 @item Dependent 2234 This scheduler watches an ``upstream'' Scheduler. When all the 2235 Builders launched by that Scheduler successfully finish, the Dependent 2236 scheduler is triggered. The next section (@pxref{Build Dependencies}) 2237 describes this scheduler in more detail. 2238 2239 @item Triggerable 2240 This scheduler does nothing until it is triggered by a Trigger 2241 step in another build. This facilitates a more general form of 2242 build dependencies, as described in the next section (@pxref{Build 2243 Dependencies}). 2314 The arguments to this scheduler are: 2244 2315 2245 @ item Periodic2246 This simple scheduler just triggers a build every N seconds. 2316 @table @code 2317 @item name 2247 2318 2248 @item Nightly 2249 This is highly configurable periodic build scheduler, which triggers a 2250 build at particular times of day, week, month, or year. The 2251 configuration syntax is very similar to the well-known @code{crontab} 2252 format, in which you provide values for minute, hour, day, and month 2253 (some of which can be wildcards), and a build is triggered whenever 2254 the current time matches the given constraints. This can run a build 2255 every night, every morning, every weekend, alternate Thursdays, on 2256 your boss's birthday, etc. 2319 @item builderNames 2257 2320 2258 @item Try_Jobdir / Try_Userpass 2259 This scheduler allows developers to use the @code{buildbot try} 2260 command to trigger builds of code they have not yet committed. See 2261 @ref{try} for complete details. 2321 @item properties 2262 2322 2263 @end table 2323 @item branches 2324 This Scheduler will pay attention to any number of branches, ignoring 2325 Changes that occur on other branches. Branches are specified just as 2326 for the @code{Scheduler} class. 2264 2327 2265 @node Build Dependencies, , Scheduler Types, Listing Change Sources and Schedulers 2266 @subsection Build Dependencies 2328 @item treeStableTimer 2329 The Scheduler will wait for this many seconds before starting the 2330 build. If new changes are made during this interval, the timer will be 2331 restarted, so really the build will be started after a change and then 2332 after this many seconds of inactivity. 2333 2334 @item fileIsImportant 2335 A callable which takes one argument, a Change instance, and returns 2336 @code{True} if the change is worth building, and @code{False} if 2337 it is not. Unimportant Changes are accumulated until the build is 2338 triggered by an important change. The default value of None means 2339 that all Changes are important. 2340 @end table 2267 2341 2342 @node Dependent Scheduler 2343 @subsection Dependent Scheduler 2268 2344 @cindex Dependent 2269 2345 @cindex Dependencies 2270 2346 @slindex buildbot.scheduler.Dependent 2271 @slindex buildbot.scheduler.Triggerable2272 2347 2273 2348 It is common to wind up with one kind of build which should only be 2274 2349 performed if the same source code was successfully handled by some … … 2284 2359 that is used by some other Builder, you'd want to make sure the 2285 2360 consuming Build is run @emph{after} the producing one. 2286 2361 2287 You can use @code{Dependencies} to express this relationship to the2288 Buildbot. There is a special kind of Scheduler named2362 You can use ``Dependencies'' to express this relationship 2363 to the Buildbot. There is a special kind of Scheduler named 2289 2364 @code{scheduler.Dependent} that will watch an ``upstream'' Scheduler 2290 for builds to complete successfully (on all of its Builders). Each 2291 time that happens, the same source code (i.e. the same 2292 @code{SourceStamp}) will be used to start a new set of builds, on a 2293 different set of Builders. This ``downstream'' scheduler doesn't pay 2294 attention to Changes at all, it only pays attention to the upstream 2295 scheduler. 2365 for builds to complete successfully (on all of its Builders). Each time 2366 that happens, the same source code (i.e. the same @code{SourceStamp}) 2367 will be used to start a new set of builds, on a different set of 2368 Builders. This ``downstream'' scheduler doesn't pay attention to 2369 Changes at all. It only pays attention to the upstream scheduler. 2370 2371 If the build fails on any of the Builders in the upstream set, 2372 the downstream builds will not fire. Note that, for SourceStamps 2373 generated by a ChangeSource, the @code{revision} is None, meaning HEAD. 2374 If any changes are committed between the time the upstream scheduler 2375 begins its build and the time the dependent scheduler begins its 2376 build, then those changes will be included in the downstream build. 2377 See the @pxref{Triggerable Scheduler} for a more flexible dependency 2378 mechanism that can avoid this problem. 2379 2380 The arguments to this scheduler are: 2381 2382 @table @code 2383 @item name 2384 2385 @item builderNames 2386 2387 @item properties 2388 2389 @item upstream 2390 The upstream scheduler to watch. Note that this is an ``instance'', 2391 not the name of the scheduler. 2392 @end table 2296 2393 2297 If the SourceStamp fails on any of the Builders in the upstream set, 2298 the downstream builds will not fire. 2394 Example: 2299 2395 2300 2396 @example 2301 2397 from buildbot import scheduler 2302 tests = scheduler.Scheduler(" tests", None, 5*60,2398 tests = scheduler.Scheduler("just-tests", None, 5*60, 2303 2399 ["full-linux", "full-netbsd", "full-OSX"]) 2304 package = scheduler.Dependent(" package",2305 tests, # upstream scheduler 2400 package = scheduler.Dependent("build-package", 2401 tests, # upstream scheduler -- no quotes! 2306 2402 ["make-tarball", "make-deb", "make-rpm"]) 2307 2403 c['schedulers'] = [tests, package] 2308 2404 @end example 2309 2405 2310 Note that @code{Dependent}'s upstream scheduler argument is given as a 2311 @code{Scheduler} @emph{instance}, not a name. This makes it impossible 2312 to create circular dependencies in the config file. 2313 2314 A more general way to coordinate builds is by ``triggering'' 2315 schedulers from builds. The Triggerable waits to be triggered by a 2316 Trigger step (@pxref{Triggering Schedulers}) in another build. That 2317 step can optionally wait for the scheduler's builds to complete. This 2406 @node Periodic Scheduler 2407 @subsection Periodic Scheduler 2408 @slindex buildbot.scheduler.Periodic 2409 2410 This simple scheduler just triggers a build every N seconds. 2411 2412 The arguments to this scheduler are: 2413 2414 @table @code 2415 @item name 2416 2417 @item builderNames 2418 2419 @item properties 2420 2421 @item periodicBuildTimer 2422 The time, in seconds, after which to start a build. 2423 @end table 2424 2425 Example: 2426 2427 @example 2428 from buildbot import scheduler 2429 nightly = scheduler.Periodic(name="nightly", 2430 builderNames=["full-solaris"], 2431 periodicBuildTimer=24*60*60) 2432 c['schedulers'] = [nightly] 2433 @end example 2434 2435 The Scheduler in this example just runs the full solaris build once 2436 per day. Note that this Scheduler only lets you control the time 2437 between builds, not the absolute time-of-day of each Build, so this 2438 could easily wind up a ``daily'' or ``every afternoon'' scheduler 2439 depending upon when it was first activated. 2440 2441 @node Nightly Scheduler 2442 @subsection Nightly Scheduler 2443 @slindex buildbot.scheduler.Nightly 2444 2445 This is highly configurable periodic build scheduler, which triggers 2446 a build at particular times of day, week, month, or year. The 2447 configuration syntax is very similar to the well-known @code{crontab} 2448 format, in which you provide values for minute, hour, day, and month 2449 (some of which can be wildcards), and a build is triggered whenever 2450 the current time matches the given constraints. This can run a build 2451 every night, every morning, every weekend, alternate Thursdays, 2452 on your boss's birthday, etc. 2453 2454 Pass some subset of @code{minute}, @code{hour}, @code{dayOfMonth}, 2455 @code{month}, and @code{dayOfWeek}; each may be a single number or 2456 a list of valid values. The builds will be triggered whenever the 2457 current time matches these values. Wildcards are represented by a 2458 '*' string. All fields default to a wildcard except 'minute', so 2459 with no fields this defaults to a build every hour, on the hour. 2460 The full list of parameters is: 2461 2462 @table @code 2463 @item name 2464 2465 @item builderNames 2466 2467 @item properties 2468 2469 @item branch 2470 The branch to build, just as for @code{Scheduler}. 2471 2472 @item minute 2473 The minute of the hour on which to start the build. This defaults 2474 to 0, meaning an hourly build. 2475 2476 @item hour 2477 The hour of the day on which to start the build, in 24-hour notation. 2478 This defaults to *, meaning every hour. 2479 2480 @item month 2481 The month in which to start the build, with January = 1. This defaults 2482 to *, meaning every month. 2483 2484 @item dayOfWeek 2485 The day of the week to start a build, with Monday = 0. This defauls 2486 to *, meaning every day of the week. 2487 @end table 2488 2489 For example, the following master.cfg clause will cause a build to be 2490 started every night at 3:00am: 2491 2492 @example 2493 s = scheduler.Nightly(name='nightly', 2494 builderNames=['builder1', 'builder2'], 2495 hour=3, 2496 minute=0) 2497 @end example 2498 2499 This scheduler will perform a build each monday morning at 6:23am and 2500 again at 8:23am: 2501 2502 @example 2503 s = scheduler.Nightly(name='BeforeWork', 2504 builderNames=['builder1'], 2505 dayOfWeek=0, 2506 hour=[6,8], 2507 minute=23) 2508 @end example 2509 2510 The following runs a build every two hours, using Python's @code{range} 2511 function: 2512 2513 @example 2514 s = Nightly(name='every2hours', 2515 builderNames=['builder1'], 2516 hour=range(0, 24, 2)) 2517 @end example 2518 2519 Finally, this example will run only on December 24th: 2520 2521 @example 2522 s = Nightly(name='SleighPreflightCheck', 2523 builderNames=['flying_circuits', 'radar'], 2524 month=12, 2525 dayOfMonth=24, 2526 hour=12, 2527 minute=0) 2528 @end example 2529 2530 @node Try Schedulers 2531 @subsection Try Schedulers 2532 @slindex buildbot.scheduler.Try_Jobdir 2533 @slindex buildbot.scheduler.Try_Userpass 2534 2535 This scheduler allows developers to use the @code{buildbot try} 2536 command to trigger builds of code they have not yet committed. See 2537 @ref{try} for complete details. 2538 2539 Two implementations are available: @code{Try_Jobdir} and 2540 @code{Try_Userpass}. The former monitors a job directory, specified 2541 by the @code{jobdir} parameter, while the latter listens for PB 2542 connections on a specific @code{port}, and authenticates against 2543 @code{userport}. 2544 2545 @node Triggerable Scheduler 2546 @subsection Triggerable Scheduler 2547 @cindex Triggers 2548 @slindex buildbot.scheduler.Triggerable 2549 2550 The @code{Triggerable} scheduler waits to be triggered by a Trigger 2551 step (see @ref{Triggering Schedulers}) in another build. That step 2552 can optionally wait for the scheduler's builds to complete. This 2318 2553 provides two advantages over Dependent schedulers. First, the same 2319 2554 scheduler can be triggered from multiple builds. Second, the ability 2320 2555 to wait for a Triggerable's builds to complete provides a form of 2321 "subroutine call", where one or more builds can "call" a scheduler to 2322 perform some work for them, perhaps on other buildslaves. 2556 "subroutine call", where one or more builds can "call" a scheduler 2557 to perform some work for them, perhaps on other buildslaves. 2558 2559 The parameters are just the basics: 2560 2561 @table @code 2562 @item name 2563 @item builderNames 2564 @item properties 2565 @end table 2566 2567 This class is only useful in conjunction with the @code{Trigger} step. 2568 Here is a fully-worked example: 2323 2569 2324 2570 @example 2325 2571 from buildbot import scheduler 2326 2572 from buildbot.steps import trigger 2327 2573 2328 checkin = scheduler.Scheduler("checkin", None, 5*60, ["checkin"]) 2329 nightly = scheduler.Scheduler("nightly", ... , ["nightly"]) 2330 2331 mktarball = scheduler.Triggerable("mktarball", 2332 ["mktarball"]) 2333 build = scheduler.Triggerable("build-all-platforms", 2334 ["build-all-platforms"]) 2335 test = scheduler.Triggerable("distributed-test", 2336 ["distributed-test"]) 2337 package = scheduler.Triggerable("package-all-platforms", 2338 ["package-all-platforms"]) 2574 checkin = scheduler.Scheduler(name="checkin", 2575 branch=None, 2576 treeStableTimer=5*60, 2577 builderNames=["checkin"]) 2578 nightly = scheduler.Nightly(name='nightly', 2579 builderNames=['nightly'], 2580 hour=3, 2581 minute=0) 2582 2583 mktarball = scheduler.Triggerable(name="mktarball", 2584 builderNames=["mktarball"]) 2585 build = scheduler.Triggerable(name="build-all-platforms", 2586 builderNames=["build-all-platforms"]) 2587 test = scheduler.Triggerable(name="distributed-test", 2588 builderNames=["distributed-test"]) 2589 package = scheduler.Triggerable(name="package-all-platforms", 2590 builderNames=["package-all-platforms"]) 2339 2591 2340 2592 c['schedulers'] = [checkin, nightly, build, test, package] 2341 2593 2594 # on checkin, make a tarball, build it, and test it 2342 2595 checkin_factory = factory.BuildFactory() 2343 2596 f.addStep(trigger.Trigger('mktarball', schedulers=['mktarball'], 2344 2597 waitForFinish=True) … … 2347 2600 f.addStep(trigger.Trigger('test', schedulers=['distributed-test'], 2348 2601 waitForFinish=True) 2349 2602 2603 # and every night, make a tarball, build it, and package it 2350 2604 nightly_factory = factory.BuildFactory() 2351 2605 f.addStep(trigger.Trigger('mktarball', schedulers=['mktarball'], 2352 2606 waitForFinish=True) … … 2356 2610 waitForFinish=True) 2357 2611 @end example 2358 2612 2359 @node Setting the slaveport, Buildslave Specifiers, ListingChange Sources and Schedulers, Configuration2613 @node Setting the slaveport, Buildslave Specifiers, Change Sources and Schedulers, Configuration 2360 2614 @section Setting the slaveport 2361 2615 2362 2616 @bcindex c['slavePortnum'] … … 2393 2647 @code{localhost:10000}. 2394 2648 2395 2649 2396 @node Buildslave Specifiers , Defining Builders, Setting the slaveport, Configuration2650 @node Buildslave Specifiers 2397 2651 @section Buildslave Specifiers 2398 2652 2399 2653 @bcindex c['slaves'] … … 2404 2658 values that need to be provided to the buildslave administrator when 2405 2659 they create the buildslave. 2406 2660 2407 @example2408 from buildbot.buildslave import BuildSlave2409 c['slaves'] = [BuildSlave('bot-solaris', 'solarispasswd'),2410 BuildSlave('bot-bsd', 'bsdpasswd'),2411 ]2412 @end example2413 2414 2661 The slavenames must be unique, of course. The password exists to 2415 2662 prevent evildoers from interfering with the buildbot by inserting 2416 2663 their own (broken) buildslaves into the system and thus displacing the … … 2420 2667 will be rejected when they attempt to connect, and a message 2421 2668 describing the problem will be put in the log file (see @ref{Logfiles}). 2422 2669 2423 The @code{BuildSlave} constructor can take an optional 2424 @code{max_builds} parameter to limit the number of builds that it will 2425 execute simultaneously: 2670 @example 2671 from buildbot.buildslave import BuildSlave 2672 c['slaves'] = [BuildSlave('bot-solaris', 'solarispasswd') 2673 BuildSlave('bot-bsd', 'bsdpasswd') 2674 ] 2675 @end example 2676 2677 @cindex Properties 2678 @code{BuildSlave} objects can also be created with an optional 2679 @code{properties} argument, a dictionary specifying properties that 2680 will be available to any builds performed on this slave. For example: 2681 2682 @example 2683 from buildbot.buildslave import BuildSlave 2684 c['slaves'] = [BuildSlave('bot-solaris', 'solarispasswd', 2685 properties=@{'os':'solaris'@}), 2686 ] 2687 @end example 2688 2689 The @code{BuildSlave} constructor can also take an optional 2690 @code{max_builds} parameter to limit the number of builds that it 2691 will execute simultaneously: 2426 2692 2427 2693 @example 2428 2694 from buildbot.buildslave import BuildSlave … … 2496 2762 ] 2497 2763 @end example 2498 2764 2765 @node Defining Global Properties 2766 @section Defining Global Properties 2767 @bcindex c['properties'] 2768 @cindex Properties 2769 2770 The @code{'properties'} configuration key defines a dictionary 2771 of properties that will be available to all builds started by the 2772 buildmaster: 2499 2773 2500 @node Defining Builders, Defining Status Targets, Buildslave Specifiers, Configuration 2774 @example 2775 c['properties'] = @{ 2776 'Widget-version' : '1.2', 2777 'release-stage' : 'alpha' 2778 @} 2779 @end example 2780 2781 @node Defining Builders 2501 2782 @section Defining Builders 2502 2783 2503 2784 @bcindex c['builders'] … … 2577 2858 @end table 2578 2859 2579 2860 2580 @node Defining Status Targets , Debug options, Defining Builders, Configuration2861 @node Defining Status Targets 2581 2862 @section Defining Status Targets 2582 2863 2583 2864 The Buildmaster has a variety of ways to present build status to … … 2969 3250 in a place where the buildmaster can find them, and configure the 2970 3251 buildmaster to parse the messages correctly. Once that is in place, 2971 3252 the email parser will create Change objects and deliver them to the 2972 Schedulers (see @pxref{ Scheduler Types}) just like any other2973 ChangeSource.3253 Schedulers (see @pxref{Change Sources and Schedulers}) just 3254 like any other ChangeSource. 2974 3255 2975 3256 There are two components to setting up an email-based ChangeSource. 2976 3257 The first is to route the email messages to the buildmaster, which is … … 3721 4002 * Build Factories:: 3722 4003 @end menu 3723 4004 3724 @node Build Steps , Interlocks, Build Process, Build Process4005 @node Build Steps 3725 4006 @section Build Steps 3726 4007 3727 4008 @code{BuildStep}s are usually specified in the buildmaster's … … 3756 4037 3757 4038 @menu 3758 4039 * Common Parameters:: 4040 * Using Build Properties:: 3759 4041 * Source Checkout:: 3760 4042 * ShellCommand:: 3761 4043 * Simple ShellCommand Subclasses:: … … 3765 4047 * Writing New BuildSteps:: 3766 4048 @end menu 3767 4049 3768 @node Common Parameters , Source Checkout, Build Steps, Build Steps4050 @node Common Parameters 3769 4051 @subsection Common Parameters 3770 4052 3771 4053 The standard @code{Build} runs a series of @code{BuildStep}s in order, … … 3816 4098 3817 4099 @end table 3818 4100 4101 @node Using Build Properties 4102 @subsection Using Build Properties 4103 @cindex Properties 4104 4105 Build properties are a generalized way to provide configuration 4106 information to build steps; see @ref{Build Properties}. 4107 4108 Some build properties are inherited from external sources -- global 4109 properties, schedulers, or buildslaves. Some build properties are 4110 set when the build starts, such as the SourceStamp information. Other 4111 properties can be set by BuildSteps as they run, for example the 4112 various Source steps will set the @code{got_revision} property to the 4113 source revision that was actually checked out (which can be useful 4114 when the SourceStamp in use merely requested the ``latest revision'': 4115 @code{got_revision} will tell you what was actually built). 4116 4117 In custom BuildSteps, you can get and set the build properties with 4118 the @code{getProperty}/@code{setProperty} methods. Each takes a string 4119 for the name of the property, and returns or accepts an 4120 arbitrary@footnote{Build properties are serialized along with the 4121 build results, so they must be serializable. For this reason, the 4122 value of any build property should be simple inert data: strings, 4123 numbers, lists, tuples, and dictionaries. They should not contain 4124 class instances.} object. For example: 4125 4126 @example 4127 class MakeTarball(ShellCommand): 4128 def start(self): 4129 if self.getProperty("os") == "win": 4130 self.setCommand([ ... ]) # windows-only command 4131 else: 4132 self.setCommand([ ... ]) # equivalent for other systems 4133 ShellCommand.start(self) 4134 @end example 4135 4136 @heading WithProperties 4137 @cindex WithProperties 4138 4139 You can use build properties in ShellCommands by using the 4140 @code{WithProperties} wrapper when setting the arguments of the 4141 ShellCommand. This interpolates the named build properties into the 4142 generated shell command. You can also use a @code{WithProperties} 4143 as the @code{workdir=} argument: this allows the working directory 4144 for a command to be varied for each build, depending upon various 4145 build properties. 4146 4147 @example 4148 from buildbot.steps.shell import ShellCommand 4149 from buildbot.process.properties import WithProperties 4150 4151 f.addStep(ShellCommand, 4152 command=["tar", "czf", 4153 WithProperties("build-%s.tar.gz", "revision"), 4154 "source"]) 4155 @end example 4156 4157 If this BuildStep were used in a tree obtained from Subversion, it 4158 would create a tarball with a name like @file{build-1234.tar.gz}. 4159 4160 The @code{WithProperties} function does @code{printf}-style string 4161 interpolation, using strings obtained by calling 4162 @code{build.getProperty(propname)}. Note that for every @code{%s} (or 4163 @code{%d}, etc), you must have exactly one additional argument to 4164 indicate which build property you want to insert. 4165 4166 You can also use python dictionary-style string interpolation by using 4167 the @code{%(propname)s} syntax. In this form, the property name goes 4168 in the parentheses, and WithProperties takes @emph{no} additional 4169 arguments: 4170 4171 @example 4172 f.addStep(ShellCommand, 4173 command=["tar", "czf", 4174 WithProperties("build-%(revision)s.tar.gz"), 4175 "source"]) 4176 @end example 4177 4178 Don't forget the extra ``s'' after the closing parenthesis! This is 4179 the cause of many confusing errors. 4180 4181 Note that, like python, you can either do positional-argument 4182 interpolation @emph{or} keyword-argument interpolation, not both. Thus 4183 you cannot use a string like 4184 @code{WithProperties("foo-%(revision)s-%s", "branch")}. 4185 4186 Most step parameters accept @code{WithProperties}. Please file bugs 4187 for any parameters which do not. 4188 4189 @heading Common Build Properties 4190 4191 The following build properties are set when the build is started, and 4192 are available to all steps. 4193 4194 @table @code 4195 @item branch 4196 4197 This comes from the build's SourceStamp, and describes which branch is 4198 being checked out. This will be @code{None} (which interpolates into 4199 @code{WithProperties} as an empty string) if the build is on the 4200 default branch, which is generally the trunk. Otherwise it will be a 4201 string like ``branches/beta1.4''. The exact syntax depends upon the VC 4202 system being used. 4203 4204 @item revision 4205 4206 This also comes from the SourceStamp, and is the revision of the 4207 source code tree that was requested from the VC system. When a build 4208 is requested of a specific revision (as is generally the case when 4209 the build is triggered by Changes), this will contain the revision 4210 specification. The syntax depends upon the VC system in use: for SVN 4211 it is an integer, for Mercurial it is a short string, for Darcs it 4212 is a rather large string, etc. 4213 4214 If the ``force build'' button was pressed, the revision will be 4215 @code{None}, which means to use the most recent revision available. 4216 This is a ``trunk build''. This will be interpolated as an empty 4217 string. 4218 4219 @item got_revision 4220 4221 This is set when a Source step checks out the source tree, and 4222 provides the revision that was actually obtained from the VC system. 4223 In general this should be the same as @code{revision}, except for 4224 trunk builds, where @code{got_revision} indicates what revision was 4225 current when the checkout was performed. This can be used to rebuild 4226 the same source code later. 4227 4228 Note that for some VC systems (Darcs in particular), the revision is a 4229 large string containing newlines, and is not suitable for interpolation 4230 into a filename. 4231 4232 @item buildername 4233 4234 This is a string that indicates which Builder the build was a part of. 4235 The combination of buildername and buildnumber uniquely identify a 4236 build. 4237 4238 @item buildnumber 4239 4240 Each build gets a number, scoped to the Builder (so the first build 4241 performed on any given Builder will have a build number of 0). This 4242 integer property contains the build's number. 4243 4244 @item slavename 4245 4246 This is a string which identifies which buildslave the build is 4247 running on. 4248 4249 @item scheduler 4250 4251 If the build was started from a scheduler, then this property will 4252 contain the name of that scheduler. 4253 4254 @end table 4255 3819 4256 3820 @node Source Checkout , ShellCommand, Common Parameters, Build Steps4257 @node Source Checkout 3821 4258 @subsection Source Checkout 3822 4259 3823 4260 The first step of any build is typically to acquire the source code … … 4475 4912 * Compile:: 4476 4913 * Test:: 4477 4914 * TreeSize:: 4478 * Build Properties::4479 4915 @end menu 4480 4916 4481 4917 @node Configure, Compile, Simple ShellCommand Subclasses, Simple ShellCommand Subclasses … … 4531 4967 This is meant to handle unit tests. The default command is @code{make 4532 4968 test}, and the @code{warnOnFailure} flag is set. 4533 4969 4534 @node TreeSize, Build Properties, Test, Simple ShellCommand Subclasses4970 @node TreeSize, , Test, Simple ShellCommand Subclasses 4535 4971 @subsubsection TreeSize 4536 4972 4537 4973 @bsindex buildbot.steps.shell.TreeSize … … 4542 4978 property named 'tree-size-KiB' with the same value. 4543 4979 4544 4980 4545 @node Build Properties, , TreeSize, Simple ShellCommand Subclasses4546 @subsubsection Build Properties4547 4548 @cindex build properties4549 4550 Each build has a set of ``Build Properties'', which can be used by its4551 BuildStep to modify their actions. For example, the SVN revision4552 number of the source code being built is available as a build4553 property, and a ShellCommand step could incorporate this number into a4554 command which create a numbered release tarball.4555 4556 Some build properties are set when the build starts, such as the4557 SourceStamp information. Other properties can be set by BuildSteps as4558 they run, for example the various Source steps will set the4559 @code{got_revision} property to the source revision that was actually4560 checked out (which can be useful when the SourceStamp in use merely4561 requested the ``latest revision'': @code{got_revision} will tell you4562 what was actually built).4563 4564 @itemize4565 @item buildername4566 Name of this builder4567 @item buildnumber4568 Number of this build (numbers are unique within the builder)4569 @item branch4570 Branch of the source being built, from the SourceStamp4571 @item revision4572 Revision of the source being built, from the SourceStamp, as a string4573 @item scheduler4574 The name of the scheduler that invoked this build4575 @item slavename4576 The name of the buildslave performing this build4577 @end itemize4578 4579 In custom BuildSteps, you can get and set the build properties with4580 the @code{getProperty}/@code{setProperty} methods. Each takes a string4581 for the name of the property, and returns or accepts an4582 arbitrary@footnote{Build properties are serialized along with the4583 build results, so they must be serializable. For this reason, the4584 value of any build property should be simple inert data: strings,4585 numbers, lists, tuples, and dictionaries. They should not contain4586 class instances.} object. For example:4587 4588 @example4589 class MakeTarball(ShellCommand):4590 def start(self):4591 self.setCommand(["tar", "czf",4592 "build-%s.tar.gz" % self.getProperty("revision"),4593 "source"])4594 ShellCommand.start(self)4595 @end example4596 4597 @cindex WithProperties4598 4599 You can use build properties in ShellCommands by using the4600 @code{WithProperties} wrapper when setting the arguments of the4601 ShellCommand. This interpolates the named build properties into the4602 generated shell command. You can also use a @code{WithProperties} as4603 the @code{workdir=} argument: this allows the working directory for a4604 command to be varied for each build, depending upon various build4605 properties.4606 4607 @example4608 from buildbot.steps.shell import ShellCommand, WithProperties4609 4610 f.addStep(ShellCommand,4611 command=["tar", "czf",4612 WithProperties("build-%s.tar.gz", "revision"),4613 "source"])4614 @end example4615 4616 If this BuildStep were used in a tree obtained from Subversion, it4617 would create a tarball with a name like @file{build-1234.tar.gz}.4618 4619 The @code{WithProperties} function does @code{printf}-style string4620 interpolation, using strings obtained by calling4621 @code{build.getProperty(propname)}. Note that for every @code{%s} (or4622 @code{%d}, etc), you must have exactly one additional argument to4623 indicate which build property you want to insert.4624 4625 4626 You can also use python dictionary-style string interpolation by using4627 the @code{%(propname)s} syntax. In this form, the property name goes4628 in the parentheses, and WithProperties takes @emph{no} additional4629 arguments:4630 4631 @example4632 f.addStep(ShellCommand,4633 command=["tar", "czf",4634 WithProperties("build-%(revision)s.tar.gz"),4635 "source"])4636 @end example4637 4638 Don't forget the extra ``s'' after the closing parenthesis! This is4639 the cause of many confusing errors. Also note that you can only use4640 WithProperties in the list form of the command= definition. You cannot4641 currently use it in the (discouraged) @code{command="stuff"}4642 single-string form. However, you can use something like4643 @code{command=["/bin/sh", "-c", "stuff", WithProperties(stuff)]} to4644 use both shell expansion and WithProperties interpolation.4645 4646 Note that, like python, you can either do positional-argument4647 interpolation @emph{or} keyword-argument interpolation, not both. Thus4648 you cannot use a string like4649 @code{WithProperties("foo-%(revision)s-%s", "branch")}.4650 4651 At the moment, the only way to set build properties is by writing a4652 custom BuildStep.4653 4654 @heading Common Build Properties4655 4656 The following build properties are set when the build is started, and4657 are available to all steps.4658 4659 @table @code4660 @item branch4661 4662 This comes from the build's SourceStamp, and describes which branch is4663 being checked out. This will be @code{None} (which interpolates into4664 @code{WithProperties} as an empty string) if the build is on the4665 default branch, which is generally the trunk. Otherwise it will be a4666 string like ``branches/beta1.4''. The exact syntax depends upon the VC4667 system being used.4668 4669 @item revision4670 4671 This also comes from the SourceStamp, and is the revision of the4672 source code tree that was requested from the VC system. When a build4673 is requested of a specific revision (as is generally the case when the4674 build is triggered by Changes), this will contain the revision4675 specification. The syntax depends upon the VC system in use: for SVN4676 it is an integer, for Mercurial it is a short string, for Darcs it is4677 a rather large string, etc.4678 4679 If the ``force build'' button was pressed, the revision will be4680 @code{None}, which means to use the most recent revision available.4681 This is a ``trunk build''. This will be interpolated as an empty4682 string.4683 4684 @item got_revision4685 4686 This is set when a Source step checks out the source tree, and4687 provides the revision that was actually obtained from the VC system.4688 In general this should be the same as @code{revision}, except for4689 trunk builds, where @code{got_revision} indicates what revision was4690 current when the checkout was performed. This can be used to rebuild4691 the same source code later.4692 4693 Note that for some VC systems (Darcs in particular), the revision is a4694 large string containing newlines, and is not suitable for4695 interpolation into a filename.4696 4697 @item buildername4698 4699 This is a string that indicates which Builder the build was a part of.4700 The combination of buildername and buildnumber uniquely identify a4701 build.4702 4703 @item buildnumber4704 4705 Each build gets a number, scoped to the Builder (so the first build4706 performed on any given Builder will have a build number of 0). This4707 integer property contains the build's number.4708 4709 @item slavename4710 4711 This is a string which identifies which buildslave the build is4712 running on.4713 4714 @end table4715 4716 4981 @node Python BuildSteps, Transferring Files, Simple ShellCommand Subclasses, Build Steps 4717 4982 @subsection Python BuildSteps 4718 4983 … … 4882 5147 @subsection Triggering Schedulers 4883 5148 4884 5149 The counterpart to the Triggerable described in section 4885 @pxref{ Build Dependencies} is the Trigger BuildStep.5150 @pxref{Triggerable Scheduler} is the Trigger BuildStep. 4886 5151 4887 5152 @example 4888 5153 from buildbot.steps.trigger import Trigger … … 5302 5567 [GCC 4.1.2 20060928 (prerelease) (Debian 4.1.1-15)] on linux2 5303 5568 Type "help", "copyright", "credits" or "license" for more information. 5304 5569 >>> import sys 5305 >>> print sys.path 5306 ['', '/usr/lib/python24.zip', '/usr/lib/python2.4', '/usr/lib/python2.4/plat-linux2', '/usr/lib/python2.4/lib-tk', '/usr/lib/python2.4/lib-dynload', '/usr/local/lib/python2.4/site-packages', '/usr/lib/python2.4/site-packages', '/usr/lib/python2.4/site-packages/Numeric', '/var/lib/python-support/python2.4', '/usr/lib/site-python'] 5307 >>> 5570 >>> import pprint 5571 >>> pprint.pprint(sys.path) 5572 ['', 5573 '/usr/lib/python24.zip', 5574 '/usr/lib/python2.4', 5575 '/usr/lib/python2.4/plat-linux2', 5576 '/usr/lib/python2.4/lib-tk', 5577 '/usr/lib/python2.4/lib-dynload', 5578 '/usr/local/lib/python2.4/site-packages', 5579 '/usr/lib/python2.4/site-packages', 5580 '/usr/lib/python2.4/site-packages/Numeric', 5581 '/var/lib/python-support/python2.4', 5582 '/usr/lib/site-python'] 5308 5583 @end example 5309 5584 5310 5585 In this case, putting the code into
![[Buildbot Logo]](/trac/chrome/site/header-text-transparent.png)