v0.15.7:改名 Arcrun.app+發 DMG(拖進 Applications)+MSIX 正式身分
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>
This commit is contained in:
Binary file not shown.
@@ -41,9 +41,9 @@ go run fyne.io/fyne/v2/cmd/fyne@latest package -os darwin -icon icon.png -name "
|
||||
|
||||
未簽章的 .app 會被 Gatekeeper 擋(右鍵開可繞,但非產品體驗)。正式版:
|
||||
```bash
|
||||
codesign --deep --force --options runtime --sign "Developer ID Application: <你>" "Arcrun RAG.app"
|
||||
codesign --deep --force --options runtime --sign "Developer ID Application: <你>" "Arcrun.app"
|
||||
xcrun notarytool submit "Arcrun RAG.zip" --apple-id … --team-id … --wait
|
||||
xcrun stapler staple "Arcrun RAG.app"
|
||||
xcrun stapler staple "Arcrun.app"
|
||||
```
|
||||
|
||||
## TCC(macOS,只有真 Mac 驗得到)
|
||||
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
# build-dmg.sh — 把 Arcrun.app 打成標準 Mac DMG(t184 配套,leo 2026-08-04 核可)。
|
||||
#
|
||||
# 🎯 為什麼要 DMG(不是繼續發 zip):
|
||||
# zip 解出來就是一個 app,使用者**很容易就地雙擊執行**(下載資料夾、桌面)。
|
||||
# Oscar 就是這樣 ⇒ 他的 app 不在 /Applications ⇒ 自更新蓋錯地方(t184 真兇)。
|
||||
# DMG 裡放「app + Applications 資料夾捷徑」,開起來就是叫你把 app 拖進去的畫面
|
||||
# ⇒ **從源頭讓 app 待在該待的位置**,這是 Mac 的標準慣例,使用者一看就懂。
|
||||
#
|
||||
# ⚠️ DMG 治源頭、t184 是安全網,**兩個都要**:
|
||||
# 已經裝好舊版在別處的人(如 Oscar),不會因為改發 DMG 就自動變好。
|
||||
#
|
||||
# 用法:VERSION=v0.15.7 bash build-dmg.sh
|
||||
# 產物:dist-mac/ArcrunRAG-<version>.dmg
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
APP="Arcrun.app"
|
||||
[[ -d "$APP" ]] || { echo "❌ 找不到 $APP —— 先跑 build-mac.sh" >&2; exit 1; }
|
||||
|
||||
VERSION="${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo dev)}"
|
||||
OUT_DIR="dist-mac"
|
||||
DMG="$OUT_DIR/ArcrunRAG-${VERSION}.dmg"
|
||||
STAGE="$(mktemp -d)/Arcrun RAG"
|
||||
|
||||
echo "🏷 版本:${VERSION}"
|
||||
mkdir -p "$OUT_DIR" "$STAGE"
|
||||
|
||||
echo "① 放 app 進暫存區"
|
||||
ditto "$APP" "$STAGE/$APP"
|
||||
|
||||
echo "② 放 Applications 捷徑(使用者拖曳的目標)"
|
||||
ln -s /Applications "$STAGE/Applications"
|
||||
|
||||
echo "③ 產 DMG"
|
||||
rm -f "$DMG"
|
||||
hdiutil create -volname "Arcrun" -srcfolder "$STAGE" -fs HFS+ -ov -format UDZO "$DMG" >/dev/null
|
||||
|
||||
rm -rf "$(dirname "$STAGE")"
|
||||
SIZE=$(ls -lh "$DMG" | awk '{print $5}')
|
||||
# 註:`$DMG(` 要寫成 `${DMG}(`——全形括號不是分隔符,bash 會把它吃進變數名。
|
||||
echo "✅ 完成:${DMG}(${SIZE})"
|
||||
echo
|
||||
echo "🔬 驗法:"
|
||||
echo " hdiutil attach '$DMG' → 應看到 Arcrun.app 與 Applications 捷徑並排"
|
||||
echo " 使用者把左邊拖到右邊即完成安裝(Mac 標準流程)"
|
||||
@@ -6,7 +6,7 @@
|
||||
#
|
||||
# 用法(真機、需 Xcode CLT):
|
||||
# cd collector/cmd/arcrun-tray && bash build-mac.sh
|
||||
# 產物:Arcrun RAG.app(含同綑 arcrun-collector)。簽章/公證另行(見 README)。
|
||||
# 產物:Arcrun.app(含同綑 arcrun-collector)。簽章/公證另行(見 README)。
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
@@ -24,7 +24,7 @@ echo "① 編 collector(同綑執行檔)"
|
||||
( cd ../.. && go build -ldflags "-s -w ${LDFLAGS_VER}" -o "cmd/arcrun-tray/arcrun-collector" . )
|
||||
|
||||
echo "② fyne package"
|
||||
go run fyne.io/fyne/v2/cmd/fyne@latest package -os darwin -icon icon.png -name "Arcrun RAG" -appID dev.arcrun.rag.tray
|
||||
go run fyne.io/fyne/v2/cmd/fyne@latest package -os darwin -icon icon.png -name "Arcrun" -appID dev.arcrun.rag.tray
|
||||
|
||||
# ②b 用 -s -w 重編托盤覆蓋回 bundle(2026-07-27,t72/t63)。
|
||||
# 為什麼:`fyne package` 內部自己 go build,吃不到 -ldflags,產出含符號表與 DWARF 的胖執行檔。
|
||||
@@ -33,9 +33,9 @@ go run fyne.io/fyne/v2/cmd/fyne@latest package -os darwin -icon icon.png -name "
|
||||
# "File size exceeded the configured limit of 20 MB.",只能走 raw;Windows 版 13MB 無此問題)。
|
||||
# ⇒ 這一步讓 Mac 版也能走 CDN,且**免除拆檔工程**。功能指紋與簽章實測皆不受影響。
|
||||
echo "②b strip 重編托盤(-s -w;為了掉進 jsDelivr 20MB 線內)+注入版本號"
|
||||
go build -ldflags "-s -w ${LDFLAGS_VER}" -o "Arcrun RAG.app/Contents/MacOS/arcrun-tray" .
|
||||
go build -ldflags "-s -w ${LDFLAGS_VER}" -o "Arcrun.app/Contents/MacOS/arcrun-tray" .
|
||||
|
||||
APP="Arcrun RAG.app"
|
||||
APP="Arcrun.app"
|
||||
PLIST="$APP/Contents/Info.plist"
|
||||
|
||||
echo "③ LSUIElement=true(托盤唯一指示器,隱 Dock icon——leo 07-24 裁決)"
|
||||
|
||||
@@ -255,7 +255,7 @@ func startUpdateWatcher() {
|
||||
// applyStagedAndRestart 套用已備妥的更新並重啟。
|
||||
// 只有使用者按下「重新啟動以完成更新」才會走到這裡(不偷偷替換正在跑的自己)。
|
||||
//
|
||||
// 做法:解壓到暫存 → 用 ditto 覆蓋 /Applications/Arcrun RAG.app → 重新 open → 結束自己。
|
||||
// 做法:解壓到暫存 → 用 ditto 覆蓋 /Applications/Arcrun.app → 重新 open → 結束自己。
|
||||
// 失敗時保留原版不動(寧可停在舊版,也不要弄出開不起來的 .app)。
|
||||
func applyStagedAndRestart() error {
|
||||
s := loadStaged()
|
||||
@@ -277,15 +277,15 @@ func applyStagedAndRestart() error {
|
||||
if out, err := runCmd("ditto", "-x", "-k", s.ZipPath, work); err != nil {
|
||||
return fmt.Errorf("解壓失敗:%v %s", err, out)
|
||||
}
|
||||
newApp := filepath.Join(work, "Arcrun RAG.app")
|
||||
newApp := filepath.Join(work, "Arcrun.app")
|
||||
if _, err := os.Stat(newApp); err != nil {
|
||||
return fmt.Errorf("更新檔內容不符(找不到 Arcrun RAG.app)")
|
||||
return fmt.Errorf("更新檔內容不符(找不到 Arcrun.app)")
|
||||
}
|
||||
// 🔴 t184(leo 08-04 實撞:Oscar 按更新→顯示「新版 v0.15.6 已就緒」→重啟後**又跳回 v0.15.4**):
|
||||
//
|
||||
// 原本寫死 `/Applications/Arcrun RAG.app`。但使用者**不一定把 app 放在 Applications**
|
||||
// 原本寫死 `/Applications/Arcrun.app`。但使用者**不一定把 app 放在 Applications**
|
||||
// (Oscar 就是從下載資料夾/桌面直接跑)。這時 `ditto` 會**自動建出**
|
||||
// `/Applications/Arcrun RAG.app` 並**回傳成功**(實測 exit 0,不是報錯)
|
||||
// `/Applications/Arcrun.app` 並**回傳成功**(實測 exit 0,不是報錯)
|
||||
// ⇒ 新版被寫到一個他根本沒在跑的路徑,他重開的還是原地那份舊的
|
||||
// ⇒ 畫面說「更新完成」、版本卻永遠是舊的=**靜默失敗**,最難查的那種。
|
||||
//
|
||||
@@ -310,7 +310,7 @@ func applyStagedAndRestart() error {
|
||||
|
||||
// runningAppBundlePath 回傳**現在正在跑的**那個 .app 路徑(t184)。
|
||||
//
|
||||
// 路徑長相:<某處>/Arcrun RAG.app/Contents/MacOS/arcrun-tray
|
||||
// 路徑長相:<某處>/Arcrun.app/Contents/MacOS/arcrun-tray
|
||||
// ⇒ 從執行檔往上三層就是 .app 本體。
|
||||
//
|
||||
// 為什麼不寫死 /Applications:使用者常常就地執行(下載資料夾/桌面/隨身碟),
|
||||
@@ -324,7 +324,7 @@ func runningAppBundlePath() (string, error) {
|
||||
if resolved, err := filepath.EvalSymlinks(exe); err == nil {
|
||||
exe = resolved // 走過 symlink 才是真身
|
||||
}
|
||||
// .../Arcrun RAG.app/Contents/MacOS/arcrun-tray → 上三層
|
||||
// .../Arcrun.app/Contents/MacOS/arcrun-tray → 上三層
|
||||
app := filepath.Dir(filepath.Dir(filepath.Dir(exe)))
|
||||
if filepath.Ext(app) != ".app" {
|
||||
return "", fmt.Errorf("這份 Arcrun RAG 不是從 .app 啟動的(%s)⇒ 請改用下載頁的 .app 版本再更新", exe)
|
||||
|
||||
@@ -21,13 +21,13 @@ func TestRunningAppBundlePathNotHardcoded(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 模擬真實 .app 結構:<dir>/Arcrun RAG.app/Contents/MacOS/arcrun-tray
|
||||
// 模擬真實 .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 RAG.app", "Contents", "MacOS", "arcrun-tray")
|
||||
exe := filepath.Join(base, "Arcrun.app", "Contents", "MacOS", "arcrun-tray")
|
||||
app := filepath.Dir(filepath.Dir(filepath.Dir(exe)))
|
||||
want := filepath.Join(base, "Arcrun RAG.app")
|
||||
want := filepath.Join(base, "Arcrun.app")
|
||||
if app != want {
|
||||
t.Errorf("從 %s 推導錯:got %q want %q", base, app, want)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user