hghooks/mozhghooks/extension.py (141 lines of code) (raw):
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
"""Mercurial extension to run hooks on repositories."""
from __future__ import absolute_import
from mercurial import (
configitems,
registrar,
)
from mozhg.util import (
identify_repo,
timers,
)
testedwith = b"4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5"
minimumhgversion = b"4.8"
buglink = b"https://bugzilla.mozilla.org/enter_bug.cgi?product=Developer%20Services&component=Mercurial%3A%20hg.mozilla.org"
configtable = {}
configitem = registrar.configitem(configtable)
configitem(b"allowedroots", b".*", generic=True)
configitem(b"mozilla", b"check.*", generic=True)
configitem(b"mozilla", b"repo_root", default=configitems.dynamicdefault)
configitem(b"mozilla", b"treeherder_repo", default=None)
configitem(b"mozilla", b"check_bug_references_repos", default=None)
configitem(b"pushlog", b"autolanduser", default=b"bind-autoland@mozilla.com")
configitem(
b"pushlog", b"landingworkeruser", default=b"lando_landing_worker@mozilla.com"
)
configitem(
b"pushlog", b"landingworkeruser2", default=b"lando_landing_worker_2@mozilla.com"
)
configitem(
b"pushlog", b"landingworkeruserdev", default=b"lando_landing_worker_dev@mozilla.com"
)
def get_check_classes(hook):
# TODO come up with a mechanism for automatically discovering checks
# so we don't have to enumerate them all.
from mozhghooks.check import (
advertise_upgrade,
merge_day,
prevent_cross_channel_messages,
check_bug_references,
prevent_conduit_arcconfig,
prevent_repolocked_commits,
prevent_subrepos,
prevent_symlinks,
prevent_sync_ipc_changes,
prevent_webidl_changes,
prevent_wptsync_changes,
single_root,
try_task_config_file,
)
# TODO check to hook mapping should also be automatically discovered.
if hook == b"pretxnchangegroup":
return (
merge_day.MergeDayCheck,
prevent_conduit_arcconfig.PreventConduitArcconfig,
prevent_cross_channel_messages.XChannelMessageCheck,
prevent_repolocked_commits.RepoLockedCheck,
prevent_subrepos.PreventSubReposCheck,
prevent_symlinks.PreventSymlinksCheck,
prevent_sync_ipc_changes.SyncIPCCheck,
prevent_webidl_changes.WebIDLCheck,
prevent_wptsync_changes.WPTSyncCheck,
single_root.SingleRootCheck,
try_task_config_file.TryConfigCheck,
)
elif hook == b"changegroup":
return (advertise_upgrade.AdvertiseUpgradeCheck,)
elif hook == b"pretxnclose":
return (check_bug_references.CheckBugReferencesCheck,)
def get_checks(ui, repo, source, classes):
"""Loads checks from classes.
Returns a list of check instances that are active for the given repo.
"""
# Never apply hooks at pull time or when re-applying from strips.
if source in (b"pull", b"strip"):
return []
info = identify_repo(repo)
# Don't apply to non-hosted repos.
if not info[b"hosted"]:
ui.write(b"(not running mozilla hooks on non-hosted repo)\n")
return []
checks = []
for cls in classes:
check = cls(ui, repo, info)
name = check.name
force_enable = False
force_disable = False
override = ui.config(b"mozilla", b"check.%s" % name)
if override in (b"enable", b"true"):
force_enable = True
elif override in (b"disable", b"false"):
force_disable = True
enabled = check.relevant()
if not isinstance(enabled, bool):
raise Exception(b"relevant() must return a bool; got %s" % enabled)
if enabled and force_disable:
ui.warn(b"(%s check disabled per config override)\n" % name)
continue
elif not enabled and force_enable:
ui.warn(b"(%s check enabled per config override)\n" % name)
enabled = True
if enabled:
checks.append(check)
return checks
def pretxnchangegroup(ui, repo, node, source=None, **kwargs):
checks = get_checks(ui, repo, source, get_check_classes(b"pretxnchangegroup"))
with timers(ui, b"mozhooks", b"mozhooks.pretxnchangegroup.") as times:
for check in checks:
with times.timeit(check.name):
check.pre(node)
for rev in repo.changelog.revs(repo[node].rev()):
ctx = repo[rev]
for check in checks:
with times.timeit(check.name):
if not check.check(ctx):
return 1
for check in checks:
with times.timeit(check.name):
if not check.post_check():
return 1
return 0
def pretxnclose(ui, repo, node=None, source=None, txnname=None, **kwargs):
# Only run hooks on a `push` transaction. `commit`, etc are not relevant
if txnname != b"push":
return 0
checks = get_checks(ui, repo, source, get_check_classes(b"pretxnclose"))
with timers(ui, b"mozhooks", b"mozhooks.pretxnclose.") as times:
for check in checks:
with times.timeit(check.name):
check.pre(node)
for rev in repo.changelog.revs(repo[node].rev()):
ctx = repo[rev]
for check in checks:
with times.timeit(check.name):
if not check.check(ctx):
return 1
for check in checks:
with times.timeit(check.name):
if not check.post_check():
return 1
return 0
def changegroup(ui, repo, source=None, **kwargs):
checks = get_checks(ui, repo, source, get_check_classes(b"changegroup"))
with timers(ui, b"mozhooks", b"mozhooks.changegroup.") as times:
for check in checks:
with times.timeit(check.name):
if not check.check(**kwargs):
return 1
return 0
def reposetup(ui, repo):
ui.setconfig(b"hooks", b"pretxnchangegroup.mozhooks", pretxnchangegroup)
ui.setconfig(b"hooks", b"changegroup.mozhooks", changegroup)
ui.setconfig(b"hooks", b"pretxnclose.mozhooks", pretxnclose)