版本號改由 changelog 決定:從頭到尾沒有人打過數字,且與更新內容天生綁定
## 為什麼推翻自己幾小時前的做法
上一版用「動過 collector/ 的 commit 數」當 patch。確實不用手打,但
**每個 commit 都變成一版**(一天 5 個 commit 就跳 5 版),
每跳一版還要補一段 changelog ⇒ 把機械化變成新的手工活。leo 要的不是這個。
## 現在的機制(daemon-version.py)
單一真相源=docs-site/.../help/changelog.md:
· 要出新版 ⇒ 最上面加一段 `## 下一版(未發佈)`,底下寫白話更新內容
· 打包時腳本把它**戳成正式版號**(上一版 patch+1)並補今天日期
· 沒有「未發佈」段 ⇒ 版本=最上面那一版(重打同一版,冪等不虛增)
⇒ 版本號沒有人打過;且不可能「升了版卻沒人知道改什麼」,
也不可能「寫了內容卻忘了升版」——兩者出自同一段文字。
換線(0.18→0.19)只改 DAEMON_LINE,下一版自動從 0.19.0 起。
刪掉 daemon-version.sh 與 DAEMON_PATCH_BASE(一版的殘留)。
## 驗(實測輸出)
· `## 下一版(未發佈)` --stamp→ `## v0.18.7(2026-08-06)`(腳本自己改的)
· 沒有未發佈段時重跑 → 仍回 v0.18.7(冪等,不虛增)
· ./build-win.sh 全程通 → dist/Arcrun-win-v0.18.7.exe(27,445,248 bytes)
strings 抽內嵌版本 = v0.18.7
· ./build-dmg.sh 全程通 → dist/Arcrun-v0.18.7.dmg(10M)
掛載後 strings 抽 App 內嵌版本 = v0.18.7
Info.plist CFBundleShortVersionString = 0.18.7(以前永遠是 1.0.0)
⇒ **兩條打包線第一次在同一個版本號上對齊**——這正是 08-06 那個病
(只重打 Mac DMG、Windows 沒動、manifest 卻宣告新版)的根治。
· 未驗:Windows/Mac 真機行為;未出貨 ⇒ ◐
This commit is contained in:
@@ -6,11 +6,11 @@
|
||||
# 而且 macOS 對「不在 /Applications 的常駐程式」權限行為也不一致。
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
# 🔴 2026-08-06:版本號改由 daemon-version.sh 機械產生(不再手打/不靠 git describe)。
|
||||
# 🔴 2026-08-06:版本號改由 daemon-version.py 機械產生(不再手打/不靠 git describe)。
|
||||
# 病根:本 repo 一個 git tag 都沒有 ⇒ git describe 永遠拿不到 v0.18.x
|
||||
# ⇒ 只能靠打包時人工敲 VERSION= ⇒ 兩條打包線各敲各的 ⇒ manifest 說謊。
|
||||
# 理由與算法全文見 daemon-version.sh。
|
||||
VERSION="${VERSION:-$(./daemon-version.sh)}"
|
||||
# 理由與算法全文見 daemon-version.py。
|
||||
VERSION="${VERSION:-$(./daemon-version.py --stamp)}"
|
||||
|
||||
echo "① 先確保 .app 是最新的"
|
||||
VERSION="$VERSION" bash build-mac.sh >/dev/null
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
# 沒同綑 ⇒ 裝了也不會同步。
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
# 🔴 2026-08-06:版本號改由 daemon-version.sh 機械產生(不再手打/不靠 git describe)。
|
||||
# 🔴 2026-08-06:版本號改由 daemon-version.py 機械產生(不再手打/不靠 git describe)。
|
||||
# 病根:本 repo 一個 git tag 都沒有 ⇒ git describe 永遠拿不到 v0.18.x
|
||||
# ⇒ 只能靠打包時人工敲 VERSION= ⇒ 兩條打包線各敲各的 ⇒ manifest 說謊。
|
||||
# 理由與算法全文見 daemon-version.sh。
|
||||
VERSION="${VERSION:-$(./daemon-version.sh)}"
|
||||
# 理由與算法全文見 daemon-version.py。
|
||||
VERSION="${VERSION:-$(./daemon-version.py --stamp)}"
|
||||
|
||||
# 🔴 2026-08-06 機械閘:這一版的「更新內容」沒寫進 changelog ⇒ 不准打包。
|
||||
# leo:「給版本號、本版更新內容、打包產品、顯示在前端,這一整串都應該是機械化」。
|
||||
|
||||
@@ -34,7 +34,7 @@ MAKEMSIX="$MSIX_SDK_DIR/.vs/bin/makemsix"
|
||||
exit 1
|
||||
}
|
||||
|
||||
VERSION_RAW="${VERSION:-$(./daemon-version.sh)}" # 版本由 daemon-version.sh 機械產生(2026-08-06)
|
||||
VERSION_RAW="${VERSION:-$(./daemon-version.py --stamp)}" # 版本由 daemon-version.py 機械產生(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 當成變數名的一部分。
|
||||
|
||||
@@ -23,9 +23,9 @@ export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
# 🔴 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 數算出來,
|
||||
# 現在由 daemon-version.py 從 DAEMON_LINE + collector/ 的 commit 數算出來,
|
||||
# 兩條打包線(win/mac)在同一個 commit 上必得同一個號碼。理由全文見該腳本。
|
||||
VERSION="${VERSION:-$(./daemon-version.sh)}"
|
||||
VERSION="${VERSION:-$(./daemon-version.py --stamp)}"
|
||||
BUILD_TIME="$(date '+%Y%m%d-%H%M')"
|
||||
echo "🏷 版本:${VERSION}(build ${BUILD_TIME})"
|
||||
|
||||
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
"""daemon-version.py — daemon 版本號的唯一產生器(2026-08-06 二版)
|
||||
|
||||
🔴 leo 08-06:「給版本號、本版更新內容、打包產品、顯示在前端,
|
||||
這一整串都應該是機械化,**不應該每次手工做**」
|
||||
|
||||
── 病根(實測)────────────────────────────────────────────────────
|
||||
manifest 宣告 daemon v0.18.5,線上產物拆開卻是 v0.18.4。
|
||||
原因:本 repo **一個 git tag 都沒有**,build 腳本寫的是 `git describe --tags`
|
||||
⇒ 拿不到 v0.18.x ⇒ 版本只能靠打包時人工 `VERSION=v0.18.4 ./build-win.sh` 敲。
|
||||
人工敲 ⇒ 兩條打包線(win/mac)各敲各的 ⇒ manifest 說謊。
|
||||
|
||||
── 為什麼不是「數 commit 數」(一版的做法,已否決)─────────────────
|
||||
第一版用「動過 collector/ 的 commit 數」當 patch。確實不用手打,
|
||||
但**每個 commit 都變成一版**(一天 5 個 commit 就跳 5 版),
|
||||
而且每跳一版就要補一段 changelog——把機械化變成新的手工活。
|
||||
|
||||
── 現在的做法:版本由 changelog 決定,且不用手打數字 ──────────────
|
||||
單一真相源= docs-site/src/content/docs/help/changelog.md(用戶語言那份)。
|
||||
|
||||
· 要出新版 ⇒ 在檔案最上面加一段標題 `## 下一版(未發佈)`,底下寫白話更新內容。
|
||||
· 打包時本腳本把它**戳成正式版號**(上一版 patch + 1)並補上今天日期。
|
||||
· 沒有「未發佈」段 ⇒ 版本=最上面那一版(重打同一版,不會虛增)。
|
||||
|
||||
⇒ **版本號從頭到尾沒有人打過**,而且「更新內容」與「版本」天生綁在一起——
|
||||
不可能出現「版本升了但沒人知道改了什麼」,也不可能「寫了內容卻忘了升版」。
|
||||
⇒ 不需要 git tag/Actions/輪詢(守 D20 紅線)。
|
||||
|
||||
換線(0.18 → 0.19):改 `DAEMON_LINE`,下一版就從 0.19.0 開始。
|
||||
|
||||
用法:
|
||||
daemon-version.py # 印出版本(唯讀,不改檔)
|
||||
daemon-version.py --stamp # 把「下一版(未發佈)」戳成正式版號+日期後印出
|
||||
"""
|
||||
import datetime
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
ROOT = Path(subprocess.check_output(
|
||||
["git", "rev-parse", "--show-toplevel"], cwd=HERE, text=True).strip())
|
||||
CHANGELOG = ROOT / "docs-site/src/content/docs/help/changelog.md"
|
||||
LINE_FILE = ROOT / "DAEMON_LINE"
|
||||
|
||||
UNRELEASED = "## 下一版(未發佈)"
|
||||
RELEASED_RE = re.compile(r"^## v(\d+)\.(\d+)\.(\d+)(", re.M)
|
||||
|
||||
|
||||
def die(msg):
|
||||
print(msg, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
stamp = "--stamp" in sys.argv
|
||||
if not CHANGELOG.exists():
|
||||
die("❌ 找不到 changelog:%s" % CHANGELOG)
|
||||
line = LINE_FILE.read_text().strip() if LINE_FILE.exists() else "0.18"
|
||||
if not re.fullmatch(r"\d+\.\d+", line):
|
||||
die("❌ DAEMON_LINE 必須是 MAJOR.MINOR(例 0.18),現在是 %r" % line)
|
||||
|
||||
text = CHANGELOG.read_text()
|
||||
top = RELEASED_RE.search(text)
|
||||
if not top:
|
||||
die("❌ changelog 裡找不到任何 `## vX.Y.Z(…)` 的版本段落")
|
||||
prev = (int(top.group(1)), int(top.group(2)), int(top.group(3)))
|
||||
|
||||
has_unreleased = UNRELEASED in text
|
||||
if has_unreleased and text.index(UNRELEASED) > top.start():
|
||||
die("❌ `%s` 必須放在所有已發佈版本的**上面**(它代表還沒出的那一版)" % UNRELEASED)
|
||||
|
||||
if not has_unreleased:
|
||||
# 沒有待發佈內容=重打同一版。冪等,不虛增。
|
||||
version = "v%d.%d.%d" % prev
|
||||
if stamp:
|
||||
print(" ℹ️ changelog 沒有「下一版(未發佈)」段,版本維持 %s(重打同一版)"
|
||||
% version, file=sys.stderr)
|
||||
print(version)
|
||||
return
|
||||
|
||||
# 有待發佈內容 ⇒ 升版。換線時從該線的 .0 開始。
|
||||
major, minor = (int(x) for x in line.split("."))
|
||||
if (major, minor) != (prev[0], prev[1]):
|
||||
nxt = (major, minor, 0)
|
||||
else:
|
||||
nxt = (prev[0], prev[1], prev[2] + 1)
|
||||
version = "v%d.%d.%d" % nxt
|
||||
|
||||
if stamp:
|
||||
today = datetime.date.today().isoformat()
|
||||
heading = "## %s(%s)" % (version, today)
|
||||
CHANGELOG.write_text(text.replace(UNRELEASED, heading, 1))
|
||||
print(" ✅ changelog:「下一版(未發佈)」→ %s" % heading, file=sys.stderr)
|
||||
|
||||
print(version)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/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}"
|
||||
Reference in New Issue
Block a user