Trees | Indices | Help |
|
---|
|
1 # ***** BEGIN LICENSE BLOCK ***** 2 # Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 # 4 # The contents of this file are subject to the Mozilla Public License Version 5 # 1.1 (the "License"); you may not use this file except in compliance with 6 # the License. You may obtain a copy of the License at 7 # http://www.mozilla.org/MPL/ 8 # 9 # Software distributed under the License is distributed on an "AS IS" basis, 10 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 # for the specific language governing rights and limitations under the 12 # License. 13 # 14 # The Original Code is Mozilla-specific Buildbot steps. 15 # 16 # The Initial Developer of the Original Code is 17 # Mozilla Foundation. 18 # Portions created by the Initial Developer are Copyright (C) 2009 19 # the Initial Developer. All Rights Reserved. 20 # 21 # Contributor(s): 22 # Brian Warner <warner@lothar.com> 23 # 24 # Alternatively, the contents of this file may be used under the terms of 25 # either the GNU General Public License Version 2 or later (the "GPL"), or 26 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 27 # in which case the provisions of the GPL or the LGPL are applicable instead 28 # of those above. If you wish to allow use of your version of this file only 29 # under the terms of either the GPL or the LGPL, and not to allow others to 30 # use your version of this file under the terms of the MPL, indicate your 31 # decision by deleting the provisions above and replace them with the notice 32 # and other provisions required by the GPL or the LGPL. If you do not delete 33 # the provisions above, a recipient may use your version of this file under 34 # the terms of any one of the MPL, the GPL or the LGPL. 35 # 36 # ***** END LICENSE BLOCK ***** 37 38 import time 39 from twisted.internet import defer 40 from twisted.python import log 41 from buildbot.sourcestamp import SourceStamp 42 from buildbot.schedulers import base 43456547 # start a build (of the tip of self.branch) 48 db = self.parent.db 49 ss = SourceStamp(branch=self.branch) 50 ssid = db.get_sourcestampid(ss, t) 51 self.create_buildset(ssid, self.reason, t)5254 # start a build with the requested list of changes on self.branch 55 db = self.parent.db 56 ss = SourceStamp(branch=self.branch, changes=relevant_changes) 57 ssid = db.get_sourcestampid(ss, t) 58 self.create_buildset(ssid, self.reason, t)5967 """Instead of watching for Changes, this Scheduler can just start a build 68 at fixed intervals. The C{periodicBuildTimer} parameter sets the number 69 of seconds to wait between such periodic builds. The first build will be 70 run immediately.""" 71 72 # TODO: consider having this watch another (changed-based) scheduler and 73 # merely enforce a minimum time between builds. 74 compare_attrs = ('name', 'builderNames', 'periodicBuildTimer', 'branch', 75 'properties') 76117 11879 base.BaseScheduler.__init__(self, name, builderNames, properties) 80 self.periodicBuildTimer = periodicBuildTimer 81 self.branch = branch 82 self.reason = ("The Periodic scheduler named '%s' triggered this build" 83 % name)84 8789 db = self.parent.db 90 s = db.runInteractionNow(self.get_state) 91 last_build = s["last_build"] 92 now = time.time() 93 if last_build is None: 94 return [now] 95 return [last_build + self.periodicBuildTimer]96 101103 now = time.time() 104 s = self.get_state(t) 105 last_build = s["last_build"] 106 if last_build is None: 107 self.start_HEAD_build(t) 108 self.update_last_build(t, now) 109 last_build = now 110 when = last_build + self.periodicBuildTimer 111 if when < now: 112 self.start_HEAD_build(t) 113 self.update_last_build(t, now) 114 last_build = now 115 when = now + self.periodicBuildTimer 116 return when + 1.0120 """Imitate 'cron' scheduling. This can be used to schedule a nightly 121 build, or one which runs are certain times of the day, week, or month. 122 123 Pass some subset of minute, hour, dayOfMonth, month, and dayOfWeek; each 124 may be a single number or a list of valid values. The builds will be 125 triggered whenever the current time matches these values. Wildcards are 126 represented by a '*' string. All fields default to a wildcard except 127 'minute', so with no fields this defaults to a build every hour, on the 128 hour. 129 130 For example, the following master.cfg clause will cause a build to be 131 started every night at 3:00am:: 132 133 s = Nightly(name='nightly', builderNames=['builder1', 'builder2'], 134 hour=3, minute=0) 135 c['schedules'].append(s) 136 137 This scheduler will perform a build each monday morning at 6:23am and 138 again at 8:23am:: 139 140 s = Nightly(name='BeforeWork', builderNames=['builder1'], 141 dayOfWeek=0, hour=[6,8], minute=23) 142 143 The following runs a build every two hours:: 144 145 s = Nightly(name='every2hours', builderNames=['builder1'], 146 hour=range(0, 24, 2)) 147 148 And this one will run only on December 24th:: 149 150 s = Nightly(name='SleighPreflightCheck', 151 builderNames=['flying_circuits', 'radar'], 152 month=12, dayOfMonth=24, hour=12, minute=0) 153 154 For dayOfWeek and dayOfMonth, builds are triggered if the date matches 155 either of them. All time values are compared against the tuple returned 156 by time.localtime(), so month and dayOfMonth numbers start at 1, not 157 zero. dayOfWeek=0 is Monday, dayOfWeek=6 is Sunday. 158 159 When onlyIfChanged is True, the build is triggered only if changes have 160 arrived on the given branch since the last build was performed. As a 161 further restriction, if fileIsImportant= is provided (a one-argument 162 callable which takes a Change object and returns a bool), then the build 163 will be triggered only if at least one of those changes qualifies as 164 'important'. The following example will run a build at 3am, but only when 165 a source code file (.c/.h) has been changed: 166 167 def isSourceFile(change): 168 for fn in change.files: 169 if fn.endswith('.c') or fn.endswith('.h'): 170 return True 171 return False 172 s = Nightly(name='nightly-when-changed', builderNames=['builder1'], 173 hour=3, minute=0, 174 onlyIfChanged=True, fileIsImportant=isSourceFile) 175 176 onlyIfChanged defaults to False, which means a build will be performed 177 even if nothing has changed. 178 """ 179 180 compare_attrs = ('name', 'builderNames', 181 'minute', 'hour', 'dayOfMonth', 'month', 182 'dayOfWeek', 'branch', 'onlyIfChanged', 183 'fileIsImportant', 'properties') 184324185 - def __init__(self, name, builderNames, minute=0, hour='*', 186 dayOfMonth='*', month='*', dayOfWeek='*', 187 branch=None, fileIsImportant=None, onlyIfChanged=False, 188 properties={}):189 # Setting minute=0 really makes this an 'Hourly' scheduler. This 190 # seemed like a better default than minute='*', which would result in 191 # a build every 60 seconds. 192 base.BaseScheduler.__init__(self, name, builderNames, properties) 193 self.minute = minute 194 self.hour = hour 195 self.dayOfMonth = dayOfMonth 196 self.month = month 197 self.dayOfWeek = dayOfWeek 198 self.branch = branch 199 self.onlyIfChanged = onlyIfChanged 200 self.delayedRun = None 201 self.nextRunTime = None 202 self.reason = ("The Nightly scheduler named '%s' triggered this build" 203 % name) 204 self.fileIsImportant = None 205 if fileIsImportant: 206 assert callable(fileIsImportant) 207 self.fileIsImportant = fileIsImportant 208 self._start_time = time.time() 209 210 # this scheduler does not support filtering, but ClassifierMixin needs a 211 # filter anyway 212 self.make_filter()213 219221 now = time.time() 222 next = self._calculateNextRunTimeFrom(now) 223 # note: this ignores onlyIfChanged 224 return [next]225227 d = defer.succeed(None) 228 db = self.parent.db 229 if self.onlyIfChanged: 230 # call classify_changes, so that we can keep last_processed 231 # up to date, in case we are configured with onlyIfChanged. 232 d.addCallback(lambda ign: db.runInteraction(self.classify_changes)) 233 d.addCallback(lambda ign: db.runInteraction(self._check_timer)) 234 return d235237 now = time.time() 238 s = self.get_state(t) 239 last_build = s["last_build"] 240 if last_build is None: 241 next = self._calculateNextRunTimeFrom(self._start_time) 242 else: 243 next = self._calculateNextRunTimeFrom(last_build) 244 245 # not ready to fire yet 246 if next >= now: 247 return next + 1.0 248 249 self._maybe_start_build(t) 250 self.update_last_build(t, now) 251 252 # reschedule for the next timer 253 return self._check_timer(t)254256 db = self.parent.db 257 if self.onlyIfChanged: 258 res = db.scheduler_get_classified_changes(self.schedulerid, t) 259 (important, unimportant) = res 260 if not important: 261 log.msg("Nightly Scheduler <%s>: " 262 "skipping build - No important change" % self.name) 263 return 264 relevant_changes = [c for c in (important + unimportant) if 265 c.branch == self.branch] 266 if not relevant_changes: 267 log.msg("Nightly Scheduler <%s>: " 268 "skipping build - No relevant change on branch" % 269 self.name) 270 return 271 self.start_requested_build(t, relevant_changes) 272 # retire the changes 273 changeids = [c.number for c in relevant_changes] 274 db.scheduler_retire_changes(self.schedulerid, changeids, t) 275 else: 276 # start it unconditionally 277 self.start_HEAD_build(t) 278 279 # Retire any changes on this scheduler 280 res = db.scheduler_get_classified_changes(self.schedulerid, t) 281 (important, unimportant) = res 282 changeids = [c.number for c in important + unimportant] 283 db.scheduler_retire_changes(self.schedulerid, changeids, t)284 287289 def check(ourvalue, value): 290 if ourvalue == '*': return True 291 if isinstance(ourvalue, int): return value == ourvalue 292 return (value in ourvalue)293 294 if not check(self.minute, timetuple[4]): 295 #print 'bad minute', timetuple[4], self.minute 296 return False 297 298 if not check(self.hour, timetuple[3]): 299 #print 'bad hour', timetuple[3], self.hour 300 return False 301 302 if not check(self.month, timetuple[1]): 303 #print 'bad month', timetuple[1], self.month 304 return False 305 306 if self.dayOfMonth != '*' and self.dayOfWeek != '*': 307 # They specified both day(s) of month AND day(s) of week. 308 # This means that we only have to match one of the two. If 309 # neither one matches, this time is not the right time. 310 if not (check(self.dayOfMonth, timetuple[2]) or 311 check(self.dayOfWeek, timetuple[6])): 312 #print 'bad day' 313 return False 314 else: 315 if not check(self.dayOfMonth, timetuple[2]): 316 #print 'bad day of month' 317 return False 318 319 if not check(self.dayOfWeek, timetuple[6]): 320 #print 'bad day of week' 321 return False 322 323 return True326 dateTime = time.localtime(now) 327 328 # Remove seconds by advancing to at least the next minute 329 dateTime = self._addTime(dateTime, 60-dateTime[5]) 330 331 # Now we just keep adding minutes until we find something that matches 332 333 # It not an efficient algorithm, but it'll *work* for now 334 yearLimit = dateTime[0]+2 335 while not self._isRunTime(dateTime): 336 dateTime = self._addTime(dateTime, 60) 337 #print 'Trying', time.asctime(dateTime) 338 assert dateTime[0] < yearLimit, 'Something is wrong with this code' 339 return time.mktime(dateTime)340
Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Fri Oct 29 10:00:48 2010 | http://epydoc.sourceforge.net |