mirror of
https://github.com/AdaCore/git-hooks.git
synced 2026-02-12 12:43:11 -08:00
This commit introduces a new class (ThirdPartyHook), which centralizes the handling of third-party hooks (scripts that project set up via the configuration file). Most of the code is simply a move of the code from maybe_call_thirdparty_hook, with a couple of enhancements: - Ability to specify the directory from which to call the hook; - Handling of the situation when calling the hook itself fails. We can already see the benefits of this in function style_check_files, where the code now necessary to call the style_checker is both clearer and more compact. In the future, the intent is to push this class one step further to allow users to specify hooks to be fetched from the repository, rather than requiring that the hooks be installed (usually by an admin) on the machine hosting the repository. The current class' API should allow us to implement this in a way that's transparent to all users. Change-Id: Ie8bf8accbbfd75a91b914628fad27d780532dac4 TN: T209-005
118 lines
4.4 KiB
Python
118 lines
4.4 KiB
Python
from argparse import ArgumentParser
|
|
from collections import OrderedDict
|
|
from shutil import rmtree
|
|
import sys
|
|
|
|
from config import ThirdPartyHook
|
|
from errors import InvalidUpdate
|
|
from git import get_object_type, git_show_ref
|
|
from init import init_all_globals
|
|
from utils import debug, warn, create_scratch_dir, FileLock
|
|
# We have to import utils, because we cannot import scratch_dir
|
|
# directly into this module. Otherwise, our scratch_dir seems
|
|
# to not see the update when create_scratch_dir is called.
|
|
import utils
|
|
|
|
from updates.factory import new_update
|
|
|
|
|
|
def parse_command_line():
|
|
"""Return a namespace built after parsing the command line.
|
|
"""
|
|
# The command-line interface is very simple, so we could possibly
|
|
# handle it by hand. But it's nice to have features such as
|
|
# -h/--help switches which come for free if we use argparse.
|
|
#
|
|
# We use ArgumentParser, which means that we are requiring
|
|
# Python version 2.7 or later, because it handles mandatory
|
|
# command-line arguments for us as well.
|
|
ap = ArgumentParser(description='Git "update" hook.')
|
|
ap.add_argument('ref_name',
|
|
help='the name of the reference being updated')
|
|
ap.add_argument('old_rev',
|
|
help='the SHA1 before update')
|
|
ap.add_argument('new_rev',
|
|
help='the new SHA1, if the update is accepted')
|
|
return ap.parse_args()
|
|
|
|
|
|
def maybe_update_hook(ref_name, old_rev, new_rev):
|
|
"""Call the update-hook if set in the repository's configuration.
|
|
|
|
Raises InvalidUpdate if the hook returned nonzero, indicating
|
|
that the update should be rejected.
|
|
|
|
PARAMETERS
|
|
ref_name: The name of the reference being update (Eg:
|
|
refs/heads/master).
|
|
old_rev: The commit SHA1 of the reference before the update.
|
|
new_rev: The new commit SHA1 that the reference will point to
|
|
if the update is accepted.
|
|
"""
|
|
result = ThirdPartyHook('hooks.update-hook').call_if_defined(
|
|
hook_args=(ref_name, old_rev, new_rev))
|
|
if result is not None:
|
|
hook_exe, p, out = result
|
|
if p.returncode != 0:
|
|
raise InvalidUpdate(
|
|
"Update rejected by this repository's hooks.update-hook"
|
|
" script",
|
|
'({}):'.format(hook_exe),
|
|
*out.splitlines())
|
|
else:
|
|
sys.stdout.write(out)
|
|
|
|
|
|
def check_update(ref_name, old_rev, new_rev):
|
|
"""General handler of the given update.
|
|
|
|
Raises InvalidUpdate if the update cannot be accepted (usually
|
|
because one of the commits fails a style-check, for instance).
|
|
|
|
PARAMETERS
|
|
ref_name: The name of the reference being update (Eg:
|
|
refs/heads/master).
|
|
old_rev: The commit SHA1 of the reference before the update.
|
|
new_rev: The new commit SHA1 that the reference will point to
|
|
if the update is accepted.
|
|
|
|
REMARKS
|
|
This function assumes that scratch_dir has been initialized.
|
|
"""
|
|
debug('check_update(ref_name=%s, old_rev=%s, new_rev=%s)'
|
|
% (ref_name, old_rev, new_rev),
|
|
level=2)
|
|
update_cls = new_update(ref_name, old_rev, new_rev, git_show_ref(),
|
|
submitter_email=None)
|
|
if update_cls is None:
|
|
# Report an error. We could look more precisely into what
|
|
# might be the reason behind this error, and print more precise
|
|
# diagnostics, but it does not seem like this would be worth
|
|
# the effort: It requires some pretty far-fetched scenarios
|
|
# for this to trigger; so, this should happen only very seldomly,
|
|
# and when a user does something very unusual.
|
|
raise InvalidUpdate(
|
|
"This type of update (%s,%s) is not valid."
|
|
% (ref_name, get_object_type(new_rev)))
|
|
with FileLock('git-hooks::update.token'):
|
|
update_cls.validate()
|
|
maybe_update_hook(ref_name, old_rev, new_rev)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = parse_command_line()
|
|
try:
|
|
init_all_globals(OrderedDict([(args.ref_name,
|
|
(args.old_rev, args.new_rev))]))
|
|
create_scratch_dir()
|
|
check_update(args.ref_name, args.old_rev, args.new_rev)
|
|
except InvalidUpdate, E:
|
|
# The update was rejected. Print the rejection reason, and
|
|
# exit with a nonzero status.
|
|
warn(*E)
|
|
sys.exit(1)
|
|
finally:
|
|
# Delete our scratch directory.
|
|
if utils.scratch_dir is not None:
|
|
rmtree(utils.scratch_dir)
|