From b783b870e61af746fb0d25f3393cf659fe5ccce9 Mon Sep 17 00:00:00 2001 From: richblack Date: Thu, 6 Aug 2026 13:16:14 +0800 Subject: [PATCH] =?UTF-8?q?=E7=89=88=E6=9C=AC=E8=99=9F=E6=94=B9=E7=94=B1?= =?UTF-8?q?=20changelog=20=E6=B1=BA=E5=AE=9A=EF=BC=9A=E5=BE=9E=E9=A0=AD?= =?UTF-8?q?=E5=88=B0=E5=B0=BE=E6=B2=92=E6=9C=89=E4=BA=BA=E6=89=93=E9=81=8E?= =?UTF-8?q?=E6=95=B8=E5=AD=97=EF=BC=8C=E4=B8=94=E8=88=87=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=85=A7=E5=AE=B9=E5=A4=A9=E7=94=9F=E7=B6=81=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 為什麼推翻自己幾小時前的做法 上一版用「動過 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 真機行為;未出貨 ⇒ ◐ --- cmd/arcrun-app/build-dmg.sh | 6 +- cmd/arcrun-app/build-mac.sh | 6 +- cmd/arcrun-app/build-msix.sh | 2 +- cmd/arcrun-app/build-win.sh | 4 +- cmd/arcrun-app/daemon-version.py | 101 +++++++++++++++++++++++++++++++ cmd/arcrun-app/daemon-version.sh | 46 -------------- 6 files changed, 110 insertions(+), 55 deletions(-) create mode 100755 cmd/arcrun-app/daemon-version.py delete mode 100755 cmd/arcrun-app/daemon-version.sh diff --git a/cmd/arcrun-app/build-dmg.sh b/cmd/arcrun-app/build-dmg.sh index af58fcb..c7ffd5f 100755 --- a/cmd/arcrun-app/build-dmg.sh +++ b/cmd/arcrun-app/build-dmg.sh @@ -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 diff --git a/cmd/arcrun-app/build-mac.sh b/cmd/arcrun-app/build-mac.sh index d27e02a..b63a7ae 100755 --- a/cmd/arcrun-app/build-mac.sh +++ b/cmd/arcrun-app/build-mac.sh @@ -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:「給版本號、本版更新內容、打包產品、顯示在前端,這一整串都應該是機械化」。 diff --git a/cmd/arcrun-app/build-msix.sh b/cmd/arcrun-app/build-msix.sh index 2a26334..4316753 100755 --- a/cmd/arcrun-app/build-msix.sh +++ b/cmd/arcrun-app/build-msix.sh @@ -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 當成變數名的一部分。 diff --git a/cmd/arcrun-app/build-win.sh b/cmd/arcrun-app/build-win.sh index 17fc32b..3a6fc59 100755 --- a/cmd/arcrun-app/build-win.sh +++ b/cmd/arcrun-app/build-win.sh @@ -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})" diff --git a/cmd/arcrun-app/daemon-version.py b/cmd/arcrun-app/daemon-version.py new file mode 100755 index 0000000..16a55d9 --- /dev/null +++ b/cmd/arcrun-app/daemon-version.py @@ -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() diff --git a/cmd/arcrun-app/daemon-version.sh b/cmd/arcrun-app/daemon-version.sh deleted file mode 100755 index 472ff40..0000000 --- a/cmd/arcrun-app/daemon-version.sh +++ /dev/null @@ -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}"