Files
KNELCloudronPackages/scripts/lint_repo.py

97 lines
3.1 KiB
Python
Executable File

#!/usr/bin/env python3
"""Basic sanity checks for Cloudron packaging scaffolds."""
import json
import os
import pathlib
import sys
from typing import Dict, List
ROOT = pathlib.Path(__file__).resolve().parents[1]
EXPECTED_BASE = os.environ.get("CLOUDRON_BASE", "cloudron/base:5.0.0")
def find_apps(apps_dir: pathlib.Path) -> List[pathlib.Path]:
return sorted(p for p in apps_dir.iterdir() if p.is_dir())
def check_manifest(app_dir: pathlib.Path) -> List[str]:
issues: List[str] = []
manifest = app_dir / "CloudronManifest.json"
if not manifest.exists():
issues.append("missing CloudronManifest.json")
return issues
try:
data = json.loads(manifest.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
issues.append(f"manifest JSON invalid: {exc}")
return issues
for key in ("id", "title", "version"):
if not data.get(key):
issues.append(f"manifest missing {key}")
tagline = data.get("tagline", "")
description = data.get("description", "")
if "TODO" in tagline:
issues.append("manifest tagline still contains TODO placeholder")
if "TODO" in description:
issues.append("manifest description still contains TODO placeholder")
return issues
def check_dockerfile(app_dir: pathlib.Path) -> List[str]:
issues: List[str] = []
dockerfile = app_dir / "Dockerfile"
if not dockerfile.exists():
issues.append("missing Dockerfile")
return issues
first_from = None
for line in dockerfile.read_text(encoding="utf-8").splitlines():
line = line.strip()
if line.startswith("FROM "):
first_from = line.split()[1]
break
if first_from != EXPECTED_BASE:
issues.append(f"Dockerfile base image '{first_from}' != '{EXPECTED_BASE}'")
return issues
def check_start_script(app_dir: pathlib.Path) -> List[str]:
issues: List[str] = []
start = app_dir / "start.sh"
if not start.exists():
issues.append("missing start.sh")
return issues
mode = start.stat().st_mode
if not mode & 0o111:
issues.append("start.sh is not executable")
if "Replace start.sh" in start.read_text(encoding="utf-8"):
issues.append("start.sh still contains placeholder command")
return issues
def main() -> int:
apps_dir = ROOT / "apps"
if not apps_dir.exists():
print("No apps directory present", file=sys.stderr)
return 1
failures = 0
for app_dir in find_apps(apps_dir):
app_issues: List[str] = []
app_issues.extend(check_manifest(app_dir))
app_issues.extend(check_dockerfile(app_dir))
app_issues.extend(check_start_script(app_dir))
if app_issues:
failures += 1
print(f"[FAIL] {app_dir.relative_to(ROOT)}")
for issue in app_issues:
print(f" - {issue}")
if failures:
print(f"\n{failures} app(s) require updates", file=sys.stderr)
return 2
print("All apps passed lint checks")
return 0
if __name__ == "__main__":
sys.exit(main())