Skip to content

Commit

Permalink
Only use buildbot standard interpolation. Refactor to fix usage of bu…
Browse files Browse the repository at this point in the history
…ild.render().
  • Loading branch information
adiroiban committed Apr 6, 2013
1 parent a688648 commit 84827eb
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 78 deletions.
124 changes: 64 additions & 60 deletions master/buildbot/status/github.py
Expand Up @@ -15,10 +15,9 @@
from __future__ import absolute_import

import datetime
from dateutil.relativedelta import relativedelta

from twisted.internet import defer
from twisted.python import log
from txgithub.api import GithubApi as GitHubAPI
from zope.interface import implements

from buildbot.interfaces import IStatusReceiver
Expand All @@ -42,21 +41,19 @@ def __init__(self, token, repoOwner, repoName, sha=None,
Token for GitHub API.
"""
StatusReceiverMultiService.__init__(self)
self._github = GitHubAPI(oauth2_token=token, sha=None)

if not sha:
sha = Interpolate("%(src::revision)s")

if not startDescription:
startDescription = "Build started at %(startDateTime)s."
self.startDescription = startDescription
startDescription = Interpolate("Build started.")
self._startDescription = startDescription

if not endDescription:
endDescription = (
"[%(state)s] Build done after %(duration)s. "
"Done at %(endDateTime)s."
)
self.endDescription = endDescription
endDescription = Interpolate("Build done.")
self._endDescription = endDescription

self._token = token
self._sha = sha
self._repoOwner = repoOwner
self._repoName = repoName
Expand All @@ -72,104 +69,109 @@ def builderAdded(self, name, builder):
"""
return self

@defer.inlineCallbacks
def buildStarted(self, builderName, build):
repo = self._getGitHubRepoProperties(build)
if not repo:
return
status = yield self._getGitHubRepoProperties(build)
if not status:
defer.returnValue(None)

(startTime, endTime) = build.getTimes()
state = 'pending'

status = {
'state': state,
'sha': repo['sha'],
'description': self.startDescription,
'targetUrl': repo['targetUrl'],
'repoOwner': repo['repoOwner'],
'repoName': repo['repoName'],
'buildNumber': repo['buildNumber'],
description = yield build.render(self._startDescription)

status.update({
'state': 'pending',
'description': description,
'builderName': builderName,
'startDateTime': datetime.datetime.fromtimestamp(
startTime).isoformat(' '),
'endDateTime': 'In progress',
'duration': 'In progress',
}

self._sendGitHubStatus(status)
return self
})
result = yield self._sendGitHubStatus(status)
defer.returnValue(result)

@defer.inlineCallbacks
def buildFinished(self, builderName, build, results):
repo = self._getGitHubRepoProperties(build)
if not repo:
return
status = yield self._getGitHubRepoProperties(build)
if not status:
defer.returnValue(None)

state = self._getGitHubState(results)
(startTime, endTime) = build.getTimes()
duration = self._timeDeltaToHumanReadable(startTime, endTime)
description = yield build.render(self._endDescription)

status = {
status.update({
'state': state,
'sha': repo['sha'],
'description': self.endDescription,
'targetUrl': repo['targetUrl'],
'repoOwner': repo['repoOwner'],
'repoName': repo['repoName'],
'buildNumber': repo['buildNumber'],
'description': description,
'builderName': builderName,
'startDateTime': datetime.datetime.fromtimestamp(
startTime).isoformat(' '),
'endDateTime': datetime.datetime.fromtimestamp(
endTime).isoformat(' '),
'duration': duration,
}
})

self._sendGitHubStatus(status)
return self
result = yield self._sendGitHubStatus(status)
defer.returnValue(result)

def _timeDeltaToHumanReadable(self, start, end):
"""
Return a string of human readable time delta.
"""
start_date = datetime.datetime.fromtimestamp(start)
end_date = datetime.datetime.fromtimestamp(end)
delta = relativedelta(end_date, start_date)

attributes = [
'years', 'months', 'days', 'hours', 'minutes', 'seconds']
delta = end_date - start_date

result = []
for attribute_name in attributes:
attribute = getattr(delta, attribute_name)
if attribute > 0:
result.append('%d %s' % (attribute, attribute_name))

return ', '.join(result)

if delta.days > 0:
result.append('%d days' % (delta.days,))
if delta.seconds > 0:
hours = delta.seconds / 3600
if hours > 0:
result.append('%d hours' % (hours,))
minutes = (delta.seconds - hours * 3600) / 60
if minutes:
result.append('%d minutes' % (minutes,))
seconds = delta.seconds % 60
if seconds > 0:
result.append('%d seconds' % (seconds,))
result = ', '.join(result)
if not result:
return 'super fast'
else:
return result

@defer.inlineCallbacks
def _getGitHubRepoProperties(self, build):
"""
Return a dictionary with GitHub related properties from `build`.
"""
repoOwner = build.render(self._repoOwner)
repoName = build.render(self._repoName)
sha = build.render(self._sha)
repoOwner, repoName, sha = yield defer.gatherResults([
build.render(self._repoOwner),
build.render(self._repoName),
build.render(self._sha),
])

if not repoOwner or not repoName or not sha:
return {}
defer.returnValue({})

return {
result = {
'repoOwner': repoOwner,
'repoName': repoName,
'sha': sha,
'targetUrl': self._status.getURLForThing(build),
'buildNumber': str(build.getNumber()),
}
defer.returnValue(result)

def _getGitHubState(self, results):
"""
Convert Buildbot states into GitHub states.
"""
# GitHub defines `succes`, `failure` and `error` states.
# We explicitly map sucess and failure. Any other BuildBot status
# GitHub defines `success`, `failure` and `error` states.
# We explicitly map success and failure. Any other BuildBot status
# is converted to `error`.
state_map = {
SUCCESS: 'success',
Expand All @@ -188,16 +190,18 @@ def _sendGitHubStatus(self, status):
if not status['sha']:
log.msg('GitHubStatus: Build has no revision')
return
from txgithub.api import GithubApi as GitHubAPI

description = status['description'] % status
d = self._github.createStatus(
github = GitHubAPI(oauth2_token=self._token)
d = github.createStatus(
repo_user=status['repoOwner'],
repo_name=status['repoName'],
sha=status['sha'],
state=status['state'],
target_url=status['targetUrl'],
description=description,
description=status['description'],
)
d.addCallback(lambda result: None)
d.addErrback(
log.err,
"while sending GitHub status for %s/%s at %s." % (
Expand Down
24 changes: 6 additions & 18 deletions master/docs/manual/cfg-statustargets.rst
Expand Up @@ -1533,18 +1533,19 @@ GitHubStatus
repoOwner = 'myorg'
repoName = Interpolate("%(prop::github_repo_name)s"
sha = Interpolate("%(src::revision)s")
startDescription = Interpolate('Build started.')
endDescription = Interpolate('Build done.')
gs = GitHubStatus(token='githubAPIToken',
repoOwner=repoOwner,
repoName=repoName)
sha=sha,
startDescription='Build started at %(startDateTime)s.',
endDescription=(
'[%(state)s] Build done after %(duration)s.'),
startDescription=startDescription,
endDescription=endDescription,
)
c['status'].append(gs)

:class:`GitHubStatus` publishes a build status using
`Github Status API <http://developer.github.com/v3/repos/statuses>`_.
`GitHub Status API <http://developer.github.com/v3/repos/statuses>`_.

It is configured with at least a GitHub API token, repoOwner and repoName
arguments.
Expand All @@ -1565,20 +1566,7 @@ In case any of `repoOwner`, `repoName` or `sha` returns `None`, `False` or
empty string, the plugin will skip sending the status.

You can define custom start and end build messages using the
`startDescription` and `endDescription` optional arguments. They can be
provided as static string or use the following placeholders
for custom message interpolation:

* state - 'pending'|'success'|'failure'|'error'
* sha
* targetUrl - URL to Buildbot build page.
* repoOwner - Name of repo owner.
* repoName - Name of the repo.
* buildNumber - Buildbot build number.
* builderName - Name of the builder.
* startDateTime
* endDateTime
* duration - Human readable representation of elapsed time.
`startDescription` and `endDescription` optional interpolation arguments.


.. [#] Apparently this is the same way http://buildd.debian.org displays build status
Expand Down

0 comments on commit 84827eb

Please sign in to comment.