bootstrap_local.py
3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
from __future__ import annotations
import argparse
import os
import shutil
import subprocess
import sys
from pathlib import Path
from services.shared.config.base import PROJECT_ROOT
REQUIREMENTS_FILE = PROJECT_ROOT / "infra" / "python" / "requirements-local.txt"
FRONTEND_DIR = PROJECT_ROOT / "apps" / "web_ui"
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Bootstrap BettaFish for pure local development on the current machine.",
)
parser.add_argument(
"--skip-pip-tools-upgrade",
action="store_true",
help="Skip upgrading pip/setuptools/wheel before installing dependencies.",
)
parser.add_argument(
"--skip-playwright",
action="store_true",
help="Skip installing the Playwright Chromium browser.",
)
parser.add_argument(
"--skip-frontend",
action="store_true",
help="Skip installing frontend dependencies in apps/web_ui.",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print the commands that would run without executing them.",
)
return parser.parse_args()
def run_command(command: list[str], *, dry_run: bool) -> int:
rendered = " ".join(command)
print(f"[bootstrap] {rendered}")
if dry_run:
return 0
result = subprocess.run(command, cwd=PROJECT_ROOT, env=build_subprocess_env())
return result.returncode
def build_subprocess_env() -> dict[str, str]:
env = os.environ.copy()
env.setdefault("PYTHONIOENCODING", "utf-8")
env.setdefault("PYTHONUTF8", "1")
env.setdefault("PYTHONUNBUFFERED", "1")
return env
def ensure_prerequisites(skip_frontend: bool) -> int:
if not REQUIREMENTS_FILE.exists():
print(f"[bootstrap] Missing requirements file: {REQUIREMENTS_FILE}", file=sys.stderr)
return 1
if not skip_frontend and shutil.which("npm") is None:
print(
"[bootstrap] npm was not found. Install Node.js first, or rerun with --skip-frontend.",
file=sys.stderr,
)
return 1
return 0
def print_next_steps() -> None:
print()
print("[bootstrap] Next steps:")
print("1. Copy .env.local.example to .env.local and fill in your local settings.")
print("2. Ensure local PostgreSQL is available at 127.0.0.1:5432 (or adjust .env.local).")
print("3. Start the project with: python -m scripts.dev.start_local --build-frontend")
def main() -> int:
args = parse_args()
prerequisite_code = ensure_prerequisites(skip_frontend=args.skip_frontend)
if prerequisite_code != 0:
return prerequisite_code
if not args.skip_pip_tools_upgrade:
code = run_command(
[sys.executable, "-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel"],
dry_run=args.dry_run,
)
if code != 0:
return code
code = run_command(
[sys.executable, "-m", "pip", "install", "-r", str(REQUIREMENTS_FILE)],
dry_run=args.dry_run,
)
if code != 0:
return code
if not args.skip_playwright:
code = run_command(
[sys.executable, "-m", "playwright", "install", "chromium"],
dry_run=args.dry_run,
)
if code != 0:
return code
if not args.skip_frontend:
code = run_command(
["npm", "--prefix", str(FRONTEND_DIR.relative_to(PROJECT_ROOT)), "install"],
dry_run=args.dry_run,
)
if code != 0:
return code
print_next_steps()
return 0
if __name__ == "__main__":
raise SystemExit(main())