releases/.forgejo/workflows/release.yml
Yun Chan 02995f4153 MEDIABROWSER 개명·업데이트 채널 전환·아이콘 tofu 근본 수정 (BACKLOG 13차) — ①개명: installer.iss AppName/출력 MediaBrowser-Setup-*(AppId·exe·데이터 경로 유지로 업그레이드 호환), 워크플로 산출물명(mediabrowser-*.tar.gz, MediaBrowser-<ver>-arm64.apk), 창 타이틀·모바일 표시명·obtainium.json·README. ②UpdateChecker: apiBase 주입(기본=자체 Forgejo /api/v1, GitHub 는 apiBase 로 호환), osx 자산 tar.gz 허용(기존 dmg 만 → mac 영원히 자산 없음 결함 수정), AppConfig 기본 UpdateRepo=Video-Downloader/releases+UpdateApiBase — 테스트 4건. ③아이콘 tofu(사용자 보고 "절대 안고쳐지네") 근본 원인: Shared.xaml 암시적 TextBlock 스타일의 FontFamily=UiFont 강제가 버튼 ContentPresenter 자동 TextBlock 의 IconFont 상속을 덮어 모든 PUA 글리프가 notdef — 폰트 설치/글리프 존재/HW·SW 렌더/RTB/DPI/런타임 8·10/자체포함·공유 전부 배제 후 실창 픽셀 비교로 격리, 콘텐츠 TextBlock 의 실폰트가 Pretendard 체인임을 테스트로 실증. 수정: 암시 스타일 FontFamily 제거(창 루트 상속으로 통일, PairingWindow/PlayerWindow 는 SetResourceReference), PUA+텍스트 혼합 콘텐츠 7곳+코드 1곳 정리, 순수 글리프 2곳 IconFont 명시. 회귀 방어: IconGlyphXamlTests(정적 스캔)·IconContentFontTests(실창 폰트 단정)·IconGlyphRenderTests(픽셀 비교) — 462/462 GREEN, 수정 전후 실창 캡처로 글리프 렌더 확인
2026-08-18 07:13:18 +09:00

208 lines
10 KiB
YAML

# Forgejo Actions 릴리스 파이프라인 (DEPLOYMENT_PLAN §5-bis)
# 자체 러너 3대(모두 host 모드): yunchan-win(windows) · kd-desktop(linux-builder) · yunchan-mac(macos)
# - 태그 v* 푸시 → 해당 태그의 draft 릴리스 생성 + 전 아티팩트 첨부 (QA 후 수동 Publish 승격)
# - workflow_dispatch → 'ci-rehearsal' draft 릴리스로 전체 파이프라인 리허설
# host 러너에는 Node 가 없어도 되도록 uses: 액션을 쓰지 않는다(체크아웃 포함 전부 run: 스텝).
# 빌드머신 전제: docs/DEPLOYMENT_PLAN.md §0 — win(SDK8+MAUI+Inno) · linux/mac(~/.dotnet SDK 8+10)
name: release
on:
push:
tags: ["v*"]
workflow_dispatch: # 수동 실행(리허설)
env:
DOTNET_NOLOGO: "true"
DOTNET_CLI_TELEMETRY_OPTOUT: "true"
# 릴리스 자산은 공개 배포 저장소로 업로드한다(소스 저장소는 비공개라 익명 다운로드 불가).
# draft 상태는 익명에게 보이지 않으므로 QA 후 수동 Publish 흐름은 그대로다.
RELEASE_REPO: "Video-Downloader/releases"
jobs:
# ── 관문: 전 플랫폼 빌드 전에 단위+E2E 통과 ──
test:
runs-on: windows
defaults: { run: { shell: pwsh } }
steps:
- name: checkout
env: { CI_TOKEN: "${{ github.token }}" }
run: |
$u = [Uri]$env:GITHUB_SERVER_URL
$url = "$($u.Scheme)://actions:$($env:CI_TOKEN)@$($u.Authority)/$($env:GITHUB_REPOSITORY).git"
if (-not (Test-Path .git)) { git init -q . }
if (git remote | Select-String -Quiet '^origin$') { git remote set-url origin $url } else { git remote add origin $url }
git fetch -q --depth 1 origin $env:GITHUB_REF
if ($LASTEXITCODE -ne 0) { throw "fetch 실패: $env:GITHUB_REF" }
git checkout -q -f FETCH_HEAD
if ($LASTEXITCODE -ne 0) { throw "checkout 실패" }
git clean -qfdx
- name: 환경 정보
run: dotnet --list-sdks
- name: dotnet test
run: dotnet test VideoDownloader.Tests -c Release
# ── Windows 설치자 (x64 / arm64 교차 publish + Inno Setup) ──
windows:
needs: test
runs-on: windows
defaults: { run: { shell: pwsh } }
steps:
- name: checkout
env: { CI_TOKEN: "${{ github.token }}" }
run: |
$u = [Uri]$env:GITHUB_SERVER_URL
$url = "$($u.Scheme)://actions:$($env:CI_TOKEN)@$($u.Authority)/$($env:GITHUB_REPOSITORY).git"
if (-not (Test-Path .git)) { git init -q . }
if (git remote | Select-String -Quiet '^origin$') { git remote set-url origin $url } else { git remote add origin $url }
git fetch -q --depth 1 origin $env:GITHUB_REF
if ($LASTEXITCODE -ne 0) { throw "fetch 실패: $env:GITHUB_REF" }
git checkout -q -f FETCH_HEAD
if ($LASTEXITCODE -ne 0) { throw "checkout 실패" }
git clean -qfdx
- name: 버전 결정 (태그 v1.2.3 → 1.2.3 / 리허설 → 0.0.0)
run: |
if ($env:GITHUB_REF -match '^refs/tags/v(\d+\.\d+\.\d+.*)$') {
"RELTAG=$($env:GITHUB_REF -replace '^refs/tags/','')" >> $env:GITHUB_ENV
"APP_VERSION=$($Matches[1])" >> $env:GITHUB_ENV
"RELEASE_PRERELEASE=false" >> $env:GITHUB_ENV
} else {
"RELTAG=ci-rehearsal" >> $env:GITHUB_ENV
"APP_VERSION=0.0.0" >> $env:GITHUB_ENV
"RELEASE_PRERELEASE=true" >> $env:GITHUB_ENV
}
- name: publish + 설치자 (x64 + arm64)
run: ./build/publish.ps1 -Config Release -Version $env:APP_VERSION
- name: 릴리스 업로드
env: { RELEASE_TOKEN: "${{ secrets.RELEASE_TOKEN }}" }
run: ./build/ci/upload-release-asset.ps1 -Tag $env:RELTAG -Files out/MediaBrowser-Setup-x64.exe, out/MediaBrowser-Setup-arm64.exe
# ── Android APK (키스토어 시크릿 없으면 디버그 서명 APK) ──
android:
needs: test
runs-on: windows
defaults: { run: { shell: pwsh } }
steps:
- name: checkout
env: { CI_TOKEN: "${{ github.token }}" }
run: |
$u = [Uri]$env:GITHUB_SERVER_URL
$url = "$($u.Scheme)://actions:$($env:CI_TOKEN)@$($u.Authority)/$($env:GITHUB_REPOSITORY).git"
if (-not (Test-Path .git)) { git init -q . }
if (git remote | Select-String -Quiet '^origin$') { git remote set-url origin $url } else { git remote add origin $url }
git fetch -q --depth 1 origin $env:GITHUB_REF
if ($LASTEXITCODE -ne 0) { throw "fetch 실패: $env:GITHUB_REF" }
git checkout -q -f FETCH_HEAD
if ($LASTEXITCODE -ne 0) { throw "checkout 실패" }
git clean -qfdx
- name: 버전 결정
run: |
if ($env:GITHUB_REF -match '^refs/tags/v(\d+\.\d+\.\d+.*)$') {
"RELTAG=$($env:GITHUB_REF -replace '^refs/tags/','')" >> $env:GITHUB_ENV
"APP_VERSION=$($Matches[1])" >> $env:GITHUB_ENV
"RELEASE_PRERELEASE=false" >> $env:GITHUB_ENV
} else {
"RELTAG=ci-rehearsal" >> $env:GITHUB_ENV
"APP_VERSION=0.0.0" >> $env:GITHUB_ENV
"RELEASE_PRERELEASE=true" >> $env:GITHUB_ENV
}
- name: publish APK
env:
ANDROID_KEYSTORE_B64: "${{ secrets.ANDROID_KEYSTORE }}"
ANDROID_KEY_ALIAS: "${{ secrets.ANDROID_KEY_ALIAS }}"
ANDROID_KEY_PASS: "${{ secrets.ANDROID_KEY_PASS }}"
run: |
$sign = @()
if ($env:ANDROID_KEYSTORE_B64) {
[IO.File]::WriteAllBytes("vd.keystore", [Convert]::FromBase64String($env:ANDROID_KEYSTORE_B64))
$sign = "-p:AndroidKeyStore=true", "-p:AndroidSigningKeyStore=$PWD/vd.keystore",
"-p:AndroidSigningKeyAlias=$env:ANDROID_KEY_ALIAS",
"-p:AndroidSigningKeyPass=$env:ANDROID_KEY_PASS", "-p:AndroidSigningStorePass=$env:ANDROID_KEY_PASS"
} else { Write-Host "ANDROID_KEYSTORE 시크릿 없음 — 디버그 서명 APK 생성" }
# RID 는 CLI 전역 속성이 아니라 csproj 게이트(CiPackArm64Only)로 좁힌다 — 전역 -p:RuntimeIdentifiers 는
# 참조 프로젝트(net8.0 Mobile.Core)까지 전파돼 MSB3030 으로 깨진다(2026-08-17 리허설 실증)
dotnet publish VideoDownloader.Mobile -f net10.0-android -c Release `
-p:AndroidPackageFormat=apk -p:CiPackArm64Only=true @sign -o out/android
if ($LASTEXITCODE -ne 0) { throw "publish 실패" }
$apk = Get-ChildItem out/android/*-Signed.apk | Select-Object -First 1
if (-not $apk) { $apk = Get-ChildItem out/android/*.apk | Select-Object -First 1 }
if (-not $apk) { throw "APK 산출물 없음" }
Copy-Item $apk.FullName "out/MediaBrowser-$env:APP_VERSION-arm64.apk"
- name: 릴리스 업로드
env: { RELEASE_TOKEN: "${{ secrets.RELEASE_TOKEN }}" }
run: ./build/ci/upload-release-asset.ps1 -Tag $env:RELTAG -Files "out/MediaBrowser-$env:APP_VERSION-arm64.apk"
# ── Linux 셸 (tar.gz self-contained — Cli + Avalonia, x64 네이티브 + arm64 교차) ──
linux:
needs: test
runs-on: linux-builder
steps:
- name: checkout
env: { CI_TOKEN: "${{ github.token }}" }
run: |
proto="${GITHUB_SERVER_URL%%://*}"; host="${GITHUB_SERVER_URL#*://}"
url="$proto://actions:${CI_TOKEN}@${host%/}/${GITHUB_REPOSITORY}.git"
[ -d .git ] || git init -q .
git remote remove origin 2>/dev/null || true
git remote add origin "$url"
git fetch -q --depth 1 origin "$GITHUB_REF"
git checkout -q -f FETCH_HEAD
git clean -qfdx
- name: 환경 준비 (~/.dotnet SDK)
run: |
echo "DOTNET_ROOT=$HOME/.dotnet" >> "$GITHUB_ENV"
echo "$HOME/.dotnet" >> "$GITHUB_PATH"
case "$GITHUB_REF" in
refs/tags/v*) echo "RELTAG=${GITHUB_REF#refs/tags/}" >> "$GITHUB_ENV"
echo "RELEASE_PRERELEASE=false" >> "$GITHUB_ENV" ;;
*) echo "RELTAG=ci-rehearsal" >> "$GITHUB_ENV"
echo "RELEASE_PRERELEASE=true" >> "$GITHUB_ENV" ;;
esac
- name: publish (Cli + Avalonia, linux-x64 + linux-arm64)
run: |
dotnet --list-sdks
for rid in linux-x64 linux-arm64; do
dotnet publish VideoDownloader.Cli -c Release -r "$rid" --self-contained -o "out/cli-$rid"
dotnet publish VideoDownloader.Avalonia -c Release -r "$rid" --self-contained -o "out/avalonia-$rid"
tar -C out -czf "out/mediabrowser-$rid.tar.gz" "cli-$rid" "avalonia-$rid"
done
- name: 릴리스 업로드
env: { RELEASE_TOKEN: "${{ secrets.RELEASE_TOKEN }}" }
run: ./build/ci/upload-release-asset.sh "$RELTAG" out/mediabrowser-linux-x64.tar.gz out/mediabrowser-linux-arm64.tar.gz
# ── macOS 셸 (tar.gz — arm64 네이티브 + x64 교차. 서명·공증은 D4, Apple Developer Program 후) ──
macos:
needs: test
runs-on: macos
steps:
- name: checkout
env: { CI_TOKEN: "${{ github.token }}" }
run: |
proto="${GITHUB_SERVER_URL%%://*}"; host="${GITHUB_SERVER_URL#*://}"
url="$proto://actions:${CI_TOKEN}@${host%/}/${GITHUB_REPOSITORY}.git"
[ -d .git ] || git init -q .
git remote remove origin 2>/dev/null || true
git remote add origin "$url"
git fetch -q --depth 1 origin "$GITHUB_REF"
git checkout -q -f FETCH_HEAD
git clean -qfdx
- name: 환경 준비 (~/.dotnet SDK)
run: |
echo "DOTNET_ROOT=$HOME/.dotnet" >> "$GITHUB_ENV"
echo "$HOME/.dotnet" >> "$GITHUB_PATH"
case "$GITHUB_REF" in
refs/tags/v*) echo "RELTAG=${GITHUB_REF#refs/tags/}" >> "$GITHUB_ENV"
echo "RELEASE_PRERELEASE=false" >> "$GITHUB_ENV" ;;
*) echo "RELTAG=ci-rehearsal" >> "$GITHUB_ENV"
echo "RELEASE_PRERELEASE=true" >> "$GITHUB_ENV" ;;
esac
- name: publish (Cli + Avalonia, osx-arm64 + osx-x64)
run: |
dotnet --list-sdks
for rid in osx-arm64 osx-x64; do
dotnet publish VideoDownloader.Cli -c Release -r "$rid" --self-contained -o "out/cli-$rid"
dotnet publish VideoDownloader.Avalonia -c Release -r "$rid" --self-contained -o "out/avalonia-$rid"
tar -C out -czf "out/mediabrowser-$rid.tar.gz" "cli-$rid" "avalonia-$rid"
done
- name: 릴리스 업로드
env: { RELEASE_TOKEN: "${{ secrets.RELEASE_TOKEN }}" }
run: ./build/ci/upload-release-asset.sh "$RELTAG" out/mediabrowser-osx-arm64.tar.gz out/mediabrowser-osx-x64.tar.gz