- index.html — 첫 페이지 전까지 「불러오는 중」(테마 바탕 · 저장 테마 · 시스템 어두움) · 20 s 뒤 느린 연결 안내 · favicon 요청 없앰 - tools/preview_up.py — 앱 · CAD 빌드 뒤 vite preview(포트 = 개발 포트 − 1000 · /api · /b07-cad 프록시) - CAD 빌드에 공용 글꼴 복사 — Tailwind 가 url 을 고치지 않아 dist/assets 에서 찾던 404 - 시험 test_555_boot_loader_preview.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
3.9 KiB
Python
105 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""빌드판 서버(555-2) — 앱 · CAD 를 빌드하고 `vite preview` 를 띄운다(폰 · 외부 접속용).
|
||
|
||
실행: ./venv/Scripts/python.exe tools/preview_up.py (빌드 + 띄움 · 수십 초)
|
||
./venv/Scripts/python.exe tools/preview_up.py --stop (끄기만)
|
||
|
||
- 개발 서버(vite)는 파일 수백 개를 하나씩 보내 느린 연결(Tailscale · 폰)에서 첫 화면이 늦다 —
|
||
빌드판은 묶은 파일 몇 개만 보낸다.
|
||
- 포트 = 개발 포트 − 1000(메인 5173 → 4173 · 서브 5181 → 4181) · 개발 포트 · API 포트는
|
||
환경변수(AISLO_VITE_PORT · AISLO_API_PORT) > `OWNERS.md` 이 폴더 줄 > 8000 · 5173.
|
||
- /api · /b07-cad 는 같은 폴더 백엔드로 넘김(`vite.config.ts` preview) — 백엔드는 따로 켜 둠.
|
||
- 빌드 산출물 = config/node_modules/.build · CAD dist(둘 다 git 밖) · 로그 tmp/preview.log.
|
||
- 다시 부르면 옛 것을 끄고 새로 빌드해 띄움(preview 는 띄울 때의 파일 목록을 기억).
|
||
"""
|
||
|
||
import os
|
||
import re
|
||
import subprocess
|
||
import sys
|
||
import time
|
||
import urllib.request
|
||
|
||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||
TMP = os.path.join(ROOT, "tmp")
|
||
PID_FILE = os.path.join(TMP, "preview.pid")
|
||
LOG = os.path.join(TMP, "preview.log")
|
||
VITE = os.path.join(ROOT, "config", "node_modules", "vite", "bin", "vite.js")
|
||
|
||
|
||
def owners_ports() -> tuple[int, int] | None:
|
||
"""OWNERS.md 표에서 이 폴더 줄의 (API, vite) 포트 — 짝 = `.claude/dev_up.py`."""
|
||
try:
|
||
with open(os.path.join(ROOT, "OWNERS.md"), encoding="utf-8-sig") as owners:
|
||
lines = owners.read().splitlines()
|
||
except OSError:
|
||
return None
|
||
here = os.path.normcase(os.path.normpath(ROOT))
|
||
for line in lines:
|
||
cells = [cell.strip().strip("`") for cell in line.strip().strip("|").split("|")]
|
||
if len(cells) >= 5 and os.path.normcase(os.path.normpath(cells[2])) == here:
|
||
ports = re.findall(r"\d{4,5}", cells[4])
|
||
if len(ports) == 2:
|
||
return int(ports[0]), int(ports[1])
|
||
return None
|
||
|
||
|
||
def stop() -> None:
|
||
try:
|
||
with open(PID_FILE, encoding="utf-8") as f:
|
||
pid = int(f.read().strip())
|
||
except (OSError, ValueError):
|
||
return
|
||
subprocess.run(["taskkill", "/T", "/F", "/PID", str(pid)], capture_output=True, check=False)
|
||
os.remove(PID_FILE)
|
||
|
||
|
||
def run(cmd: list[str], env: dict[str, str]) -> None:
|
||
print("RUN", " ".join(cmd), flush=True)
|
||
subprocess.run(cmd, cwd=ROOT, env=env, check=True, shell=os.name == "nt" and cmd[0] == "npm")
|
||
|
||
|
||
def main() -> int:
|
||
os.makedirs(TMP, exist_ok=True)
|
||
stop()
|
||
if "--stop" in sys.argv:
|
||
print("PREVIEW=stopped")
|
||
return 0
|
||
|
||
api, vite = owners_ports() or (8000, 5173)
|
||
api = int(os.environ.get("AISLO_API_PORT") or api)
|
||
vite = int(os.environ.get("AISLO_VITE_PORT") or vite)
|
||
port = vite - 1000
|
||
env = {**os.environ, "AISLO_API_PORT": str(api), "FRONTEND_DEV_PORT": str(vite)}
|
||
|
||
run(["node", VITE, "build", "--configLoader", "runner"], env)
|
||
run(["npm", "run", "build:b07-cad"], env)
|
||
|
||
log = open(LOG, "ab") # noqa: SIM115 — 자식 프로세스가 물려받는다
|
||
flags = subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP
|
||
process = subprocess.Popen(
|
||
["node", VITE, "preview", "--configLoader", "runner"],
|
||
cwd=ROOT,
|
||
env=env,
|
||
stdout=log,
|
||
stderr=subprocess.STDOUT,
|
||
creationflags=flags,
|
||
)
|
||
with open(PID_FILE, "w", encoding="utf-8") as f:
|
||
f.write(str(process.pid))
|
||
|
||
url = f"http://127.0.0.1:{port}/"
|
||
for _ in range(60):
|
||
try:
|
||
urllib.request.urlopen(url, timeout=2)
|
||
print(f"PREVIEW=up {url} (API {api}) · 로그 {LOG}")
|
||
return 0
|
||
except OSError:
|
||
time.sleep(0.5)
|
||
print(f"PREVIEW=TIMEOUT — {LOG} 확인")
|
||
return 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|