blob: 45a9f81ad1ae947f9d75c9844d6d05b2de25a987 [file] [edit]
#!/usr/bin/env python3
# Copyright 2026 Marc-Antoine Ruel. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
"""Lint git-tracked files for unexpected binary data and executable files."""
import os
import subprocess
import sys
ALLOWED_BINARY_EXTENSIONS = {
".aac",
".avif",
".br",
".flac",
".gif",
".ico",
".jar",
".jpg",
".mov",
".mp3",
".mp4",
".ogg",
".pdf",
".png",
".svg",
".syso",
".wasm",
".wav",
".webm",
".webp",
".zst",
}
ALLOWED_EXECUTABLE_EXTENSIONS = {".desktop"}
def is_binary(path: str) -> bool:
"""Return whether path contains a null byte near its start."""
if os.path.islink(path) or not os.path.isfile(path):
return False
with open(path, "rb") as f:
return b"\0" in f.read(1024)
def has_executable_preamble(path: str) -> bool:
"""Return whether path starts with a supported executable declaration."""
with open(path, "rb") as f:
first_line = f.readline(256)
# Go generator files use this shell/Go polyglot preamble to run with go run.
return first_line.startswith((b"#!/", b"///usr/bin/true; exec /usr/bin/env go run "))
def tracked_files() -> list[tuple[str, bool]]:
"""Return tracked paths and Git executable modes, including paths with whitespace."""
raw = subprocess.check_output(("git", "ls-files", "--stage", "-z"))
files: list[tuple[str, bool]] = []
for record in raw.split(b"\0"):
if record:
metadata, path = record.split(b"\t", 1)
mode = metadata.split(b" ", 1)[0]
files.append((os.fsdecode(path), mode == b"100755"))
return files
def main() -> int:
"""Report tracked files that are binary or executable without a declaration."""
unexpected_binaries: list[str] = []
unexpected_executables: list[str] = []
for path, executable in tracked_files():
if is_binary(path) and os.path.splitext(path)[1].lower() not in ALLOWED_BINARY_EXTENSIONS:
unexpected_binaries.append(path)
if (
executable
and os.path.isfile(path)
and not os.path.islink(path)
and os.path.splitext(path)[1].lower() not in ALLOWED_EXECUTABLE_EXTENSIONS
and not has_executable_preamble(path)
):
unexpected_executables.append(path)
if unexpected_binaries:
print("Unexpected binary files:", file=sys.stderr)
print(*(f" {path}" for path in unexpected_binaries), sep="\n", file=sys.stderr)
if unexpected_executables:
print(
"Executable files without a shebang, Go launcher preamble, or allowed extension:",
file=sys.stderr,
)
print(*(f" {path}" for path in unexpected_executables), sep="\n", file=sys.stderr)
return int(bool(unexpected_binaries or unexpected_executables))
if __name__ == "__main__":
sys.exit(main())