mirror of
https://github.com/AdaCore/git-hooks.git
synced 2026-02-12 12:43:11 -08:00
We noticed that Gerrit's ref-updated hook was reporting some
errors when Gerrit makes updates to some of its internal references.
For instance:
| Traceback (most recent call last):
| File "./hooks/post_receive.py", line 126, in <module>
[...]
| errors.InvalidUpdate: ('Unable to determine the type of reference
| for: refs/changes/01/104201/meta', '',
[...]
This commit avoids this error by making sure that we properly
ignore all references that match the hooks.ignore-refs config
(note that, by default, hooks.ignore-refs includes matches for
those special Gerrit references).
A new testcase is added to verify that this is the case.
Note that the minor reformatting in maybe_update_hook comes from
black, which adds the extra coma only when the changes made in
this commit are made as well. This is why this reformatting change
is bundled with this commit.
Change-Id: Id94129ad1a9d84ccba6acafef3d8e98f62ec0239
TN: UA21-052
129 lines
4.7 KiB
Python
129 lines
4.7 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 requirements import check_minimum_system_requirements
|
|
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,
|
|
)
|
|
|
|
check_minimum_system_requirements()
|
|
|
|
# Do nothing if the reference is in the hooks.ignore-refs list.
|
|
ignore_refs_match = utils.search_config_option_list("hooks.ignore-refs", ref_name)
|
|
if ignore_refs_match is not None:
|
|
debug(f"{ref_name} ignored due to hooks.ignore-refs" f" ({ignore_refs_match})")
|
|
return
|
|
|
|
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 as E:
|
|
# The update was rejected. Print the rejection reason, and
|
|
# exit with a nonzero status.
|
|
warn(*E.args)
|
|
sys.exit(1)
|
|
finally:
|
|
# Delete our scratch directory.
|
|
if utils.scratch_dir is not None:
|
|
rmtree(utils.scratch_dir)
|