jim800121chen c54f16fca0 Initial commit: visionA monorepo with local-tool subproject
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>
2026-04-11 22:10:38 +08:00

8.8 KiB
Raw Permalink Blame History

M1-10 Review — 改寫 app.go + Python 雙策略空殼 + 生命週期邏輯

審查日期2026-04-10 審查對象/Users/jimchen/visionA/local-tool/visiona-local/app.go 重寫、main.goplatform_*.goembed.go 已刪) 任務描述:把原 installer 的肥胖 app.go 整份砍掉重寫留下啟動殼層single-instance、舊路徑遷移、port picking、Python 雙策略空殼、spawn server 子行程、graceful shutdown、前端 binding

結論: 通過

全部檢查點過關。舊 installerRelay / Gitea / Tray / Cluster / Firmware / auto-update / libusb / ffmpeg setup / installer wizard完全清光go build ./...go vet ./... 都乾淨通過;三平台資料路徑與 tray-and-lifecycle.md §6 完全一致Python 雙策略介面符合 dependency-bundling.md §1.5生命週期邏輯lock、migrate、port、start/stop、health都照 TDD 落地。Backend 回報的五個 TODO 均屬 M1+/M2 範圍,不阻斷 M1-10 驗收。可以進入 M1-12wails build + dmg


檢查清單

1. 平台資料路徑正確

平台 期望 實際(platform_*.go + appName 狀態
macOS ~/Library/Application Support/visiona-local filepath.Join(home, "Library", "Application Support", appName)appName=visiona-local
Linux $XDG_DATA_HOME/visiona-local → fallback ~/.local/share/visiona-local XDG 檢查齊全fallback 正確
Windows %APPDATA%\visiona-local os.Getenv("APPDATA") + home fallback

殘留舊名檢查:只在 oldDataDirCandidates() 中出現 .edge-ai-platform / visionA-local(駝峰)/ EdgeAIPlatform,全部為 migration source path屬正確用法。active 路徑無殘留。

2. 被砍功能確實不在

grep 掃過整個 visiona-local/*.go

被砍項目 出現位置 狀態
Relay
Gitea
Tray 只出現在檔頭註解提到「tray 已被整份刪除」 無實作
Cluster
Firmware
auto-update 只出現在檔頭「已刪除」註解 無實作
libusb
ffmpeg setup
installer wizard 只出現在檔頭「已刪除」註解 無實作
embed.go 檔案已刪
Installer 型別

main.goBind: []interface{}{app} 只綁 AppNewApp()),不是舊的 Installer

3. Python 雙策略介面

對照 dependency-bundling.md §1.5 決策樹:

檢查項 實作 狀態
PythonMode const auto / bundled / system 三值齊全
ensurePythonRuntime(auto) → 先 system fallback bundled case PythonModeAutofindSystemPython()ensureBundledPython() (符合 R4 決策M1 先 system
findSystemPython() 檢查 ≥ 3.10 isPython310OrNewer() 解析 Python 3.X.Y 並比對
候選名單 python3.12 / 3.11 / 3.10 / python3 / python
避開 Windows Store stub strings.Contains(strings.ToLower(p), "windowsapps") → skip
ensureBundledPython() 為 placeholder 直接回 "bundled python runtime not yet implemented (M2 feature)" 並附 TODO(M2) 註解指向 §1.3

小建議(不阻擋)findSystemPython() 沒有把 venv 建立 / wheel 安裝包進去這是刻意的——M1 範圍只要「找到 interpreter 並把路徑傳給 server」venv/wheels 留到 M2 由 bundled flow 一起處理。符合 M1 縮限範圍。

4. 生命週期邏輯

對照 tray-and-lifecycle.md

檢查項 § 實作 狀態
acquireSingleInstance lock + PID check §2.2 `O_CREATE O_EXCL+ read PID +processAlive` + stale lock 清理
processAlive 跨平台 §2.4 Unix: proc.Signal(0)Windows: tasklist /FI "PID eq ..."
喚起既有 instance §2.3 tryRaiseExistingInstance.ipc-port 並打 /api/system/healthM1+ 才改成 /ipc/raise,已有 TODO 註解) M1 可接受)
migrateOldDataDirs 在 lock 之前 §4.5 startup() 順序:MkdirAllmigrateacquireSingleInstance
遷移失敗不擋啟動 §4.5 continue + stderr 警告
新路徑已存在不覆蓋 §4.5 檢查空目錄才 os.Remove + Rename (比 TDD 稍寬鬆但更實用:允許剛 MkdirAll 的空資料夾被替換)
.migrated-from breadcrumb §4.5 寫入 old\n + RFC3339 time
pickPort(3721) §3.2 defaultPreferredPort=3721 起跳,掃 20 個
isOurStaleServer 偵測 §3.2 尚未實作,已註 TODO(M1+) ⚠️ 非阻斷Backend 回報 known TODO
startServer 流程 §4.1 ensurePython → pickPort → locateBinary → spawn → writeIPCPort → waitHealthy
stopServer / ServerProcess.stop() SIGTERM → grace → SIGKILL §4.1 shutdownGracePeriod=5s,超時後 Kill + <-doneWindows 分支直接 Kill TDD 寫 3s實作 5s 更寬鬆,可接受)
waitHealthy(port, timeout) §4.1 /api/system/health 輪詢,healthCheckTimeout=15s300ms 間隔 TDD 寫 10s實作 15s 更寬鬆)
watchServer 每 10 秒輪詢 §4.2 尚未實作Backend 回報 M1+ ⚠️ 非阻斷
writeIPCPort §2.3 寫到 dataDir/visiona-local.ipc-port

timeout 差異說明TDD 寫 3s grace / 10s health實作用 5s / 15s。實作放寬不是縮緊對正確性無影響若 reviewer 嚴格要對齊 TDD 可留待 M1+ 統一調,不阻斷 M1-10。

5. go build ./... 重現

$ cd /Users/jimchen/visionA/local-tool/visiona-local && go build ./...
(無輸出)
$ go vet ./...
(無輸出)

乾淨通過,無 warning、無 unused import。

6. Wails binding 清單

main.go:

Bind: []interface{}{app}

app*AppNewApp() 回傳)。可被前端呼叫的 exported methods

  • GetServerStatus() ServerStatus
  • GetServerURL() string
  • OpenBrowser(url string) error

未綁舊 Installer、未綁任何 installer wizard method。

main.go Title"visionA Local"(不是 Edge AI Platform Installer)。

7. 已知 TODO 清單Backend 回報)

TODO 位置 M 版本 阻斷 M1-10
ensureBundledPython app.go:417 M2 不阻斷M1 先 system
/ipc/raise endpoint app.go:550 TODO 註解 M1+ 不阻斷
watchServer healthcheck 迴圈 M1+ 不阻斷
isOurStaleServer + 自動 kill app.go:452 TODO 註解 M1+ 不阻斷
Fatal 原生對話框(目前只寫 stderr + event emit reportFatal M1+ 不阻斷

五個 TODO 全部都有明確註解或計畫位置符合「M1 為最小可跑殼層」的定位。


發現的次要問題(不阻擋 M1-10

  1. startServer 的 error 分支對 mockMode 判斷位置怪異app.go:205-208

    pyBin, pyMode, err := a.ensurePythonRuntime(a.pythonMode)
    if err != nil && !a.mockMode {
        return fmt.Errorf(...)
    }
    

    mockMode=true 且 python 失敗時,pyBin 會是空字串、pyMode 可能是 PythonModeAuto,後續 a.pythonBin = pyBin 會存空字串。邏輯上能跑(因為 mock 不需要 python但會讓 GetServerStatus 回報空的 pythonBin,前端顯示可能怪。建議 M1+ 在 mock 分支明確記一個 sentinel value"<mock>")。

  2. log 檔開啟錯誤被吞app.go:243-246OpenFile 回傳 error 被丟棄,只有 nil check。如果 logsDir 磁碟滿或權限異常,會靜默退到 io.Discard。建議 M1+ 把 error 寫進 lastError

  3. locateServerBinary 的 candidates 順序在開發模式下,cwd/dist/ 在「與 exe 同目錄」之後。實務上 wails dev 會把 exe 放在 build/bin/,與 server binary 的實際位置(dist/)不同目錄,所以 candidate 3-4 會被用到。順序合理,但建議在 M1-12 build packaging 時驗證打包後 candidate 1 能命中。

  4. migrateOldDataDirs 的 stderr 警告在 GUI app 情境下使用者看不到。搭配 #Fatal 原生對話框M1+ TODO一起改。

以上全部都是建議,不影響 M1-10 驗收


結論與下一步

M1-10 通過 ,可以進入 M1-12wails build + dmg packaging

下一階段的重點:

  • M1-12 用 wails build 產生 macOS .app
  • 驗證 locateServerBinary candidate 1與 Wails exe 同目錄)能正確命中打包後的 visiona-local-server
  • 產生 .dmg 並確認資料路徑在實機能正確建出

Backend 列出的 M1+ TODO/ipc/raisewatchServerisOurStaleServer、Fatal dialog建議收在一個 M1-13 追蹤 issue跟 M1-12 平行進行或接在其後。