1from __future__ import annotations
 2
 3import os
 4import subprocess
 5import tomllib
 6from importlib.util import find_spec
 7from pathlib import Path
 8
 9import click
10
11from plain.cli.print import print_event
12from plain.cli.runtime import common_command
13
14
15def plain_db_connected() -> bool:
16    result = subprocess.run(
17        ["plain", "migrations", "list"],
18        stdout=subprocess.DEVNULL,
19        stderr=subprocess.DEVNULL,
20    )
21    return result.returncode == 0
22
23
24def check_short(message: str, *args: str) -> None:
25    print_event(message, newline=False)
26    env = {**os.environ, "FORCE_COLOR": "1"}
27    result = subprocess.run(
28        args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env
29    )
30    if result.returncode != 0:
31        click.secho("✘", fg="red")
32        click.secho(result.stdout.decode("utf-8"))
33        raise SystemExit(1)
34    else:
35        click.secho("✔", fg="green")
36
37
38def run_custom_checks() -> None:
39    """Run custom checks from [tool.plain.check.run] in pyproject.toml."""
40    pyproject_path = Path("pyproject.toml")
41
42    if not pyproject_path.exists():
43        return
44
45    with open(pyproject_path, "rb") as f:
46        pyproject = tomllib.load(f)
47
48    for name, data in (
49        pyproject.get("tool", {}).get("plain", {}).get("check", {}).get("run", {})
50    ).items():
51        cmd = data["cmd"]
52        print_event(f"Custom: {cmd}")
53        result = subprocess.run(cmd, shell=True)
54        if result.returncode != 0:
55            raise SystemExit(result.returncode)
56
57
58def run_core_checks(*, skip_test: bool = False) -> None:
59    """Run core validation checks: custom, code, preflight, migrations, tests."""
60
61    run_custom_checks()
62
63    if find_spec("plain.code"):
64        check_short("plain code check", "plain", "code", "check")
65
66    if plain_db_connected():
67        check_short("plain preflight", "plain", "preflight", "--quiet")
68        check_short("plain migrate --check", "plain", "migrate", "--check")
69        check_short(
70            "plain makemigrations --dry-run --check",
71            "plain",
72            "makemigrations",
73            "--dry-run",
74            "--check",
75        )
76    else:
77        check_short("plain preflight", "plain", "preflight", "--quiet")
78        click.secho("--> Skipping migration checks", bold=True, fg="yellow")
79
80    if not skip_test and find_spec("plain.pytest"):
81        print_event("plain test")
82        result = subprocess.run(["plain", "test"])
83        if result.returncode != 0:
84            raise SystemExit(result.returncode)
85
86
87@common_command
88@click.command()
89@click.option("--skip-test", is_flag=True, help="Skip running tests")
90def check(skip_test: bool) -> None:
91    """Run core validation checks"""
92    run_core_checks(skip_test=skip_test)