Plain is headed towards 1.0! Subscribe for development updates โ†’

  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")