summaryrefslogtreecommitdiffstats
path: root/pre_commit_hooks/file_contents_sorter.py
diff options
context:
space:
mode:
Diffstat (limited to 'pre_commit_hooks/file_contents_sorter.py')
-rw-r--r--pre_commit_hooks/file_contents_sorter.py84
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())