2 # Copyright 2020 The Pigweed Authors
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 # use this file except in compliance with the License. You may obtain a copy of
8 # https://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations under
15 """Creates a Git hook that calls a script with certain arguments."""
20 from pathlib import Path
24 from typing import Sequence, Union
26 _LOG: logging.Logger = logging.getLogger(__name__)
29 def git_repo_root(path: Union[Path, str]) -> Path:
31 subprocess.run(['git', '-C', path, 'rev-parse', '--show-toplevel'],
33 stdout=subprocess.PIPE).stdout.strip().decode())
36 def install_hook(script,
38 args: Sequence[str] = (),
39 repository: Union[Path, str] = '.') -> None:
40 """Installs a simple Git hook that calls a script with arguments."""
41 root = git_repo_root(repository).resolve()
42 script = os.path.relpath(script, root)
44 if root.joinpath('.git').is_dir():
45 hook_path = root.joinpath('.git', 'hooks', hook)
46 else: # This repo is probably a submodule with a .git file instead
47 match = re.match('^gitdir: (.*)$', root.joinpath('.git').read_text())
49 raise ValueError('Unexpected format for .git file')
51 hook_path = root.joinpath(match.group(1), 'hooks', hook).resolve()
53 hook_path.parent.mkdir(exist_ok=True)
55 command = ' '.join(shlex.quote(arg) for arg in (script, *args))
57 with hook_path.open('w') as file:
58 line = lambda *args: print(*args, file=file)
61 line(f'# {hook} hook generated by {__file__}')
65 hook_path.chmod(0o755)
66 logging.info('Created %s hook for %s at %s', hook, script, hook_path)
69 def argument_parser(parser=None) -> argparse.ArgumentParser:
71 parser = argparse.ArgumentParser(description=__doc__)
73 def path(arg: str) -> Path:
74 if not os.path.exists(arg):
75 raise argparse.ArgumentTypeError(f'"{arg}" is not a valid path')
84 help='Path to the repository in which to install the hook')
85 parser.add_argument('--hook',
87 help='Which type of Git hook to create')
88 parser.add_argument('-s',
92 help='Path to the script to execute in the hook')
93 parser.add_argument('args',
95 help='Arguments to provide to the commit hook')
100 if __name__ == '__main__':
101 logging.basicConfig(format='%(message)s', level=logging.INFO)
102 install_hook(**vars(argument_parser().parse_args()))