- pulp_client: get_repo / sync_repo / get_task (비동기 task href 반환)
- POST /repos/{uuid}/sync: remote 확인 후 동기화 시작, 진행률 조각 반환
- GET /repos/{uuid}/progress: progress_reports 합산 → 바/퍼센트,
완료/실패 시 hx-trigger 제거로 폴링 중단
- task_store: uuid→task_href 메모리 추적 (단일 인스턴스), 대시보드 '동기화 중' 집계
- views.task_progress: state/done/total/percent + created_resources에서 새 vN
- 동기화는 안전 동작이라 확인 모달 없이 실행 (스펙 §5-1)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
116 lines
4.2 KiB
Python
116 lines
4.2 KiB
Python
"""Pulp JSON → 화면용 표시 모델 변환 (순수 함수, httpx 의존 없음).
|
|
|
|
GPG 검증 배지 로직(✅/⚠️/⚪)은 화면 4 에서도 재사용하므로 여기 함수로 둔다(스펙 §6 화면4).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
|
|
def gpg_status(repo: dict[str, Any]) -> dict[str, str]:
|
|
"""repo_config 의 gpgcheck / repo-gpgcheck 로 3단계 검증 배지 산출.
|
|
|
|
- pass(✅): 패키지 서명 + repo 메타데이터 서명 모두 검증
|
|
- warn(⚠️): 둘 중 하나만 검증
|
|
- unset(⚪): 검증 미설정
|
|
"""
|
|
cfg = repo.get("repo_config") or {}
|
|
pkg = bool(cfg.get("gpgcheck", 0))
|
|
meta = bool(cfg.get("repo-gpgcheck", 0))
|
|
# level 은 색상 칩(badge-{level})으로 렌더 — 장식 이모지는 쓰지 않는다(design-ref §7).
|
|
if pkg and meta:
|
|
return {"level": "pass", "label": "검증됨"}
|
|
if pkg or meta:
|
|
return {"level": "warn", "label": "부분 검증"}
|
|
return {"level": "unset", "label": "미설정"}
|
|
|
|
|
|
def version_number(version_href: str | None) -> int | None:
|
|
"""latest_version_href 끝의 정수(vN) 추출. 예: .../versions/3/ → 3"""
|
|
if not version_href:
|
|
return None
|
|
parts = [p for p in version_href.split("/") if p]
|
|
try:
|
|
return int(parts[-1])
|
|
except (ValueError, IndexError):
|
|
return None
|
|
|
|
|
|
def package_count(version: dict[str, Any] | None) -> int | None:
|
|
"""RepositoryVersion 의 content_summary.present 카운트 합계."""
|
|
if version is None:
|
|
return None
|
|
present = (version.get("content_summary") or {}).get("present") or {}
|
|
return sum((info or {}).get("count", 0) for info in present.values())
|
|
|
|
|
|
def _uuid_from_href(href: str) -> str:
|
|
parts = [p for p in href.split("/") if p]
|
|
return parts[-1] if parts else ""
|
|
|
|
|
|
def build_repo_view(
|
|
repo: dict[str, Any], version: dict[str, Any] | None = None
|
|
) -> dict[str, Any]:
|
|
"""저장소 1건 + (선택) 최신 버전 상세 → 대시보드 행 모델."""
|
|
return {
|
|
"name": repo.get("name", ""),
|
|
"uuid": _uuid_from_href(repo.get("pulp_href", "")),
|
|
"href": repo.get("pulp_href", ""),
|
|
"latest_version": version_number(repo.get("latest_version_href")),
|
|
"package_count": package_count(version),
|
|
# 최신 스냅샷 생성 시각 ≈ 마지막 동기화 시각
|
|
"last_sync": (version or {}).get("pulp_created"),
|
|
"gpg": gpg_status(repo),
|
|
# TODO(단계3/4): 현재 운영 배포 버전은 Distribution→publication→version 역추적 필요.
|
|
# 단계1에선 최신 버전만 표시한다.
|
|
"deployed_version": None,
|
|
}
|
|
|
|
|
|
def summarize(repo_views: list[dict[str, Any]], syncing: int = 0) -> dict[str, int]:
|
|
"""상단 요약 카드 수치. syncing 은 task_store.active_count() 에서 주입."""
|
|
return {
|
|
"total": len(repo_views),
|
|
"syncing": syncing,
|
|
"gpg_pass": sum(1 for r in repo_views if r["gpg"]["level"] == "pass"),
|
|
"total_packages": sum(r["package_count"] or 0 for r in repo_views),
|
|
}
|
|
|
|
|
|
def task_progress(task: dict[str, Any]) -> dict[str, Any]:
|
|
"""Pulp task → 진행률 표시 모델.
|
|
|
|
progress_reports[] 의 done/total 을 합산해 퍼센트를 낸다. 완료 시
|
|
created_resources 에서 새 RepositoryVersion 번호(vN)를 추출한다.
|
|
"""
|
|
state = task.get("state", "waiting")
|
|
reports = task.get("progress_reports") or []
|
|
done = sum((r or {}).get("done") or 0 for r in reports)
|
|
total = sum((r or {}).get("total") or 0 for r in reports)
|
|
percent = int(done / total * 100) if total else 0
|
|
|
|
new_version = None
|
|
for href in task.get("created_resources") or []:
|
|
if "/versions/" in href:
|
|
new_version = version_number(href)
|
|
break
|
|
|
|
error = None
|
|
if state == "failed":
|
|
err = task.get("error") or {}
|
|
error = err.get("description") or "동기화 작업이 실패했습니다."
|
|
|
|
return {
|
|
"state": state,
|
|
"done": done,
|
|
"total": total,
|
|
"percent": percent,
|
|
"new_version": new_version,
|
|
"error": error,
|
|
"completed": state == "completed",
|
|
"failed": state == "failed",
|
|
"running": state in ("waiting", "running"),
|
|
}
|