# Packaging — visionA-local > macOS .dmg、Windows Inno Setup .exe、Linux AppImage 的具體打包流程。 > **無任何程式碼簽章**(使用者決策 Q2 = C)。 --- ## 1. 總覽:三平台產出物 | 平台 | 格式 | 簽章 | 大小(預估) | |------|------|------|------------| | macOS 14/15 x86_64 | `visiona-local-v{ver}-macos-x64.dmg` 內含 `.app` | ad-hoc(`codesign -s -`) | ~195MB | | Windows 10/11 x64 | `visiona-local-v{ver}-windows-x64.exe`(Inno Setup installer) | **無** | ~205MB | | Ubuntu 22.04/24.04 x64 | `visiona-local-v{ver}-linux-x64.AppImage` | 無 | ~210MB | ## 2. macOS:.app + .dmg ### 2.1 建置流程 ```bash # 1. 準備 payload make payload-macos # 把 python + wheels + ffmpeg + models 塞到 visiona-local/payload/ # 2. Wails 編譯 cd visiona-local && wails build -platform darwin/amd64 -clean # 3. ad-hoc sign codesign --force --deep --sign - build/bin/visiona-local.app # 4. 驗證簽章 codesign -dv --verbose=4 build/bin/visiona-local.app # 5. 打包成 dmg dmgbuild -s dmg-config.py "visionA-local" dist/visiona-local-v${VERSION}-macos-x64.dmg # 顯示標題沿用產品名 visionA-local ``` ### 2.2 `wails.json` 關鍵設定 ```json { "name": "visiona-local", "outputfilename": "visiona-local", "frontend:install": "echo skip", "frontend:build": "echo skip", "info": { "companyName": "Innovedus", "productName": "visionA-local", "productVersion": "1.0.0", "copyright": "© 2026 Innovedus", "comments": "Kneron KL520/KL720 本地開發工具" }, "nsisType": "multiple", "appid": "com.innovedus.visiona-local" } ``` ### 2.3 Info.plist 關鍵項 - `CFBundleIdentifier = com.innovedus.visiona-local` - `CFBundleExecutable = visiona-local`(binary 全小寫;`CFBundleName` / `CFBundleDisplayName` 仍為 `visionA-local` 作為顯示名) - `NSHighResolutionCapable = true` - `LSMinimumSystemVersion = 14.0` - `NSCameraUsageDescription = visionA-local 需要存取攝影機以執行 AI 推論展示` - `NSAppleEventsUsageDescription = visionA-local 需要使用 AppleScript 開啟瀏覽器` - **不需要** `NSAppTransportSecurity` 因為只用 localhost ### 2.4 dmgbuild 設定檔(`dmg-config.py`) ```python # dmg-config.py format = 'UDBZ' # bzip2 壓縮,壓縮率好 size = '400M' files = ['build/bin/visiona-local.app'] symlinks = {'Applications': '/Applications'} badge_icon = 'build/icon.icns' icon_locations = { 'visiona-local.app': (150, 200), 'Applications': (450, 200), } window_rect = ((200, 200), (600, 400)) background = 'build/dmg-background.png' ``` ### 2.5 首次啟動:Gatekeeper workaround **因為沒有 notarization,使用者第一次打開 .app 會跳警告:** > "visionA-local" can't be opened because it is from an unidentified developer. **解法(必須寫進 README 與首次啟動說明頁):** 1. 在 Finder 中對 `visiona-local.app` **按右鍵 → 開啟** 2. 在警告對話框按「開啟」 3. 之後可以正常雙擊開啟 或命令列:`xattr -d com.apple.quarantine /Applications/visiona-local.app` ## 3. Windows:Inno Setup .exe ### 3.1 為何選 Inno Setup 而不是 NSIS / MSI - **UI 現代化**:Inno Setup 6 的預設樣式比 NSIS 好看 - **腳本簡單**:Pascal-like DSL,比 NSIS 的 MakeNSIS 好維護 - **安裝 driver 方便**:內建 `DriverInstall` 流程支援 pnputil - **不用 MSI**:MSI 需要 WiX 工具鏈與 signing 才順,我們不簽章,Inno Setup 更簡單 ### 3.2 建置流程 ```bash # 1. 準備 payload make payload-windows # 2. Wails 編譯 cd visiona-local && wails build -platform windows/amd64 -clean # 3. 執行 Inno Setup Compiler iscc visiona-local-installer.iss # 產物:dist/visiona-local-v{ver}-windows-x64.exe ``` ### 3.3 `visiona-local-installer.iss` 骨架 ```pascal [Setup] AppId={{A7F3E891-4B2C-4D5E-9F1A-8B3C2D1E0F9A} AppName=visionA-local AppVersion=1.0.0 AppPublisher=Innovedus AppPublisherURL=https://innovedus.com DefaultDirName={autopf}\visiona-local DefaultGroupName=visiona-local OutputDir=..\dist OutputBaseFilename=visiona-local-v1.0.0-windows-x64 SetupIconFile=assets\icon.ico Compression=lzma2/ultra64 SolidCompression=yes WizardStyle=modern PrivilegesRequired=admin ArchitecturesInstallIn64BitMode=x64 MinVersion=10.0.17763 [Files] Source: "visiona-local\build\bin\visiona-local.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "visiona-local\payload\*"; DestDir: "{app}\payload"; Flags: ignoreversion recursesubdirs [Icons] Name: "{group}\visionA-local"; Filename: "{app}\visiona-local.exe" Name: "{autodesktop}\visionA-local"; Filename: "{app}\visiona-local.exe"; Tasks: desktopicon [Tasks] Name: "desktopicon"; Description: "建立桌面捷徑"; GroupDescription: "附加選項:" [Run] ; 安裝 WinUSB driver Filename: "{sys}\pnputil.exe"; \ Parameters: "/add-driver ""{app}\payload\drivers\kneron_winusb.inf"" /install"; \ StatusMsg: "正在安裝 Kneron USB driver..."; \ Flags: runhidden waituntilterminated ; 啟動 app Filename: "{app}\visiona-local.exe"; \ Description: "啟動 visionA-local"; \ Flags: postinstall nowait skipifsilent [UninstallDelete] Type: filesandordirs; Name: "{localappdata}\visiona-local" ``` ### 3.4 SmartScreen 警告處理 **沒有 Authenticode 簽章,Windows SmartScreen 會擋:** > Windows protected your PC — Microsoft Defender SmartScreen prevented an unrecognized app from starting. **解法(寫進安裝說明):** 1. 點「更多資訊」 2. 點「仍要執行」 使用者第一次下載後會遇到這個警告,執行過一次之後 Windows 會記住。這是可接受的摩擦成本(使用者決策)。 ## 4. Linux:AppImage ### 4.1 為何選 AppImage 而不是 .deb / snap / flatpak - **單檔可攜**:一個 `.AppImage` 檔案,雙擊即跑,不需安裝 - **跨發行版**:只要 glibc >= 2.28(Ubuntu 18.04+)就能跑 - **不需 sudo**(正常情境下) - **符合「像一般 app」的體驗** `.deb` 需要 apt install + sudo;snap / flatpak 需要發到 store 或有 runtime 依賴,不適合內部工具分發。 ### 4.2 建置流程 ```bash # 1. 準備 payload make payload-linux # 2. Wails 編譯 cd visiona-local && wails build -platform linux/amd64 -clean # 3. 建立 AppDir rm -rf AppDir && mkdir -p AppDir/usr/bin AppDir/usr/lib cp build/bin/visiona-local AppDir/usr/bin/ cp -r payload AppDir/usr/bin/payload # 4. 複製 libusb 到 AppDir(避免依賴系統 libusb) cp /usr/lib/x86_64-linux-gnu/libusb-1.0.so.0 AppDir/usr/lib/ # 5. 寫入 .desktop 與 AppRun cat > AppDir/visiona-local.desktop < AppDir/AppRun <<'EOF' #!/bin/bash HERE="$(dirname "$(readlink -f "$0")")" export LD_LIBRARY_PATH="$HERE/usr/lib:${LD_LIBRARY_PATH}" exec "$HERE/usr/bin/visiona-local" "$@" EOF chmod +x AppDir/AppRun cp assets/icon.png AppDir/visiona-local.png # 6. 用 appimagetool 打包 ARCH=x86_64 appimagetool AppDir dist/visiona-local-v${VERSION}-linux-x64.AppImage ``` ### 4.3 首次執行需要的權限 因為要寫入 `/etc/udev/rules.d/99-kneron.rules` 讓非 root 使用者存取 USB: **首次執行時跳 pkexec 提權對話框(GNOME 預設可用),執行:** ```bash pkexec cp ~/.local/share/visiona-local/scripts/99-kneron.rules /etc/udev/rules.d/ pkexec udevadm control --reload-rules pkexec udevadm trigger ``` 使用者拒絕或系統沒有 pkexec(例如 minimal server 安裝):顯示錯誤訊息提示手動執行 `sudo ./install-udev.sh`,內附的 script 由我們提供。 ### 4.4 AppImage 的已知限制 - **沒有 .desktop integration**:使用者要自己放到 `~/Applications/` 或用 `appimaged` - **第一次解壓慢**:AppImage 是 squashfs,第一次啟動會把內容 mount 到 `/tmp/.mount_*/` ## 5. 圖示與品牌資產 ### 5.1 檔案清單 | 平台 | 檔案 | 尺寸 | |------|------|------| | macOS | `visiona-local.icns` | 512×512 @1x + @2x | | Windows | `visiona-local.ico` | 16, 32, 48, 64, 128, 256 | | Linux | `visiona-local.png` | 256×256 | > Tray 圖示已移除(第三輪使用者決策 Q-A=A3:砍 tray)。 ### 5.2 來源 使用者決策 Q14:**先沿用 edge-ai-platform 既有視覺(字母 E)**,品牌換名但圖示暫時不動。 從 `edge-ai-platform/installer/frontend/src-tauri/icons/` 直接複製(`server/tray/assets/` 不再使用)。 ## 6. 首次啟動警告匯總(使用者要看到的文件) 必須在以下位置寫清楚: 1. `README.md`(GitHub / Gitea 發布頁) 2. 下載頁(Gitea Release 描述) 3. 首次啟動歡迎頁(如果有的話,可跳過) **內容模板:** ```markdown ## 首次啟動提示 visionA-local 是內部工具,**沒有 Apple / Microsoft 的程式碼簽章**。 首次執行時作業系統可能會顯示警告,這是正常的。 ### macOS 在 Finder 中找到 `visiona-local.app`,**按右鍵 → 開啟**,在彈出對話框中選「開啟」。 之後可以正常雙擊開啟。 ### Windows 執行安裝檔時若看到 "Windows 已保護您的電腦" 警告: 點「更多資訊」→「仍要執行」。 ### Linux AppImage 預設沒有執行權限: ``` chmod +x visiona-local-v1.0.0-linux-x64.AppImage ./visiona-local-v1.0.0-linux-x64.AppImage ``` ``` ## 7. 發布流程 | 步驟 | 負責 | 工具 | |------|------|------| | 1. Tag release | 開發者 | `git tag v1.0.0 && git push --tags` | | 2. CI build macOS | GitHub Actions / local | `make installer-macos` | | 3. CI build Windows | GitHub Actions / local | `make installer-windows` | | 4. CI build Linux | GitHub Actions / Docker | `make installer-linux` | | 5. 上傳到 Gitea Release | 開發者 | `gh release create` 或手動 | | 6. 更新 `latest.json`(如果未來做 auto-update) | - | 目前不做 | **不建置 universal / multi-arch**,只有 x64。