Skip to content

Commit 13991f0

Browse files
committed
Add a checker for executables without shebangs
1 parent 4a457a7 commit 13991f0

File tree

6 files changed

+93
-0
lines changed

6 files changed

+93
-0
lines changed

.pre-commit-hooks.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@
5151
# for backward compatibility
5252
files: ''
5353
minimum_pre_commit_version: 0.15.0
54+
- id: check-executables-have-shebangs
55+
name: Check that executables have shebangs
56+
description: Ensures that (non-binary) executables have a shebang.
57+
entry: check-executables-have-shebangs
58+
language: python
59+
types: [text, executable]
60+
# for backward compatibility
61+
files: ''
62+
minimum_pre_commit_version: 0.15.0
5463
- id: check-json
5564
name: Check JSON
5665
description: This hook checks json files for parseable syntax.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Add this to your `.pre-commit-config.yaml`
3434
case-insensitive filesystem like MacOS HFS+ or Windows FAT.
3535
- `check-docstring-first` - Checks for a common error of placing code before
3636
the docstring.
37+
- `check-executables-have-shebangs` - Checks that non-binary executables have a
38+
proper shebang.
3739
- `check-json` - Attempts to load all json files to verify syntax.
3840
- `check-merge-conflict` - Check for files that contain merge conflict strings.
3941
- `check-symlinks` - Checks for symlinks which do not point to anything.

hooks.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
entry: upgrade-your-pre-commit-version
3535
files: ''
3636
minimum_pre_commit_version: 0.15.0
37+
- id: check-executables-have-shebangs
38+
language: system
39+
name: upgrade-your-pre-commit-version
40+
entry: upgrade-your-pre-commit-version
41+
files: ''
42+
minimum_pre_commit_version: 0.15.0
3743
- id: check-json
3844
language: system
3945
name: upgrade-your-pre-commit-version
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Check that executable text files have a shebang."""
2+
from __future__ import absolute_import
3+
from __future__ import print_function
4+
from __future__ import unicode_literals
5+
6+
import argparse
7+
import pipes
8+
import sys
9+
10+
11+
def check_has_shebang(path):
12+
with open(path, 'rb') as f:
13+
first_bytes = f.read(2)
14+
15+
if first_bytes != b'#!':
16+
print(
17+
'{path}: marked executable but has no (or invalid) shebang!\n'
18+
" If it isn't supposed to be executable, try: chmod -x {quoted}\n"
19+
' If it is supposed to be executable, double-check its shebang.'.format(
20+
path=path,
21+
quoted=pipes.quote(path),
22+
),
23+
file=sys.stderr,
24+
)
25+
return 1
26+
else:
27+
return 0
28+
29+
30+
def main(argv=None):
31+
parser = argparse.ArgumentParser(description=__doc__)
32+
parser.add_argument('filenames', nargs='*')
33+
args = parser.parse_args(argv)
34+
35+
retv = 0
36+
37+
for filename in args.filenames:
38+
retv |= check_has_shebang(filename)
39+
40+
return retv

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
'check-byte-order-marker = pre_commit_hooks.check_byte_order_marker:main',
4040
'check-case-conflict = pre_commit_hooks.check_case_conflict:main',
4141
'check-docstring-first = pre_commit_hooks.check_docstring_first:main',
42+
'check-executables-have-shebangs = pre_commit_hooks.check_executables_have_shebangs:main',
4243
'check-json = pre_commit_hooks.check_json:check_json',
4344
'check-merge-conflict = pre_commit_hooks.check_merge_conflict:detect_merge_conflict',
4445
'check-symlinks = pre_commit_hooks.check_symlinks:check_symlinks',
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import
3+
from __future__ import unicode_literals
4+
5+
import pytest
6+
7+
from pre_commit_hooks.check_executables_have_shebangs import main
8+
9+
10+
@pytest.mark.parametrize('content', (
11+
b'#!/bin/bash\nhello world\n',
12+
b'#!/usr/bin/env python3.6',
13+
b'#!python',
14+
'#!☃'.encode('UTF-8'),
15+
))
16+
def test_has_shebang(content, tmpdir):
17+
path = tmpdir.join('path')
18+
path.write(content, 'wb')
19+
assert main((path.strpath,)) == 0
20+
21+
22+
@pytest.mark.parametrize('content', (
23+
b'',
24+
b' #!python\n',
25+
b'\n#!python\n',
26+
b'python\n',
27+
'☃'.encode('UTF-8'),
28+
29+
))
30+
def test_bad_shebang(content, tmpdir, capsys):
31+
path = tmpdir.join('path')
32+
path.write(content, 'wb')
33+
assert main((path.strpath,)) == 1
34+
_, stderr = capsys.readouterr()
35+
assert stderr.startswith('{}: marked executable but'.format(path.strpath))

0 commit comments

Comments
 (0)