Windows 交付改單一 exe+版本號不再手打(leo 08-06 回報②與「整串該機械化」)
## ② 兩個檔很迷惑 → 一個檔
leo 圖一標註:「下載還是 ZIP ⇒ 應該是 MSIX 或 EXE;解開還是 2 個檔 ⇒ 應該只有 1 個檔案」。
舊做法 zip 裡放 Arcrun.exe+arcrun-collector.exe 同層,少解一個檔就永遠不會同步,
而畫面不會說是這個原因。
修:collector 用 go:embed 收進 Arcrun.exe,第一次執行攤到 ~/.arcrun-rag/bin/
(冪等:sha256 相同不重寫;先寫暫存再 rename,避免留半截執行檔)。
build-win.sh 直接產單一 Arcrun-win-<ver>.exe,不再打 zip。
加機械閘:產出的 exe 若比 collector 還小 ⇒ 沒 embed 成功 ⇒ 中止(過去只會
默默得到一顆「裝了也不會同步」的空殼)。
不走 MSIX:未上架的 MSIX 要使用者先開「開發人員模式」,比 zip 更麻煩。
## 版本號機械化(leo:「這一整串都應該是機械化,不應該每次手工做」)
病根實測:本 repo **一個 git tag 都沒有**,而四支 build 腳本寫的是
`git describe --tags` ⇒ 永遠拿不到 v0.18.x ⇒ 只能靠打包時人工敲 VERSION=
⇒ 兩條打包線各敲各的 ⇒ manifest 宣告 0.18.5、產物其實是 0.18.4。
修:新增 daemon-version.sh 當唯一產生器,沿用 release.mjs 已在跑的原則
(「版本號由內容算出來,不是由人宣告」),不另立新制:
MINOR ← DAEMON_LINE(人決定,大改版才動)
PATCH ← 動過 collector/ 的 commit 數 − DAEMON_PATCH_BASE(機器決定)
⇒ 沒改 daemon 重跑打包不會虛增;改了才 +1;四支腳本在同一 commit 必得同號。
⇒ 不需要 git tag/Actions/輪詢(守 D20 紅線)。
校準:DAEMON_LINE=0.18、BASE=87 ⇒ 現在的樹算出 v0.18.7(上次出貨過 0.18.6)。
build-win/mac/dmg/msix 四支全部改用它。
## icon 漂移根治接上打包
build-win.sh 步驟⓪ 每次都跑 gen-icons.py 重產 icon.ico/trayicon.ico。
## 驗(實測輸出,非推測)
· ./build-win.sh 全程跑通,產出 dist/Arcrun-win-v0.18.7.exe(27,445,248 bytes)
· strings 抽內嵌版本 = v0.18.7、buildTime = 20260806-1233(機器算的,沒人敲)
· 位元組比對該 exe:
CIS trayicon 在裡面 = True
Wails「W」圖還在 = False
collector 有 embed 進去 = True(16,104,960 bytes)
· go vet 全綠(darwin + windows);mac build OK
· 未驗:Windows 真機行為(需封測者實跑)⇒ 狀態 ◐,不是 ✅
· 未做:CHANGELOG 閘、manifest「宣告版本 == 產物版本」驗證閘、前端顯示版本與下載入口
This commit is contained in:
@@ -6,7 +6,11 @@
|
||||
# 而且 macOS 對「不在 /Applications 的常駐程式」權限行為也不一致。
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
VERSION="${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo dev)}"
|
||||
# 🔴 2026-08-06:版本號改由 daemon-version.sh 機械產生(不再手打/不靠 git describe)。
|
||||
# 病根:本 repo 一個 git tag 都沒有 ⇒ git describe 永遠拿不到 v0.18.x
|
||||
# ⇒ 只能靠打包時人工敲 VERSION= ⇒ 兩條打包線各敲各的 ⇒ manifest 說謊。
|
||||
# 理由與算法全文見 daemon-version.sh。
|
||||
VERSION="${VERSION:-$(./daemon-version.sh)}"
|
||||
|
||||
echo "① 先確保 .app 是最新的"
|
||||
VERSION="$VERSION" bash build-mac.sh >/dev/null
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
# 沒同綑 ⇒ 裝了也不會同步。
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
VERSION="${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo dev)}"
|
||||
# 🔴 2026-08-06:版本號改由 daemon-version.sh 機械產生(不再手打/不靠 git describe)。
|
||||
# 病根:本 repo 一個 git tag 都沒有 ⇒ git describe 永遠拿不到 v0.18.x
|
||||
# ⇒ 只能靠打包時人工敲 VERSION= ⇒ 兩條打包線各敲各的 ⇒ manifest 說謊。
|
||||
# 理由與算法全文見 daemon-version.sh。
|
||||
VERSION="${VERSION:-$(./daemon-version.sh)}"
|
||||
BUILD_TIME="$(date '+%Y%m%d-%H%M')"
|
||||
LDFLAGS_VER="-X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}"
|
||||
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
|
||||
@@ -34,7 +34,7 @@ MAKEMSIX="$MSIX_SDK_DIR/.vs/bin/makemsix"
|
||||
exit 1
|
||||
}
|
||||
|
||||
VERSION_RAW="${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo dev)}"
|
||||
VERSION_RAW="${VERSION:-$(./daemon-version.sh)}" # 版本由 daemon-version.sh 機械產生(2026-08-06)
|
||||
# msix 版本必須是四段數字 a.b.c.d,且**最後一段必須是 0**(Store 規定,保留給微軟)。
|
||||
MSIX_VERSION="$(echo "$VERSION_RAW" | sed 's/^v//' | awk -F. '{printf "%d.%d.%d.0", $1, $2, $3}')"
|
||||
# ⚠️ 變數後緊接全形括號必須用 ${} 包起來——全形字元會被 bash 當成變數名的一部分。
|
||||
|
||||
+40
-16
@@ -20,7 +20,12 @@ set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
|
||||
VERSION="${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo dev)}"
|
||||
# 🔴 2026-08-06:版本號**不再手打、也不再靠 git describe**。
|
||||
# 舊寫法 `git describe --tags` 在這個 repo 永遠拿不到 v0.18.x(一個 tag 都沒有)
|
||||
# ⇒ 版本只能靠打包時人工 `VERSION=v0.18.4 ./build-win.sh` 敲 ⇒ manifest 說謊的病根。
|
||||
# 現在由 daemon-version.sh 從 DAEMON_LINE + collector/ 的 commit 數算出來,
|
||||
# 兩條打包線(win/mac)在同一個 commit 上必得同一個號碼。理由全文見該腳本。
|
||||
VERSION="${VERSION:-$(./daemon-version.sh)}"
|
||||
BUILD_TIME="$(date '+%Y%m%d-%H%M')"
|
||||
echo "🏷 版本:${VERSION}(build ${BUILD_TIME})"
|
||||
|
||||
@@ -30,7 +35,13 @@ command -v x86_64-w64-mingw32-gcc >/dev/null 2>&1 || {
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "① 編 collector.exe(同綑執行檔,純 stdlib、CGO_ENABLED=0)"
|
||||
echo "⓪ 由 appicon.png 產生 Windows 圖示(icon.ico/trayicon.ico)"
|
||||
# 🔴 2026-08-06:不產就會沿用上一次留在磁碟上的檔——而上一次那顆是 `wails init`
|
||||
# 留下的 Wails 預設「W」圖,leo 封測看到視窗與托盤兩處都是 W。
|
||||
# 每次 build 都重產 ⇒ 「換了 appicon 卻沒換 Windows」這種漂移不可能再發生。
|
||||
python3 gen-icons.py
|
||||
|
||||
echo "① 編 collector.exe(會被 embed 進 Arcrun.exe,純 stdlib、CGO_ENABLED=0)"
|
||||
# 🔴 2026-08-06:同 build-mac.sh 補回版本注入(Wails 換代時掉的,理由見該檔註解)。
|
||||
# 2b83c47 當年就修過一次「Mac 有、Windows 漏了」——換代後兩邊又一起掉,這次一起補。
|
||||
( cd ../.. && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 \
|
||||
@@ -57,23 +68,36 @@ CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc \
|
||||
-ldflags "-X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}"
|
||||
|
||||
OUT="dist"; mkdir -p "$OUT"
|
||||
STAGE="$(mktemp -d)/Arcrun"
|
||||
mkdir -p "$STAGE"
|
||||
|
||||
echo "③ 同綑 collector.exe(App 用 os.Executable() 找同層,見 supervise.go)"
|
||||
cp build/bin/arcrun-app.exe "$STAGE/Arcrun.exe"
|
||||
cp arcrun-collector.exe "$STAGE/arcrun-collector.exe"
|
||||
echo "③ 產出**單一執行檔**"
|
||||
# 🔴 2026-08-06 交付形態改變(leo 封測回報②,圖一標註):
|
||||
# 「下載還是 ZIP ⇒ 應該是 MSIX 或 EXE」「解開還是 2 個檔 ⇒ 應該只有 1 個檔案」
|
||||
# 「兩個檔案令人很迷惑,User unfriendly」
|
||||
#
|
||||
# 舊做法=zip 裡放 Arcrun.exe+arcrun-collector.exe 同層。使用者只要少解一個檔、
|
||||
# 或只把 Arcrun.exe 拖出來,同步就永遠不會動,而畫面不會說是這個原因。
|
||||
# 新做法=collector 在步驟②被 go:embed 收進 Arcrun.exe(見 bundled_collector_windows.go),
|
||||
# 第一次執行時攤到 ~/.arcrun-rag/bin/。**下載下來就是一個檔,雙擊就跑。**
|
||||
#
|
||||
# 為什麼不出 MSIX:未上架的 MSIX 要使用者先開「開發人員模式」才裝得起來,
|
||||
# 比 zip 更麻煩。上架 MS Store 後再回頭走 build-msix.sh。
|
||||
EXE="$(cd "$OUT" && pwd)/Arcrun-win-${VERSION}.exe"
|
||||
rm -f "$EXE"
|
||||
cp build/bin/arcrun-app.exe "$EXE"
|
||||
|
||||
echo "④ 打包 zip"
|
||||
# 用絕對路徑:下面會 cd 進暫存目錄打包,相對路徑會找不到(實撞:zip I/O error)。
|
||||
ZIP="$(cd "$OUT" && pwd)/ArcrunRAG-win-unsigned-${VERSION}.zip"
|
||||
rm -f "$ZIP"
|
||||
( cd "$(dirname "$STAGE")" && zip -qr "$ZIP" "Arcrun" )
|
||||
rm -rf "$(dirname "$STAGE")"
|
||||
# 🔴 機械閘:embed 進去的 collector 至少要有幾 MB,否則就是「編了個空殼」。
|
||||
# (步驟①失敗但 wails build 仍成功時,過去只會得到一個不會同步的 exe。)
|
||||
APP_SIZE=$(stat -f%z "$EXE" 2>/dev/null || stat -c%s "$EXE")
|
||||
COL_SIZE=$(stat -f%z arcrun-collector.exe 2>/dev/null || stat -c%s arcrun-collector.exe)
|
||||
if [ "$APP_SIZE" -lt "$COL_SIZE" ]; then
|
||||
echo "❌ 產出的 exe(${APP_SIZE} bytes)比 collector(${COL_SIZE} bytes)還小"
|
||||
echo " ⇒ collector 沒被 embed 進去,這顆裝了也不會同步。中止。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SIZE=$(ls -lh "$ZIP" | awk '{print $5}')
|
||||
echo "✅ 完成:${ZIP}(${SIZE})"
|
||||
SIZE=$(ls -lh "$EXE" | awk '{print $5}')
|
||||
echo "✅ 完成:${EXE}(${SIZE},內含同步引擎)"
|
||||
echo
|
||||
echo "🔬 使用者流程:解壓縮 → 執行 Arcrun.exe"
|
||||
echo "🔬 使用者流程:下載 → 雙擊 Arcrun.exe(不必解壓縮、不必找第二個檔)"
|
||||
echo " ⚠️ 未簽章 ⇒ SmartScreen 會攔(「更多資訊」→「仍要執行」)。"
|
||||
echo " 要免除這一步=上 MS Store(見 build-msix.sh)或買 EV 憑證。"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build !windows
|
||||
|
||||
package main
|
||||
|
||||
// bundled_collector_other.go — 非 Windows 平台不內嵌 collector。
|
||||
//
|
||||
// macOS 走 .app bundle:collector 就放在 Contents/MacOS/ 裡,對使用者而言
|
||||
// **本來就只有一個可拖曳的東西**(Arcrun.app),沒有「兩個檔很迷惑」的問題。
|
||||
// 所以只有 Windows 需要把 collector 收進單一 exe(見 bundled_collector_windows.go)。
|
||||
|
||||
// bundledCollectorPath 回空字串=沒有內嵌版,呼叫端退回「找同層」。
|
||||
func bundledCollectorPath() string { return "" }
|
||||
@@ -0,0 +1,75 @@
|
||||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
// bundled_collector_windows.go — 把 collector 內嵌進單一 Arcrun.exe(2026-08-06)
|
||||
//
|
||||
// 🔴 leo 08-06 封測回報②:
|
||||
//
|
||||
// 「下載還是 ZIP ⇒ 應該是 MSIX 或 EXE;解開還是 2 個檔 ⇒ 應該只有 1 個檔案」
|
||||
// 「兩個檔案令人很迷惑,User unfriendly」
|
||||
//
|
||||
// 舊做法:zip 裡放 Arcrun.exe+arcrun-collector.exe 同層,App 用 os.Executable()
|
||||
// 找隔壁那支。使用者只要少解壓一個檔、或只把 Arcrun.exe 拖出來,同步就永遠不會動
|
||||
// ——而畫面不會說是這個原因。
|
||||
//
|
||||
// 新做法:collector 直接 embed 進 Arcrun.exe,第一次執行時攤到
|
||||
// `~/.arcrun-rag/bin/arcrun-collector.exe`(與 config.json/collector.log 同一個資料夾,
|
||||
// 使用者要備份或清乾淨只有一個地方)。
|
||||
//
|
||||
// 為什麼不選 MSIX:未上架的 MSIX 要使用者先開「開發人員模式」才裝得起來,
|
||||
// 比 zip 更麻煩(見 docs-site 的 install-windows 方式 B)。上架後再回頭走 MSIX。
|
||||
//
|
||||
// ⚠️ 編譯前提:`arcrun-collector.exe` 必須先存在同目錄(build-win.sh 步驟①會先編)。
|
||||
// 它被 .gitignore 排除,所以**不能**直接 `go build` 一個乾淨 clone——一律走 build-win.sh。
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
_ "embed"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
//go:embed arcrun-collector.exe
|
||||
var bundledCollector []byte
|
||||
|
||||
// bundledCollectorPath 把內嵌的 collector 攤到磁碟並回傳路徑。
|
||||
//
|
||||
// 冪等:內容一樣就不重寫(避免每次啟動都寫 16MB,也避免正在跑的那支被覆寫)。
|
||||
// 出錯回空字串——呼叫端會退回「找同層」的舊行為,並由 appLog 留下痕跡。
|
||||
func bundledCollectorPath() string {
|
||||
if len(bundledCollector) == 0 {
|
||||
return ""
|
||||
}
|
||||
dir := filepath.Join(appDir(), "bin")
|
||||
dst := filepath.Join(dir, "arcrun-collector.exe")
|
||||
|
||||
want := sha256.Sum256(bundledCollector)
|
||||
if have, err := os.ReadFile(dst); err == nil {
|
||||
if got := sha256.Sum256(have); got == want {
|
||||
return dst // 已是同一版,不動它
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
appLog("攤開內嵌同步引擎失敗(建資料夾):%v", err)
|
||||
return ""
|
||||
}
|
||||
// 先寫暫存再 rename:中途失敗不會留下半截的執行檔。
|
||||
tmp := dst + ".new"
|
||||
if err := os.WriteFile(tmp, bundledCollector, 0o755); err != nil {
|
||||
appLog("攤開內嵌同步引擎失敗(寫檔):%v", err)
|
||||
return ""
|
||||
}
|
||||
// Windows 不允許覆寫「正在執行」的檔案;先移開舊的再換上新的。
|
||||
_ = os.Remove(dst + ".old")
|
||||
_ = os.Rename(dst, dst+".old")
|
||||
if err := os.Rename(tmp, dst); err != nil {
|
||||
appLog("攤開內嵌同步引擎失敗(換檔):%v", err)
|
||||
return ""
|
||||
}
|
||||
_ = os.Remove(dst + ".old")
|
||||
appLog("已攤開內嵌同步引擎:%s(sha256 %s)", dst, hex.EncodeToString(want[:8]))
|
||||
return dst
|
||||
}
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
# daemon-version.sh — daemon(桌面 App)版本號的**唯一**產生器(2026-08-06)
|
||||
#
|
||||
# 🔴 leo 08-06:「給版本號、本版更新內容、打包產品、顯示在前端,
|
||||
# 這一整串都應該是機械化,**不應該每次手工做**」
|
||||
#
|
||||
# ── 病根(實測,不是猜的)────────────────────────────────────────────
|
||||
# manifest 宣告 daemon v0.18.5,但線上 zip 拆開內嵌版本是 **v0.18.4**。
|
||||
# 原因:本 repo **一個 git tag 都沒有**,而各 build 腳本寫的是
|
||||
# `VERSION=$(git describe --tags --always --dirty)` ⇒ 永遠拿不到 v0.18.x
|
||||
# ⇒ 那個號碼只可能是每次打包時人工 `VERSION=v0.18.4 ./build-win.sh` 敲進去的。
|
||||
# 人工敲 ⇒ 兩條打包線(DMG/zip)只重打一條就對不上 ⇒ manifest 說謊。
|
||||
#
|
||||
# ── 做法(沿用 installer/scripts/release.mjs 已經在跑的原則,不另立新制)──
|
||||
# release.mjs 開宗明義:「版本號**由內容算出來**,不是由人(或 AI)宣告」。
|
||||
# 雲端 bundle(1.4.x)早就這樣跑了,daemon 只是一直被排除在外。這裡補上:
|
||||
#
|
||||
# MINOR ← 人決定:`DAEMON_LINE` 檔(大改版才動,平常不碰)
|
||||
# PATCH ← 機器決定:**動過 collector/ 的 commit 數** 減去 `DAEMON_PATCH_BASE`
|
||||
#
|
||||
# ⇒ 沒改 daemon 就重跑打包,版本**不會虛增**(commit 數沒變)。
|
||||
# ⇒ 改了 daemon 才會 +1,且兩條打包線在同一個 commit 上算出**同一個號碼**。
|
||||
# ⇒ 不需要 git tag、不需要 Actions、不需要輪詢(守 D20 紅線)。
|
||||
#
|
||||
# 換線(0.18 → 0.19)時:改 DAEMON_LINE,並把 DAEMON_PATCH_BASE 設成當下的
|
||||
# `git rev-list --count HEAD -- collector/`,讓新線從 .0 開始。
|
||||
#
|
||||
# 用法:VERSION="$(./daemon-version.sh)" → 印出 v0.18.7 這種字串
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
|
||||
LINE="$(tr -d ' \n' < "$REPO_ROOT/DAEMON_LINE")"
|
||||
BASE="$(tr -d ' \n' < "$REPO_ROOT/DAEMON_PATCH_BASE")"
|
||||
|
||||
[[ "$LINE" =~ ^[0-9]+\.[0-9]+$ ]] || { echo "DAEMON_LINE 必須是 MAJOR.MINOR(例 0.18),現在是 '$LINE'" >&2; exit 1; }
|
||||
[[ "$BASE" =~ ^[0-9]+$ ]] || { echo "DAEMON_PATCH_BASE 必須是整數,現在是 '$BASE'" >&2; exit 1; }
|
||||
|
||||
COUNT="$(git -C "$REPO_ROOT" rev-list --count HEAD -- collector/)"
|
||||
PATCH=$(( COUNT - BASE ))
|
||||
if [ "$PATCH" -lt 0 ]; then
|
||||
echo "DAEMON_PATCH_BASE($BASE)大於實際 commit 數($COUNT)——換線時忘了更新?" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "v${LINE}.${PATCH}"
|
||||
@@ -23,8 +23,17 @@ import (
|
||||
var sup *supervisor.Supervisor
|
||||
|
||||
// collectorBinPath 找同綑的 collector 執行檔。
|
||||
// 規則與 arcrun-tray 一致:跟 App 執行檔同層(build 時會複製進 .app)。
|
||||
//
|
||||
// 兩種同綑方式,依平台:
|
||||
// - Windows:collector **embed 在 Arcrun.exe 裡**,第一次執行攤到 ~/.arcrun-rag/bin/
|
||||
// (leo 08-06:「解開還是 2 個檔 ⇒ 應該只有 1 個檔案」)。見 bundled_collector_windows.go。
|
||||
// - macOS:放在 .app 的 Contents/MacOS/ 同層(使用者只看到一個可拖曳的 Arcrun.app)。
|
||||
//
|
||||
// 內嵌攤開失敗時**退回找同層**——舊版打包出來的資料夾仍然能跑,不會因為換機制而全壞。
|
||||
func collectorBinPath() string {
|
||||
if p := bundledCollectorPath(); p != "" {
|
||||
return p
|
||||
}
|
||||
name := "arcrun-collector"
|
||||
if runtime.GOOS == "windows" {
|
||||
name += ".exe"
|
||||
|
||||
Reference in New Issue
Block a user