def watchedBuildFinished(self, build): builder = yield self.getBuilder(builderid=build['builderid']) # only notify about builders we are interested in if (self.bot.tags is not None and not self.builderMatchesAnyTag(builder.get('tags', []))): log.msg('Not notifying for a build that does not match any tags') return builder_name = builder['name'] buildnum = build['number'] if not self.shouldReportBuild(builder_name, buildnum): return results = self.getResultsDescriptionAndColor(build['results']) if self.useRevisions: revisions = yield self.getRevisionsForBuild(build) r = "Build %s containing revision(s) [%s] is complete: %s" % \ (builder_name, ','.join(revisions), results[0]) else: r = "Build %s #%d is complete: %s" % \ (builder_name, buildnum, results[0]) r += ' [%s]' % maybeColorize(build['state_string'], results[1], self.useColors) r += " - %s" % utils.getURLForBuild(self.master, builder['builderid'], buildnum) self.send(r)
def __call__(self, mode, buildername, buildset, build, master, previous_results, blamelist): """Generate a buildbot mail message and return a tuple of message text and type.""" ss_list = buildset['sourcestamps'] results = build['results'] tpl = self.env.get_template(self.template_name) cxt = dict(results=build['results'], mode=mode, buildername=buildername, workername=build['properties'].get( 'workername', ["<unknown>"])[0], buildset=buildset, build=build, projects=self.getProjects(ss_list, master), previous_results=previous_results, status_detected=self.getDetectedStatus( mode, results, previous_results), build_url=utils.getURLForBuild( master, build['builder']['builderid'], build['number']), buildbot_url=master.config.buildbotURL, blamelist=blamelist, summary=self.messageSummary(build, results), sourcestamps=self.messageSourceStamps(ss_list) ) contents = tpl.render(cxt) return {'body': contents, 'type': self.template_type}
def command_STOP(self, args, **kwargs): """stop a running build""" args = self.splitArgs(args) if len(args) < 3 or args[0] != 'build': raise UsageError("Try '" + self.bot.commandPrefix + "stop build _which_ _reason_'.") which = args[1] reason = ' '.join(args[2:]) r = f"stopped: by {self.describeUser()}: {reason}" # find an in-progress build builder = yield self.bot.getBuilder(buildername=which) builderid = builder['builderid'] builds = yield self.bot.getRunningBuilds(builderid) if not builds: self.send("Sorry, no build is currently running.") return for bdict in builds: num = bdict['number'] yield self.master.data.control( 'stop', {'reason': r}, ('builders', builderid, 'builds', num)) if self.bot.useRevisions: revisions = yield self.bot.getRevisionsForBuild(bdict) response = f"Build containing revision(s) {','.join(revisions)} interrupted" else: url = utils.getURLForBuild(self.master, builderid, num) response = f"Build [#{num}]({url}) of `{which}` interrupted." self.send(response)
def watchedBuildFinished(self, build): builder = yield self.getBuilder(builderid=build['builderid']) # only notify about builders we are interested in if (self.bot.tags is not None and not self.builderMatchesAnyTag(builder.get('tags', []))): log.msg('Not notifying for a build that does not match any tags') return builder_name = builder['name'] buildnum = build['number'] if not self.shouldReportBuild(builder_name, buildnum): return results = self.getResultsDescriptionAndColor(build['results']) if self.useRevisions: revisions = yield self.getRevisionsForBuild(build) r = "Build %s containing revision(s) [%s] is complete: %s" % \ (builder_name, ','.join(revisions), results[0]) else: r = "Build %s #%d is complete: %s" % \ (builder_name, buildnum, results[0]) r += ' [%s]' % maybeColorize(build['state_string'], results[1], self.useColors) r += " - %s" % utils.getURLForBuild( self.master, builder['builderid'], buildnum) self.send(r)
def formatMessageForBuildResults(self, mode, buildername, buildset, build, master, previous_results, blamelist): """Generate a buildbot mail message and return a dictionary containing the message body, type and subject.""" ss_list = buildset['sourcestamps'] results = build['results'] ctx = dict( results=build['results'], mode=mode, buildername=buildername, workername=build['properties'].get('workername', ["<unknown>"])[0], buildset=buildset, build=build, projects=self.getProjects(ss_list, master), previous_results=previous_results, status_detected=self.getDetectedStatus(mode, results, previous_results), build_url=utils.getURLForBuild(master, build['builder']['builderid'], build['number']), buildbot_url=master.config.buildbotURL, blamelist=blamelist, summary=self.messageSummary(build, results), sourcestamps=self.messageSourceStamps(ss_list)) yield self.buildAdditionalContext(master, ctx) msgdict = self.renderMessage(ctx) defer.returnValue(msgdict)
def __call__(self, mode, buildername, buildset, build, master, previous_results, blamelist): """Generate a buildbot mail message and return a dictionary containing the message body, type and subject.""" ss_list = buildset['sourcestamps'] results = build['results'] ctx = dict(results=build['results'], mode=mode, buildername=buildername, workername=build['properties'].get( 'workername', ["<unknown>"])[0], buildset=buildset, build=build, projects=self.getProjects(ss_list, master), previous_results=previous_results, status_detected=self.getDetectedStatus( mode, results, previous_results), build_url=utils.getURLForBuild( master, build['builder']['builderid'], build['number']), buildbot_url=master.config.buildbotURL, blamelist=blamelist, summary=self.messageSummary(build, results), sourcestamps=self.messageSourceStamps(ss_list) ) ctx.update(self.ctx) body = self.body_template.render(ctx) email = {'body': body, 'type': self.template_type} if self.subject_template is not None: email['subject'] = self.subject_template.render(ctx) return email
def getBuildInfo(build): result = build['results'] resultText = { SUCCESS: "succeeded", FAILURE: "failed", WARNINGS: "completed with warnings", EXCEPTION: "encountered an exception", }.get(result, "completed with unknown result %d" % result) return { 'name': build['builder']['name'], 'result': result, 'resultText': resultText, 'text': build['state_string'], 'url': utils.getURLForBuild(self.master, build['builder']['builderid'], build['number']), 'build': build }
def create_context_for_build(mode, buildername, build, master, blamelist): buildset = build['buildset'] ss_list = buildset['sourcestamps'] results = build['results'] if 'prev_build' in build and build['prev_build'] is not None: previous_results = build['prev_build']['results'] else: previous_results = None return { 'results': build['results'], 'mode': mode, 'buildername': buildername, 'workername': build['properties'].get('workername', ["<unknown>"])[0], 'buildset': buildset, 'build': build, 'projects': get_projects_text(ss_list, master), 'previous_results': previous_results, 'status_detected': get_detected_status_text(mode, results, previous_results), 'build_url': utils.getURLForBuild(master, build['builder']['builderid'], build['number']), 'buildbot_url': master.config.buildbotURL, 'blamelist': blamelist, 'summary': get_message_summary_text(build, results), 'sourcestamps': get_message_source_stamp_text(ss_list) }
def formatMessageForBuildResults(self, mode, buildername, buildset, build, master, previous_results, blamelist): """Generate a buildbot mail message and return a dictionary containing the message body, type and subject.""" ss_list = buildset['sourcestamps'] results = build['results'] ctx = dict(results=build['results'], mode=mode, buildername=buildername, workername=build['properties'].get( 'workername', ["<unknown>"])[0], buildset=buildset, build=build, projects=self.getProjects(ss_list, master), previous_results=previous_results, status_detected=self.getDetectedStatus( mode, results, previous_results), build_url=utils.getURLForBuild( master, build['builder']['builderid'], build['number']), buildbot_url=master.config.buildbotURL, blamelist=blamelist, summary=self.messageSummary(build, results), sourcestamps=self.messageSourceStamps(ss_list) ) yield self.buildAdditionalContext(master, ctx) msgdict = self.renderMessage(ctx) defer.returnValue(msgdict)
def buildStarted(self, build): builder = yield self.bot.getBuilder(builderid=build['builderid']) builderName = builder['name'] buildNumber = build['number'] log.msg(f"[Contact] Builder {builder['name']} started") # only notify about builders we are interested in if (self.bot.tags is not None and not self.builderMatchesAnyTag(builder.get('tags', []))): log.msg('Not notifying for a build that does not match any tags') return if not self.notify_for('started'): return if self.useRevisions: revisions = yield self.getRevisionsForBuild(build) r = f"Build containing revision(s) {','.join(revisions)} on {builderName} started" else: # Abbreviate long lists of changes to simply two # revisions, and the number of additional changes. # TODO: We can't get the list of the changes related to a build in # nine changes_str = "" url = utils.getURLForBuild(self.master, builder['builderid'], build['number']) r = f"Build [#{buildNumber}]({url}) of `{builderName}` started" if changes_str: r += f" ({changes_str})" self.send(r + ".")
def __call__(self, mode, buildername, buildset, build, master, previous_results, blamelist): """Generate a buildbot mail message and return a tuple of message text and type.""" ss_list = buildset['sourcestamps'] results = build['results'] tpl = self.env.get_template(self.template_name) cxt = dict( results=build['results'], mode=mode, buildername=buildername, slavename=build['properties'].get('slavename', ["<unknown>"])[0], buildset=buildset, build=build, projects=self.getProjects(ss_list, master), previous_results=previous_results, status_detected=self.getDetectedStatus(mode, results, previous_results), build_url=utils.getURLForBuild(master, build['builder']['builderid'], build['number']), buildbot_url=master.config.buildbotURL, blamelist=blamelist, summary=self.messageSummary(build, results), sourcestamps=self.messageSourceStamps(ss_list)) contents = tpl.render(cxt) return {'body': contents, 'type': 'plain'}
def buildComplete(self, key, build): if self.reviewCB is None: return br = yield self.master.data.get(("buildrequests", build['buildrequestid'])) buildset = yield self.master.data.get(("buildsets", br['buildsetid'])) yield utils.getDetailsForBuilds(self.master, buildset, [build]) build['url'] = utils.getURLForBuild(self.master, build['builder']['builderid'], build['number']) self.buildFinished(build['builder']['name'], build, build['results'])
def buildComplete(self, key, build): if self.reviewCB is None: return br = yield self.master.data.get(("buildrequests", build["buildrequestid"])) buildset = yield self.master.data.get(("buildsets", br["buildsetid"])) yield utils.getDetailsForBuilds(self.master, buildset, [build]) build["url"] = utils.getURLForBuild(self.master, build["builder"]["builderid"], build["number"]) if self.isBuildReported(build): self.buildFinished(build["builder"]["name"], build, build["results"])
def default_context(self, build, master=None): props = build['properties'] context = { 'build': build, 'revision': props.get('revision', ['unknown'])[0], 'worker_name': props.get('workername', ['unknown'])[0], 'builder_name': props.get('buildername', ['unknown'])[0], 'buildbot_url': master.config.buildbotURL, 'build_id': build['buildid'], 'build_url': utils.getURLForBuild( master, build['builder']['builderid'], build['number'] ) } return toolz.merge(context, self.context)
def getBuildInfo(build): result = build['results'] resultText = { SUCCESS: "succeeded", FAILURE: "failed", WARNINGS: "completed with warnings", EXCEPTION: "encountered an exception", }.get(result, "completed with unknown result %d" % result) return {'name': build['builder']['name'], 'result': result, 'resultText': resultText, 'text': build['state_string'], 'url': utils.getURLForBuild(self.master, build['builder']['builderid'], build['number']) }
def getBuildStatus(self, which, short=False): response = f'`{which}`: ' builder = yield self.getBuilder(buildername=which) builderid = builder['builderid'] runningBuilds = yield self.getRunningBuilds(builderid) # pylint: disable=too-many-nested-blocks if not runningBuilds: onlineBuilders = yield self.getOnlineBuilders() if builderid in onlineBuilders: response += self.idle_string lastBuild = yield self.getLastCompletedBuild(builderid) if lastBuild: complete_at = lastBuild['complete_at'] if complete_at: complete_at = util.datetime2epoch(complete_at) ago = util.fuzzyInterval( int(reactor.seconds() - complete_at)) else: ago = "??" status = self.format_build_status(lastBuild, short=short) if not short: status = ", " + status if lastBuild['results'] != SUCCESS: status_string = lastBuild.get('status_string') if status_string: status += ": " + status_string response += f' last build {ago} ago{status}' else: response += self.offline_string else: response += self.running_string buildInfo = [] for build in runningBuilds: step = yield self.getCurrentBuildstep(build) if step: s = f"({step[-1]['state_string']})" else: s = "(no current step)" bnum = build['number'] url = utils.getURLForBuild(self.master, builderid, bnum) buildInfo.append(f"build [#{bnum}]({url}) {s}") response += ' ' + ', '.join(buildInfo) return response
def getBuildInfo(build): result = build["results"] resultText = { SUCCESS: "succeeded", FAILURE: "failed", WARNINGS: "completed with warnings", EXCEPTION: "encountered an exception", }.get(result, "completed with unknown result %d" % result) return { "name": build["builder"]["name"], "result": result, "resultText": resultText, "text": build["state_string"], "url": utils.getURLForBuild(self.master, build["builder"]["builderid"], build["number"]), "build": build, }
def getBuildInfo(build): if build: buildId = build[0]['buildid'] return master.data.get( ("builds", buildId, 'properties')).addCallback( lambda properties: { 'buildId': buildId, 'name': (properties.get('virtual_builder_name') or properties.get('buildername'))[0], 'build_url': utils.getURLForBuild(master, build[0]['builderid'], build[0]['number']), 'results': build[0]['results'], 'properties': properties })
def formatMessageForBuildResults(self, mode, buildername, buildset, build, master, previous_build, blamelist): ctx = dict( mode=mode, buildername=buildername, workername=build["properties"].get("workername", ["<unknown>"])[0], buildset=buildset, build=build, previous_build=previous_build, build_url=utils.getURLForBuild(master, build["builder"]["builderid"], build["number"]), buildbot_url=master.config.buildbotURL, blamelist=blamelist, ) yield self.buildAdditionalContext(master, ctx) msgdict = self.renderMessage(ctx) return msgdict
def command_WATCH(self, args, **kwargs): """announce the completion of an active build""" args = self.splitArgs(args) if len(args) != 1: raise UsageError("Try '" + self.bot.commandPrefix + "watch _builder_'.") which = args[0] builder = yield self.bot.getBuilder(buildername=which) # Get current builds on this builder. builds = yield self.bot.getRunningBuilds(builder['builderid']) if not builds: self.send("There are no currently running builds.") return def watchForCompleteEvent(key, msg): if key[-1] in ('finished', 'complete'): return self.channel.buildFinished(msg, watched=True) return None for build in builds: startConsuming = self.master.mq.startConsuming handle = yield startConsuming( watchForCompleteEvent, ('builds', str(build['buildid']), None)) self.channel.build_subscriptions.append((build['buildid'], handle)) url = utils.getURLForBuild(self.master, builder['builderid'], build['number']) if self.bot.useRevisions: revisions = yield self.bot.getRevisionsForBuild(build) r = (f"Watching build on `{which}` containing revision(s) " f"{','.join(revisions)} until it finishes...") else: r = f"Watching build [#{build['number']}]({url}) of `{which}` until it finishes..." self.send(r)
def buildFinished(self, build, watched=False): builder = yield self.bot.getBuilder(builderid=build['builderid']) builderName = builder['name'] buildNumber = build['number'] # only notify about builders we are interested in if (self.bot.tags is not None and not self.bot.builderMatchesAnyTag(builder.get('tags', []))): log.msg('Not notifying for a build that does not match any tags') return if not (watched or (yield self.notify_for_finished(build))): return if not self.shouldReportBuild(builderName, buildNumber): return url = utils.getURLForBuild(self.master, builder['builderid'], buildNumber) if self.useRevisions: revisions = yield self.getRevisionsForBuild(build) r = ( f"Build on `{builderName}` containing revision(s) {','.join(revisions)} " f"{self.bot.format_build_status(build)}") else: r = (f"Build [#{buildNumber}]({url}) of `{builderName}` " f"{self.bot.format_build_status(build)}") s = build.get('status_string') if build['results'] != SUCCESS and s is not None: r += ": " + s else: r += "." # FIXME: where do we get the list of changes for a build ? # if self.bot.showBlameList and buildResult != SUCCESS and len(build.changes) != 0: # r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes]))) self.send(r)
def addBuildUrls(self, rclist): brids = {} for was_cb, results in rclist: if isinstance(results, tuple): results, brids = results builderNames = {} if was_cb: # errors were already logged in worstStatus for builderid, br in brids.items(): builds = yield self.master.db.builds.getBuilds( buildrequestid=br) for build in builds: builderid = build['builderid'] # When virtual builders are used, the builderid used for triggering # is not the same as the one that the build actually got if builderid not in builderNames: builderDict = yield self.master.data.get( ("builders", builderid)) builderNames[builderid] = builderDict["name"] num = build['number'] url = getURLForBuild(self.master, builderid, num) yield self.addURL( f'{statusToString(build["results"])}: ' f'{builderNames[builderid]} #{num}', url)
def buildFinished(self, build): builder = yield self.getBuilder(builderid=build['builderid']) builderName = builder['name'] buildNumber = build['number'] buildResult = build['results'] # only notify about builders we are interested in if (self.bot.tags is not None and not self.builderMatchesAnyTag(builder.get('tags', []))): log.msg('Not notifying for a build that does not match any tags') return if not (yield self.notify_for_finished(build)): return if not self.shouldReportBuild(builderName, buildNumber): return results = self.getResultsDescriptionAndColor(buildResult) if self.useRevisions: revisions = yield self.getRevisionsForBuild(build) r = "Build %s containing revision(s) [%s] is complete: %s" % \ (builderName, ','.join(revisions), results[0]) else: r = "Build %s #%d is complete: %s" % \ (builderName, buildNumber, results[0]) r += ' [%s]' % maybeColorize(build['state_string'], results[1], self.useColors) # FIXME: where do we get the list of changes for a build ? # if self.bot.showBlameList and buildResult != SUCCESS and len(build.changes) != 0: # r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes]))) r += " - %s" % utils.getURLForBuild(self.master, builder['builderid'], buildNumber) self.send(r)
def buildFinished(self, build): builder = yield self.getBuilder(builderid=build['builderid']) builderName = builder['name'] buildNumber = build['number'] buildResult = build['results'] # only notify about builders we are interested in if (self.bot.tags is not None and not self.builderMatchesAnyTag(builder.get('tags', []))): log.msg('Not notifying for a build that does not match any tags') return if not (yield self.notify_for_finished(build)): return if not self.shouldReportBuild(builderName, buildNumber): return results = self.getResultsDescriptionAndColor(buildResult) if self.useRevisions: revisions = yield self.getRevisionsForBuild(build) r = "Build %s containing revision(s) [%s] is complete: %s" % \ (builderName, ','.join(revisions), results[0]) else: r = "Build %s #%d is complete: %s" % \ (builderName, buildNumber, results[0]) r += ' [%s]' % maybeColorize(build['state_string'], results[1], self.useColors) # FIXME: where do we get the list of changes for a build ? # if self.bot.showBlameList and buildResult != SUCCESS and len(build.changes) != 0: # r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes]))) r += " - %s" % utils.getURLForBuild( self.master, builder['builderid'], buildNumber) self.send(r)
def test_UrlForBuild(self): self.assertEqual(utils.getURLForBuild(self.master, 1, 3), 'http://localhost:8080/#builders/1/builds/3')
def getUrl(self): builder_id = yield self.getBuilderId() return getURLForBuild(self.master, builder_id, self.number)
def getUrl(self): builder_id = yield self.builder.getBuilderId() defer.returnValue(getURLForBuild(self.master, builder_id, self.number))