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)