Launch multiple honcho files depending on the context

This commit is contained in:
2025-03-04 14:48:44 +01:00
parent 87f790a044
commit 6b27a97e7b
11 changed files with 66 additions and 48 deletions

View File

@ -9,30 +9,37 @@ import psutil
from sith import settings
def get_pid() -> int | None:
def get_pid_file(procfile: Path) -> Path:
"""Get the PID file associated with a procfile"""
return settings.BASE_DIR / procfile.with_suffix(f"{procfile.suffix}.pid")
def get_pid(procfile: Path) -> int | None:
"""Read the PID file to get the currently running composer if it exists"""
if not settings.COMPOSER_PID_PATH.exists():
file = get_pid_file(procfile)
if not file.exists():
return None
with open(settings.COMPOSER_PID_PATH, "r", encoding="utf8") as f:
with open(file, "r", encoding="utf8") as f:
return int(f.read())
def write_pid(pid: int):
def write_pid(procfile: Path, pid: int):
"""Write currently running composer pid in PID file"""
if not settings.COMPOSER_PID_PATH.exists():
settings.COMPOSER_PID_PATH.parent.mkdir(parents=True, exist_ok=True)
with open(settings.COMPOSER_PID_PATH, "w", encoding="utf8") as f:
file = get_pid_file(procfile)
if not file.exists():
file.parent.mkdir(parents=True, exist_ok=True)
with open(file, "w", encoding="utf8") as f:
_ = f.write(str(pid))
def delete_pid():
def delete_pid(procfile: Path):
"""Delete PID file for cleanup"""
settings.COMPOSER_PID_PATH.unlink(missing_ok=True)
get_pid_file(procfile).unlink(missing_ok=True)
def is_composer_running() -> bool:
def is_composer_running(procfile: Path) -> bool:
"""Check if the process in the PID file is running"""
pid = get_pid()
pid = get_pid(procfile)
if pid is None:
return False
try:
@ -45,25 +52,29 @@ def start_composer(procfile: Path):
"""Starts the composer and stores the PID as an environment variable
This allows for running smoothly with the django reloader
"""
if is_composer_running():
logging.info(f"Composer is already running with pid {get_pid()}")
if is_composer_running(procfile):
logging.info(
f"If this is a mistake, please delete {settings.COMPOSER_PID_PATH} and restart the process"
f"Composer for {procfile} is already running with pid {get_pid(procfile)}"
)
logging.info(
f"If this is a mistake, please delete {get_pid_file(procfile)} and restart the process"
)
return
process = subprocess.Popen(
[sys.executable, "-m", "honcho", "-f", str(procfile), "start"],
)
write_pid(process.pid)
write_pid(procfile, process.pid)
def stop_composer():
def stop_composer(procfile: Path):
"""Stops the composer if it was started before"""
if is_composer_running():
process = psutil.Process(get_pid())
if is_composer_running(procfile):
process = psutil.Process(get_pid(procfile))
if process.parent() != psutil.Process():
logging.info("Currently running composer is controlled by another process")
logging.info(
f"Currently running composer for {procfile} is controlled by another process"
)
return
process.send_signal(signal.SIGTERM)
_ = process.wait()
delete_pid()
delete_pid(procfile)

View File

@ -3,7 +3,7 @@ import atexit
import pytest
from .composer import start_composer, stop_composer
from .settings import PROCFILE_MINIMAL
from .settings import PROCFILE_SERVICE
# it's the first hook loaded by pytest and can only
# be defined in a proper pytest plugin
@ -15,6 +15,6 @@ from .settings import PROCFILE_MINIMAL
@pytest.hookimpl(tryfirst=True)
def pytest_load_initial_conftests(early_config, parser, args):
"""Hook that loads the composer before the pytest-django plugin"""
if PROCFILE_MINIMAL is not None:
start_composer(PROCFILE_MINIMAL)
_ = atexit.register(stop_composer)
if PROCFILE_SERVICE is not None:
start_composer(PROCFILE_SERVICE)
_ = atexit.register(stop_composer, procfile=PROCFILE_SERVICE)

View File

@ -64,11 +64,8 @@ def optional_file_parser(value: str) -> Path | None:
BASE_DIR = Path(__file__).parent.parent.resolve()
# Composer settings
PROCFILE_FULL = env.optional_file("PROCFILE_FULL", None)
PROCFILE_MINIMAL = env.optional_file("PROCFILE_MINIMAL", None)
## File path used to avoid running the composer multiple times at the same time
COMPOSER_PID_PATH = env.path("COMPOSER_PID_PATH", BASE_DIR / "composer.pid")
PROCFILE_STATIC = env.optional_file("PROCFILE_STATIC", None)
PROCFILE_SERVICE = env.optional_file("PROCFILE_SERVICE", None)
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/