1import os
2import subprocess
3import sys
4from importlib.util import find_spec
5from pathlib import Path
6
7from plain.cli.print import print_event
8
9from ..services import Services
10
11try:
12 import tomllib
13except ModuleNotFoundError:
14 import tomli as tomllib
15
16import click
17
18
19def install_git_hook():
20 hook_path = os.path.join(".git", "hooks", "pre-commit")
21 if os.path.exists(hook_path):
22 print("pre-commit hook already exists")
23 else:
24 with open(hook_path, "w") as f:
25 f.write(
26 """#!/bin/sh
27plain pre-commit"""
28 )
29 os.chmod(hook_path, 0o755)
30 print("pre-commit hook installed")
31
32
33@click.command()
34@click.option("--install", is_flag=True)
35def cli(install):
36 """Git pre-commit checks"""
37 if install:
38 install_git_hook()
39 return
40
41 pyproject = Path("pyproject.toml")
42
43 with Services():
44 if pyproject.exists():
45 with open(pyproject, "rb") as f:
46 pyproject = tomllib.load(f)
47 for name, data in (
48 pyproject.get("tool", {})
49 .get("plain", {})
50 .get("pre-commit", {})
51 .get("run", {})
52 ).items():
53 cmd = data["cmd"]
54 print_event(f"Custom: {name} -> {cmd}")
55 result = subprocess.run(cmd, shell=True)
56 if result.returncode != 0:
57 sys.exit(result.returncode)
58
59 # Run this first since it's probably the most likely to fail
60 if find_spec("plain.code"):
61 check_short("Running plain code checks", "plain", "code", "check")
62
63 if Path("poetry.lock").exists():
64 check_short("Checking poetry.lock", "poetry", "check", "--lock")
65
66 if plain_db_connected():
67 check_short(
68 "Running preflight checks",
69 "plain",
70 "preflight",
71 "--database",
72 "default",
73 )
74 check_short("Checking Plain migrations", "plain", "migrate", "--check")
75 check_short(
76 "Checking for Plain models missing migrations",
77 "plain",
78 "makemigrations",
79 "--dry-run",
80 "--check",
81 )
82 else:
83 check_short("Running Plain checks (without database)", "plain", "preflight")
84 click.secho("--> Skipping migration checks", bold=True, fg="yellow")
85
86 print_event("Running plain compile")
87 result = subprocess.run(["plain", "compile"])
88 if result.returncode != 0:
89 sys.exit(result.returncode)
90
91 if find_spec("plain.pytest"):
92 print_event("Running tests")
93 result = subprocess.run(["plain", "test"])
94 if result.returncode != 0:
95 sys.exit(result.returncode)
96
97
98def plain_db_connected():
99 result = subprocess.run(
100 [
101 "plain",
102 "models",
103 "showmigrations",
104 "--skip-checks",
105 ],
106 stdout=subprocess.DEVNULL,
107 stderr=subprocess.DEVNULL,
108 )
109 return result.returncode == 0
110
111
112def check_short(message, *args):
113 print_event(message, newline=False)
114 result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
115 if result.returncode != 0:
116 click.secho("โ", fg="red")
117 click.secho(result.stdout.decode("utf-8"))
118 sys.exit(1)
119 else:
120 click.secho("โ", fg="green")