Package buildbot :: Package schedulers :: Module forcesched
[frames] | no frames]

Source Code for Module buildbot.schedulers.forcesched

  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. 
  4  # 
  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 
  8  # details. 
  9  # 
 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. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16  import traceback 
 17  import re 
 18  from twisted.internet import defer 
 19  try: 
 20      import email.utils as email_utils 
 21      email_utils = email_utils 
 22  except ImportError: 
 23      # Python-2.4 capitalization 
 24      import email.Utils as email_utils 
 25   
 26  from buildbot.process.properties import Properties 
 27  from buildbot.schedulers import base 
 28   
29 -class BaseParameter(object):
30 name = "" 31 label = "" 32 type = "" 33 default = "" 34 required = False 35 multiple = False 36 regex = None 37 debug=True 38
39 - def __init__(self, name, label=None, regex=None, **kw):
40 self.label = self.name = name 41 if label: 42 self.label = label 43 if regex: 44 self.regex = re.compile(regex) 45 # all other properties are generically passed via **kw 46 self.__dict__.update(kw)
47
48 - def update_from_post(self, master, properties, changes, req):
49 args = req.args.get(self.name, []) 50 if len(args) == 0: 51 if self.required: 52 raise ValueError("'%s' needs to be specified" % (self.label)) 53 if self.multiple: 54 args = self.default 55 else: 56 args = [self.default] 57 if self.regex: 58 for arg in args: 59 if not self.regex.match(arg): 60 raise ValueError("%s:'%s' does not match pattern '%s'" 61 % (self.label, arg, self.regex.pattern)) 62 try: 63 arg = self.parse_from_args(args) 64 except Exception, e: 65 # an exception will just display an alert in the web UI 66 # also log the exception 67 if self.debug: 68 traceback.print_exc() 69 raise e 70 if arg == None: 71 raise ValueError("need %s: no default provided by config" 72 % (self.name,)) 73 properties[self.name] = arg
74
75 - def parse_from_args(self, l):
76 if self.multiple: 77 return map(self.parse_from_arg, l) 78 else: 79 return self.parse_from_arg(l[0])
80
81 - def parse_from_arg(self, s):
82 return s
83 84
85 -class FixedParameter(BaseParameter):
86 type = "fixed" 87 hide = True 88 default = "" 89
90 - def parse_from_args(self, l):
91 return self.default
92 93
94 -class StringParameter(BaseParameter):
95 type = "text" 96 size = 10 97
98 - def parse_from_arg(self, s):
99 return s
100 101
102 -class TextParameter(StringParameter):
103 type = "textarea" 104 cols = 80 105 rows = 20 106
107 - def value_to_text(self, value):
108 return str(value)
109 110
111 -class IntParameter(StringParameter):
112 type = "int" 113 114 parse_from_arg = int # will throw an exception if parse fail
115 116
117 -class BooleanParameter(BaseParameter):
118 type = "bool" 119
120 - def update_from_post(self, master, properties, changes, req):
121 # damn html's ungeneric checkbox implementation... 122 checkbox = req.args.get("checkbox", [""]) 123 properties[self.name] = self.name in checkbox
124 125
126 -class UserNameParameter(StringParameter):
127 type = "text" 128 default = "" 129 size = 30 130 need_email = True 131
132 - def __init__(self, name="username", label="Your name:", **kw):
133 BaseParameter.__init__(self, name, label, **kw)
134
135 - def parse_from_arg(self, s):
136 if self.need_email: 137 e = email_utils.parseaddr(s) 138 if e[0]=='' or e[1] == '': 139 raise ValueError("%s: please fill in email address in the " 140 " form User <email@email.com>" % (self.label,)) 141 return s
142 143
144 -class ChoiceStringParameter(BaseParameter):
145 type = "list" 146 choices = [] 147 strict = True 148
149 - def parse_from_arg(self, s):
150 if self.strict and not s in self.choices: 151 raise ValueError("'%s' does not belongs to list of available choices '%s'"%(s, self.choices)) 152 return s
153 154
155 -class InheritBuildParameter(ChoiceStringParameter):
156 name = "inherit" 157 compatible_builds = None 158
159 - def update_from_post(self, master, properties, changes, req):
160 arg = req.args.get(self.name, [""])[0] 161 splitted_arg = arg.split(" ")[0].split("/") 162 if len(splitted_arg) != 2: 163 raise ValueError("bad build: %s"%(arg)) 164 builder, num = splitted_arg 165 builder_status = master.status.getBuilder(builder) 166 if not builder_status: 167 raise ValueError("unknown builder: %s in %s"%(builder, arg)) 168 b = builder_status.getBuild(int(num)) 169 if not b: 170 raise ValueError("unknown build: %d in %s"%(num, arg)) 171 props = {self.name:(arg.split(" ")[0])} 172 for name, value, source in b.getProperties().asList(): 173 if source == "Force Build Form": 174 if name == "owner": 175 name = "orig_owner" 176 props[name] = value 177 properties.update(props) 178 changes.extend(b.changes)
179 180
181 -class AnyPropertyParameter(BaseParameter):
182 type = "anyproperty" 183
184 - def update_from_post(self, master, properties, changes, req):
185 validation = master.config.validation 186 pname = req.args.get("%sname" % self.name, [""])[0] 187 pvalue = req.args.get("%svalue" % self.name, [""])[0] 188 if not pname: 189 return 190 pname_validate = validation['property_name'] 191 pval_validate = validation['property_value'] 192 if not pname_validate.match(pname) \ 193 or not pval_validate.match(pvalue): 194 raise ValueError("bad property name='%s', value='%s'" % (pname, pvalue)) 195 properties[pname] = pvalue
196 197
198 -class ForceScheduler(base.BaseScheduler):
199 200 compare_attrs = ( 'name', 'builderNames', 'branch', 'reason', 201 'revision', 'repository', 'project', 'forcedProperties' ) 202
203 - def __init__(self, name, builderNames, 204 branch=StringParameter(name="branch",default=""), 205 reason=StringParameter(name="reason", default="force build"), 206 revision=StringParameter(name="revision",default=""), 207 repository=StringParameter(name="repository",default=""), 208 project=StringParameter(name="project",default=""), 209 username=UserNameParameter(), 210 properties=[ 211 AnyPropertyParameter("property1"), 212 AnyPropertyParameter("property2"), 213 AnyPropertyParameter("property3"), 214 AnyPropertyParameter("property4"), 215 ]):
216 217 base.BaseScheduler.__init__(self, name=name, 218 builderNames=builderNames,properties={}) 219 self.branch = branch 220 self.reason = reason 221 self.repository = repository 222 self.revision = revision 223 self.project = project 224 self.username = username 225 self.forcedProperties = properties 226 # this is used to simplify the template 227 self.all_fields = [ branch, username, reason, repository, 228 revision, project ] 229 self.all_fields.extend(properties)
230
231 - def startService(self):
232 pass
233
234 - def stopService(self):
235 pass
236
237 - def forceWithWebRequest(self, owner, builder_name, req):
238 """Called by the web UI. 239 Authentication is already done, thus owner is passed as argument 240 We check the parameters, and launch the build, if everything is correct 241 """ 242 if not builder_name in self.builderNames: 243 # in the case of buildAll, this method will be called several times 244 # for all the builders 245 # we just do nothing on a builder that is not in our builderNames 246 return defer.succeed(None) 247 master = self.master 248 properties = {} 249 changeids = [] 250 # probably need to clean that out later as the IProperty is already a 251 # validation mechanism 252 253 validation = master.config.validation 254 if self.branch.regex == None: 255 self.branch.regex = validation['branch'] 256 if self.revision.regex == None: 257 self.revision.regex = validation['revision'] 258 259 for param in self.all_fields: 260 if owner and param==self.username: 261 continue # dont enforce username if auth 262 param.update_from_post(master, properties, changeids, req) 263 264 changeids = map(lambda a: type(a)==int and a or a.number, changeids) 265 # everything is validated, we can create our source stamp, and buildrequest 266 reason = properties[self.reason.name] 267 branch = properties[self.branch.name] 268 revision = properties[self.revision.name] 269 repository = properties[self.repository.name] 270 project = properties[self.project.name] 271 if owner is None: 272 owner = properties[self.username.name] 273 274 std_prop_names = [self.branch.name, 275 self.revision.name, self.repository.name, 276 self.project.name, self.username.name] 277 real_properties = Properties() 278 for pname, pvalue in properties.items(): 279 if not pname in std_prop_names: 280 real_properties.setProperty(pname, pvalue, "Force Build Form") 281 282 real_properties.setProperty("owner", owner, "Force Build Form") 283 284 r = ("The web-page 'force build' button was pressed by '%s': %s\n" 285 % (owner, reason)) 286 287 d = master.db.sourcestampsets.addSourceStampSet() 288 def add_master_with_setid(sourcestampsetid): 289 master.db.sourcestamps.addSourceStamp( 290 sourcestampsetid = sourcestampsetid, 291 branch=branch, 292 revision=revision, project=project, 293 repository=repository,changeids=changeids) 294 return sourcestampsetid
295 296 d.addCallback(add_master_with_setid) 297 def got_setid(sourcestampsetid): 298 return self.addBuildsetForSourceStamp(builderNames=[builder_name], 299 setid=sourcestampsetid, reason=r, 300 properties=real_properties)
301 d.addCallback(got_setid) 302 return d 303