Some checks failed
deploy-site / deploy (push) Failing after 3m26s
The manual install path still needed 7-Zip, which the target machine does not have, so "installable without a certificate" was not yet true. The channel now also publishes the app as byte-split zip parts, and the install script joins them and extracts with the built-in Windows Expand-Archive after verifying every part and the joined archive. Version 1.3.1 republishes the channel from a single build, because a version's artifacts can only match one build and published volumes are never overwritten.
131 lines
No EOL
5.7 KiB
PowerShell
131 lines
No EOL
5.7 KiB
PowerShell
# scripts/install/install-d3ro-voice.ps1
|
|
# 서명 없이 D3RO Voice를 설치하는 수동 설치 스크립트.
|
|
#
|
|
# 왜 스크립트인가: canonical feed는 Cloudflare 뒤에 있어 업로드 본문이 100MiB를 넘으면
|
|
# 거부된다. 사이드카(faster-whisper)를 포함한 앱은 그보다 크므로 zip을 90MiB 단위로
|
|
# 나누어 게시하고, 이 스크립트가 부품을 이어 붙여 설치한다. Windows 내장
|
|
# Expand-Archive만 사용하므로 7-Zip 같은 추가 도구가 필요 없고 관리자 권한도 필요 없다.
|
|
#
|
|
# 사용:
|
|
# irm https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-latest/install-d3ro-voice.ps1 | iex
|
|
# 또는 저장 후:
|
|
# powershell -ExecutionPolicy Bypass -File install-d3ro-voice.ps1
|
|
#
|
|
# Scoop을 쓸 수 있으면 그쪽이 더 작고(7z 162MiB) 업데이트도 자동이다:
|
|
# scoop bucket add d3ro https://git.chanpaca.net/yunchan/d3ro-voice.git
|
|
# scoop install d3ro/d3ro-voice
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$FeedBase = 'https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-latest',
|
|
[string]$InstallDir = (Join-Path $env:LOCALAPPDATA 'Programs\D3RO Voice'),
|
|
[switch]$Force
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
|
|
function Write-Step($message) { Write-Host "[d3ro] $message" -ForegroundColor Cyan }
|
|
|
|
function Get-Sha256($path) {
|
|
$sha = [System.Security.Cryptography.SHA256]::Create()
|
|
try {
|
|
$stream = [System.IO.File]::OpenRead($path)
|
|
try { $bytes = $sha.ComputeHash($stream) } finally { $stream.Dispose() }
|
|
} finally { $sha.Dispose() }
|
|
return ($bytes | ForEach-Object { $_.ToString('x2') }) -join ''
|
|
}
|
|
|
|
Write-Step 'D3RO Voice 설치를 시작합니다 (서명되지 않은 빌드).'
|
|
|
|
# 1. 인덱스 내려받기
|
|
$indexUrl = "$FeedBase/portable.json"
|
|
Write-Step "인덱스: $indexUrl"
|
|
$index = Invoke-RestMethod -Uri $indexUrl -UseBasicParsing
|
|
$version = $index.version
|
|
|
|
if (-not $index.zipParts -or $index.zipParts.Count -eq 0) {
|
|
throw '인덱스에 zip 부품 정보가 없습니다. 이 스크립트는 zipParts가 있는 버전(1.3.0+)을 지원합니다.'
|
|
}
|
|
|
|
Write-Step "버전 $version, 부품 $($index.zipParts.Count)개 (합계 $([math]::Round($index.zipSize / 1MB, 1)) MB)"
|
|
|
|
# 2. 임시 디렉터리에 부품 내려받기 + 해시 검증
|
|
$tempRoot = [System.IO.Path]::GetTempPath()
|
|
if ($env:TEMP) { $tempRoot = $env:TEMP }
|
|
elseif ($env:TMP) { $tempRoot = $env:TMP }
|
|
|
|
$workDir = Join-Path $tempRoot "d3ro-voice-$version-portable"
|
|
if (Test-Path $workDir) { Remove-Item -Recurse -Force $workDir }
|
|
New-Item -ItemType Directory -Path $workDir | Out-Null
|
|
|
|
foreach ($part in $index.zipParts) {
|
|
$dest = Join-Path $workDir $part.name
|
|
Write-Step "내려받기: $($part.name) ($([math]::Round($part.size / 1MB, 1)) MB)"
|
|
Invoke-WebRequest -Uri "$FeedBase/$($part.name)" -OutFile $dest -UseBasicParsing
|
|
|
|
$hash = Get-Sha256 $dest
|
|
if ($hash -ne $part.sha256) {
|
|
throw "해시가 일치하지 않습니다: $($part.name)`n 기대: $($part.sha256)`n 실제: $hash"
|
|
}
|
|
}
|
|
Write-Step '모든 부품의 SHA-256 검증 완료'
|
|
|
|
# 3. 부품 이어 붙이기
|
|
$archive = Join-Path $workDir $index.zipArchive
|
|
$stream = [System.IO.File]::Create($archive)
|
|
try {
|
|
foreach ($part in $index.zipParts) {
|
|
$piece = [System.IO.File]::OpenRead((Join-Path $workDir $part.name))
|
|
try { $piece.CopyTo($stream) } finally { $piece.Dispose() }
|
|
}
|
|
} finally {
|
|
$stream.Dispose()
|
|
}
|
|
|
|
$archiveHash = Get-Sha256 $archive
|
|
if ($index.zipSha256 -and $archiveHash -ne $index.zipSha256) {
|
|
throw "결합한 아카이브의 해시가 인덱스와 다릅니다.`n 기대: $($index.zipSha256)`n 실제: $archiveHash"
|
|
}
|
|
Write-Step "아카이브 결합 완료: $([math]::Round((Get-Item $archive).Length / 1MB, 1)) MB"
|
|
|
|
# 4. 압축 해제 (Windows 내장 Expand-Archive — 추가 도구 불필요)
|
|
$extractDir = Join-Path $workDir 'extract'
|
|
Write-Step '압축 해제 중 (수백 MB, 시간이 걸릴 수 있습니다)'
|
|
Expand-Archive -LiteralPath $archive -DestinationPath $extractDir -Force
|
|
|
|
# 5. 설치 디렉터리로 배치
|
|
if (Test-Path $InstallDir) {
|
|
if (-not $Force) {
|
|
throw "설치 경로가 이미 있습니다: $InstallDir`n 다시 설치하려면 -Force 를 붙이세요."
|
|
}
|
|
Write-Step "기존 설치를 교체합니다: $InstallDir"
|
|
Remove-Item -Recurse -Force $InstallDir
|
|
}
|
|
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
|
Copy-Item -Path (Join-Path $extractDir '*') -Destination $InstallDir -Recurse -Force
|
|
|
|
# 6. 시작 메뉴 바로가기
|
|
$exe = Join-Path $InstallDir 'D3RO Voice.exe'
|
|
if (-not (Test-Path $exe)) { throw "실행 파일을 찾을 수 없습니다: $exe" }
|
|
|
|
$startMenu = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs'
|
|
$shortcutPath = Join-Path $startMenu 'D3RO Voice.lnk'
|
|
$shell = New-Object -ComObject WScript.Shell
|
|
$shortcut = $shell.CreateShortcut($shortcutPath)
|
|
$shortcut.TargetPath = $exe
|
|
$shortcut.WorkingDirectory = $InstallDir
|
|
$shortcut.Save()
|
|
|
|
Remove-Item -Recurse -Force $workDir -ErrorAction SilentlyContinue
|
|
|
|
Write-Step "설치 완료: $InstallDir"
|
|
Write-Step "시작 메뉴 바로가기: $shortcutPath"
|
|
Write-Host ''
|
|
Write-Host '참고:' -ForegroundColor Yellow
|
|
Write-Host ' - 이 빌드는 Authenticode 서명이 없어 SmartScreen 경고가 뜰 수 있습니다("추가 정보 -> 실행").'
|
|
Write-Host ' - 자동 업데이트는 서명된 릴리스가 게시된 뒤부터 동작합니다(현재 설치본은 그 피드를 봅니다).'
|
|
Write-Host ' - 설정/모델/기록은 %APPDATA%\d3ro-voice 를 공유하므로 기존 설치와 동일하게 유지됩니다.'
|
|
Write-Host ' - Scoop 사용자는 scoop update d3ro-voice 로 갱신할 수 있습니다(7z 162MiB로 더 작음).'
|
|
Write-Host ''
|
|
Write-Host "실행: `"$exe`"" -ForegroundColor Green |