Example #1
0
    def command_BUILD(self, args, who):
        args = shlex.split(args)
        repos = { 'apiextractor' : None,
                  'generatorrunner' : None,
                  'shiboken' : None,
                  'pyside' : None
                }

        if not who in config.gitCustomers:
            self.send('%s, I\'ll not make this build for you. Do you think I found my genitalia in the trash?' % who)
            return

        builder = None
        for arg in args:
            try:
                repo, target = arg.split('=')
            except:
                self.send('Usage: ' + PySideContact.command_BUILD.usage)
                return
            if repo == 'builder':
                builder = target
            else:
                if not repo in repos:
                    self.send('%s, there\'s no "%s" repository' % (who, repo))
                    return
                repos[repo] = target

        slaves = ['build-pyside-' + arch for arch in config.slavesByArch.keys()]

        if builder:
            if builder not in slaves:
                self.send("%s, the slave '%s' that you asked for doesn't exist." % (who, builder))
                return
            slaves = [builder]

        for which in slaves:
            bc = self.getControl(which)

            build_properties = Properties()
            build_properties.setProperty('owner', who, 'Build requested from IRC bot on behalf of %s.' % who)
            for propName, propValue in [(pName, pValue) for pName, pValue in repos.items() if pValue]:
                build_properties.setProperty(propName + '_hashtag', propValue, 'Build requested from IRC bot.')

            for repoName, gitUrl in config.gitCustomers[who].items():
                build_properties.setProperty(repoName.lower() + '_gitUrl', config.baseGitURL + gitUrl,
                                             'Personal %s repository of %s.' % (repoName, who))

            r = "forced: by %s: %s" % (self.describeUser(who), 'He had his reasons.')
            s = SourceStamp(branch='BRANCH', revision='REVISION')
            req = BuildRequest(r, s, which, properties=build_properties)
            try:
                bc.requestBuildSoon(req)
            except interfaces.NoSlaveError:
                self.send("%s, sorry, I can't force a build: all slaves are offline" % who)
                return
            ireq = IrcBuildRequest(self)
            req.subscribe(ireq.started)
 def doBuild(self, buildername, reason="forced"):
     # we need to prevent these builds from being merged, so we create
     # each of them with a different revision specifier. The revision is
     # ignored because our build process does not have a source checkout
     # step.
     self.revision += 1
     br = BuildRequest(reason, SourceStamp(revision=self.revision))
     d = br.waitUntilFinished()
     self.control.getBuilder(buildername).requestBuild(br)
     return d
 def requestBuild(self, builder, locale):
     # returns a Deferred that fires with an IBuildStatus object when the
     # build is finished
     props = Properties()
     props.setProperty('locale', locale, 'scheduler')
     props.setProperty('tree', 'app', 'scheduler')
     req = BuildRequest("forced build", SourceStamp(), builder,
                        properties=props)
     self.control.getBuilder(builder).requestBuild(req)
     return req.waitUntilFinished()
Example #4
0
 def submitBuild(self):
     ss = SourceStamp()
     br = BuildRequest("forced build", ss, "dummy")
     self.control.getBuilder("dummy").requestBuild(br)
     d = defer.Deferred()
     def _started(bc):
         br.unsubscribe(_started)
         d.callback(bc)
     br.subscribe(_started)
     return d
Example #5
0
 def send(res):
     # send some build requests
     ss = SourceStamp()
     for i in range(5):
         req = BuildRequest(str(i), ss, "dummy")
         self.master.botmaster.builders['dummy'].submitBuildRequest(req)
         req.submittedAt = i
         def append(build):
             start_order.append(int(build.reason))
         req.subscribe(append)
         reqs.append(req.waitUntilFinished())
Example #6
0
        def _send(res):
            # send some build requests
            reqs = []
            ss = SourceStamp()
            for i in range(5):
                req = BuildRequest(str(i), ss, "dummy")
                self.master.botmaster.builders['dummy'].submitBuildRequest(req)
                reqs.append(req.waitUntilFinished())

            dl = defer.DeferredList(reqs)
            dl.addCallback(check)
            return dl
Example #7
0
    def testPriority(self):
        self.rmtree("basedir")
        os.mkdir("basedir")
        self.master.loadConfig(config_priority)
        self.master.readConfig = True
        self.master.startService()

        # Our fake source stamp
        # we override canBeMergedWith so that our requests don't get merged together
        ss = SourceStamp()
        ss.canBeMergedWith = lambda x: False

        # Send 10 requests to alternating builders
        # We fudge the submittedAt field after submitting since they're all
        # getting submitted so close together according to time.time()
        # and all we care about is what order they're run in.
        reqs = []
        self.start_order = []
        for i in range(10):
            req = BuildRequest(str(i), ss, "test_builder")
            j = i % 2 + 1
            self.master.botmaster.builders['quick%i' % j].submitBuildRequest(req)
            req.submittedAt = i
            # Keep track of what order the builds start in
            def append(build):
                self.start_order.append(int(build.reason))
            req.subscribe(append)
            reqs.append(req.waitUntilFinished())

        dl = defer.DeferredList(reqs)
        dl.addCallback(self._all_finished)

        def _delay(res):
            d1 = defer.Deferred()
            reactor.callLater(0.5, d1.callback, None)
            # this test depends upon this 0.5s delay landing us in the middle
            # of one of the builds.
            return d1

        def _connect(res, i):
            return self.connectSlave(slavename="bot%i" % i, builders=["quick1", "quick2"])

        # Now add the slaves
        d = self.connectSlave(slavename="bot0", builders=["quick1", "quick2"])
        for i in range(1,5):
            d.addCallback(_delay)
            d.addCallback(_connect, i)

        d.addCallback(lambda x: dl)

        return d
    def _testSlave_1(self, res, t1):
        self.failUnlessEqual(len(t1.events), 2)
        self.failUnlessEqual(t1.events[0],
                             ("builderChangedState", "dummy", "idle"))
        self.failUnlessEqual(t1.events[1],
                             ("builderChangedState", "testdummy", "idle"))
        t1.events = []

        c = interfaces.IControl(self.master)
        req = BuildRequest("forced build for testing", SourceStamp())
        c.getBuilder("dummy").requestBuild(req)
        d = req.waitUntilFinished()
        d2 = self.master.botmaster.waitUntilBuilderIdle("dummy")
        dl = defer.DeferredList([d, d2])
        dl.addCallback(self._testSlave_2)
        return dl
Example #9
0
 def testDontClaimPingingSlave(self):
     # have two slaves connect for the same builder. Do something to the
     # first one so that slavepings are delayed (but do not fail
     # outright).
     timers = []
     self.slaves['bot1'].debugOpts["stallPings"] = (10, timers)
     br = BuildRequest("forced", SourceStamp(), 'test_builder')
     d1 = br.waitUntilFinished()
     self.master.botmaster.builders["b1"].CHOOSE_SLAVES_RANDOMLY = False
     self.control.getBuilder("b1").requestBuild(br)
     s1 = br.status # this is a BuildRequestStatus
     # give it a chance to start pinging
     d2 = defer.Deferred()
     d2.addCallback(self._testDontClaimPingingSlave_1, d1, s1, timers)
     reactor.callLater(1, d2.callback, None)
     return d2
Example #10
0
    def _connected(self, *args):
        # Our fake source stamp
        # we override canBeMergedWith so that our requests don't get merged together
        ss = SourceStamp()
        ss.canBeMergedWith = lambda x: False

        # Send one request to tie up the slave before sending future requests
        req0 = BuildRequest("reason", ss, "test_builder")
        self.master.botmaster.builders['quick1'].submitBuildRequest(req0)

        # Send 10 requests to alternating builders
        # We fudge the submittedAt field after submitting since they're all
        # getting submitted so close together according to time.time()
        # and all we care about is what order they're run in.
        reqs = []
        self.finish_order = []
        for i in range(10):
            req = BuildRequest(str(i), ss, "test_builder")
            j = i % 2 + 1
            self.master.botmaster.builders['quick%i' % j].submitBuildRequest(req)
            req.submittedAt = i
            # Keep track of what order the builds finished in
            def append(item, arg):
                self.finish_order.append(item)
            req.waitUntilFinished().addCallback(append, req)
            reqs.append(req.waitUntilFinished())

        dl = defer.DeferredList(reqs)
        dl.addCallback(self._all_finished)

        # After our first build finishes, we should wait for the rest to finish
        d = req0.waitUntilFinished()
        d.addCallback(lambda x: dl)
        return d
Example #11
0
    def command_FORCE(self, args, who):
        args = shlex.split(args) # TODO: this requires python2.3 or newer
        if not args:
            raise UsageError("try 'force build WHICH <REASON>'")
        what = args.pop(0)
        if what != "build":
            raise UsageError("try 'force build WHICH <REASON>'")
        opts = ForceOptions()
        opts.parseOptions(args)
        
        which = opts['builder']
        branch = opts['branch']
        revision = opts['revision']
        reason = opts['reason']

        if which is None:
            raise UsageError("you must provide a Builder, "
                             "try 'force build WHICH <REASON>'")

        # keep weird stuff out of the branch and revision strings. TODO:
        # centralize this somewhere.
        if branch and not re.match(r'^[\w\.\-\/]*$', branch):
            log.msg("bad branch '%s'" % branch)
            self.send("sorry, bad branch '%s'" % branch)
            return
        if revision and not re.match(r'^[\w\.\-\/]*$', revision):
            log.msg("bad revision '%s'" % revision)
            self.send("sorry, bad revision '%s'" % revision)
            return

        bc = self.getControl(which)

        r = "forced: by %s: %s" % (self.describeUser(who), reason)
        # TODO: maybe give certain users the ability to request builds of
        # certain branches
        s = SourceStamp(branch=branch, revision=revision)
        req = BuildRequest(r, s, which)
        try:
            bc.requestBuildSoon(req)
        except interfaces.NoSlaveError:
            self.send("sorry, I can't force a build: all slaves are offline")
            return
        ireq = IrcBuildRequest(self)
        req.subscribe(ireq.started)
Example #12
0
 def send(res):
     # send some build requests
     ss = SourceStamp()
     for i in range(5):
         req1 = BuildRequest(str(i), ss, "dummy")
         self.master.botmaster.builders['dummy1'].submitBuildRequest(req1)
         req2 = BuildRequest(str(i), ss, "dummy")
         self.master.botmaster.builders['dummy2'].submitBuildRequest(req2)
         def append(build):
             builder_names.append(build.builder.name)
         req1.subscribe(append)
         req2.subscribe(append)
         reqs.append(req1.waitUntilFinished())
         complete_reqs.append(req1)
         incomplete_reqs.append(req2)
Example #13
0
    def testIdle2(self):
        # now suppose the slave goes missing
        self.disappearSlave(allowReconnect=False)

        # forcing a build will work: the build detect that the slave is no
        # longer available and will be re-queued. Wait 5 seconds, then check
        # to make sure the build is still in the 'waiting for a slave' queue.
        req = BuildRequest("forced build", SourceStamp(), "test_builder")
        self.failUnlessEqual(req.startCount, 0)
        self.control.getBuilder("dummy").requestBuild(req)
        # this should ping the slave, which doesn't respond (and eventually
        # times out). The BuildRequest will be re-queued, and its .startCount
        # will be incremented.
        self.killSlave()
        d = defer.Deferred()
        d.addCallback(self._testIdle2_1, req)
        reactor.callLater(3, d.callback, None)
        return d
Example #14
0
        def send(res):
            # send some build requests
            ss = SourceStamp()
            for i in range(5):
                req = BuildRequest(str(i), ss, "dummy")
                self.master.botmaster.builders['dummy'].submitBuildRequest(req)
                req.submittedAt = i

                def append(build):
                    start_order.append(int(build.reason))

                req.subscribe(append)
                reqs.append(req.waitUntilFinished())
Example #15
0
    def testPriority(self):
        self.rmtree("basedir")
        os.mkdir("basedir")
        self.master.loadConfig(config_priority)
        self.master.readConfig = True
        self.master.startService()

        # Our fake source stamp
        # we override canBeMergedWith so that our requests don't get merged together
        ss = SourceStamp()
        ss.canBeMergedWith = lambda x: False

        # Send 10 requests to alternating builders
        # We fudge the submittedAt field after submitting since they're all
        # getting submitted so close together according to time.time()
        # and all we care about is what order they're run in.
        reqs = []
        self.start_order = []
        for i in range(10):
            req = BuildRequest(str(i), ss, "test_builder")
            j = i % 2 + 1
            self.master.botmaster.builders['quick%i' % j].submitBuildRequest(req)
            req.submittedAt = i
            # Keep track of what order the builds start in
            def append(build):
                self.start_order.append(int(build.reason))
            req.subscribe(append)
            reqs.append(req.waitUntilFinished())

        dl = defer.DeferredList(reqs)
        dl.addCallback(self._all_finished)

        def _delay(res):
            d1 = defer.Deferred()
            reactor.callLater(0.5, d1.callback, None)
            # this test depends upon this 0.5s delay landing us in the middle
            # of one of the builds.
            return d1

        def _connect(res, i):
            return self.connectSlave(slavename="bot%i" % i, builders=["quick1", "quick2"])

        # Now add the slaves
        d = self.connectSlave(slavename="bot0", builders=["quick1", "quick2"])
        for i in range(1,5):
            d.addCallback(_delay)
            d.addCallback(_connect, i)

        d.addCallback(lambda x: dl)

        return d
Example #16
0
def makeBuildStep(basedir, step_class=BuildStep, **kwargs):
    bss = setupBuildStepStatus(basedir)

    ss = SourceStamp()
    setup = {
        'name': "builder1",
        "slavename": "bot1",
        'builddir': "builddir",
        'factory': None
    }
    b0 = Builder(setup, bss.getBuild().getBuilder())
    br = BuildRequest("reason", ss)
    b = Build([br])
    b.setBuilder(b0)
    s = step_class(**kwargs)
    s.setBuild(b)
    s.setStepStatus(bss)
    b.setupStatus(bss.getBuild())
    s.slaveVersion = fake_slaveVersion
    return s
    def force(self, req):
        name = req.args.get("username", ["<unknown>"])[0]
        reason = req.args.get("comments", ["<no reason specified>"])[0]
        branch = req.args.get("branch", [""])[0]
        revision = req.args.get("revision", [""])[0]

        r = "The web-page 'force build' button was pressed by '%s': %s\n" \
            % (name, reason)
        log.msg("web forcebuild of builder '%s', branch='%s', revision='%s'" %
                (self.builder_status.getName(), branch, revision))

        if not self.builder_control:
            # TODO: tell the web user that their request was denied
            log.msg("but builder control is disabled")
            return Redirect("..")

        # keep weird stuff out of the branch and revision strings. TODO:
        # centralize this somewhere.
        if not re.match(r'^[\w\.\-\/]*$', branch):
            log.msg("bad branch '%s'" % branch)
            return Redirect("..")
        if not re.match(r'^[\w\.\-\/]*$', revision):
            log.msg("bad revision '%s'" % revision)
            return Redirect("..")
        if not branch:
            branch = None
        if not revision:
            revision = None

        # TODO: if we can authenticate that a particular User pushed the
        # button, use their name instead of None, so they'll be informed of
        # the results.
        s = SourceStamp(branch=branch, revision=revision)
        req = BuildRequest(r, s, self.builder_status.getName())
        try:
            self.builder_control.requestBuildSoon(req)
        except interfaces.NoSlaveError:
            # TODO: tell the web user that their request could not be
            # honored
            pass
        return Redirect("../../waterfall")
 def perspective_requestBuild(self, buildername, reason, branch, revision):
     c = interfaces.IControl(self.master)
     bc = c.getBuilder(buildername)
     ss = SourceStamp(branch, revision)
     br = BuildRequest(reason, ss, buildername)
     bc.requestBuild(br)
 def doBuild(self, buildername):
     br = BuildRequest("forced", SourceStamp())
     d = br.waitUntilFinished()
     self.control.getBuilder(buildername).requestBuild(br)
     return d
    def _testSlave_2(self, res):
        # t1 subscribes to builds, but not anything lower-level
        ev = self.t1.events
        self.failUnlessEqual(len(ev), 4)
        self.failUnlessEqual(ev[0][0:3],
                             ("builderChangedState", "dummy", "building"))
        self.failUnlessEqual(ev[1][0], "buildStarted")
        self.failUnlessEqual(ev[2][0:2] + ev[2][3:4],
                             ("buildFinished", "dummy", builder.SUCCESS))
        self.failUnlessEqual(ev[3][0:3],
                             ("builderChangedState", "dummy", "idle"))

        self.failUnlessEqual(
            [ev[0] for ev in self.t3.events],
            [
                "builderAdded",
                "builderChangedState",  # offline
                "builderAdded",
                "builderChangedState",  # idle
                "builderChangedState",  # offline
                "builderChangedState",  # idle
                "builderChangedState",  # building
                "buildStarted",
                "stepStarted",
                "stepETAUpdate",
                "stepFinished",
                "stepStarted",
                "stepETAUpdate",
                "logStarted",
                "logFinished",
                "stepFinished",
                "buildFinished",
                "builderChangedState",  # idle
            ])

        b = self.s1.getLastFinishedBuild()
        self.failUnless(b)
        self.failUnlessEqual(b.getBuilder().getName(), "dummy")
        self.failUnlessEqual(b.getNumber(), 0)
        self.failUnlessEqual(b.getSourceStamp().branch, None)
        self.failUnlessEqual(b.getSourceStamp().patch, None)
        self.failUnlessEqual(b.getSourceStamp().revision, None)
        self.failUnlessEqual(b.getReason(), "forced build for testing")
        self.failUnlessEqual(b.getChanges(), ())
        self.failUnlessEqual(b.getResponsibleUsers(), [])
        self.failUnless(b.isFinished())
        self.failUnlessEqual(b.getText(), ['build', 'successful'])
        self.failUnlessEqual(b.getColor(), "green")
        self.failUnlessEqual(b.getResults(), builder.SUCCESS)

        steps = b.getSteps()
        self.failUnlessEqual(len(steps), 2)

        eta = 0
        st1 = steps[0]
        self.failUnlessEqual(st1.getName(), "dummy")
        self.failUnless(st1.isFinished())
        self.failUnlessEqual(st1.getText(), ["delay", "1 secs"])
        start, finish = st1.getTimes()
        self.failUnless(0.5 < (finish - start) < 10)
        self.failUnlessEqual(st1.getExpectations(), [])
        self.failUnlessEqual(st1.getLogs(), [])
        eta += finish - start

        st2 = steps[1]
        self.failUnlessEqual(st2.getName(), "remote dummy")
        self.failUnless(st2.isFinished())
        self.failUnlessEqual(st2.getText(), ["remote", "delay", "2 secs"])
        start, finish = st2.getTimes()
        self.failUnless(1.5 < (finish - start) < 10)
        eta += finish - start
        self.failUnlessEqual(st2.getExpectations(), [('output', 38, None)])
        logs = st2.getLogs()
        self.failUnlessEqual(len(logs), 1)
        self.failUnlessEqual(logs[0].getName(), "stdio")
        self.failUnlessEqual(logs[0].getText(), "data")

        self.eta = eta
        # now we run it a second time, and we should have an ETA

        self.t4 = t4 = STarget(["builder", "build", "eta"])
        self.master.getStatus().subscribe(t4)
        c = interfaces.IControl(self.master)
        req = BuildRequest("forced build for testing", SourceStamp())
        c.getBuilder("dummy").requestBuild(req)
        d = req.waitUntilFinished()
        d2 = self.master.botmaster.waitUntilBuilderIdle("dummy")
        dl = defer.DeferredList([d, d2])
        dl.addCallback(self._testSlave_3)
        return dl
Example #21
0
 def doBuild(self):
     br = BuildRequest("forced", SourceStamp(), 'test_builder')
     d = br.waitUntilFinished()
     self.control.getBuilder('b1').requestBuild(br)
     return d
 def doBuild(self):
     br = BuildRequest("forced", SourceStamp(), 'test_builder')
     d = br.waitUntilFinished()
     self.control.getBuilder('b1').requestBuild(br)
     return d
 def requestBuild(self, builder):
     # returns a Deferred that fires with an IBuildStatus object when the
     # build is finished
     req = BuildRequest("forced build", SourceStamp())
     self.control.getBuilder(builder).requestBuild(req)
     return req.waitUntilFinished()
Example #24
0
    def _testSlave_2(self, res):
        # t1 subscribes to builds, but not anything lower-level
        ev = self.t1.events
        self.failUnlessEqual(len(ev), 4)
        self.failUnlessEqual(ev[0][0:3],
                             ("builderChangedState", "dummy", "building"))
        self.failUnlessEqual(ev[1][0], "buildStarted")
        self.failUnlessEqual(ev[2][0:2]+ev[2][3:4],
                             ("buildFinished", "dummy", builder.SUCCESS))
        self.failUnlessEqual(ev[3][0:3],
                             ("builderChangedState", "dummy", "idle"))

        self.failUnlessEqual([ev[0] for ev in self.t3.events],
                             ["builderAdded",
                              "builderChangedState", # offline
                              "builderAdded",
                              "builderChangedState", # idle
                              "builderChangedState", # offline
                              "builderChangedState", # idle
                              "builderChangedState", # building
                              "buildStarted",
                              "stepStarted", "stepETAUpdate", "stepFinished",
                              "stepStarted", "stepETAUpdate",
                              "logStarted", "logFinished", "stepFinished",
                              "buildFinished",
                              "builderChangedState", # idle
                              ])

        b = self.s1.getLastFinishedBuild()
        self.failUnless(b)
        self.failUnlessEqual(b.getBuilder().getName(), "dummy")
        self.failUnlessEqual(b.getNumber(), 0)
        self.failUnlessEqual(b.getSourceStamp().branch, None)
        self.failUnlessEqual(b.getSourceStamp().patch, None)
        self.failUnlessEqual(b.getSourceStamp().revision, None)
        self.failUnlessEqual(b.getReason(), "forced build for testing")
        self.failUnlessEqual(b.getChanges(), ())
        self.failUnlessEqual(b.getResponsibleUsers(), [])
        self.failUnless(b.isFinished())
        self.failUnlessEqual(b.getText(), ['build', 'successful'])
        self.failUnlessEqual(b.getColor(), "green")
        self.failUnlessEqual(b.getResults(), builder.SUCCESS)

        steps = b.getSteps()
        self.failUnlessEqual(len(steps), 2)

        eta = 0
        st1 = steps[0]
        self.failUnlessEqual(st1.getName(), "dummy")
        self.failUnless(st1.isFinished())
        self.failUnlessEqual(st1.getText(), ["delay", "1 secs"])
        start,finish = st1.getTimes()
        self.failUnless(0.5 < (finish-start) < 10)
        self.failUnlessEqual(st1.getExpectations(), [])
        self.failUnlessEqual(st1.getLogs(), [])
        eta += finish-start

        st2 = steps[1]
        self.failUnlessEqual(st2.getName(), "remote dummy")
        self.failUnless(st2.isFinished())
        self.failUnlessEqual(st2.getText(),
                             ["remote", "delay", "2 secs"])
        start,finish = st2.getTimes()
        self.failUnless(1.5 < (finish-start) < 10)
        eta += finish-start
        self.failUnlessEqual(st2.getExpectations(), [('output', 38, None)])
        logs = st2.getLogs()
        self.failUnlessEqual(len(logs), 1)
        self.failUnlessEqual(logs[0].getName(), "stdio")
        self.failUnlessEqual(logs[0].getText(), "data")

        self.eta = eta
        # now we run it a second time, and we should have an ETA

        self.t4 = t4 = STarget(["builder", "build", "eta"])
        self.master.getStatus().subscribe(t4)
        c = interfaces.IControl(self.master)
        req = BuildRequest("forced build for testing", SourceStamp())
        c.getBuilder("dummy").requestBuild(req)
        d = req.waitUntilFinished()
        d2 = self.master.botmaster.waitUntilBuilderIdle("dummy")
        dl = defer.DeferredList([d, d2])
        dl.addCallback(self._testSlave_3)
        return dl
Example #25
0
 def requestBuild(self, builder):
     # returns a Deferred that fires with an IBuildStatus object when the
     # build is finished
     req = BuildRequest("forced build", SourceStamp())
     self.control.getBuilder(builder).requestBuild(req)
     return req.waitUntilFinished()
Example #26
0
    def force(self, req):
        """

        Custom properties can be passed from the web form.  To do
        this, subclass this class, overriding the force() method.  You
        can then determine the properties (usually from form values,
        by inspecting req.args), then pass them to this superclass
        force method.
        
        """
        name = req.args.get("username", ["<unknown>"])[0]
        reason = req.args.get("comments", ["<no reason specified>"])[0]
        branch = req.args.get("branch", [""])[0]
        revision = req.args.get("revision", [""])[0]

        r = "The web-page 'force build' button was pressed by '%s': %s\n" \
            % (html.escape(name), html.escape(reason))
        log.msg("web forcebuild of builder '%s', branch='%s', revision='%s'"
                " by user '%s'" %
                (self.builder_status.getName(), branch, revision, name))

        if not self.builder_control:
            # TODO: tell the web user that their request was denied
            log.msg("but builder control is disabled")
            return Redirect("..")

        if self.isUsingUserPasswd(req):
            if not self.authUser(req):
                return Redirect("../../authfail")

        # keep weird stuff out of the branch revision, and property strings.
        # TODO: centralize this somewhere.
        if not re.match(r'^[\w\.\-\/]*$', branch):
            log.msg("bad branch '%s'" % branch)
            return Redirect("..")
        if not re.match(r'^[\w\.\-\/]*$', revision):
            log.msg("bad revision '%s'" % revision)
            return Redirect("..")
        properties = getAndCheckProperties(req)
        if properties is None:
            return Redirect("..")
        if not branch:
            branch = None
        if not revision:
            revision = None

        # TODO: if we can authenticate that a particular User pushed the
        # button, use their name instead of None, so they'll be informed of
        # the results.
        # TODO2: we can authenticate that a particular User pushed the button
        # now, so someone can write this support. but it requires a
        # buildbot.changes.changes.Change instance which is tedious at this
        # stage to compute
        s = SourceStamp(branch=branch, revision=revision)
        req = BuildRequest(r,
                           s,
                           builderName=self.builder_status.getName(),
                           properties=properties)
        try:
            self.builder_control.requestBuildSoon(req)
        except interfaces.NoSlaveError:
            # TODO: tell the web user that their request could not be
            # honored
            pass
        # send the user back to the builder page
        return Redirect(".")