Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions scripts/docker_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,19 @@ def has_shell_shebang(root: Path, relative_path: str) -> bool:
"""Report whether a tracked file's shebang directly names bash or sh.

Never follows a tracked symlink: `is_symlink()` uses `lstat`, keeping the target unreached.
A read failure raises `CommandFailed` instead of returning `False`.
That keeps a tracked file this cannot open from silently dropping out of the target list.
Comment thread
Copilot marked this conversation as resolved.
"""
path = root / relative_path
if path.is_symlink():
return False
try:
if path.is_symlink():
return False
with path.open("rb") as handle:
first_line = handle.readline(256)
except OSError:
return False
except OSError as error:
raise CommandFailed(
f"target discovery failed: could not read {relative_path}: {error}"
) from error
try:
text = first_line.decode("utf-8").rstrip("\n")
except UnicodeDecodeError:
Expand Down
8 changes: 8 additions & 0 deletions scripts/tests/test_docker_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ def test_has_shell_shebang_reports_false_for_a_symlink_without_reading_it(self)
with mock.patch.object(Path, "open", side_effect=AssertionError("symlink target opened")):
self.assertFalse(docker_lint.has_shell_shebang(self.root, "ops/evil-symlink"))

def test_has_shell_shebang_raises_rather_than_swallowing_a_read_failure(self) -> None:
self.track("ops/unreadable")
with (
mock.patch.object(Path, "open", side_effect=PermissionError("denied")),
self.assertRaisesRegex(docker_lint.CommandFailed, "could not read"),
):
docker_lint.has_shell_shebang(self.root, "ops/unreadable")

def test_extensionless_untracked_shebang_script_is_not_picked_up(self) -> None:
path = self.root / "ops" / "vps-backup-pull"
path.parent.mkdir(parents=True, exist_ok=True)
Expand Down