133 lines
4.8 KiB
Python
133 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""Vignette 배포 파이프라인 관리 도구.
|
|
|
|
소스 SSOT = Forgejo git.chanpaca.net (master)
|
|
배포 대상 = NAS Production /volume1/docker/vignette-prod (docker-compose.nas.yml)
|
|
이 PC = 개발 전용
|
|
|
|
역할:
|
|
- Forgejo master == 로컬 master == 배포 기준선 무결성 확인
|
|
- github(origin, private 백업 미러) 존재 확인
|
|
- NAS production 컨테이너/이미지 compose ref 조회 (read-only)
|
|
- 배포 전 검증 게이트(SSOT checker) 존재 확인
|
|
|
|
※ 실제 NAS 이미지 빌드·주입·compose up은 소유자 승인 후 별도 실행.
|
|
이 도구는 배포 파이프라인의 관리/점검(read-only)을 담당한다.
|
|
|
|
사용법:
|
|
python -X utf8 scripts/vignette-pipeline.py --check
|
|
python -X utf8 scripts/vignette-pipeline.py --config
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
SSOT_DASHBOARD = REPO_ROOT / "docs" / "dev_dashboard.html"
|
|
FORGEJO_SSOT_BRANCH = "master"
|
|
FORGEJO_REMOTE = "forgejo"
|
|
GITHUB_REMOTE = "origin"
|
|
NAS_COMPOSE_HOST = "yunchan-nas" # ssh config Host (192.168.0.38, user yunchan)
|
|
NAS_COMPOSE_PATH = "/volume1/docker/vignette-prod/docker-compose.nas.yml"
|
|
NAS_COMPOSE_FILE = "docker-compose.nas.yml"
|
|
NAS_PROJECT = "vignette-prod"
|
|
|
|
|
|
def _run(cmd: list[str], cwd: Path | None = None, ok_codes=(0,)) -> str:
|
|
r = subprocess.run(cmd, cwd=cwd or REPO_ROOT, capture_output=True, text=True)
|
|
if r.returncode not in ok_codes:
|
|
raise RuntimeError(f"cmd failed ({r.returncode}): {' '.join(cmd)}\n{r.stderr[-800:]}")
|
|
return r.stdout
|
|
|
|
|
|
def _git(*args: str) -> str:
|
|
return _run(["git", *args]).strip()
|
|
|
|
|
|
def check(forgejo_available: bool = True) -> int:
|
|
problems = 0
|
|
|
|
def ok(msg: str) -> None:
|
|
print(f" [PASS] {msg}")
|
|
|
|
def warn(msg: str) -> None:
|
|
nonlocal problems
|
|
problems += 1
|
|
print(f" [FAIL] {msg}")
|
|
|
|
print("== 배포 파이프라인 점검 ==")
|
|
|
|
# 1) Forgejo SSOT == 로컬 master
|
|
local_master = _git("rev-parse", FORGEJO_SSOT_BRANCH)
|
|
ok(f"로컬 {FORGEJO_SSOT_BRANCH} = {local_master[:12]}")
|
|
|
|
if forgejo_available:
|
|
remote_master = _git("ls-remote", f"{FORGEJO_REMOTE}", "refs/heads/master").split("\t")[0]
|
|
if remote_master and remote_master == local_master:
|
|
ok(f"Forgejo master = {remote_master[:12]} == 로컬 (배포 기준선 일치)")
|
|
else:
|
|
warn(f"Forgejo master {remote_master[:12] if remote_master else 'MISSING'} != 로컬 {local_master[:12]}. push 필요.")
|
|
else:
|
|
warn("Forgejo 원격/접근 없음 — 이관 미완료")
|
|
|
|
# 2) remote 목록
|
|
remotes = _git("remote", "-v")
|
|
if github := [f.split()[1] for f in remotes.splitlines() if f.startswith("origin\t")]:
|
|
ok(f"github origin(private 백업) = {github[0]}")
|
|
else:
|
|
warn("github origin 없음")
|
|
if f"{FORGEJO_REMOTE}\t" in remotes:
|
|
ok(f"Forgejo {FORGEJO_REMOTE} 원격 존재 (SSOT)")
|
|
else:
|
|
warn(f"Forgejo {FORGEJO_REMOTE} 원격 없음")
|
|
|
|
# 3) SSOT checker 존재
|
|
checker = REPO_ROOT / "scripts" / "test_dev_dashboard_ssot.py"
|
|
if checker.exists():
|
|
ok(f"SSOT checker 존재: {checker.name}")
|
|
else:
|
|
warn("SSOT checker 없음")
|
|
|
|
# 4) 배포 파이프라인 문서
|
|
pipe_doc = REPO_ROOT / "docs" / "ops" / "deployment-pipeline.md"
|
|
if pipe_doc.exists():
|
|
ok(f"배포 파이프라인 문서 존재")
|
|
else:
|
|
warn("deployment-pipeline.md 없음")
|
|
|
|
return problems
|
|
|
|
|
|
def config() -> None:
|
|
env = REPO_ROOT / "infra" / ".env.deploy"
|
|
print(f"== 배포 파이프라인 구성 ==")
|
|
print(f" Forgejo SSOT : {FORGEJO_REMOTE} (ssh://git@git.chanpaca.net:2222/yunchan/vignette.git)")
|
|
print(f" github 백업 : {GITHUB_REMOTE} (https://github.com/yunchan8804-blip/vignette.git, private)")
|
|
print(f" NAS 대상 : {NAS_COMPOSE_HOST}:{NAS_COMPOSE_PATH} ({NAS_PROJECT})")
|
|
print(f" 로컬 마스터 : {_git('rev-parse', FORGEJO_SSOT_BRANCH)[:12]}")
|
|
print(f" .env.deploy : {env} (토큰 gitignore, 채움 필요)")
|
|
|
|
|
|
def main() -> int:
|
|
ap = argparse.ArgumentParser(description="Vignette 배포 파이프라인 관리")
|
|
ap.add_argument("--check", action="store_true", help="배포 기준선 무결성 점검")
|
|
ap.add_argument("--config", action="store_true", help="파이프라인 구성 요약")
|
|
ap.add_argument("--skip-forgejo", action="store_true", help="Forgejo 원격 접근 생략(오프라인 점검)")
|
|
args = ap.parse_args()
|
|
|
|
if args.config:
|
|
config()
|
|
return 0
|
|
problems = check(forgejo_available=not args.skip_forgejo)
|
|
print(f"\n결과: {'ALL OK' if problems == 0 else f'{problems} 개 문제'}")
|
|
return 0 if problems == 0 else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|