Lint a Dockerfile with a Mock Hadolint in Python
A lightweight Python script that simulates hadolint by scanning Dockerfile text for common lint rules and printing violations.
Python code
48 linesimport subprocess
import tempfile
from pathlib import Path
def lint_dockerfile(content: str) -> list[str]:
"""Mock hadolint by checking a few rules and returning violations."""
violations = []
lines = content.splitlines()
for idx, line in enumerate(lines, start=1):
stripped = line.strip()
if not stripped or stripped.startswith("#"):
continue
if stripped.upper().startswith("FROM"):
if ":" not in stripped and "@" not in stripped:
violations.append(f"DL3006: Always tag the version of an image explicitly (line {idx})")
if stripped.upper().startswith("RUN") and ("curl" in stripped.lower() or "wget" in stripped.lower()):
if "--no-cache" not in stripped.lower():
violations.append(f"DL3009: Delete the apt-get lists after installing something (line {idx})")
if stripped.upper().startswith("CMD") and not stripped.startswith("CMD ["):
violations.append(f"DL3025: Use JSON array syntax for CMD (line {idx})")
return violations
if __name__ == "__main__":
dockerfile_content = """
FROM ubuntu
RUN apt-get update && apt-get install -y curl
CMD echo "hello"
"""
with tempfile.NamedTemporaryFile(mode="w", suffix=".dockerfile", delete=False) as f:
f.write(dockerfile_content)
temp_path = Path(f.name)
print(f"Linting {temp_path.name}:")
result = lint_dockerfile(dockerfile_content)
if result:
for violation in result:
print(violation)
else:
print("No violations found")
temp_path.unlink(missing_ok=True)
Output
Linting tmpXXXXXXXX.dockerfile:
DL3006: Always tag the version of an image explicitly (line 2)
DL3009: Delete the apt-get lists after installing something (line 3)
DL3025: Use JSON array syntax for CMD (line 4)
How it works
This mock linter parses the Dockerfile line by line, skipping blanks and comments, then checks for specific patterns: untagged FROM images, RUN commands with curl/wget lacking --no-cache, and CMD instructions not using JSON array syntax. Each rule appends a violation string with a line number. The if __name__ == "__main__" block writes the Dockerfile to a temporary file to simulate a real file operation, then prints violations or a clean message. This gives you a lightweight, dependency-free way to test linting logic locally or in CI without installing hadolint.
Common mistakes
- Assuming all violations are caught — this mock covers only a few rules, not the full hadolint suite.
- Forgetting that `--no-cache` is case‑sensitive in the check (lowercased only).
- Not handling quoted or multiline RUN commands correctly.
- Skipping the temporary file cleanup if an exception occurs.
Variations
- Use `requests` to call the hadolint Docker image via a shell command instead of a pure mock.
- Parse the Dockerfile with a parser library like `dockerfile-parse` for more accurate rules.
Real-world use cases
- Running a pre‑commit hook that quickly flags common Dockerfile mistakes without installing external tools.
- Validating Dockerfiles in a CI pipeline as a lightweight smoke test before full hadolint.
- Teaching Docker best practices in a workshop or tutorial with an illustrated mock example.
Sponsored
More from Modern tooling
- Build a Recipe Runner Mock in Python easy
- Build a Textual TUI App Skeleton in Python easy
- Configure ruff linter rules in pyproject.toml with Python easy
- Data Conversion Helper Functions in Python easy
- How to Bind and Mock structlog Context in Python medium
- How to Build a Chainable Filter Helper in Python easy
Keep learning
Related tutorials and quizzes for this topic.