local-tool/: visionA-local desktop app
- M1: Wails shell + Go server + Next.js UI + Mock mode (macOS dmg ready)
- M2: i18n (zh-TW/en) + Settings 4-tab refactor
- M3: Embedded Python 3.12 runtime (python-build-standalone) + KneronPLUS wheels
- M4: Windows Inno Setup script (build on Windows runner)
- M5: Linux AppImage script + udev rule (build on Linux runner)
- M6: ffmpeg (GPL, pending legal review) + yt-dlp bundled
- Lifecycle: watchServer health check, fatal native dialog,
Wails IPC raise endpoint, stale process cleanup
.autoflow/: full PRD / Design Spec / Architecture / Testing docs
(4 rounds tri-party discussion + cross review)
.github/workflows/: macOS / Windows / Linux build CI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.7 KiB
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 建置流程
# 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 關鍵設定
{
"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-localCFBundleExecutable = visiona-local(binary 全小寫;CFBundleName/CFBundleDisplayName仍為visionA-local作為顯示名)NSHighResolutionCapable = trueLSMinimumSystemVersion = 14.0NSCameraUsageDescription = visionA-local 需要存取攝影機以執行 AI 推論展示NSAppleEventsUsageDescription = visionA-local 需要使用 AppleScript 開啟瀏覽器- 不需要
NSAppTransportSecurity因為只用 localhost
2.4 dmgbuild 設定檔(dmg-config.py)
# 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 與首次啟動說明頁):
- 在 Finder 中對
visiona-local.app按右鍵 → 開啟 - 在警告對話框按「開啟」
- 之後可以正常雙擊開啟
或命令列: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 建置流程
# 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 骨架
[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.
解法(寫進安裝說明):
- 點「更多資訊」
- 點「仍要執行」
使用者第一次下載後會遇到這個警告,執行過一次之後 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 建置流程
# 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 <<EOF
[Desktop Entry]
Type=Application
Name=visionA-local
Exec=visiona-local
Icon=visiona-local
Categories=Development;
EOF
cat > 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 預設可用),執行:
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. 首次啟動警告匯總(使用者要看到的文件)
必須在以下位置寫清楚:
README.md(GitHub / Gitea 發布頁)- 下載頁(Gitea Release 描述)
- 首次啟動歡迎頁(如果有的話,可跳過)
內容模板:
## 首次啟動提示
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。