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)