fix(local-tool): locateServerBinary 找 {app}\bin\ 子目錄(Windows/Linux installer 佈局)

根因:Windows installer 把 visiona-local-server.exe 裝到 {app}\bin\
      (.iss 的 DestDir: "{app}\bin"),但 locateServerBinary 只搜
      exeDir\visiona-local-server.exe 和 macOS bundle 專屬路徑,
      Windows/Linux 打包後的 bin/ 子目錄完全沒被搜尋,導致執行時
      "server binary not found" 嚴重錯誤。

修法:
- locateServerBinary 第一優先搜 exeDir\bin\visiona-local-server
  (Inno Setup installer / AppImage 的標準佈局)
- 開發模式 fallback 新增 payload/<goos>/bin/ 候選路徑
- 同時修正 locateBundleBinDir / locateBundledPythonAssets 的開發模式
  fallback 寫死 "payload/darwin",改用 runtime.GOOS 動態選 payload 子目錄

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jim800121chen 2026-04-12 03:45:17 +08:00
parent 83981e5b1b
commit eb52f8c690

View File

@ -698,12 +698,13 @@ func locateBundledPythonAssets() (tarball, wheelsDir string, err error) {
} }
} }
// 2. 開發模式 fallback // 2. 開發模式 fallback(依 GOOS 挑對應 payload 子目錄)
if cwd, e := os.Getwd(); e == nil { if cwd, e := os.Getwd(); e == nil {
osName := runtime.GOOS // darwin / windows / linux
candidates := []struct{ t, w string }{ candidates := []struct{ t, w string }{
{filepath.Join(cwd, "payload", "darwin", "python", "python.tar.gz"), filepath.Join(cwd, "payload", "darwin", "wheels")}, {filepath.Join(cwd, "payload", osName, "python", "python.tar.gz"), filepath.Join(cwd, "payload", osName, "wheels")},
{filepath.Join(cwd, "..", "payload", "darwin", "python", "python.tar.gz"), filepath.Join(cwd, "..", "payload", "darwin", "wheels")}, {filepath.Join(cwd, "..", "payload", osName, "python", "python.tar.gz"), filepath.Join(cwd, "..", "payload", osName, "wheels")},
{filepath.Join(cwd, "..", "..", "payload", "darwin", "python", "python.tar.gz"), filepath.Join(cwd, "..", "..", "payload", "darwin", "wheels")}, {filepath.Join(cwd, "..", "..", "payload", osName, "python", "python.tar.gz"), filepath.Join(cwd, "..", "..", "payload", osName, "wheels")},
} }
for _, c := range candidates { for _, c := range candidates {
if fileExists(c.t) && dirExists(c.w) { if fileExists(c.t) && dirExists(c.w) {
@ -712,7 +713,7 @@ func locateBundledPythonAssets() (tarball, wheelsDir string, err error) {
} }
} }
return "", "", fmt.Errorf("bundled python assets not found (tried .app Resources + payload/darwin)") return "", "", fmt.Errorf("bundled python assets not found (tried .app Resources + same-dir + payload/%s)", runtime.GOOS)
} }
// locateBundleBinDir 找 bundle 內的 bin 目錄(含 ffmpeg / yt-dlp / visiona-local-server // locateBundleBinDir 找 bundle 內的 bin 目錄(含 ffmpeg / yt-dlp / visiona-local-server
@ -743,12 +744,13 @@ func locateBundleBinDir() (string, error) {
} }
} }
// 開發模式 fallback // 開發模式 fallback(依 GOOS 挑對應 payload 子目錄)
if cwd, err := os.Getwd(); err == nil { if cwd, err := os.Getwd(); err == nil {
osName := runtime.GOOS // darwin / windows / linux
candidates := []string{ candidates := []string{
filepath.Join(cwd, "payload", "darwin", "bin"), filepath.Join(cwd, "payload", osName, "bin"),
filepath.Join(cwd, "..", "payload", "darwin", "bin"), filepath.Join(cwd, "..", "payload", osName, "bin"),
filepath.Join(cwd, "..", "..", "payload", "darwin", "bin"), filepath.Join(cwd, "..", "..", "payload", osName, "bin"),
} }
for _, c := range candidates { for _, c := range candidates {
if dirExists(c) { if dirExists(c) {
@ -1210,10 +1212,12 @@ func oldDataDirCandidates() []string {
// locateServerBinary 找 visiona-local-server 執行檔。 // locateServerBinary 找 visiona-local-server 執行檔。
// //
// 搜尋順序: // 搜尋順序:
// 1. 與 Wails app 可執行檔同目錄(打包後的位置) // 1. 與 Wails app 可執行檔同目錄下的 bin/Windows/Linux installer 佈局Inno Setup .iss 裝到 {app}\bin
// 2. macOS app bundle 內Contents/MacOS/ 或 Contents/Resources/ // 2. 與 Wails app 可執行檔同目錄(開發/舊佈局)
// 3. 開發模式:<repo>/dist/visiona-local-server相對 cwd // 3. macOS app bundle 內Contents/Resources/bin 或 Contents/Resources
// 4. 開發模式:<repo>/server/visiona-local-server // 4. 開發模式:<repo>/payload/<os>/bin/visiona-local-server
// 5. 開發模式:<repo>/dist/visiona-local-server
// 6. 開發模式:<repo>/server/visiona-local-server
func locateServerBinary() (string, error) { func locateServerBinary() (string, error) {
binName := "visiona-local-server" binName := "visiona-local-server"
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
@ -1222,12 +1226,15 @@ func locateServerBinary() (string, error) {
candidates := []string{} candidates := []string{}
// 1. 與 Wails app 同目錄 // 1-2. 打包後佈局installer 生成的目錄結構)
if exe, err := os.Executable(); err == nil { if exe, err := os.Executable(); err == nil {
exeDir := filepath.Dir(exe) exeDir := filepath.Dir(exe)
// {app}\bin\visiona-local-server.exe — Windows / Linux installer 標準佈局
candidates = append(candidates, filepath.Join(exeDir, "bin", binName))
// {app}\visiona-local-server.exe — exe 跟 server 同目錄的扁平佈局
candidates = append(candidates, filepath.Join(exeDir, binName)) candidates = append(candidates, filepath.Join(exeDir, binName))
// 2. macOS app bundle 常見位置 // 3. macOS app bundle 佈局
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
candidates = append(candidates, candidates = append(candidates,
filepath.Join(exeDir, "..", "Resources", "bin", binName), filepath.Join(exeDir, "..", "Resources", "bin", binName),
@ -1237,9 +1244,12 @@ func locateServerBinary() (string, error) {
} }
} }
// 3-4. 開發模式 // 4-6. 開發模式
if cwd, err := os.Getwd(); err == nil { if cwd, err := os.Getwd(); err == nil {
osName := runtime.GOOS // darwin / windows / linux
candidates = append(candidates, candidates = append(candidates,
filepath.Join(cwd, "payload", osName, "bin", binName),
filepath.Join(cwd, "..", "payload", osName, "bin", binName),
filepath.Join(cwd, "dist", binName), filepath.Join(cwd, "dist", binName),
filepath.Join(cwd, "..", "dist", binName), filepath.Join(cwd, "..", "dist", binName),
filepath.Join(cwd, "server", binName), filepath.Join(cwd, "server", binName),