Merge branch 'master' into file_contents_sorter_hook
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index e7f433b..c681c45 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -105,6 +105,12 @@
entry: end-of-file-fixer
language: python
files: \.(asciidoc|adoc|coffee|cpp|css|c|ejs|erb|groovy|h|haml|hh|hpp|hxx|html|in|j2|jade|json|js|less|markdown|md|ml|mli|pp|py|rb|rs|R|scala|scss|sh|slim|tex|tmpl|ts|txt|yaml|yml)$
+- id: file-contents-sorter
+ name: File Contents Sorter
+ description: Sort the lines in specified files (defaults to alphabetical). You must provide list of target files as input in your .pre-commit-config.yaml file.
+ entry: file-contents-sorter
+ language: python
+ files: '^$'
- id: fix-encoding-pragma
name: Fix python encoding pragma
language: python
diff --git a/README.md b/README.md
index 8db7eef..92fb408 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@
with single quoted strings.
- `end-of-file-fixer` - Makes sure files end in a newline and only a newline.
- `fix-encoding-pragma` - Add `# -*- coding: utf-8 -*-` to the top of python files.
+- `file-contents-sorter` - Sort the lines in specified files (defaults to alphabetical). You must provide list of target files as input to it.
- To remove the coding pragma pass `--remove` (useful in a python3-only codebase)
- `flake8` - Run flake8 on your python files.
- `forbid-new-submodules` - Prevent addition of new git submodules.
diff --git a/hooks.yaml b/hooks.yaml
index e7f433b..c681c45 100644
--- a/hooks.yaml
+++ b/hooks.yaml
@@ -105,6 +105,12 @@
entry: end-of-file-fixer
language: python
files: \.(asciidoc|adoc|coffee|cpp|css|c|ejs|erb|groovy|h|haml|hh|hpp|hxx|html|in|j2|jade|json|js|less|markdown|md|ml|mli|pp|py|rb|rs|R|scala|scss|sh|slim|tex|tmpl|ts|txt|yaml|yml)$
+- id: file-contents-sorter
+ name: File Contents Sorter
+ description: Sort the lines in specified files (defaults to alphabetical). You must provide list of target files as input in your .pre-commit-config.yaml file.
+ entry: file-contents-sorter
+ language: python
+ files: '^$'
- id: fix-encoding-pragma
name: Fix python encoding pragma
language: python
diff --git a/pre_commit_hooks/file_contents_sorter.py b/pre_commit_hooks/file_contents_sorter.py
new file mode 100644
index 0000000..e01eb8c
--- /dev/null
+++ b/pre_commit_hooks/file_contents_sorter.py
@@ -0,0 +1,57 @@
+"""
+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-whitelist.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.
+"""
+from __future__ import print_function
+
+import argparse
+
+PASS = 0
+FAIL = 1
+
+
+def sort_file_contents(f):
+ before = list(f)
+ after = sorted(before)
+
+ before_string = b''.join(before)
+ after_string = b''.join(after)
+
+ if before_string == after_string:
+ return PASS
+ else:
+ f.seek(0)
+ f.write(after_string)
+ f.truncate()
+ return FAIL
+
+
+def parse_commandline_input(argv):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('filenames', nargs='+', help='Files to sort')
+ args = parser.parse_args(argv)
+ return args
+
+
+def main(argv=None):
+ args = parse_commandline_input(argv)
+
+ retv = PASS
+
+ for arg in args.filenames:
+ with open(arg, 'rb+') as file_obj:
+ ret_for_file = sort_file_contents(file_obj)
+
+ if ret_for_file:
+ print('Sorting {}'.format(arg))
+
+ retv |= ret_for_file
+
+ return retv
diff --git a/setup.py b/setup.py
index af21e16..c5cceb7 100644
--- a/setup.py
+++ b/setup.py
@@ -49,6 +49,7 @@
'detect-private-key = pre_commit_hooks.detect_private_key:detect_private_key',
'double-quote-string-fixer = pre_commit_hooks.string_fixer:main',
'end-of-file-fixer = pre_commit_hooks.end_of_file_fixer:end_of_file_fixer',
+ 'file-contents-sorter = pre_commit_hooks.file_contents_sorter:main',
'fix-encoding-pragma = pre_commit_hooks.fix_encoding_pragma:main',
'forbid-new-submodules = pre_commit_hooks.forbid_new_submodules:main',
'name-tests-test = pre_commit_hooks.tests_should_end_in_test:validate_files',
diff --git a/tests/file_contents_sorter_test.py b/tests/file_contents_sorter_test.py
new file mode 100644
index 0000000..7b3d098
--- /dev/null
+++ b/tests/file_contents_sorter_test.py
@@ -0,0 +1,30 @@
+import pytest
+
+from pre_commit_hooks.file_contents_sorter import FAIL
+from pre_commit_hooks.file_contents_sorter import main
+from pre_commit_hooks.file_contents_sorter import PASS
+
+
+# Input, expected return value, expected output
+TESTS = (
+ (b'', PASS, b''),
+ (b'lonesome\n', PASS, b'lonesome\n'),
+ (b'missing_newline', PASS, b'missing_newline'),
+ (b'alpha\nbeta\n', PASS, b'alpha\nbeta\n'),
+ (b'beta\nalpha\n', FAIL, b'alpha\nbeta\n'),
+ (b'C\nc\n', PASS, b'C\nc\n'),
+ (b'c\nC\n', FAIL, b'C\nc\n'),
+ (b'mag ical \n tre vor\n', FAIL, b' tre vor\nmag ical \n'),
+ (b'@\n-\n_\n#\n', FAIL, b'#\n-\n@\n_\n'),
+)
+
+
+@pytest.mark.parametrize(('input_s', 'expected_retval', 'output'), TESTS)
+def test_integration(input_s, expected_retval, output, tmpdir):
+ path = tmpdir.join('file.txt')
+ path.write_binary(input_s)
+
+ output_retval = main([path.strpath])
+
+ assert path.read_binary() == output
+ assert output_retval == expected_retval