#!/usr/bin/env bash # GoldenChart Desktop — macOS Jenkins 빌드 (macOS dmg + Windows NSIS cross-build) set -euo pipefail SCRIPT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" WORK_TREE="${WORK_TREE:-$SCRIPT_ROOT}" cd "$WORK_TREE" # shellcheck disable=SC1091 source "$WORK_TREE/scripts/desktop-dev-path.sh" # shellcheck disable=SC1091 source "$WORK_TREE/scripts/desktop-installer-version.sh" # shellcheck disable=SC1091 [[ -f "$HOME/.cargo/env" ]] && source "$HOME/.cargo/env" BUILD_MAC=1 BUILD_WIN=1 BUMP_VERSION="${BUMP_VERSION:-}" while [[ $# -gt 0 ]]; do case "$1" in --mac) BUILD_WIN=0; shift ;; --win) BUILD_MAC=0; shift ;; --all) BUILD_MAC=1; BUILD_WIN=1; shift ;; --bump-version) BUMP_VERSION="$2"; shift 2 ;; *) echo "Unknown arg: $1"; exit 1 ;; esac done echo "=== GoldenChart Desktop Build ===" echo "Work tree: $WORK_TREE" echo "Git: $(git -C "$WORK_TREE" rev-parse --short HEAD 2>/dev/null || echo 'n/a')" need_cmd() { if ! command -v "$1" >/dev/null 2>&1; then echo "Missing: $1 — run ./scripts/install-desktop-build-deps.sh" exit 1 fi } need_cmd node need_cmd npm need_cmd cargo need_cmd rustc if [[ "$BUILD_WIN" -eq 1 ]]; then need_cmd cargo-xwin rustup target add x86_64-pc-windows-msvc 2>/dev/null || true fi CONF="$WORK_TREE/desktop/src-tauri/tauri.conf.json" # CI patch bump (jenkins-desktop-pipeline 에서 선행 bump 시 DESKTOP_SKIP_VERSION_BUMP=1) if [[ "${DESKTOP_SKIP_VERSION_BUMP:-}" != "1" ]] \ && [[ -z "${BUMP_VERSION:-}" ]] \ && [[ "${DESKTOP_BUMP_ON_CI:-1}" == "1" ]] \ && [[ -n "${BUILD_NUMBER:-}${DESKTOP_AUTO_BUMP:-}" ]]; then chmod +x "$WORK_TREE/scripts/bump-desktop-version-from-release.sh" export WORK_TREE BUMPED="$("$WORK_TREE/scripts/bump-desktop-version-from-release.sh")" echo "Desktop CI version bump → $BUMPED" fi if [[ -n "$BUMP_VERSION" ]]; then node -e " const fs=require('fs'); const p=process.argv[1]; const b=process.argv[2]; const c=JSON.parse(fs.readFileSync(p,'utf8')); const [a,x,y]=c.version.split('.').map(Number); let nv=c.version; if(b==='patch') nv=[a,x,y+1].join('.'); else if(b==='minor') nv=[a,x+1,0].join('.'); else if(b==='major') nv=[a+1,0,0].join('.'); else nv=b; c.version=nv; fs.writeFileSync(p, JSON.stringify(c,null,2)+'\n'); console.log('version', nv); " "$CONF" "$BUMP_VERSION" fi VERSION="$(node -p "require('$CONF').version")" echo "Version: $VERSION" node -e " const fs = require('fs'); const confPath = process.argv[1]; const version = process.argv[2]; const title = 'GoldenChart (v' + version + ')'; const conf = JSON.parse(fs.readFileSync(confPath, 'utf8')); if (conf.app?.windows?.[0]) conf.app.windows[0].title = title; fs.writeFileSync(confPath, JSON.stringify(conf, null, 2) + '\n'); console.log('window title →', title); " "$CONF" "$VERSION" if [[ -f "$WORK_TREE/scripts/patch-tauri-updater-pubkey.sh" ]]; then chmod +x "$WORK_TREE/scripts/patch-tauri-updater-pubkey.sh" fi chmod +x "$WORK_TREE/scripts/prepare-tauri-build.sh" 2>/dev/null || true # shellcheck disable=SC1091 source "$WORK_TREE/scripts/prepare-tauri-build.sh" npm install echo "--- Vite build (desktop) ---" export DESKTOP_APP_VERSION="$VERSION" npm run build -w @goldenchart/desktop DESKTOP_DIR="$WORK_TREE/desktop" TAURI_TARGET="$DESKTOP_DIR/src-tauri/target" ARTIFACT_DIR="${ARTIFACT_DIR:-$WORK_TREE/dist-desktop}" UPDATES_DIR="$ARTIFACT_DIR/updates" rm -rf "$ARTIFACT_DIR" mkdir -p "$ARTIFACT_DIR" "$UPDATES_DIR" collect_updater_artifacts() { find "$TAURI_TARGET" \( \ -name '*.app.tar.gz' -o \ -name '*.tar.gz' -path '*/bundle/macos/*' -o \ -name '*.nsis.zip' -o \ -name '*-setup.nsis.zip' -o \ -name '*-setup.exe' -path '*/bundle/nsis/*' \ \) -type f 2>/dev/null | while read -r f; do cp -f "$f" "$UPDATES_DIR/" [[ -f "${f}.sig" ]] && cp -f "${f}.sig" "$UPDATES_DIR/" echo " updater bundle: $(basename "$f")" done } copy_installer_matching_version() { local kind="$1" shift local found="" while IFS= read -r f; do [[ -z "$f" ]] && continue if desktop_installer_matches_version "$(basename "$f")" "$VERSION"; then found="$f" break fi done < <("$@") if [[ -z "$found" ]]; then echo "ERROR: ${kind} installer for v${VERSION} not found (stale target cache ignored)." >&2 echo " Searched: $*" >&2 return 1 fi cp -f "$found" "$ARTIFACT_DIR/" echo " ${kind} installer: $(basename "$found")" } if [[ "$BUILD_MAC" -eq 1 ]]; then echo "--- Tauri build (macOS) ---" find "$TAURI_TARGET/release/bundle" -name '*.dmg' -delete 2>/dev/null || true (cd "$DESKTOP_DIR" && npm run tauri:build:mac) copy_installer_matching_version "macOS" \ find "$TAURI_TARGET/release/bundle" -name '*.dmg' 2>/dev/null collect_updater_artifacts fi if [[ "$BUILD_WIN" -eq 1 ]]; then echo "--- Tauri build (Windows NSIS cross) ---" find "$TAURI_TARGET" -path '*x86_64-pc-windows-msvc*' -name '*-setup.exe' -delete 2>/dev/null || true (cd "$DESKTOP_DIR" && npm run tauri:build:win) copy_installer_matching_version "Windows" \ find "$TAURI_TARGET" -path '*x86_64-pc-windows-msvc*' -name '*-setup.exe' 2>/dev/null collect_updater_artifacts fi # mac·win 설치 파일 semver 일치 검증 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" </dev/null || true echo "Done."