Skip to content

chore: Cargo.lock 버전 3.7.1 반영 #144

chore: Cargo.lock 버전 3.7.1 반영

chore: Cargo.lock 버전 3.7.1 반영 #144

Workflow file for this run

name: Publish Release
on:
push:
tags:
- 'v*'
concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: true
jobs:
# Release gate — tag push 시 CI(tsc/cargo fmt/check/clippy/test) 를 먼저 통과시킨다.
# ci.yml 을 workflow_call reuse 하므로 검증 본문은 한 곳에서 관리.
validate:
name: CI gate
uses: ./.github/workflows/ci.yml
permissions:
contents: read
publish-tauri:
name: Build & Release (Windows)
needs: validate
permissions:
contents: write
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
# kordoc 사이드카 소스 체크아웃 (HWP/HWPX/DOCX/PDF 파싱 런타임)
# 리포가 private이면 token: ${{ secrets.KORDOC_PAT }} 추가 필요
#
# SHA 고정으로 공급망 공격 방지. 새 kordoc 버전 릴리스 시 이 SHA를 갱신.
# 현재: v4.4.0 (비밀번호 문서 열기 #59 — v3.6.0 암호 미리보기 필수, HWP3 아래아·PUA·CDATA 소실 수정) — 2026-08-04
- name: Checkout kordoc
uses: actions/checkout@v4
with:
repository: chrisryugj/kordoc
path: kordoc-src
ref: e92525f394245e5afd859908ad20af9082726b50
- name: Build kordoc dist
working-directory: kordoc-src
shell: pwsh
run: |
npm ci
npm run build
if (-not (Test-Path "dist/cli.js")) {
Write-Error "kordoc build output (dist/cli.js) not found"
exit 1
}
- name: Bundle kordoc into Tauri resources
shell: pwsh
run: |
& scripts/bundle-kordoc.ps1 -KordocDir "${{ github.workspace }}/kordoc-src"
- name: Download ONNX models
shell: pwsh
run: |
& scripts/download-model.ps1
- name: Download VC++ Redistributable
shell: pwsh
run: |
& scripts/download-vcredist.ps1
# 자동 업데이트:
# - TAURI_SIGNING_PRIVATE_KEY: ed25519 개인키 (`pnpm tauri signer generate -w`로 생성한 ~/.tauri/docufinder.key 내용)
# - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: 키 생성 시 비밀번호 (없으면 생략 가능)
# includeUpdaterJson=true 이면 tauri-action 이 latest.json 을 NSIS zip + .sig 와 함께
# Release 에 업로드 → tauri-plugin-updater 가 자동 인식.
# 태그 커밋 메시지에서 "release:" 이후 본문을 Release notes 로 추출
- name: Extract release notes from tag commit
id: notes
shell: bash
run: |
MSG=$(git log -1 --pretty=%B "${{ github.ref_name }}" | sed -e 's/^release: [^—]*—\s*//')
{
echo "body<<NOTES_EOF"
echo "$MSG"
echo "NOTES_EOF"
} >> "$GITHUB_OUTPUT"
- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# 오류 자동 리포트 (build-time 환경변수로 바이너리에 주입)
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
with:
tagName: v__VERSION__
releaseName: 'Anything v__VERSION__'
releaseBody: ${{ steps.notes.outputs.body }}
releaseDraft: false
prerelease: false
includeUpdaterJson: true
updaterJsonPreferNsis: true
# 디버그 심볼 (PDB) 업로드 — 사용자 크래시 덤프(.dmp) 의 콜스택 풀이용.
# Cargo.toml [profile.release] 의 strip / debug 설정에 따라 PDB 가 생성될 때만 업로드.
- name: Upload PDB to release (if present)
if: success()
shell: pwsh
run: |
$pdb = "src-tauri/target/release/docufinder.pdb"
if (Test-Path $pdb) {
gh release upload "${{ github.ref_name }}" $pdb --clobber
Write-Host "Uploaded $pdb to release ${{ github.ref_name }}"
} else {
Write-Host "No PDB found at $pdb (release profile likely has strip enabled), skipping"
}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Microsoft Edge WebView2 Runtime Standalone Installer 동봉.
# 일반 사용자는 NSIS installer 가 알아서 WebView2 를 깔지만,
# WebView2 가 없는 환경에서 installMode:currentUser 권한으로 설치 시 일부
# 시스템 등록 단계가 부분 실패하여 wry 가 detect 못 하는 경로가 있음
# (회사 PC / LTSC 1809 / GPO 차단 환경 등). 이 경우 사용자가 별도로
# 다운받아 관리자 권한으로 선설치할 수 있도록 release 에 함께 첨부.
#
# [v2.6.24] 사내 폐쇄망 권장 설치 경로의 핵심 산출물.
# 단일 통합본(ltsc-setup ~573MB, Fixed Runtime 번들)은 (1) 망간/단방향 전송
# 솔루션에서 파일 무결성이 깨지고, (2) Fixed Runtime inject 경로가 controller
# 생성 단계에서 hang 하는 회귀가 #23/#24 내내 반복돼 폐기했다(이슈 #29 JS190-prog
# 실증: 일반 setup.exe + 이 standalone 을 따로 전송해 각각 설치하면 성공).
# 이제 사내망 사용자는 [일반 setup.exe] + [이 standalone WebView2] 두 파일을
# 나눠 전송 후 설치한다 — 큰 단일 파일보다 전송 성공률이 높고, system runtime
# 경로라 controller hang 도 없다. 고급 사용자용 수동 Fixed Runtime fallback 은
# webview2_runtime.rs 의 EBWebView 탐색으로 여전히 동작한다.
#
# [v2.6.18] linkid 교정: 2099617 은 X86 installer 다 (이슈 #23 검증 —
# go.microsoft.com redirect 가 ...InstallerX86.exe). X64 런타임용 standalone
# 은 linkid=2124701 (...InstallerX64.exe). 잘못된 파일명(X64.exe)으로 X86
# 을 올려 사용자가 x86 런타임을 깔던 배포 버그를 수정.
# NOTE: standalone installer exe **자체**의 PE 는 x86 stub 이 정상이다 (어느
# 아키텍처 머신에서도 실행돼야 하므로). 설치되는 런타임 arch 는 linkid 가
# 결정하므로 installer exe 의 PE 검증은 무의미 — 다운로드 크기만 sanity check.
- name: Download & upload WebView2 Standalone Installer
if: success()
shell: pwsh
run: |
$url = "https://go.microsoft.com/fwlink/?linkid=2124701"
$out = "MicrosoftEdgeWebView2RuntimeInstallerX64.exe"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri $url -OutFile $out -UseBasicParsing
$size = (Get-Item $out).Length
# sanity check — 정상 standalone installer 는 ~190MB. HTML 에러 페이지 등은 수 KB.
if ($size -lt 50MB) {
Write-Error "standalone installer 다운로드 비정상 ($size bytes) — 중단."
exit 1
}
Write-Host "Downloaded WebView2 X64 standalone installer: $size bytes"
gh release upload "${{ github.ref_name }}" $out --clobber
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ── Anything Lite (내부망 전용 빌드) ────────────────────────────────
# 반드시 위 온라인 빌드/업로드가 **끝난 뒤** 에 돌린다:
# 1) bundle-kordoc.ps1 -Lite 는 resources/kordoc 을 지우고 다시 깐다 (수식 OCR
# 네이티브 deps 4종 제외) — 순서가 뒤바뀌면 온라인 산출물이 깨진다.
# 2) lite 빌드의 beforeBuildCommand 가 dist/ 를 VITE_LITE=1 로 덮어쓴다.
# 3) target/release/*.pdb 도 lite 것으로 덮이므로 PDB 업로드 뒤여야 한다.
# 배경과 제외 항목은 docs/LITE-BUILD.md 참고.
- name: Bundle kordoc for lite (수식 OCR 네이티브 deps 제외)
if: success()
shell: pwsh
run: |
& scripts/bundle-kordoc.ps1 -KordocDir "${{ github.workspace }}/kordoc-src" -Lite
# `--no-default-features` 는 tauri CLI 자체 플래그가 아니다 — `--` 뒤에 둬야
# 러너(cargo)로 전달된다. 앞에 두면 "unexpected argument" 로 즉시 실패한다 (v3.7.0 실패).
- name: Build Anything Lite installer
if: success()
shell: pwsh
run: |
pnpm exec tauri build --config src-tauri/tauri.lite.conf.json --features custom-protocol -- --no-default-features
# tauri-action 이 만든 릴리스에 lite 설치본만 추가로 올린다. latest.json 에는
# 넣지 않는다 — lite 는 자동 업데이트 대상이 아니다(updater 플러그인 자체가 없음).
- name: Upload Lite installer to release
if: success()
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$nsis = "src-tauri/target/release/bundle/nsis"
$setup = Get-ChildItem $nsis -Filter "*Lite*setup.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $setup) {
Write-Error "Lite installer 를 찾지 못했다. $nsis 내용: $((Get-ChildItem $nsis -ErrorAction SilentlyContinue).Name -join ', ')"
exit 1
}
Write-Host "Lite installer: $($setup.Name) ($([math]::Round($setup.Length / 1MB, 1)) MB)"
gh release upload "${{ github.ref_name }}" $setup.FullName --clobber
# 내부망 검토 제출용 증빙 — lite 바이너리에 외부 호스트 문자열이 없어야 한다.
# 하나라도 걸리면 online 경로가 lite 로 새어 들어간 것이므로 릴리스를 실패시킨다.
#
# `Select-String -Encoding Byte` 는 Windows PowerShell 5.1 전용이라 이 스텝(pwsh 7)
# 에서는 못 쓴다. 바이트를 ISO-8859-1(28591)로 읽어 그대로 문자열 매칭한다 —
# 두 PowerShell 에서 모두 동작하고, Rust 문자열 리터럴(UTF-8)을 그대로 잡는다.
# 실측 대조군: 같은 검사에서 online 빌드는 아래 4종을 전부 검출한다.
- name: Verify lite binary has no external host strings
if: success()
shell: pwsh
run: |
$exe = "src-tauri/target/release/docufinder.exe"
if (-not (Test-Path $exe)) { Write-Error "lite 바이너리 없음: $exe"; exit 1 }
$text = [System.Text.Encoding]::GetEncoding(28591).GetString([System.IO.File]::ReadAllBytes($exe))
$patterns = @('api.telegram.org', 'huggingface.co', 'api.github.com', 'generativelanguage.googleapis.com')
$hits = $patterns | Where-Object { $text.Contains($_) }
if ($hits) {
Write-Error "lite 바이너리에서 외부 호스트 문자열 검출: $($hits -join ', ')"
exit 1
}
Write-Host "OK — lite 바이너리에 외부 호스트 문자열 없음 ($($patterns -join ', '))"
# [v2.6.24] LTSC 단일 통합 installer(ltsc-setup ~573MB) 빌드를 폐기했다.
# 배경: Fixed Version Runtime 을 NSIS 에 통째 bundle 하는 노선은
# 1) 573MB 단일 exe 가 망간/단방향 전송에서 무결성이 깨져 설치 불가,
# 2) Fixed Runtime inject 후 WebView2 controller 생성이 hang (#23/#24),
# 두 문제로 사내망에서 실패했다. 대신 [일반 setup.exe(offlineInstaller)] +
# [위 standalone WebView2] 분리 배포로 전환 — 두 파일을 나눠 전송/설치하면
# 망간 전송 무결성·system runtime 안정성을 모두 확보한다(이슈 #29).
# 관련 스크립트(scripts/setup-webview2-runtime.ps1, src-tauri/tauri.windows-ltsc.conf.json)는
# 고급 사용자용 수동 Fixed Runtime 배포 참고용으로 남겨둔다.
publish-macos:
name: Build & Release (macOS arm64)
needs: validate
permissions:
contents: write
runs-on: macos-14 # Apple Silicon runner
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
key: macos-arm64
- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
# kordoc 소스 체크아웃 (Windows job 과 동일한 SHA 고정)
# GitHub Actions 보안상 path 는 workspace 내부만 허용 → kordoc-src 사용.
# SHA 고정 — windows job 과 동일 SHA 유지 (버전 동기화).
- name: Checkout kordoc
uses: actions/checkout@v4
with:
repository: chrisryugj/kordoc
path: kordoc-src
ref: e92525f394245e5afd859908ad20af9082726b50
- name: Build kordoc dist
working-directory: kordoc-src
run: |
npm ci
npm run build
test -f dist/cli.js || { echo "kordoc build output missing"; exit 1; }
- name: Bundle macOS resources (node + kordoc + ORT dylib)
env:
KORDOC_DIR: ${{ github.workspace }}/kordoc-src
run: bash scripts/setup-macos-resources.sh
- name: Tauri build (aarch64-apple-darwin, ad-hoc signed)
run: pnpm exec tauri build --target aarch64-apple-darwin
# v2.6.10: --options runtime (Hardened Runtime) 제거.
# 배경:
# v2.6.7 에서 entitlements.plist + Hardened Runtime 으로 OCR(onnxruntime) dlopen
# SIGKILL 을 풀려 했으나, ad-hoc 서명(signingIdentity:"-") 환경에서는 OS 가
# entitlements 의 신뢰성을 검증하지 못해 disable-library-validation 권한이
# 사실상 무시된다. 결과적으로 Hardened Runtime 만 켜지고 entitlements 는
# 안 먹어 Library Validation 이 외부 dylib 페이지를 invalid 로 판단,
# SIGKILL(Code Signature Invalid / Namespace CODESIGNING, Code 2, Invalid Page).
# 해결: Hardened Runtime 자체를 끄면 Library Validation 강제가 없어진다.
# ad-hoc + no hardened runtime → dlopen 정상.
# inside-out 서명 순서는 그대로 (resources/dylib → 메인 바이너리 → .app 번들).
#
# ⚠️ v3.0.9 실검증(2026-07-03): 이 스텝은 배포 dmg 에 반영되지 않는다 —
# tauri build 가 .app 서명 직후 dmg 까지 만들어버려, 여기서 고친 .app 은
# 업로드되지 않는 bundle/macos/ 쪽뿐이다. dylib 자체 서명은
# scripts/setup-macos-resources.sh 가 번들 투입 전에 보장한다 (근원 수정).
# 이 스텝은 .app 직접 배포 대비용으로만 유지.
- name: Re-apply ad-hoc signature (no hardened runtime)
run: |
APP="src-tauri/target/aarch64-apple-darwin/release/bundle/macos/Anything.app"
if [ -d "$APP" ]; then
# 1) 번들된 외부 dylib (libonnxruntime.dylib 등) 먼저 서명
find "$APP/Contents/Resources" -name "*.dylib" -print0 2>/dev/null \
| xargs -0 -I{} codesign --force --sign - "{}"
# 2) 메인 실행 바이너리 ad-hoc 재서명 (hardened runtime 미적용)
codesign --force --sign - "$APP/Contents/MacOS/docufinder" || true
# 3) .app 번들 전체 deep 재서명
codesign --force --deep --sign - "$APP"
# 검증 로그
codesign -dv "$APP" 2>&1 | head -20 || true
fi
- name: Upload dmg to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
DMG=$(ls src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/*.dmg 2>/dev/null | head -1)
if [ -z "$DMG" ]; then
echo "no dmg artifact found"; exit 1
fi
TAG="${{ github.ref_name }}"
# Windows job(tauri-action)의 release 생성과 레이스 주의 — 우리가 만든 draft 를
# tauri-action 이 대체하면 create 의 asset 업로드가 404 로 죽는다 (v3.2.8 실사고:
# view=없음 → create 시작 → 그 사이 Windows 가 release 생성 → 우리 draft 소멸).
# 실패 시 잠시 대기 후 재판정 — 두 번째 시도부턴 상대가 만든 release 로 upload 수렴.
for attempt in 1 2 3 4 5; do
if gh release view "$TAG" --json id >/dev/null 2>&1; then
echo "[$attempt] Release $TAG 존재 → dmg 추가 업로드"
gh release upload "$TAG" "$DMG" --clobber && exit 0
else
echo "[$attempt] Release $TAG 없음 → 새로 생성"
gh release create "$TAG" \
--title "Anything $TAG" \
--generate-notes \
"$DMG" && exit 0
fi
echo "[$attempt] 실패 — 20초 후 재시도"
sleep 20
done
echo "5회 시도 모두 실패"; exit 1