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', 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' + )