diff options
Diffstat (limited to 'pre_commit_hooks/file_contents_sorter.py')
-rw-r--r-- | pre_commit_hooks/file_contents_sorter.py | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/pre_commit_hooks/file_contents_sorter.py b/pre_commit_hooks/file_contents_sorter.py new file mode 100644 index 0000000..392e226 --- /dev/null +++ b/pre_commit_hooks/file_contents_sorter.py @@ -0,0 +1,84 @@ +""" +A very simple pre-commit hook that, when passed one or more filenames +as arguments, will sort the lines in those files. + +An example use case for this: you have a deploy-allowlist.txt file +in a repo that contains a list of filenames that is used to specify +files to be included in a docker container. This file has one filename +per line. Various users are adding/removing lines from this file; using +this hook on that file should reduce the instances of git merge +conflicts and keep the file nicely ordered. +""" +import argparse +from typing import Any +from typing import Callable +from typing import IO +from typing import Iterable +from typing import Optional +from typing import Sequence + +PASS = 0 +FAIL = 1 + + +def sort_file_contents( + f: IO[bytes], + key: Optional[Callable[[bytes], Any]], + *, + unique: bool = False, +) -> int: + before = list(f) + lines: Iterable[bytes] = ( + line.rstrip(b'\n\r') for line in before if line.strip() + ) + if unique: + lines = set(lines) + after = sorted(lines, key=key) + + before_string = b''.join(before) + after_string = b'\n'.join(after) + b'\n' + + if before_string == after_string: + return PASS + else: + f.seek(0) + f.write(after_string) + f.truncate() + return FAIL + + +def main(argv: Optional[Sequence[str]] = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument('filenames', nargs='+', help='Files to sort') + parser.add_argument( + '--ignore-case', + action='store_const', + const=bytes.lower, + default=None, + help='fold lower case to upper case characters', + ) + parser.add_argument( + '--unique', + action='store_true', + help='ensure each line is unique', + ) + args = parser.parse_args(argv) + + retv = PASS + + for arg in args.filenames: + with open(arg, 'rb+') as file_obj: + ret_for_file = sort_file_contents( + file_obj, key=args.ignore_case, unique=args.unique, + ) + + if ret_for_file: + print(f'Sorting {arg}') + + retv |= ret_for_file + + return retv + + +if __name__ == '__main__': + raise SystemExit(main()) |