Ville Skyttä | 391ae30 | 2021-01-08 17:36:55 +0200 | [diff] [blame] | 1 | """Check that text files with a shebang are executable.""" |
Anthony Sottile | 8f61529 | 2022-01-15 19:24:05 -0500 | [diff] [blame] | 2 | from __future__ import annotations |
| 3 | |
Ville Skyttä | 391ae30 | 2021-01-08 17:36:55 +0200 | [diff] [blame] | 4 | import argparse |
| 5 | import shlex |
| 6 | import sys |
Ville Skyttä | 391ae30 | 2021-01-08 17:36:55 +0200 | [diff] [blame] | 7 | from typing import Sequence |
Ville Skyttä | 391ae30 | 2021-01-08 17:36:55 +0200 | [diff] [blame] | 8 | |
| 9 | from pre_commit_hooks.check_executables_have_shebangs import EXECUTABLE_VALUES |
| 10 | from pre_commit_hooks.check_executables_have_shebangs import git_ls_files |
| 11 | from pre_commit_hooks.check_executables_have_shebangs import has_shebang |
| 12 | |
| 13 | |
Anthony Sottile | 8f61529 | 2022-01-15 19:24:05 -0500 | [diff] [blame] | 14 | def check_shebangs(paths: list[str]) -> int: |
Ville Skyttä | 391ae30 | 2021-01-08 17:36:55 +0200 | [diff] [blame] | 15 | # Cannot optimize on non-executability here if we intend this check to |
| 16 | # work on win32 -- and that's where problems caused by non-executability |
| 17 | # (elsewhere) are most likely to arise from. |
| 18 | return _check_git_filemode(paths) |
| 19 | |
| 20 | |
| 21 | def _check_git_filemode(paths: Sequence[str]) -> int: |
Anthony Sottile | 8f61529 | 2022-01-15 19:24:05 -0500 | [diff] [blame] | 22 | seen: set[str] = set() |
Ville Skyttä | 391ae30 | 2021-01-08 17:36:55 +0200 | [diff] [blame] | 23 | for ls_file in git_ls_files(paths): |
| 24 | is_executable = any(b in EXECUTABLE_VALUES for b in ls_file.mode[-3:]) |
| 25 | if not is_executable and has_shebang(ls_file.filename): |
| 26 | _message(ls_file.filename) |
| 27 | seen.add(ls_file.filename) |
| 28 | |
| 29 | return int(bool(seen)) |
| 30 | |
| 31 | |
| 32 | def _message(path: str) -> None: |
| 33 | print( |
| 34 | f'{path}: has a shebang but is not marked executable!\n' |
| 35 | f' If it is supposed to be executable, try: ' |
| 36 | f'`chmod +x {shlex.quote(path)}`\n' |
MDW | bd70bc1 | 2022-05-26 17:31:24 +0200 | [diff] [blame] | 37 | f' If on Windows, you may also need to: ' |
| 38 | f'`git add --chmod=+x {shlex.quote(path)}`\n' |
Ville Skyttä | 391ae30 | 2021-01-08 17:36:55 +0200 | [diff] [blame] | 39 | f' If it not supposed to be executable, double-check its shebang ' |
| 40 | f'is wanted.\n', |
| 41 | file=sys.stderr, |
| 42 | ) |
| 43 | |
| 44 | |
Anthony Sottile | 8f61529 | 2022-01-15 19:24:05 -0500 | [diff] [blame] | 45 | def main(argv: Sequence[str] | None = None) -> int: |
Ville Skyttä | 391ae30 | 2021-01-08 17:36:55 +0200 | [diff] [blame] | 46 | parser = argparse.ArgumentParser(description=__doc__) |
| 47 | parser.add_argument('filenames', nargs='*') |
| 48 | args = parser.parse_args(argv) |
| 49 | |
| 50 | return check_shebangs(args.filenames) |
| 51 | |
| 52 | |
| 53 | if __name__ == '__main__': |
Anthony Sottile | 39ab2ed | 2021-10-23 13:23:50 -0400 | [diff] [blame] | 54 | raise SystemExit(main()) |