shoc-frontend-new/scripts/check_app_terraform_isolation.py

83 lines
2.2 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Fail when a change set mixes Terraform with deployable application files.
Workflow, docs, and gate-script changes may travel with either side.
"""
from __future__ import annotations
import argparse
import sys
APP_SCRIPT_NAMES = {
"scripts/deploy-web.sh",
}
APP_ROOTS = (
"src/",
"public/",
"pages/",
"config/",
)
APP_FILES = {
"index.html",
"vite.config.ts",
"vitest.config.ts",
}
def is_terraform_path(path: str) -> bool:
return path == "terraform" or path.startswith("terraform/")
def is_app_path(path: str) -> bool:
normalized = path.replace("\\", "/")
if normalized in APP_SCRIPT_NAMES or normalized in APP_FILES:
return True
if normalized in {"src", "public", "pages", "config"}:
return True
if normalized.startswith(APP_ROOTS):
return True
if normalized.startswith("tsconfig"):
return True
return normalized.startswith(".env")
def isolation_violation(paths: list[str]) -> tuple[list[str], list[str]] | None:
terraform_files = sorted({path for path in paths if is_terraform_path(path)})
app_files = sorted({path for path in paths if is_app_path(path)})
if terraform_files and app_files:
return terraform_files, app_files
return None
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
"paths",
nargs="*",
help="Changed paths. Omit and pass newline-separated paths on stdin.",
)
args = parser.parse_args()
paths = list(args.paths)
if not paths and not sys.stdin.isatty():
paths = [line.strip() for line in sys.stdin if line.strip()]
violation = isolation_violation(paths)
if violation is None:
print("PASS: application and Terraform changes are isolated")
return 0
terraform_files, app_files = violation
print("FAIL: do not mix deployable application files with terraform/", file=sys.stderr)
print("terraform:", file=sys.stderr)
for path in terraform_files:
print(f" {path}", file=sys.stderr)
print("application:", file=sys.stderr)
for path in app_files:
print(f" {path}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())