Initialer Import des AssetManagers
This commit is contained in:
Executable
+148
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Lightweight release checks for AssetManager.
|
||||
|
||||
The checks are intentionally read-only and do not require a running database.
|
||||
A failure exits with status 1 so the script can be used in CI or a release job.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import compileall
|
||||
import importlib.util
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
APP_DIR = ROOT / "app"
|
||||
|
||||
|
||||
def fail(message: str) -> None:
|
||||
print(f"[FAIL] {message}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def ok(message: str) -> None:
|
||||
print(f"[ OK ] {message}")
|
||||
|
||||
|
||||
def check_version() -> None:
|
||||
path = APP_DIR / "version.py"
|
||||
spec = importlib.util.spec_from_file_location("assetmanager_release_version", path)
|
||||
if spec is None or spec.loader is None:
|
||||
fail("app/version.py could not be loaded")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
app_version = getattr(module, "APP_VERSION", None)
|
||||
conventional = getattr(module, "__version__", None)
|
||||
if not isinstance(app_version, str) or not app_version.strip():
|
||||
fail("APP_VERSION is missing or empty")
|
||||
if conventional != app_version:
|
||||
fail("__version__ must be an alias of APP_VERSION")
|
||||
version_file = (ROOT / "VERSION").read_text(encoding="utf-8").strip()
|
||||
if version_file != app_version:
|
||||
fail(f"VERSION ({version_file}) and app/version.py ({app_version}) differ")
|
||||
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
if app_version not in readme:
|
||||
fail(f"README.md does not contain current version {app_version}")
|
||||
ok(f"Version exports are valid ({app_version})")
|
||||
|
||||
|
||||
def check_compile() -> None:
|
||||
if not compileall.compile_dir(str(APP_DIR), quiet=1, force=True):
|
||||
fail("Python compilation failed")
|
||||
ok("Python sources compile")
|
||||
|
||||
|
||||
def check_translations() -> None:
|
||||
script = ROOT / "tools" / "check_translations.py"
|
||||
result = subprocess.run([sys.executable, str(script)], cwd=ROOT, text=True)
|
||||
if result.returncode:
|
||||
fail("Translation check failed")
|
||||
ok("Translation check passed")
|
||||
|
||||
|
||||
def check_templates() -> None:
|
||||
try:
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
except ImportError as exc:
|
||||
fail(f"Jinja2 is unavailable: {exc}")
|
||||
template_dir = APP_DIR / "templates"
|
||||
env = Environment(loader=FileSystemLoader(str(template_dir)))
|
||||
failures: list[str] = []
|
||||
for path in sorted(template_dir.rglob("*.html")):
|
||||
name = path.relative_to(template_dir).as_posix()
|
||||
try:
|
||||
env.get_template(name)
|
||||
except Exception as exc: # syntax/load validation
|
||||
failures.append(f"{name}: {exc}")
|
||||
if failures:
|
||||
fail("Template validation failed:\n " + "\n ".join(failures))
|
||||
ok("Templates load without syntax errors")
|
||||
|
||||
|
||||
|
||||
def check_job_definition_frontend() -> None:
|
||||
form_path = APP_DIR / "templates" / "job_definition_form.html"
|
||||
content = form_path.read_text(encoding="utf-8")
|
||||
required_frontend = [
|
||||
'id="insert-job-test-script"',
|
||||
'id="job-parameters-json"',
|
||||
'id="add-job-parameter"',
|
||||
'job-enabled-top',
|
||||
'jobdefs.own_script',
|
||||
]
|
||||
missing = [item for item in required_frontend if item not in content]
|
||||
if missing:
|
||||
fail("Job-definition frontend validation failed; missing: " + ", ".join(missing))
|
||||
|
||||
template_dir = APP_DIR / "templates" / "jobs"
|
||||
required_templates = {
|
||||
"powershell_wrapper.ps1": ["{{JobId}}", "{{CallbackUrl}}", "Eigene Befehle hier einfügen"],
|
||||
"python_wrapper.py.txt": ["{{JobId}}", "{{CallbackUrl}}", "Eigene Befehle hier einfügen"],
|
||||
"bash_wrapper.sh": ["{{JobId}}", "{{CallbackUrl}}", "${status}", "${code}"],
|
||||
"cmd_wrapper.cmd": ["{{JobId}}", "{{CallbackUrl}}", "Eigene Befehle hier einfügen"],
|
||||
"powershell_test.txt": ["{{AssetName}}", "{{Hostname}}"],
|
||||
"python_test.txt": ["{{AssetName}}", "{{Hostname}}"],
|
||||
"bash_test.txt": ["{{AssetName}}", "{{Hostname}}"],
|
||||
"cmd_test.txt": ["{{AssetName}}", "{{Hostname}}"],
|
||||
}
|
||||
failures: list[str] = []
|
||||
for filename, markers in required_templates.items():
|
||||
path = template_dir / filename
|
||||
if not path.is_file():
|
||||
failures.append(f"missing file: {filename}")
|
||||
continue
|
||||
template_content = path.read_text(encoding="utf-8")
|
||||
for marker in markers:
|
||||
if marker not in template_content:
|
||||
failures.append(f"{filename}: missing {marker}")
|
||||
if failures:
|
||||
fail("Job script template validation failed:\n " + "\n ".join(failures))
|
||||
ok("Job-definition backend generator safeguards are present")
|
||||
|
||||
|
||||
def check_fastapi_import() -> None:
|
||||
# Import only. Do not start Uvicorn and do not connect to external services.
|
||||
sys.path.insert(0, str(ROOT))
|
||||
os.environ.setdefault("DATABASE_URL", "sqlite+pysqlite:///:memory:")
|
||||
try:
|
||||
from app.main import app # noqa: F401
|
||||
except Exception as exc:
|
||||
fail(f"FastAPI application import failed: {type(exc).__name__}: {exc}")
|
||||
ok("FastAPI application imports")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print("AssetManager release validation")
|
||||
check_version()
|
||||
check_compile()
|
||||
check_translations()
|
||||
check_templates()
|
||||
check_job_definition_frontend()
|
||||
check_fastapi_import()
|
||||
print("All release checks passed.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user