1 # This file is part of Buildbot. Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2.
5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
14 # Copyright Buildbot Team Members
16 from zope
.interface
import implements
17 from twisted
.application
import service
19 from buildbot
import interfaces
20 from buildbot
.process
.properties
import Properties
21 from buildbot
.util
import ComparableMixin
, NotABranch
22 from buildbot
.schedulers
import filter
27 class BaseScheduler(service
.MultiService
, ComparableMixin
):
28 implements(interfaces
.IScheduler
)
29 # subclasses must set .compare_attrs
31 upstream_name
= None # set to be notified about upstream buildsets
33 def __init__(self
, name
, builderNames
, properties
):
34 service
.MultiService
.__init
__(self
)
36 self
.properties
= Properties()
37 self
.properties
.update(properties
, "Scheduler")
38 self
.properties
.setProperty("scheduler", name
, "Scheduler")
39 errmsg
= ("The builderNames= argument to Scheduler must be a list "
40 "of Builder description names (i.e. the 'name' key of the "
41 "Builder specification dictionary)")
42 assert isinstance(builderNames
, (list, tuple)), errmsg
43 for b
in builderNames
:
44 assert isinstance(b
, str), errmsg
45 self
.builderNames
= builderNames
46 # I will acquire a .schedulerid value before I'm started
48 def compareToOther(self
, them
):
49 # like ComparableMixin.__cmp__, but only used by our manager
50 # TODO: why?? why not use __cmp__?
51 result
= cmp(type(self
), type(them
))
54 result
= cmp(self
.__class
__, them
.__class
__)
57 assert self
.compare_attrs
== them
.compare_attrs
58 self_list
= [getattr(self
, name
, _None
) for name
in self
.compare_attrs
]
59 them_list
= [getattr(them
, name
, _None
) for name
in self
.compare_attrs
]
60 return cmp(self_list
, them_list
)
62 def get_initial_state(self
, max_changeid
):
63 # override this if you pay attention to Changes, probably to:
64 #return {"last_processed": max_changeid}
67 def get_state(self
, t
):
68 return self
.parent
.db
.scheduler_get_state(self
.schedulerid
, t
)
70 def set_state(self
, t
, state
):
71 self
.parent
.db
.scheduler_set_state(self
.schedulerid
, t
, state
)
73 def listBuilderNames(self
):
74 return self
.builderNames
76 def getPendingBuildTimes(self
):
79 def create_buildset(self
, ssid
, reason
, t
, props
=None, builderNames
=None):
82 props
= self
.properties
83 if builderNames
is None:
84 builderNames
= self
.builderNames
85 bsid
= db
.create_buildset(ssid
, reason
, props
, builderNames
, t
)
86 # notify downstream schedulers so they can watch for it to complete
87 self
.parent
.publish_buildset(self
.name
, bsid
, t
)
90 class ClassifierMixin
:
92 Mixin to classify changes using self.change_filter, a filter.ChangeFilter instance.
95 def make_filter(self
, change_filter
=None, branch
=NotABranch
, categories
=None):
97 if (branch
is not NotABranch
or categories
is not None):
98 raise RuntimeError("cannot specify both change_filter and either branch or categories")
99 self
.change_filter
= change_filter
102 # build a change filter from the deprecated category and branch args
104 if branch
is not NotABranch
: cfargs
['branch'] = branch
105 if categories
: cfargs
['category'] = categories
106 self
.change_filter
= filter.ChangeFilter(**cfargs
)
108 def classify_changes(self
, t
):
110 cm
= self
.parent
.change_svc
111 state
= self
.get_state(t
)
112 state_changed
= False
113 last_processed
= state
.get("last_processed", None)
115 if last_processed
is None:
116 last_processed
= state
['last_processed'] = cm
.getLatestChangeNumberNow(t
)
119 changes
= cm
.getChangesGreaterThan(last_processed
, t
)
121 if self
.change_filter
.filter_change(c
):
123 if self
.fileIsImportant
:
124 important
= self
.fileIsImportant(c
)
125 db
.scheduler_classify_change(self
.schedulerid
, c
.number
,
127 # now that we've recorded a decision about each, we can update the
128 # last_processed record
130 max_changeid
= max([c
.number
for c
in changes
])
131 state
["last_processed"] = max_changeid
# retain other keys
135 self
.set_state(t
, state
)