Repository: pre-commit/pre-commit-hooks
Pull request #241 (2017-10-07 06:33:58)

Add hook for ensuring vcs permalinks


Patch:
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index 454f258..f5b73d3 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -96,6 +96,15 @@
     # for backward compatibility
     files: ''
     minimum_pre_commit_version: 0.15.0
+-   id: check-vcs-permalinks
+    name: Check vcs permalinks
+    description: Ensures that links to vcs websites are permalinks.
+    entry: check-vcs-permalinks
+    language: python
+    types: [text]
+    # for backward compatibility
+    files: ''
+    minimum_pre_commit_version: 0.15.0
 -   id: check-xml
     name: Check Xml
     description: This hook checks xml files for parseable syntax.
diff --git a/README.md b/README.md
index 147b6ba..501401e 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,7 @@ Add this to your `.pre-commit-config.yaml`
 - `check-json` - Attempts to load all json files to verify syntax.
 - `check-merge-conflict` - Check for files that contain merge conflict strings.
 - `check-symlinks` - Checks for symlinks which do not point to anything.
+- `check-vcs-permalinks` - Ensures that links to vcs websites are permalinks.
 - `check-xml` - Attempts to load all xml files to verify syntax.
 - `check-yaml` - Attempts to load all yaml files to verify syntax.
 - `debug-statements` - Check for pdb / ipdb / pudb statements in code.
diff --git a/hooks.yaml b/hooks.yaml
index 59cc320..6d66935 100644
--- a/hooks.yaml
+++ b/hooks.yaml
@@ -64,6 +64,12 @@
     entry: upgrade-your-pre-commit-version
     files: ''
     minimum_pre_commit_version: 0.15.0
+-   id: check-vcs-permalinks
+    language: system
+    name: upgrade-your-pre-commit-version
+    entry: upgrade-your-pre-commit-version
+    files: ''
+    minimum_pre_commit_version: 0.15.0
 -   id: check-xml
     language: system
     name: upgrade-your-pre-commit-version
diff --git a/pre_commit_hooks/check_vcs_permalinks.py b/pre_commit_hooks/check_vcs_permalinks.py
new file mode 100644
index 0000000..130abf6
--- /dev/null
+++ b/pre_commit_hooks/check_vcs_permalinks.py
@@ -0,0 +1,43 @@
+from __future__ import absolute_import
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import argparse
+import re
+import sys
+
+
+GITHUB_NON_PERMALINK = re.compile(
+    b'https://github.com/[^/]+/[^/]+/blob/master/[^# ]+#L\d+',
+)
+
+
+def _check_filename(filename):
+    retv = 0
+    with open(filename, 'rb') as f:
+        for i, line in enumerate(f, 1):
+            if GITHUB_NON_PERMALINK.search(line):
+                sys.stdout.write('{}:{}:'.format(filename, i))
+                getattr(sys.stdout, 'buffer', sys.stdout).write(line)
+                retv = 1
+    return retv
+
+
+def main(argv=None):
+    parser = argparse.ArgumentParser()
+    parser.add_argument('filenames', nargs='*')
+    args = parser.parse_args(argv)
+
+    retv = 0
+    for filename in args.filenames:
+        retv |= _check_filename(filename)
+
+    if retv:
+        print()
+        print('Non-permanent github link detected.')
+        print('On any page on github press [y] to load a permalink.')
+    return retv
+
+
+if __name__ == '__main__':
+    exit(main())
diff --git a/setup.py b/setup.py
index 2c26c9b..73cace5 100644
--- a/setup.py
+++ b/setup.py
@@ -43,6 +43,7 @@ setup(
             'check-json = pre_commit_hooks.check_json:check_json',
             'check-merge-conflict = pre_commit_hooks.check_merge_conflict:detect_merge_conflict',
             'check-symlinks = pre_commit_hooks.check_symlinks:check_symlinks',
+            'check-vcs-permalinks = pre_commit_hooks.check_vcs_permalinks:main',
             'check-xml = pre_commit_hooks.check_xml:check_xml',
             'check-yaml = pre_commit_hooks.check_yaml:check_yaml',
             'debug-statement-hook = pre_commit_hooks.debug_statement_hook:debug_statement_hook',


Test patch:
diff --git a/tests/check_vcs_permalinks_test.py b/tests/check_vcs_permalinks_test.py
new file mode 100644
index 0000000..31fd608
--- /dev/null
+++ b/tests/check_vcs_permalinks_test.py
@@ -0,0 +1,36 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+from pre_commit_hooks.check_vcs_permalinks import main
+
+
+def test_trivial(tmpdir):
+    f = tmpdir.join('f.txt').ensure()
+    assert not main((f.strpath,))
+
+
+def test_passing(tmpdir):
+    f = tmpdir.join('f.txt')
+    f.write_binary(
+        # permalinks are ok
+        b'https://github.com/asottile/test/blob/649e6/foo%20bar#L1\n'
+        # links to files but not line numbers are ok
+        b'https://github.com/asottile/test/blob/master/foo%20bar\n',
+    )
+    assert not main((f.strpath,))
+
+
+def test_failing(tmpdir, capsys):
+    with tmpdir.as_cwd():
+        tmpdir.join('f.txt').write_binary(
+            b'https://github.com/asottile/test/blob/master/foo#L1\n',
+        )
+
+        assert main(('f.txt',))
+        out, _ = capsys.readouterr()
+        assert out == (
+            'f.txt:1:https://github.com/asottile/test/blob/master/foo#L1\n'
+            '\n'
+            'Non-permanent github link detected.\n'
+            'On any page on github press [y] to load a permalink.\n'
+        )