9fcd82b517
leo 08-04:「如果當初 mac 打包就是 dmg 用拖的,現在 oscar 那裡就解決了,
如果當初 msix 就用了,現在另一個人也是更新就可以」——遠慮該在第一版就有。
原則:**用戶好用、白癡化**。
① 改名 Arcrun RAG.app → Arcrun.app(leo:「不要 rag,要 arcrun.app」)
五處引用一起改(selfupdate.go/build-mac.sh/build-dmg.sh/t184_test.go/README)
② 新增 build-dmg.sh:DMG 內含 Arcrun.app + Applications 捷徑
=Mac 幾十年的標準做法,開起來就暗示你拖進去 ⇒ **從源頭讓 app 待在對的位置**。
坑:hdiutil 預設檔案系統會把「Arcrun RAG.app」截成「RAG.app」⇒ 加 -fs HFS+ 解。
DMG 治源頭、t184 是安全網,兩個都要(已裝在別處的人不會因改發 DMG 就自動變好)。
③ MSIX 用 .env 的正式 Partner Center 身分重打(**不是佔位值**)
回讀 AppxManifest 逐欄驗證:
Identity Name Uncle6.Arcrun
Publisher CN=DE038286-31A5-47CF-B83F-FF6B206BF591
PublisherDisplayName Uncle6
Version 0.15.7.0
仍含 PLACEHOLDER False
實測(t184 端到端,模擬 Oscar 的情境):
app 放 /tmp/oscar-test/Downloads/(不在 /Applications)→ 跑更新覆蓋
→ 該處 app 變成 v0.15.7 ✅
→ /Applications 沒有被無中生有一個副本 ✅
路徑推導三種放置位置皆正確(Downloads/Applications/Desktop)
產物 sha256:
dmg 0eb3f76b60bf6891 mac zip f264dda7391f3006
win 32fe44450ca77c62 msix 5bf7c911b8f3a817
⛔ 誠實標記:MSIX 能否實際安裝**這台 Mac 驗不到**(Add-AppxPackage 只有 Windows)。
已驗的是包結構合法(unpack 過 blockmap 雜湊)+身分欄位正確。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
56 lines
2.1 KiB
Go
56 lines
2.1 KiB
Go
package main
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
// t184:更新必須蓋在「正在跑的那個 .app」,不是寫死 /Applications。
|
||
// leo 08-04 實撞:Oscar 從下載資料夾跑 → ditto 自動建 /Applications 副本並回成功
|
||
// → 他重開仍是舊版,畫面卻說更新完成(靜默失敗)。
|
||
func TestRunningAppBundlePathNotHardcoded(t *testing.T) {
|
||
got, err := runningAppBundlePath()
|
||
// 測試執行檔不在 .app 裡 → 應誠實回錯,**不可**回傳寫死的 /Applications 路徑
|
||
if err == nil && strings.HasPrefix(got, "/Applications/") {
|
||
t.Errorf("不該回寫死的 /Applications 路徑:%q", got)
|
||
}
|
||
if err == nil && !strings.HasSuffix(got, ".app") {
|
||
t.Errorf("回傳的不是 .app:%q", got)
|
||
}
|
||
}
|
||
|
||
// 模擬真實 .app 結構:<dir>/Arcrun.app/Contents/MacOS/arcrun-tray
|
||
// 驗證「往上三層」的推導正確——放在哪個目錄都要算得出來。
|
||
func TestAppBundleDerivationFromExePath(t *testing.T) {
|
||
for _, base := range []string{"/Applications", "/Users/oscar/Downloads", "/Volumes/USB"} {
|
||
exe := filepath.Join(base, "Arcrun.app", "Contents", "MacOS", "arcrun-tray")
|
||
app := filepath.Dir(filepath.Dir(filepath.Dir(exe)))
|
||
want := filepath.Join(base, "Arcrun.app")
|
||
if app != want {
|
||
t.Errorf("從 %s 推導錯:got %q want %q", base, app, want)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ditto 對不存在的目標會自動建目錄且回成功——這正是靜默失敗的成因,留測防回歸認知。
|
||
func TestDittoCreatesMissingTargetSilently(t *testing.T) {
|
||
dir := t.TempDir()
|
||
src := filepath.Join(dir, "Fake.app", "Contents")
|
||
if err := os.MkdirAll(src, 0o755); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := os.WriteFile(filepath.Join(src, "x"), []byte("hi"), 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
dst := filepath.Join(dir, "nowhere", "Fake.app")
|
||
if _, err := runCmd("ditto", filepath.Join(dir, "Fake.app"), dst); err != nil {
|
||
t.Skipf("此環境沒有 ditto:%v", err)
|
||
}
|
||
if _, err := os.Stat(dst); err != nil {
|
||
t.Fatal("前提失效:ditto 應自動建出目標")
|
||
}
|
||
t.Log("確認:ditto 蓋到不存在的路徑會成功建出 ⇒ 寫死路徑必然靜默失敗")
|
||
}
|