"""발행된 사이트 **전부**를 Azure Blob(`$web`)에 다시 올린다.""" import argparse, os, sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) os.environ.setdefault("APP_ENV", "local") from azure.storage.blob import BlobServiceClient # noqa: E402 from services import azure_static # noqa: E402 def site_slugs(root) -> list[str]: base = root / azure_static.SITE_ROOT_DIR if not base.is_dir(): raise SystemExit(f"산출물 디렉터리가 없습니다: {base}") # index.html 이 있는 디렉토리만 발행본으로 본다(작업 중 잔재를 올리지 않는다). return sorted(d.name for d in base.iterdir() if d.is_dir() and (d / "index.html").is_file()) def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--dry-run", action="store_true", help="업로드 없이 대상만 출력") args = parser.parse_args() root = azure_static.output_dir() slugs = site_slugs(root) print(f"[republish] 산출물: {root} · 사이트 {len(slugs)}개") if args.dry_run: for slug in slugs: print(f" - {slug}") return if not azure_static.is_configured(): raise SystemExit("AZURE_STORAGE_CONNECTION_STRING 이 비어 있습니다 — 올릴 곳이 없습니다.") container_name = os.environ.get("AZURE_STORAGE_CONTAINER", azure_static.DEFAULT_CONTAINER).strip() container_name = container_name or azure_static.DEFAULT_CONTAINER prefix = os.environ.get("AZURE_STORAGE_PREFIX", azure_static.DEFAULT_PREFIX).strip().strip("/") service = BlobServiceClient.from_connection_string(os.environ["AZURE_STORAGE_CONNECTION_STRING"].strip()) container = service.get_container_client(container_name) # ── 공용 자산 한 번 ── shared = azure_static._upload_shared(container, root, prefix) print(f"[republish] 공용 {len(shared)}개 (컨테이너 {container_name} · 접두사 {prefix or '(없음)'})") # ── 사이트별 ── 한 사이트가 실패해도 나머지는 계속 올린다. failed: list[tuple[str, str]] = [] for i, slug in enumerate(slugs, 1): try: files = azure_static._upload_tree(container, root, f"{azure_static.SITE_ROOT_DIR}/{slug}", prefix) site_prefix = "/".join(p for p in (prefix, azure_static.SITE_ROOT_DIR, slug) if p) removed = azure_static._remove_stale_site_files(container, site_prefix, files) print(f" [{i}/{len(slugs)}] {slug} — 파일 {len(files)}개, 정리 {removed}개") except Exception as ex: # noqa: BLE001 — 사이트별 실패 격리가 목적이다 failed.append((slug, f"{type(ex).__name__}: {ex}")) print(f" [{i}/{len(slugs)}] {slug} — 실패: {type(ex).__name__}: {ex}") if failed: print(f"\n[republish] 실패 {len(failed)}개:") for slug, reason in failed: print(f" - {slug}: {reason}") raise SystemExit(1) print(f"\n[republish] 완료 — {len(slugs)}개 사이트") if __name__ == "__main__": main()