Skip to content

Commit

Permalink
Merge pull request #1781 from sscherfke/patch-1
Browse files Browse the repository at this point in the history
Update contrib/hgbuildbot.py
  • Loading branch information
Mikhail Sobolev committed Jul 22, 2015
2 parents 8818fb6 + 9b6f729 commit 9e7aa24
Showing 1 changed file with 211 additions and 112 deletions.
323 changes: 211 additions & 112 deletions master/contrib/hgbuildbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,138 +13,233 @@
#
# Portions Copyright Buildbot Team Members
# Portions Copyright 2007 Frederic Leroy <fredo@starox.org>

# hook extension to send change notifications to buildbot when a changeset is
# brought into the repository from elsewhere.
#
# See the Buildbot manual for configuration instructions.

# WARNING
# This code does not work with recent versions of Twisted and Mercurial. It
# was never a good idea to try to run Twisted code within Mercurial, and now it
# doesn't work. Use this code with caution.
#
# Documentation
# =============
#
# Mercurial "changegroup" hook that notifies Buildbot when a number of
# changsets is brought into the repository from elsewhere.
#
# Your Buildmaster needs to define a PBChangeSource. You should at least set
# a strong password for it. Changing the username or port is optional.
# See the docs for more details:
# http://docs.buildbot.net/latest/manual/cfg-changesources.html#pbchangesource
#
# Copy this file to ".hg/hgbuildbot.py" in the repository that should notify
# Buildbot.
#
# Add it to the "[hooks]" section of ".hg/hgrc". Also add a "[hgbuildbot]"
# section with additional parameters, for example:
#
# [hooks]
# changegroup.buildbot = python:.hg/hgbuiltbot.py:hook
#
# [hgbuildbot]
# venv = /home/buildbot/.virtualenvs/builtbot/lib/python2.7/site-packages
# master = localhost:9987
# passwd = aA8(-adf8j3-_3uX
#
#
# Available parmeters
# -------------------
#
# venv
# The hook needs the Python package "buildbot". You can optionally point to
# virtualenv if it is not installed globally:
#
# Optional; default: None
#
# Example:
#
# venv = /path/to/venv/lib/pythonX.Y/site-packages
#
# master
# Host and port of the Buildmaster(s) to notify.
# Can be a single entry or a comma-separated list.
#
# Mandatory.
#
# Examples:
#
# master = localhost:9989
# master = bm1.example.org:9989,bm2.example.org:9989
#
# user
# User for connecting to the Buildmaster.
#
# Optional; default: change
#
# passwd
# Password for connecting to the Buildmaster.
#
# Optional; default: changepw
#
# branchtype
# The branchmodel you use: "inrepo" for named branches (managed by
# "hg branch") or "dirname" for directory based branches (the last component
# of the repository's directory will then be used as branch name).
#
# Optional; default: inrepo
#
# branch
# Explicitly specify a branchname instead of using the repo's basename when
# using "branchtype = dirname".
#
# Optional.
#
# baseurl
# Prefix for the repository URL sent to the Buildmaster. See below for
# details.
#
# Optional. The hook will also check the [web] section for this parameter.
#
# strip
# Strip as many slashes from the repo dir before appending it to baseurl.
# See below for details.
#
# Optional; default: 0; The hook will also check the [notify] section for
# this parameter.
#
# category
# Category to assign to all change sets.
#
# Optional.
#
# project
# Project that the repo belongs to.
#
# Optional.
#
# codebase
# Codebase name for the repo.
#
# Optional.
#
#
# Repository URLs
# ---------------
#
# The hook sends a repository URL to the Buildmasters. It can be used by
# schedulers (e.g., for filtering) and is also used in the webview to create
# a link to the corresponding changeset.
#
# By default, the absolute repository path (e.g., "/home/hg/repos/myrepo") will
# be used. The webview will in this case simply append the path to its own
# hostname in order to create a link to that change (e.g.,
# "http://localhost:8020/home/hg/repos/myrepo").
#
# You can alternatively strip some of the repo path's components and prepend
# a custom base URL instead. For example, if you want to create an URL like
# "https://code.company.com/myrepo", you must specify the following parameters:
#
# baseurl = https://code.company.com/
# strip = 4
#
# This would strip everything until (and including) the 4th "/" in the repo's
# path leaving only "myrepo" left. This would then be append to the base URL.

import os
import os.path
import sys

from mercurial.node import bin
from mercurial.encoding import fromlocal
from mercurial.node import hex
from mercurial.node import nullid # @UnresolvedImport

# mercurial's on-demand-importing hacks interfere with the:
# from zope.interface import Interface
# that Twisted needs to do, so disable it.
try:
from mercurial import demandimport
demandimport.disable()
except ImportError:
pass

# In Mercurial post-1.7, some strings might be stored as a
# encoding.localstr class. encoding.fromlocal will translate
# those back to UTF-8 strings.
try:
from mercurial.encoding import fromlocal
_hush_pyflakes = [fromlocal]
del _hush_pyflakes
except ImportError:
def fromlocal(s):
return s
from mercurial.node import nullid


def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
# read config parameters
baseurl = ui.config('hgbuildbot', 'baseurl',
ui.config('web', 'baseurl', ''))
masters = ui.configlist('hgbuildbot', 'master')
if masters:
branchtype = ui.config('hgbuildbot', 'branchtype', 'inrepo')
branch = ui.config('hgbuildbot', 'branch')
fork = ui.configbool('hgbuildbot', 'fork', False)
# notify also has this setting
stripcount = int(ui.config('notify', 'strip') or ui.config('hgbuildbot', 'strip', 3))
category = ui.config('hgbuildbot', 'category', None)
project = ui.config('hgbuildbot', 'project', '')
auth = ui.config('hgbuildbot', 'auth', None)
else:
ui.write("* You must add a [hgbuildbot] section to .hg/hgrc in "
"order to use buildbot hook\n")
if hooktype != 'changegroup':
ui.status('hgbuildbot: hooktype %s not supported.\n' % hooktype)
return

if hooktype != "changegroup":
ui.status("hgbuildbot: hooktype %s not supported.\n" % hooktype)
# Read config parameters
masters = ui.configlist('hgbuildbot', 'master')
if not masters:
ui.write('* You must add a [hgbuildbot] section to .hg/hgrc in '
'order to use the Buildbot hook\n')
return

if fork:
child_pid = os.fork()
if child_pid == 0:
# child
pass
else:
# parent
ui.status("Notifying buildbot...\n")
return

# only import inside the fork if forked
# - virtualenv
venv = ui.config('hgbuildbot', 'venv', None)
if venv is not None:
if not os.path.isdir(venv):
ui.write('* Virtualenv "%s" does not exist.\n' % venv)
sys.path.insert(0, venv)

# - auth
username = ui.config('hgbuildbot', 'user', 'change')
password = ui.config('hgbuildbot', 'passwd', 'changepw')

# - branch
branchtype = ui.config('hgbuildbot', 'branchtype', 'inrepo')
branch = ui.config('hgbuildbot', 'branch', None)

# - repo URL
baseurl = ui.config('hgbuildbot', 'baseurl',
ui.config('web', 'baseurl', ''))
stripcount = int(ui.config('hgbuildbot', 'strip',
ui.config('notify', 'strip', 0)))

# - category, project and codebase
category = ui.config('hgbuildbot', 'category', None)
project = ui.config('hgbuildbot', 'project', '')
codebase = ui.config('hgbuildbot', 'codebase', '')

# Only import this after the (optional) venv has been added to sys.path:
from buildbot.clients import sendchange
from twisted.internet import defer, reactor

if branch is None:
if branchtype == 'dirname':
branch = os.path.basename(repo.root)

if not auth:
auth = 'change:changepw'
auth = auth.split(':', 1)

# process changesets
def _send(res, s, c):
if not fork:
ui.status("rev %s sent\n" % c['revision'])
return s.send(c['branch'], c['revision'], c['comments'],
c['files'], c['username'], category=category,
repository=repository, project=project, vc='hg',
properties=c['properties'])

try: # first try Mercurial 1.1+ api
start = repo[node].rev()
end = len(repo)
except TypeError: # else fall back to old api
start = repo.changelog.rev(bin(node))
end = repo.changelog.count()
# Process changesets
if branch is None and branchtype == 'dirname':
branch = os.path.basename(repo.root)
# If branchtype == 'inrepo', update "branch" for each commit later.

repository = strip(repo.root, stripcount)
repository = baseurl + repository

start = repo[node].rev()
end = len(repo)

for master in masters:
s = sendchange.Sender(master, auth=auth)
s = sendchange.Sender(master, auth=(username, password))
d = defer.Deferred()
reactor.callLater(0, d.callback, None)

for rev in xrange(start, end):
for rev in range(start, end):
# send changeset
node = repo.changelog.node(rev)
manifest, user, (time, timezone), files, desc, extra = repo.changelog.read(node)
parents = filter(lambda p: not p == nullid, repo.changelog.parents(node))
log = repo.changelog.read(node)
manifest, user, (time, timezone), files, desc, extra = log
parents = [p for p in repo.changelog.parents(node) if p != nullid]

if branchtype == 'inrepo':
branch = extra['branch']
if branch:
branch = fromlocal(branch)

is_merge = len(parents) > 1
# merges don't always contain files, but at least one file is required by buildbot
# merges don't always contain files, but at least one file is
# required by buildbot
if is_merge and not files:
files = ["merge"]
properties = {'is_merge': is_merge}
if branch:
branch = fromlocal(branch)

change = {
'master': master,
'username': fromlocal(user),
# 'master': master,
'branch': branch,
'revision': hex(node),
'comments': fromlocal(desc),
'files': files,
'branch': branch,
'properties': properties
'username': fromlocal(user),
'category': category,
'time': time,
'properties': properties,
'repository': repository,
'project': project,
'codebase': codebase,
}
d.addCallback(_send, s, change)
d.addCallback(send_cs, s, change)

def _printSuccess(res):
ui.status(s.getSuccessString(res) + '\n')
Expand All @@ -156,24 +251,28 @@ def _printFailure(why):
d.addBoth(lambda _: reactor.stop())
reactor.run()

if fork:
os._exit(os.EX_OK)
else:
return

# taken from the mercurial notify extension


def strip(path, count):
'''Strip the count first slash of the path'''

"""Strip the count first slash of the path"""
# First normalize it
path = '/'.join(path.split(os.sep))
# and strip it part after part
while count > 0:
c = path.find('/')
if c == -1:
break
path = path[c + 1:]
count -= 1
return path
# and strip the *count* first slash
return path.split('/', count)[-1]


def send_cs(res, sender, change):
"""Send a changeset *change* via *sender*."""
return sender.send(
change['branch'],
change['revision'],
change['comments'],
change['files'],
who=change['username'],
category=change['category'],
when=change['time'],
properties=change['properties'],
repository=change['repository'],
vc='hg',
project=change['project'],
# revlink='',
codebase=change['codebase'])

0 comments on commit 9e7aa24

Please sign in to comment.