97 lines
3.1 KiB
Python
Executable File
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())
|