Skip to content

Commit

Permalink
Merge branch 'change_hook' of git://github.com/AndyHowell/buildbot
Browse files Browse the repository at this point in the history
This adds a script to submit an arbitrary change request to buildbot via
the change hooks

Fixes #1005

* 'change_hook' of git://github.com/AndyHowell/buildbot:
  master/buildbuilt/status/web/hooks/base.py   Added passing properties a dictionary master/buildbuilt/status/web/change_hook.py   render_POST returns a json object for the submitted change request(s)   Fixed typo in msg   submitChanges returns an array of changes as dictionaries master/buildbuilt/changes/managers.py   Added project to addChange messages. master/contrib/post_build_request.py   New script to manually post a change request via the /change_hook/base   web interface.
  • Loading branch information
Dustin J. Mitchell committed Oct 2, 2010
2 parents 19ca753 + 1602dcb commit 29d8854
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 16 deletions.
4 changes: 2 additions & 2 deletions master/buildbot/changes/manager.py
Expand Up @@ -103,9 +103,9 @@ def addChange(self, change):
"""Deliver a file change event. The event should be a Change object.
This method will timestamp the object as it is received."""
msg = ("adding change, who %s, %d files, rev=%s, branch=%s, repository=%s, "
"comments %s, category %s" % (change.who, len(change.files),
"comments %s, category %s, project %s" % (change.who, len(change.files),
change.revision, change.branch, change.repository,
change.comments, change.category))
change.comments, change.category, change.project))
log.msg(msg.encode('utf-8', 'replace'))

# this sets change.number, if it wasn't already set (by the
Expand Down
19 changes: 11 additions & 8 deletions master/buildbot/status/web/change_hook.py
Expand Up @@ -13,6 +13,7 @@
from buildbot.changes.changes import Change
from twisted.python.reflect import namedModule
from twisted.python.log import msg,err
from buildbot.util import json

class ChangeHookResource(resource.Resource):
# this is a cheap sort of template thingy
Expand Down Expand Up @@ -47,10 +48,10 @@ def render_POST(self, request):
if not changes:
msg("No changes found")
return "no changes found"
self.submitChanges( changes, request )
return "changes processed"
submitted = self.submitChanges( changes, request )
return json.dumps(submitted)


def getChanges(self, request):
"""
Take the logic from the change hook, and then delegate it
Expand Down Expand Up @@ -79,22 +80,24 @@ def getChanges(self, request):
dialect = 'base'

if dialect in self.dialects.keys():
msg("Attempting to load module buildbot.status.web.hooks" + dialect)
msg("Attempting to load module buildbot.status.web.hooks." + dialect)
tempModule = namedModule('buildbot.status.web.hooks.' + dialect)
changes = tempModule.getChanges(request,self.dialects[dialect])
msg("Got the following changes %s" % changes)

else:
msg("The dialect specified %s wasn't whitelisted in change_hook" % dialect)
msg("Note: if dialect is 'base' then it's possible your URL is malformed and we didn't regex it properly")

return changes

def submitChanges(self, changes, request):
# get a control object
changeMaster = request.site.buildbot_service.master.change_svc
submitted = []
for onechange in changes:
msg("injecting change %s" % onechange)
changeMaster.addChange( onechange )


d = onechange.asDict()
msg("injected change %s" % d)
submitted.append(d)
return submitted
18 changes: 12 additions & 6 deletions master/buildbot/status/web/hooks/base.py
Expand Up @@ -14,14 +14,15 @@
from buildbot.changes.changes import Change
from twisted.python.reflect import namedModule
from buildbot.util import json
from twisted.python.log import msg,err

def getChanges(request, options=None):
"""
Consumes a naive build notification (the default for now)
basically, set POST variables to match commit object parameters:
revision, revlink, comments, branch, who, files, links
files and links will be de-json'd, the rest are interpreted as strings
files, links and properties will be de-json'd, the rest are interpreted as strings
"""

def firstOrNothing( value ):
Expand All @@ -33,10 +34,10 @@ def firstOrNothing( value ):
return value[0]
else:
return value

args = request.args

# first, convert files and links
# first, convert files, links and properties
files = None
if args.get('files'):
files = json.loads( args.get('files')[0] )
Expand All @@ -48,6 +49,12 @@ def firstOrNothing( value ):
links = json.loads( args.get('links')[0] )
else:
links = []

properties = None
if args.get('properties'):
properties = json.loads( args.get('properties')[0] )
else:
properties = {}

revision = firstOrNothing(args.get('revision'))
when = firstOrNothing(args.get('when'))
Expand All @@ -57,14 +64,13 @@ def firstOrNothing( value ):
branch = firstOrNothing(args.get('branch'))
category = firstOrNothing(args.get('category'))
revlink = firstOrNothing(args.get('revlink'))
properties = Properties()
# properties.update(properties, "Change")
repository = firstOrNothing(args.get('repository'))
project = firstOrNothing(args.get('project'))

ourchange = Change(who = who, files = files, comments = comments, isdir = isdir, links = links,
revision=revision, when = when, branch = branch, category = category,
revlink = revlink, repository = repository, project = project)
revlink = revlink, properties = properties, repository = repository,
project = project)
return [ourchange]


Expand Down
226 changes: 226 additions & 0 deletions master/contrib/post_build_request.py
@@ -0,0 +1,226 @@
#!/usr/bin/env python
import httplib, urllib
import getopt
import optparse
import textwrap
import getpass
import os

# Find a working json module. Code is from
# Paul Wise <pabs@debian.org>:
# http://lists.debian.org/debian-python/2010/02/msg00016.html
try:
import json # python 2.6
except ImportError:
import simplejson as json # python 2.4 to 2.5
try:
_tmp = json.loads
except AttributeError:
import warnings
import sys
warnings.warn("Use simplejson, not the old json module.")
sys.modules.pop('json') # get rid of the bad json module
import simplejson as json

# Make a dictionary with options from command line
def buildURL( options ):
urlDict = {}
if options.who:
who = options.who
else:
who = getpass.getuser()

urlDict['who'] = who

if options.files:
urlDict['files'] = json.dumps(options.files)

if options.comments:
urlDict['comments'] = options.comments
else:
# A comment is required by the buildbot DB
urlDict['comments'] = 'post_build_request submission'

if options.links:
urlDict['links'] = json.dumps(options.links)

if options.revision:
urlDict['revision'] = options.revision

if options.when:
urlDict['when'] = options.when

if options.branch:
urlDict['branch'] = options.branch

if options.category:
urlDict['category'] = options.category

if options.revlink:
urlDict['revlink'] = options.revlink

if options.properties:
urlDict['properties'] = json.dumps(options.properties)

if options.repository:
urlDict['repository'] = options.repository

if options.project:
urlDict['project'] = options.project

return urlDict

def propertyCB(option, opt, value, parser):
pdict=eval(value)
for key in pdict.keys():
parser.values.properties[key]=pdict[key]

__version__='0.1'

description=""

usage="""%prog [options]
This script is used to submit a change to the buildbot master using the
/change_hook web interface. Options are url encoded and submitted
using a HTTP POST. The repository and project must be specified.
This can be used to force a build. For example, create a scheduler that
listens for changes on a category 'release':
releaseFilt = ChangeFilter(category="release")
s=Scheduler(name="Release", change_filter=releaseFilt,
treeStableTimer=10,
builderNames=["UB10.4 x86_64 Release"]))
c['schedulers'].append(s)
Then run this script with the options:
--repostitory <REPOSTORY> --project <PROJECT> --category release
"""

parser = optparse.OptionParser(description=description,
usage=usage,
add_help_option=True,
version=__version__)

parser.add_option("-w", "--who", dest='who', metavar="WHO",
help=textwrap.dedent("""\
Who is submitting this request.
This becomes the Change.who attribute.
This defaults to the name of the user running this script
"""))
parser.add_option("-f", "--file", dest='files', action="append", metavar="FILE",
help=textwrap.dedent("""\
Add a file to the change request.
This is added to the Change.files attribute.
NOTE: Setting the file URL is not supported
"""))
parser.add_option("-c", "--comments", dest='comments', metavar="COMMENTS",
help=textwrap.dedent("""\
Comments for the change. This becomes the Change.comments attribute
"""))
parser.add_option("-l", "--link", dest='links', action="append", metavar="LINKS",
help=textwrap.dedent("""\
These are links for the source.
This becomes the Change.links attribute.
"""))
parser.add_option("-R", "--revision", dest='revision', metavar="REVISION",
help=textwrap.dedent("""\
This is the revision of the change.
This becomes the Change.revision attribute.
"""))
parser.add_option("-W", "--when", dest='when', metavar="WHEN",
help=textwrap.dedent("""\
This this the date of the change.
This becomes the Change.when attribute.
"""))
parser.add_option("-b", "--branch", dest='branch', metavar="BRANCH",
help=textwrap.dedent("""\
This this the branch of the change.
This becomes the Change.branch attribute.
"""))
parser.add_option("-C", "--category", dest='category', metavar="CAT",
help=textwrap.dedent("""\
Category for change. This becomes the Change.category attribute, which
can be used within the buildmaster to filter changes.
"""))
parser.add_option("--revlink", dest='revlink', metavar="REVLINK",
help=textwrap.dedent("""\
This this the revlink of the change.
This becomes the Change.revlink.
"""))
parser.add_option("-p", "--property", dest='properties', action="callback", callback=propertyCB,
type="string", metavar="PROP",
help=textwrap.dedent("""\
This adds a single property. This can be specified multiple times.
The argument is a string representing python dictionary. For example,
{'foo' : [ 'bar', 'baz' ]}
This becomes the Change.properties attribute.
"""))
parser.add_option("-r", "--repository", dest='repository', metavar="PATH",
help=textwrap.dedent("""\
Repository for use by buildbot slaves to checkout code.
This becomes the Change.repository attribute.
Exmaple: :ext:myhost:/cvsroot
"""))
parser.add_option("-P", "--project", dest='project', metavar="PROJ",
help=textwrap.dedent("""\
The project for the source. Often set to the CVS module being modified. This becomes
the Change.project attribute.
"""))
parser.add_option("-q", "--quiet", dest='verbose', action="store_false",
default=True,
help=textwrap.dedent("""\
Don't print as much status to stdout.
"""))
parser.add_option("-H", "--host", dest='host', metavar="HOST",
default='localhost:8010',
help=textwrap.dedent("""\
Host and optional port of buildbot. For example, bbhost:8010
Defaults to %default
"""))
parser.add_option("-u", "--urlpath", dest='urlpath', metavar="URLPATH",
default='/change_hook/base',
help=textwrap.dedent("""\
Path portion of URL. Defaults to %default
"""))
parser.add_option("-t", "--testing", action="store_true", dest="amTesting", default=False,
help=textwrap.dedent("""\
Just print values and exit.
"""))
parser.set_defaults(properties={})

(options, args) = parser.parse_args()

if options.repository is None:
print "repository must be specified"
parser.print_usage()
os._exit(2)

if options.project is None:
print "project must be specified"
parser.print_usage()
os._exit(2)

urlDict = buildURL(options)

params = urllib.urlencode(urlDict)
headers = {"Content-type": "application/x-www-form-urlencoded",
"Accept": "text/plain"}
if options.amTesting:
print "params: %s" % params
print "host: %s" % options.host
print "urlpath: %s" % options.urlpath
else:
conn = httplib.HTTPConnection(options.host)
conn.request("POST", "/change_hook/base", params, headers)
response = conn.getresponse()
data = response.read()
if options.verbose:
print response.status, response.reason
print data
res =json.loads(data)
print "Request %d at %s" % (res[0]['number'], res[0]['at'])
conn.close()

0 comments on commit 29d8854

Please sign in to comment.