버전 불일치 문제

This commit is contained in:
Macbook
2026-06-14 20:01:33 +09:00
parent a6a3bfbe38
commit dec812089d
8 changed files with 180 additions and 55 deletions
@@ -59,14 +59,17 @@ public class DesktopAppReleaseService {
String publishedVer = readLatestJsonVersion(); String publishedVer = readLatestJsonVersion();
String buildInfoVer = readBuildInfoVersion(releaseDirectory()); String buildInfoVer = readBuildInfoVersion(releaseDirectory());
// 헤더 버전: 실제 설치 파일 > build-info > 설정값 (latest.json은 업데이터용이라 다운로드와 어긋날 수 있음) // 헤더 = build-info(마지막 성공 Jenkins 빌드) → mac/win 설치 파일 → 설정값
// latest.json 은 업데이터용 — 설치 파일보다 높으면 헤더에 쓰지 않음 (UI 불일치 방지)
String installerVer = maxSemver(macVer, winVer); String installerVer = maxSemver(macVer, winVer);
String version = maxSemver( String version = maxSemver(
installerVer,
buildInfoVer, buildInfoVer,
nullIfBlank(configuredVersion), installerVer,
publishedVer nullIfBlank(configuredVersion)
); );
if (version == null) {
version = publishedVer;
}
Instant latestUpdated = Stream.of(macFile, winFile) Instant latestUpdated = Stream.of(macFile, winFile)
.flatMap(Optional::stream) .flatMap(Optional::stream)
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@goldenchart/desktop", "name": "@goldenchart/desktop",
"version": "0.1.4", "version": "0.1.18",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "GoldenChart", "productName": "GoldenChart",
"version": "0.1.4", "version": "0.1.18",
"identifier": "com.goldenchart.desktop", "identifier": "com.goldenchart.desktop",
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",
+15 -1
View File
@@ -161,7 +161,7 @@ const PlatformCard: React.FC<{
const configs = { const configs = {
mac: { label: 'macOS (Apple Silicon · Intel)', icon: '🍎', ext: '.dmg', isLink: false }, mac: { label: 'macOS (Apple Silicon · Intel)', icon: '🍎', ext: '.dmg', isLink: false },
windows: { label: 'Windows (64-bit)', icon: '🪟', ext: '.exe', isLink: false }, windows: { label: 'Windows (64-bit)', icon: '🪟', ext: '.exe', isLink: false },
android: { label: 'Android (패드 · 폰)', icon: '🤖', ext: '.apk', isLink: false }, android: { label: 'Android (패드 · 폰 · 별도 버전)', icon: '🤖', ext: '.apk', isLink: false },
} as const; } as const;
const { label, icon, ext } = configs[platform]; const { label, icon, ext } = configs[platform];
@@ -435,6 +435,14 @@ const DesktopTab: React.FC<{ canBuildDesktop: boolean }> = ({ canBuildDesktop })
const hasAny = info?.mac?.available || info?.windows?.available || mobileInfo?.available; const hasAny = info?.mac?.available || info?.windows?.available || mobileInfo?.available;
const desktopVersionMismatch = Boolean(
info?.version
&& (
(info.mac?.available && info.mac.version && info.mac.version !== info.version)
|| (info.windows?.available && info.windows.version && info.windows.version !== info.version)
),
);
if (loading) return <p className="app-download-muted">PC </p>; if (loading) return <p className="app-download-muted">PC </p>;
if (error) return <p className="app-download-error">{error}</p>; if (error) return <p className="app-download-error">{error}</p>;
@@ -468,6 +476,12 @@ const DesktopTab: React.FC<{ canBuildDesktop: boolean }> = ({ canBuildDesktop })
)} )}
</div> </div>
{desktopVersionMismatch && (
<p className="app-download-error" style={{ marginBottom: 8 }}>
macOS·Windows (v{info?.version}) . .
</p>
)}
{showBuildProgress && ( {showBuildProgress && (
<DesktopBuildProgressPanel <DesktopBuildProgressPanel
status={buildStatus} status={buildStatus}
+49 -20
View File
@@ -2,11 +2,14 @@
# GoldenChart Desktop — macOS Jenkins 빌드 (macOS dmg + Windows NSIS cross-build) # GoldenChart Desktop — macOS Jenkins 빌드 (macOS dmg + Windows NSIS cross-build)
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)" SCRIPT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT" WORK_TREE="${WORK_TREE:-$SCRIPT_ROOT}"
cd "$WORK_TREE"
# shellcheck disable=SC1091 # shellcheck disable=SC1091
source "$ROOT/scripts/desktop-dev-path.sh" source "$WORK_TREE/scripts/desktop-dev-path.sh"
# shellcheck disable=SC1091
source "$WORK_TREE/scripts/desktop-installer-version.sh"
# shellcheck disable=SC1091 # shellcheck disable=SC1091
[[ -f "$HOME/.cargo/env" ]] && source "$HOME/.cargo/env" [[ -f "$HOME/.cargo/env" ]] && source "$HOME/.cargo/env"
@@ -25,8 +28,8 @@ while [[ $# -gt 0 ]]; do
done done
echo "=== GoldenChart Desktop Build ===" echo "=== GoldenChart Desktop Build ==="
echo "Root: $ROOT" echo "Work tree: $WORK_TREE"
echo "Git: $(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo 'n/a')" echo "Git: $(git -C "$WORK_TREE" rev-parse --short HEAD 2>/dev/null || echo 'n/a')"
need_cmd() { need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then if ! command -v "$1" >/dev/null 2>&1; then
@@ -45,14 +48,16 @@ if [[ "$BUILD_WIN" -eq 1 ]]; then
rustup target add x86_64-pc-windows-msvc 2>/dev/null || true rustup target add x86_64-pc-windows-msvc 2>/dev/null || true
fi fi
CONF="$ROOT/desktop/src-tauri/tauri.conf.json" CONF="$WORK_TREE/desktop/src-tauri/tauri.conf.json"
# CI: 매 Jenkins 빌드마다 patch 버전 bump (git tauri.conf 0.1.4 고정이어도 산출물 버전 증가) # CI patch bump (jenkins-desktop-pipeline 에서 선행 bump 시 DESKTOP_SKIP_VERSION_BUMP=1)
if [[ -z "${BUMP_VERSION:-}" ]] && [[ "${DESKTOP_BUMP_ON_CI:-1}" == "1" ]] \ if [[ "${DESKTOP_SKIP_VERSION_BUMP:-}" != "1" ]] \
&& [[ -z "${BUMP_VERSION:-}" ]] \
&& [[ "${DESKTOP_BUMP_ON_CI:-1}" == "1" ]] \
&& [[ -n "${BUILD_NUMBER:-}${DESKTOP_AUTO_BUMP:-}" ]]; then && [[ -n "${BUILD_NUMBER:-}${DESKTOP_AUTO_BUMP:-}" ]]; then
chmod +x "$ROOT/scripts/bump-desktop-version-from-release.sh" chmod +x "$WORK_TREE/scripts/bump-desktop-version-from-release.sh"
export WORK_TREE="$ROOT" export WORK_TREE
BUMPED="$("$ROOT/scripts/bump-desktop-version-from-release.sh")" BUMPED="$("$WORK_TREE/scripts/bump-desktop-version-from-release.sh")"
echo "Desktop CI version bump → $BUMPED" echo "Desktop CI version bump → $BUMPED"
fi fi
@@ -85,12 +90,12 @@ fs.writeFileSync(confPath, JSON.stringify(conf, null, 2) + '\n');
console.log('window title →', title); console.log('window title →', title);
" "$CONF" "$VERSION" " "$CONF" "$VERSION"
if [[ -f "$ROOT/scripts/patch-tauri-updater-pubkey.sh" ]]; then if [[ -f "$WORK_TREE/scripts/patch-tauri-updater-pubkey.sh" ]]; then
chmod +x "$ROOT/scripts/patch-tauri-updater-pubkey.sh" chmod +x "$WORK_TREE/scripts/patch-tauri-updater-pubkey.sh"
fi fi
chmod +x "$ROOT/scripts/prepare-tauri-build.sh" 2>/dev/null || true chmod +x "$WORK_TREE/scripts/prepare-tauri-build.sh" 2>/dev/null || true
# shellcheck disable=SC1091 # shellcheck disable=SC1091
source "$ROOT/scripts/prepare-tauri-build.sh" source "$WORK_TREE/scripts/prepare-tauri-build.sh"
npm install npm install
@@ -98,9 +103,9 @@ echo "--- Vite build (desktop) ---"
export DESKTOP_APP_VERSION="$VERSION" export DESKTOP_APP_VERSION="$VERSION"
npm run build -w @goldenchart/desktop npm run build -w @goldenchart/desktop
DESKTOP_DIR="$ROOT/desktop" DESKTOP_DIR="$WORK_TREE/desktop"
TAURI_TARGET="$DESKTOP_DIR/src-tauri/target" TAURI_TARGET="$DESKTOP_DIR/src-tauri/target"
ARTIFACT_DIR="$ROOT/dist-desktop" ARTIFACT_DIR="${ARTIFACT_DIR:-$WORK_TREE/dist-desktop}"
UPDATES_DIR="$ARTIFACT_DIR/updates" UPDATES_DIR="$ARTIFACT_DIR/updates"
rm -rf "$ARTIFACT_DIR" rm -rf "$ARTIFACT_DIR"
mkdir -p "$ARTIFACT_DIR" "$UPDATES_DIR" mkdir -p "$ARTIFACT_DIR" "$UPDATES_DIR"
@@ -125,7 +130,7 @@ copy_installer_matching_version() {
local found="" local found=""
while IFS= read -r f; do while IFS= read -r f; do
[[ -z "$f" ]] && continue [[ -z "$f" ]] && continue
if [[ "$f" == *"${VERSION}"* ]]; then if desktop_installer_matches_version "$(basename "$f")" "$VERSION"; then
found="$f" found="$f"
break break
fi fi
@@ -157,8 +162,32 @@ if [[ "$BUILD_WIN" -eq 1 ]]; then
collect_updater_artifacts collect_updater_artifacts
fi fi
GIT_SHA="$(git -C "$ROOT" rev-parse HEAD 2>/dev/null || echo '')" # mac·win 설치 파일 semver 일치 검증
GIT_SHORT="$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo '')" MAC_VER="" WIN_VER=""
shopt -s nullglob
for f in "$ARTIFACT_DIR"/*.dmg; do
MAC_VER="$(desktop_extract_version_from_name "$(basename "$f")")"
done
for f in "$ARTIFACT_DIR"/*setup.exe; do
WIN_VER="$(desktop_extract_version_from_name "$(basename "$f")")"
done
shopt -u nullglob
if [[ "$BUILD_MAC" -eq 1 && -n "$MAC_VER" && "$MAC_VER" != "$VERSION" ]]; then
echo "ERROR: macOS installer v${MAC_VER} != build v${VERSION}" >&2
exit 1
fi
if [[ "$BUILD_WIN" -eq 1 && -n "$WIN_VER" && "$WIN_VER" != "$VERSION" ]]; then
echo "ERROR: Windows installer v${WIN_VER} != build v${VERSION}" >&2
exit 1
fi
if [[ "$BUILD_MAC" -eq 1 && "$BUILD_WIN" -eq 1 && -n "$MAC_VER" && -n "$WIN_VER" && "$MAC_VER" != "$WIN_VER" ]]; then
echo "ERROR: macOS v${MAC_VER} != Windows v${WIN_VER}" >&2
exit 1
fi
GIT_SHA="$(git -C "$WORK_TREE" rev-parse HEAD 2>/dev/null || echo '')"
GIT_SHORT="$(git -C "$WORK_TREE" rev-parse --short HEAD 2>/dev/null || echo '')"
cat > "$ARTIFACT_DIR/build-info.json" <<EOF cat > "$ARTIFACT_DIR/build-info.json" <<EOF
{ {
"version": "$VERSION", "version": "$VERSION",
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# GoldenChart Desktop 설치 파일명 ↔ semver 일치 검사 (부분 문자열 오매칭 방지)
set -euo pipefail
desktop_installer_matches_version() {
local file_name="$1"
local version="$2"
[[ -n "$file_name" && -n "$version" ]] || return 1
local escaped="${version//./\\.}"
[[ "$file_name" =~ GoldenChart[_-]${escaped}([_.-]|\.(dmg|exe)$) ]]
}
desktop_extract_version_from_name() {
local file_name="$1"
if [[ "$file_name" =~ GoldenChart[_-]([0-9]+\.[0-9]+\.[0-9]+) ]]; then
echo "${BASH_REMATCH[1]}"
fi
}
if [[ "${BASH_SOURCE[0]:-}" == "${0:-}" ]]; then
case "${1:-}" in
match)
desktop_installer_matches_version "${2:-}" "${3:-}"
;;
extract)
desktop_extract_version_from_name "${2:-}"
;;
*)
echo "Usage: $0 match <file> <version> | extract <file>" >&2
exit 1
;;
esac
fi
+26 -15
View File
@@ -2,8 +2,8 @@
# Jenkins goldenChart-Desktop-Pipeline — 전체 빌드·배포 진입점 # Jenkins goldenChart-Desktop-Pipeline — 전체 빌드·배포 진입점
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
WORK_TREE="${WORK_TREE:-/Users/aidev/apps/goldenChart}" WORK_TREE="${WORK_TREE:-/Users/aidev/apps/goldenChart}"
ROOT="$WORK_TREE"
export PATH="/opt/homebrew/opt/llvm/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:${PATH:-/usr/bin:/bin}" export PATH="/opt/homebrew/opt/llvm/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:${PATH:-/usr/bin:/bin}"
for prefix in "$HOME/homebrew" /opt/homebrew; do for prefix in "$HOME/homebrew" /opt/homebrew; do
[[ -x "$prefix/bin/brew" ]] && eval "$("$prefix/bin/brew" shellenv)" [[ -x "$prefix/bin/brew" ]] && eval "$("$prefix/bin/brew" shellenv)"
@@ -18,25 +18,25 @@ log "WORK_TREE=$WORK_TREE"
cd "$WORK_TREE" cd "$WORK_TREE"
# Git 동기화 (Jenkins SCM checkout 후에도 file:// bare 와 맞춤) # Git 동기화 (checkout 후 tauri.conf 가 git 버전으로 리셋됨 → bump 필수)
if [[ -d "${GIT_DIR:-/Volumes/ADATA/git/goldenChart.git}" ]]; then if [[ -d "${GIT_DIR:-/Volumes/ADATA/git/goldenChart.git}" ]]; then
git --git-dir="${GIT_DIR:-/Volumes/ADATA/git/goldenChart.git}" \ git --git-dir="${GIT_DIR:-/Volumes/ADATA/git/goldenChart.git}" \
--work-tree="$WORK_TREE" checkout -f main 2>/dev/null || true --work-tree="$WORK_TREE" checkout -f main 2>/dev/null || true
fi fi
chmod +x "$ROOT/scripts/"*.sh 2>/dev/null || true chmod +x "$WORK_TREE/scripts/"*.sh 2>/dev/null || true
# 의존성 (최초 1회 — 이미 있으면 빠르게 통과) # 의존성 (최초 1회 — 이미 있으면 빠르게 통과)
if ! command -v cargo-xwin >/dev/null 2>&1 || ! command -v rustc >/dev/null 2>&1; then if ! command -v cargo-xwin >/dev/null 2>&1 || ! command -v rustc >/dev/null 2>&1; then
log "Desktop build deps 설치..." log "Desktop build deps 설치..."
"$ROOT/scripts/install-desktop-build-deps.sh" "$WORK_TREE/scripts/install-desktop-build-deps.sh"
fi fi
# updater pubkey (repo에 desktop/updater.pub 있으면 patch만) # updater pubkey (repo에 desktop/updater.pub 있으면 patch만)
if [[ -f "$ROOT/desktop/updater.pub" ]]; then if [[ -f "$WORK_TREE/desktop/updater.pub" ]]; then
"$ROOT/scripts/patch-tauri-updater-pubkey.sh" "$WORK_TREE/scripts/patch-tauri-updater-pubkey.sh"
elif [[ -f "${TAURI_SIGNER_KEY:-$HOME/.tauri/goldenchart.key}" ]]; then elif [[ -f "${TAURI_SIGNER_KEY:-$HOME/.tauri/goldenchart.key}" ]]; then
"$ROOT/scripts/setup-desktop-updater-keys.sh" "$WORK_TREE/scripts/setup-desktop-updater-keys.sh"
fi fi
BUILD_ARGS=(--all) BUILD_ARGS=(--all)
@@ -45,25 +45,36 @@ if [[ -n "${DESKTOP_BUILD_WIN_ONLY:-}" ]]; then BUILD_ARGS=(--win); fi
if [[ -n "${BUMP_VERSION:-}" ]]; then if [[ -n "${BUMP_VERSION:-}" ]]; then
BUILD_ARGS+=(--bump-version "$BUMP_VERSION") BUILD_ARGS+=(--bump-version "$BUMP_VERSION")
fi fi
# 버전 bump 는 scripts/build-desktop.sh (CI BUILD_NUMBER) 에서 처리
"$ROOT/scripts/build-desktop.sh" "${BUILD_ARGS[@]}" export WORK_TREE
export BUILD_NUMBER="${BUILD_NUMBER:-}"
export DESKTOP_BUMP_ON_CI="${DESKTOP_BUMP_ON_CI:-1}"
# mobile pipeline 과 동일: git checkout 직후 semver bump (0.1.4 고정 tauri.conf 방지)
if [[ -z "${BUMP_VERSION:-}" ]] && [[ -n "${BUILD_NUMBER:-}" ]] && [[ "${DESKTOP_BUMP_ON_CI:-1}" == "1" ]]; then
log "CI Desktop version bump..."
BUMPED="$("$WORK_TREE/scripts/bump-desktop-version-from-release.sh")"
export DESKTOP_SKIP_VERSION_BUMP=1
log "Desktop version → $BUMPED"
fi
"$WORK_TREE/scripts/build-desktop.sh" "${BUILD_ARGS[@]}"
WORK_TREE="$WORK_TREE" \ WORK_TREE="$WORK_TREE" \
ARTIFACT_DIR="$ROOT/dist-desktop" \ ARTIFACT_DIR="$WORK_TREE/dist-desktop" \
DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" \ DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" \
TAURI_SIGNER_KEY="${TAURI_SIGNER_KEY:-$HOME/.tauri/goldenchart.key}" \ TAURI_SIGNER_KEY="${TAURI_SIGNER_KEY:-$HOME/.tauri/goldenchart.key}" \
DESKTOP_RELEASE_NOTES="${DESKTOP_RELEASE_NOTES:-Build ${BUILD_NUMBER:-local}}" \ DESKTOP_RELEASE_NOTES="${DESKTOP_RELEASE_NOTES:-Build ${BUILD_NUMBER:-local}}" \
"$ROOT/scripts/publish-desktop-update.sh" "$WORK_TREE/scripts/publish-desktop-update.sh"
# latest.json·설치 파일 nginx 즉시 반영 (볼륨 마운트) # latest.json·설치 파일 nginx 즉시 반영 (볼륨 마운트)
if [[ -x "$ROOT/scripts/reload-frontend-desktop-static.sh" ]]; then if [[ -x "$WORK_TREE/scripts/reload-frontend-desktop-static.sh" ]]; then
WORK_TREE="$WORK_TREE" "$ROOT/scripts/reload-frontend-desktop-static.sh" || true WORK_TREE="$WORK_TREE" "$WORK_TREE/scripts/reload-frontend-desktop-static.sh" || true
fi fi
# Jenkins artifact archive 경로 # Jenkins artifact archive 경로
if [[ -d "$ROOT/dist-desktop" ]]; then if [[ -d "$WORK_TREE/dist-desktop" ]]; then
log "Artifacts ready: $ROOT/dist-desktop" log "Artifacts ready: $WORK_TREE/dist-desktop"
fi fi
# 웹 frontend Docker 재배포 (latest.json static 포함) — 선택 # 웹 frontend Docker 재배포 (latest.json static 포함) — 선택
+48 -13
View File
@@ -2,26 +2,29 @@
# GoldenChart Desktop — updater 서명 + latest.json + 웹 static 배포 # GoldenChart Desktop — updater 서명 + latest.json + 웹 static 배포
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)" SCRIPT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
ARTIFACT_DIR="${ARTIFACT_DIR:-$ROOT/dist-desktop}" WORK_TREE="${WORK_TREE:-$SCRIPT_ROOT}"
ARTIFACT_DIR="${ARTIFACT_DIR:-$WORK_TREE/dist-desktop}"
UPDATES_SRC="${UPDATES_SRC:-$ARTIFACT_DIR/updates}" UPDATES_SRC="${UPDATES_SRC:-$ARTIFACT_DIR/updates}"
UPDATES_DIR="${UPDATES_DIR:-${WORK_TREE:-$ROOT}/frontend/public/desktop/updates}" UPDATES_DIR="${UPDATES_DIR:-$WORK_TREE/frontend/public/desktop/updates}"
WEB_STATIC_DIR="${WEB_STATIC_DIR:-$UPDATES_DIR}" WEB_STATIC_DIR="${WEB_STATIC_DIR:-$UPDATES_DIR}"
BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}"
BASE_URL="${BASE_URL/http:\/\//https:\/\/}" BASE_URL="${BASE_URL/http:\/\//https:\/\/}"
DESKTOP_RELEASE_DIR="${GC_DESKTOP_APP_RELEASE_DIR:-$ROOT/data/desktop-releases}" DESKTOP_RELEASE_DIR="${GC_DESKTOP_APP_RELEASE_DIR:-$WORK_TREE/data/desktop-releases}"
KEY_PATH="${TAURI_SIGNER_KEY:-$HOME/.tauri/goldenchart.key}" KEY_PATH="${TAURI_SIGNER_KEY:-$HOME/.tauri/goldenchart.key}"
DESKTOP_DIR="$ROOT/desktop" DESKTOP_DIR="$WORK_TREE/desktop"
CONF="$DESKTOP_DIR/src-tauri/tauri.conf.json" CONF="$DESKTOP_DIR/src-tauri/tauri.conf.json"
BUILD_INFO="$ARTIFACT_DIR/build-info.json" BUILD_INFO="$ARTIFACT_DIR/build-info.json"
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:${PATH:-/usr/bin:/bin}" export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:${PATH:-/usr/bin:/bin}"
# shellcheck disable=SC1091 # shellcheck disable=SC1091
[[ -f "$HOME/.cargo/env" ]] && source "$HOME/.cargo/env" [[ -f "$HOME/.cargo/env" ]] && source "$HOME/.cargo/env"
# shellcheck disable=SC1091
source "$WORK_TREE/scripts/desktop-installer-version.sh"
log() { echo "[publish-desktop] $*"; } log() { echo "[publish-desktop] $*"; }
# CI bump 후 deploy checkout 으로 tauri.conf가 되돌아갈 수 있음 — build-info 우선 # build-info 가 단일 버전 소스 (tauri.conf 는 git checkout 으로 구버전일 수 있음)
VERSION="$(node -p "require('$CONF').version" 2>/dev/null || echo '0.1.0')" VERSION="$(node -p "require('$CONF').version" 2>/dev/null || echo '0.1.0')"
if [[ -f "$BUILD_INFO" ]]; then if [[ -f "$BUILD_INFO" ]]; then
VERSION="$(node -e "try{console.log(JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).version||'')}catch{}" "$BUILD_INFO" 2>/dev/null || echo "$VERSION")" VERSION="$(node -e "try{console.log(JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).version||'')}catch{}" "$BUILD_INFO" 2>/dev/null || echo "$VERSION")"
@@ -30,8 +33,10 @@ fi
NOTES="${DESKTOP_RELEASE_NOTES:-GoldenChart Desktop $VERSION}" NOTES="${DESKTOP_RELEASE_NOTES:-GoldenChart Desktop $VERSION}"
log "=== Publish Desktop v$VERSION ===" log "=== Publish Desktop v$VERSION ==="
log "Work tree: $WORK_TREE"
log "Updates src: $UPDATES_SRC" log "Updates src: $UPDATES_SRC"
log "Web static: $WEB_STATIC_DIR" log "Web static: $WEB_STATIC_DIR"
log "Release dir: $DESKTOP_RELEASE_DIR"
mkdir -p "$WEB_STATIC_DIR" mkdir -p "$WEB_STATIC_DIR"
@@ -46,16 +51,18 @@ prune_old_installers() {
} }
# 설치 파일(dist-desktop/*.dmg, *setup.exe) 복사 — nginx static + backend API 디렉터리 # 설치 파일(dist-desktop/*.dmg, *setup.exe) 복사 — nginx static + backend API 디렉터리
MAC_VER="" WIN_VER=""
INSTALLER_COUNT=0
if [[ -d "$ARTIFACT_DIR" ]]; then if [[ -d "$ARTIFACT_DIR" ]]; then
mkdir -p "$DESKTOP_RELEASE_DIR" mkdir -p "$DESKTOP_RELEASE_DIR"
prune_old_installers "$DESKTOP_RELEASE_DIR" prune_old_installers "$DESKTOP_RELEASE_DIR"
prune_old_installers "$WEB_STATIC_DIR" prune_old_installers "$WEB_STATIC_DIR"
INSTALLER_COUNT=0
while IFS= read -r f; do while IFS= read -r f; do
[[ -z "$f" ]] && continue [[ -z "$f" ]] && continue
base="$(basename "$f")" base="$(basename "$f")"
if [[ "$base" != *"${VERSION}"* ]]; then if ! desktop_installer_matches_version "$base" "$VERSION"; then
log "[WARN] skip installer (version mismatch): $base (expected v${VERSION})" log "[WARN] skip installer (version mismatch): $base (expected v${VERSION})"
continue continue
fi fi
@@ -63,10 +70,28 @@ if [[ -d "$ARTIFACT_DIR" ]]; then
cp -f "$f" "$DESKTOP_RELEASE_DIR/" cp -f "$f" "$DESKTOP_RELEASE_DIR/"
log "Installer: $base" log "Installer: $base"
INSTALLER_COUNT=$((INSTALLER_COUNT + 1)) INSTALLER_COUNT=$((INSTALLER_COUNT + 1))
case "$base" in
*.dmg) MAC_VER="$(desktop_extract_version_from_name "$base")" ;;
*setup.exe) WIN_VER="$(desktop_extract_version_from_name "$base")" ;;
esac
done < <(find "$ARTIFACT_DIR" -maxdepth 1 -type f \( -name '*.dmg' -o -name '*setup.exe' \) 2>/dev/null) done < <(find "$ARTIFACT_DIR" -maxdepth 1 -type f \( -name '*.dmg' -o -name '*setup.exe' \) 2>/dev/null)
if [[ "$INSTALLER_COUNT" -eq 0 ]]; then if [[ "$INSTALLER_COUNT" -eq 0 ]]; then
log "[WARN] v${VERSION} 설치 파일(dmg/exe) 없음 — latest.json 만 갱신될 수 있음" log "ERROR: v${VERSION} 설치 파일(dmg/exe) 없음 — latest.json 만 갱신하면 UI 버전이 어긋납니다." >&2
exit 1
fi
if [[ -n "$MAC_VER" && "$MAC_VER" != "$VERSION" ]]; then
log "ERROR: macOS installer v${MAC_VER} != publish v${VERSION}" >&2
exit 1
fi
if [[ -n "$WIN_VER" && "$WIN_VER" != "$VERSION" ]]; then
log "ERROR: Windows installer v${WIN_VER} != publish v${VERSION}" >&2
exit 1
fi
if [[ -n "$MAC_VER" && -n "$WIN_VER" && "$MAC_VER" != "$WIN_VER" ]]; then
log "ERROR: macOS v${MAC_VER} != Windows v${WIN_VER}" >&2
exit 1
fi fi
if [[ -f "$BUILD_INFO" ]]; then if [[ -f "$BUILD_INFO" ]]; then
@@ -102,9 +127,9 @@ if [[ -f "$BUILD_INFO" ]]; then
cp -f "$BUILD_INFO" "$PUBLISH_UPDATES/build-info.json" cp -f "$BUILD_INFO" "$PUBLISH_UPDATES/build-info.json"
fi fi
# latest.json # latest.json — 설치 파일과 동일 semver
set +e set +e
node "$ROOT/scripts/generate-desktop-latest-json.mjs" \ node "$WORK_TREE/scripts/generate-desktop-latest-json.mjs" \
--version "$VERSION" \ --version "$VERSION" \
--dir "$PUBLISH_UPDATES" \ --dir "$PUBLISH_UPDATES" \
--base-url "$BASE_URL" \ --base-url "$BASE_URL" \
@@ -113,12 +138,22 @@ GEN_RC=$?
set -e set -e
if [[ "$GEN_RC" -eq 2 ]]; then if [[ "$GEN_RC" -eq 2 ]]; then
log "[WARN] signed updater bundle 없음 — latest.json 유지 (placeholder 덮어쓰기 안 함)" log "[WARN] signed updater bundle 없음 — latest.json 은 v${VERSION} build-info 기준 유지"
if [[ -f "$BUILD_INFO" ]]; then
node -e "
const fs=require('fs'); const p=process.argv[1]; const v=process.argv[2]; const u=process.argv[3]; const n=process.argv[4];
const latestPath=p+'/latest.json';
let cur={version:v,notes:n,pub_date:new Date().toISOString(),platforms:{}};
if(fs.existsSync(latestPath)){try{cur=JSON.parse(fs.readFileSync(latestPath,'utf8'));cur.version=v;cur.notes=n;}catch{}}
fs.writeFileSync(latestPath, JSON.stringify(cur,null,2)+'\n');
console.log('latest.json version →', v);
" "$PUBLISH_UPDATES" "$VERSION" "$BASE_URL" "$NOTES"
fi
elif [[ "$GEN_RC" -ne 0 ]]; then elif [[ "$GEN_RC" -ne 0 ]]; then
exit "$GEN_RC" exit "$GEN_RC"
fi fi
log "Published:" log "Published v${VERSION}:"
ls -la "$PUBLISH_UPDATES" ls -la "$PUBLISH_UPDATES"
log "nginx: /desktop/updates/ → frontend/public/desktop/updates (Docker rebuild 시 반영)" log "nginx: /desktop/updates/ → frontend/public/desktop/updates (Docker rebuild 시 반영)"