feat: complete release preparation, 10+ ad mediation, CI/CD, and docker deployment
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped

This commit is contained in:
Yun Chan 2026-08-20 11:12:05 +09:00
parent 5cd1de6859
commit 708e20f747
406 changed files with 42464 additions and 6199 deletions

View file

@ -1,138 +0,0 @@
"""
scripts/build-sidecar.py
faster-whisper sidecar를 PyInstaller로 빌드한다. (크로스 플랫폼)
사용법:
pip install pyinstaller
pip install -r sidecar/requirements.txt
python scripts/build-sidecar.py
출력:
sidecar-dist/sidecar/sidecar(.exe) (onedir 모드)
플랫폼:
Windows --noconsole (콘솔 숨김)
macOS --windowed (.app 번들 형식은 여기서는 미사용, onedir만)
Linux 콘솔 옵션 없음
"""
import platform
import subprocess
import sys
import shutil
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent
SIDECAR_DIR = PROJECT_ROOT / "sidecar"
MAIN_PY = SIDECAR_DIR / "main.py"
OUTPUT_DIR = PROJECT_ROOT / "sidecar-dist"
IS_WINDOWS = platform.system() == "Windows"
IS_MACOS = platform.system() == "Darwin"
EXE_SUFFIX = ".exe" if IS_WINDOWS else ""
def check_prerequisites():
"""필수 도구가 설치되어 있는지 확인한다."""
try:
import PyInstaller # noqa: F401
except ImportError:
print("PyInstaller가 설치되어 있지 않습니다.")
print("실행: pip install pyinstaller")
sys.exit(1)
try:
import faster_whisper # noqa: F401
except ImportError:
print("faster-whisper가 설치되어 있지 않습니다.")
print("실행: pip install -r sidecar/requirements.txt")
sys.exit(1)
if not MAIN_PY.exists():
print(f"sidecar 소스를 찾을 수 없습니다: {MAIN_PY}")
sys.exit(1)
def build():
"""PyInstaller로 sidecar를 빌드한다."""
print("=" * 60)
print(f"D3RO-VOICE Sidecar 빌드 시작 ({platform.system()} {platform.machine()})")
print("=" * 60)
# 기존 빌드 정리
if OUTPUT_DIR.exists():
shutil.rmtree(OUTPUT_DIR)
cmd = [
sys.executable, "-m", "PyInstaller",
"--name", "sidecar",
"--distpath", str(OUTPUT_DIR),
"--workpath", str(PROJECT_ROOT / "build" / "sidecar-build"),
"--specpath", str(PROJECT_ROOT / "build"),
"--noconfirm",
"--clean",
# hidden imports
"--hidden-import", "faster_whisper",
"--hidden-import", "ctranslate2",
"--hidden-import", "huggingface_hub",
"--hidden-import", "tokenizers",
"--hidden-import", "uvicorn.logging",
"--hidden-import", "uvicorn.protocols.http",
"--hidden-import", "uvicorn.protocols.http.auto",
"--hidden-import", "uvicorn.protocols.http.h11_impl",
"--hidden-import", "uvicorn.protocols.websockets",
"--hidden-import", "uvicorn.protocols.websockets.auto",
"--hidden-import", "uvicorn.lifespan",
"--hidden-import", "uvicorn.lifespan.on",
"--hidden-import", "uvicorn.lifespan.off",
]
# 플랫폼별 콘솔 옵션
if IS_WINDOWS:
cmd.append("--noconsole")
elif IS_MACOS:
# macOS에서 --windowed는 .app 번들 생성을 의미.
# onedir 모드로도 .app이 만들어질 수 있어서 명시적으로는 쓰지 않음.
# electron-builder가 extraResources로 Binary만 포함하므로 기본 콘솔 형태 유지.
pass
# Linux: 옵션 없음
cmd.append(str(MAIN_PY))
print(f"\n실행 명령:\n {' '.join(cmd)}\n")
result = subprocess.run(cmd, cwd=str(PROJECT_ROOT))
if result.returncode != 0:
print(f"\nPyInstaller 빌드 실패 (exit code: {result.returncode})")
sys.exit(1)
# 빌드 결과 확인
exe_path = OUTPUT_DIR / "sidecar" / f"sidecar{EXE_SUFFIX}"
if exe_path.exists():
size_mb = exe_path.stat().st_size / (1024 * 1024)
print(f"\n빌드 성공!")
print(f" 경로: {exe_path}")
print(f" 크기: {size_mb:.1f} MB")
total_size = sum(
f.stat().st_size for f in (OUTPUT_DIR / "sidecar").rglob("*") if f.is_file()
)
total_mb = total_size / (1024 * 1024)
print(f" 전체 디렉토리: {total_mb:.1f} MB")
else:
print(f"\n빌드 출력을 찾을 수 없습니다: {exe_path}")
# onedir 모드에서는 디렉토리 내부에 바이너리가 있음
pattern = "*.exe" if IS_WINDOWS else "sidecar"
for item in OUTPUT_DIR.rglob(pattern):
print(f" 발견: {item}")
print("\n빌드 완료!")
print("electron-builder에서 이 경로를 extraResources로 지정하세요:")
print(" from: sidecar-dist/sidecar/")
print(" to: sidecar/")
if __name__ == "__main__":
check_prerequisites()
build()

View file

@ -1,103 +0,0 @@
# scripts/download-ollama.ps1
# Downloads the portable Ollama Windows binary into resources/ollama/.
# Usage: powershell -ExecutionPolicy Bypass -File scripts/download-ollama.ps1
#
# Note: Ollama only ships an NSIS installer officially. We use the
# ollama-windows-amd64.zip release asset on GitHub instead.
$ErrorActionPreference = "Stop"
$OLLAMA_VERSION = if ($env:OLLAMA_VERSION) { $env:OLLAMA_VERSION } else { "v0.32.1" }
$OLLAMA_URL = "https://github.com/ollama/ollama/releases/download/$OLLAMA_VERSION/ollama-windows-amd64.zip"
$DEST_DIR = Join-Path $PSScriptRoot "..\resources\ollama"
$TEMP_ZIP = Join-Path $env:TEMP "ollama-$OLLAMA_VERSION-windows-amd64.zip"
$TEMP_DIR = Join-Path $env:TEMP "ollama-extract"
$VERSION_STAMP = Join-Path $DEST_DIR ".ollama-version"
Write-Host "=== Ollama $OLLAMA_VERSION (Windows amd64) ===" -ForegroundColor Cyan
# Version-aware cache: CI runner reuses the checkout dir, so an old bundle can
# linger. Re-download whenever the stamped version differs from the wanted one.
if (Test-Path (Join-Path $DEST_DIR "ollama.exe")) {
$stamped = if (Test-Path $VERSION_STAMP) { (Get-Content $VERSION_STAMP -Raw).Trim() } else { "" }
if ($stamped -eq $OLLAMA_VERSION) {
Write-Host "Ollama $OLLAMA_VERSION already present: $DEST_DIR\ollama.exe" -ForegroundColor Green
exit 0
}
Write-Host "Cached Ollama version '$stamped' != '$OLLAMA_VERSION' - re-downloading" -ForegroundColor Yellow
Remove-Item -Recurse -Force $DEST_DIR
}
if (Test-Path $TEMP_ZIP) { Remove-Item -Force $TEMP_ZIP }
Write-Host "Downloading: $OLLAMA_URL"
# curl.exe (Win10+ native) handles GitHub redirects, progress output, large files
# better than Invoke-WebRequest. -L follow redirects, --fail error on HTTP >=400,
# --retry 3, --max-time 1800 (30min), --progress-bar human readable.
$curlExe = "$env:SystemRoot\System32\curl.exe"
if (-not (Test-Path $curlExe)) { $curlExe = "curl.exe" }
& $curlExe -L --fail --retry 3 --max-time 1800 --progress-bar `
-o $TEMP_ZIP $OLLAMA_URL
if ($LASTEXITCODE -ne 0) {
Write-Host "curl download failed with exit code $LASTEXITCODE" -ForegroundColor Red
Write-Host ""
Write-Host "=== Manual install ===" -ForegroundColor Yellow
Write-Host "Download ollama-windows-amd64.zip from:"
Write-Host " https://github.com/ollama/ollama/releases"
Write-Host "and extract its contents into:"
Write-Host " $DEST_DIR"
exit 1
}
$fileSize = (Get-Item $TEMP_ZIP).Length
if ($fileSize -lt 1000000) {
Write-Host ("Downloaded file too small ({0} bytes)." -f $fileSize) -ForegroundColor Red
exit 1
}
$sizeMB = [math]::Round($fileSize / 1MB, 1)
Write-Host ("Download complete ({0} MB)" -f $sizeMB)
Write-Host "Extracting..."
if (Test-Path $TEMP_DIR) { Remove-Item -Recurse -Force $TEMP_DIR }
Expand-Archive -Path $TEMP_ZIP -DestinationPath $TEMP_DIR -Force
New-Item -ItemType Directory -Force -Path $DEST_DIR | Out-Null
# zip layout: ollama.exe + lib/ (runners + DLLs).
# Prune GPU runners (CUDA v11/v12, ROCm) to keep installer under NSIS 2GB limit.
# CPU runners (cpu_avx2, cpu_avx) are sufficient for initial bundle; users needing GPU
# can install Ollama separately or via future builds.
Get-ChildItem -Path $TEMP_DIR -Force | ForEach-Object {
Copy-Item $_.FullName -Destination $DEST_DIR -Recurse -Force
Write-Host (" copied: {0}" -f $_.Name) -ForegroundColor Green
}
# Remove heavy GPU runtimes to stay below NSIS size limits.
# Layout differs across versions (old: lib\ollama\runners\cuda*, new: lib\ollama\cuda_v12 etc.)
# so prune ANY cuda*/rocm* directory recursively - version-agnostic.
Get-ChildItem -Path $DEST_DIR -Directory -Recurse |
Where-Object { $_.Name -like "cuda*" -or $_.Name -like "rocm*" } |
ForEach-Object {
if (Test-Path $_.FullName) {
Remove-Item -Recurse -Force $_.FullName
Write-Host (" pruned GPU runtime: {0}" -f $_.FullName.Substring($DEST_DIR.Length)) -ForegroundColor Yellow
}
}
Remove-Item -Recurse -Force $TEMP_DIR -ErrorAction SilentlyContinue
Remove-Item -Force $TEMP_ZIP -ErrorAction SilentlyContinue
if (Test-Path (Join-Path $DEST_DIR "ollama.exe")) {
Set-Content -Path $VERSION_STAMP -Value $OLLAMA_VERSION
$totalSize = (Get-ChildItem $DEST_DIR -Recurse -File | Measure-Object -Property Length -Sum).Sum
$totalMB = [math]::Round($totalSize / 1MB, 0)
Write-Host ""
Write-Host ("Ollama $OLLAMA_VERSION installed: {0} (pruned total {1} MB)" -f $DEST_DIR, $totalMB) -ForegroundColor Green
if ($totalSize -gt 1500MB) {
Write-Host "WARNING: pruned bundle exceeds 1.5GB - NSIS 2GB installer limit at risk" -ForegroundColor Red
}
} else {
Write-Host "ollama.exe not found after extraction. Inspect the zip layout." -ForegroundColor Red
exit 1
}

View file

@ -1,85 +0,0 @@
#!/usr/bin/env bash
# scripts/download-ollama.sh
# Ollama 포터블 바이너리를 resources/ollama/에 배치한다. (macOS/Linux)
# 실행: bash scripts/download-ollama.sh
set -euo pipefail
OLLAMA_VERSION="${OLLAMA_VERSION:-v0.32.1}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEST_DIR="$SCRIPT_DIR/../resources/ollama"
VERSION_STAMP="$DEST_DIR/.ollama-version"
OS="$(uname -s)"
ARCH="$(uname -m)"
case "$OS" in
Darwin)
# v0.6+ 기준 tgz 제공 (metal 포함, ~145MB). v0.5.x는 단일 바이너리였음.
ASSET="ollama-darwin.tgz"
;;
Linux)
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
ASSET="ollama-linux-arm64.tgz"
else
ASSET="ollama-linux-amd64.tgz"
fi
;;
*)
echo "지원하지 않는 OS: $OS" >&2
exit 1
;;
esac
URL="https://github.com/ollama/ollama/releases/download/${OLLAMA_VERSION}/${ASSET}"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
# 버전 스탬프 캐시 무효화 — runner가 checkout 디렉토리를 재사용하므로
# 구버전 번들이 남아있으면 갈아엎는다.
if [ -f "$DEST_DIR/ollama" ]; then
STAMPED="$(cat "$VERSION_STAMP" 2>/dev/null || echo '')"
if [ "$STAMPED" = "$OLLAMA_VERSION" ]; then
echo "Ollama ${OLLAMA_VERSION}가 이미 존재합니다: $DEST_DIR/ollama"
exit 0
fi
echo "캐시된 Ollama 버전('$STAMPED') != '$OLLAMA_VERSION' — 재다운로드"
rm -rf "$DEST_DIR"
fi
mkdir -p "$DEST_DIR"
echo "=== Ollama ${OLLAMA_VERSION} ${OS} ${ARCH} 다운로드 ==="
echo "URL: $URL"
curl -L --fail --retry 3 -o "$TMP/$ASSET" "$URL"
case "$ASSET" in
*.tgz)
tar -xzf "$TMP/$ASSET" -C "$TMP"
# tgz 구조는 버전에 따라 bin/ollama 또는 루트 ollama — 둘 다 지원
if [ -f "$TMP/bin/ollama" ]; then
cp "$TMP/bin/ollama" "$DEST_DIR/ollama"
elif [ -f "$TMP/ollama" ]; then
cp "$TMP/ollama" "$DEST_DIR/ollama"
else
echo "ERROR: tgz 안에서 ollama 바이너리를 찾지 못했습니다" >&2
find "$TMP" -maxdepth 2 -type f | head -20 >&2
exit 1
fi
chmod +x "$DEST_DIR/ollama"
# 라이브러리 동봉 (linux: CUDA/ROCm은 용량 문제로 제외, darwin: metal 등 유지)
if [ -d "$TMP/lib" ]; then
cp -R "$TMP/lib" "$DEST_DIR/"
find "$DEST_DIR/lib" -maxdepth 3 -type d \( -name "cuda*" -o -name "rocm*" \) -exec rm -rf {} + 2>/dev/null || true
fi
;;
*)
# 단일 실행 바이너리 asset
cp "$TMP/$ASSET" "$DEST_DIR/ollama"
chmod +x "$DEST_DIR/ollama"
;;
esac
echo "$OLLAMA_VERSION" > "$VERSION_STAMP"
echo "Ollama ${OLLAMA_VERSION} 설치 완료: $DEST_DIR ($(du -sh "$DEST_DIR" | cut -f1))"