Files
ligent/tools/get-version.sh
T
2026-10-03 14:13:21 +08:00

127 lines
6.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
# get-version.sh - 算出版本号,供 Makefile 用 -D 注入
#
# 输出六行(顺序固定,Makefile 按行取):
# 1. CalVer 主版本号 YYYY.MM.DD
# 2. git 短哈希 abc1234 / 1a2b3c4.dirty / unknown
# 3. 分支名 master / unknown(detached HEAD 或非 git)
# 4. 构建日期 YYYY-MM-DD(UTC)
# 5. 构建时间 HH:MM:SS(UTC)
# 6. 本脚本算出的版本串(仅供人看/调试)
#
# 版本方案:**CalVer(发布日的 YYYY.MM.DD)+ git 短哈希**。
#
# 2026.02.14 打在 tag 上的发布版
# 2026.02.14-1a2b3c4 tag 之后又提交了
# 2026.02.14-1a2b3c4.dirty 工作区还有没提交的改动
#
# 为什么 CalVer 而不是 SemVer:ligent 是个终端工具,不是被别人链接的库,
# "第几个大版本"这种信息对它没什么用;"这是哪天的东西"才有用 —— 用户看到
# 2026.02.14 立刻知道新旧。真需要表达破坏性变更时,git 历史和 README 比版本
# 号数字说得清楚。
#
# 为什么还要带哈希:CalVer 的粒度是"天",同一天可能发好几次;而且用户手上
# 的二进制未必来自 tag(可能自己改了源码)。带上哈希,报 bug 时能精确定位。
#
# 分隔符用 "-" 而不是 git describe 传统的 "+g":我们只用 git,不存在"区分是
# 哪种 SCM"的需求(那正是 g 前缀存在的理由),而 2026.09.22-1a2b3c4 少两个
# 符号、念起来也顺。
#
# 取版本号的优先级(从高到低):
# 1. 环境变量 LIGENT_VERSION_OVERRIDE —— 给 CI 用,tag 打得不对时能手动指定
# 2. git tag 里最新的 CalVer 形态的 tag(v2026.02.14 或 2026.02.14)
# 3. 源码目录的 mtime(从 tarball 解出来时没有 .git,用文件的日期当版本)
#
# 任何一步失败都不算致命:退回一个明确的默认值,绝不让构建挂掉 ——
# 版本号不对只是难看,构建挂掉是真的不能用。
set -eu
ROOT=$(cd "$(dirname "$0")/.." && pwd)
# ------------------------------------------------------- 构建日期与时间
# SOURCE_DATE_EPOCH 是"可复现构建"的标准约定:设了它就用它当构建时间,
# 这样同一个源码在不同时间编出来的二进制是**逐字节相同**的。
# 不认这个约定的话,CI 上每次构建的产物哈希都不一样,没法校验。
if [ -n "${SOURCE_DATE_EPOCH:-}" ]; then
BUILD_DATE=$(date -u -d "@$SOURCE_DATE_EPOCH" +%Y-%m-%d 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" +%Y-%m-%d 2>/dev/null \
|| echo unknown)
BUILD_TIME=$(date -u -d "@$SOURCE_DATE_EPOCH" +%H:%M:%S 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" +%H:%M:%S 2>/dev/null \
|| echo unknown)
else
BUILD_DATE=$(date -u +%Y-%m-%d 2>/dev/null || echo unknown)
BUILD_TIME=$(date -u +%H:%M:%S 2>/dev/null || echo unknown)
fi
# ---------------------------------------------------------------- 主版本号
if [ -n "${LIGENT_VERSION_OVERRIDE:-}" ]; then
CALVER=$LIGENT_VERSION_OVERRIDE
else
CALVER=
# 只在真的在 git 工作区里、且 git 可用时才问 git
if command -v git >/dev/null 2>&1 && [ -d "$ROOT/.git" ]; then
# 找最近的、长得像 CalVer 的 tag。用 grep 过滤而不是 --match:不同
# git 版本对 --match 的 glob 支持略有差异,而这里只要"第一个符合
# YYYY.MM.DD 形态的"就够了,过滤更直观也更可移植。
CALVER=$(git -C "$ROOT" tag --list 2>/dev/null \
| grep -E '^v?[0-9]{4}\.[0-9]{2}\.[0-9]{2}$' \
| sed 's/^v//' \
| sort \
| tail -1 || true)
fi
# 没有 tag:用源码目录的日期当版本。tarball 场景下这是"这份源码什么时候
# 打的包",虽然不如 tag 精确,但比写死一个 0.0.0 有用。
if [ -z "$CALVER" ]; then
CALVER=$(date -u -r "$ROOT/src/main.c" +%Y.%m.%d 2>/dev/null || true)
fi
[ -n "$CALVER" ] || CALVER=0.0.0-unknown
fi
# ---------------------------------------------------------------- git 哈希
# LIGENT_HASH_OVERRIDE:给容器/CI 用。容器里没有 .git(.dockerignore 排除了),
# 那里问不出哈希来,只能由外面算好传进来。
HASH=${LIGENT_HASH_OVERRIDE:-}
if [ -z "$HASH" ] && command -v git >/dev/null 2>&1 && [ -d "$ROOT/.git" ]; then
if h=$(git -C "$ROOT" rev-parse --short=7 HEAD 2>/dev/null); then
HASH=$h
# 工作区脏了就标出来。**这一条很重要**:不带这个标记的话,一个改过
# 源码的构建和一个干净的构建会报出同样的版本号,排查时会被误导。
if ! git -C "$ROOT" diff --quiet HEAD -- 2>/dev/null; then
HASH="$HASH.dirty"
elif [ -n "$(git -C "$ROOT" ls-files --others --exclude-standard 2>/dev/null)" ]; then
# 未跟踪的新文件也算脏 —— 它可能正是被编译进去的那个源文件
HASH="$HASH.dirty"
fi
fi
fi
[ -n "$HASH" ] || HASH=unknown
# ---------------------------------------------------------------- 分支名
# LIGENT_BRANCH_OVERRIDE 同理给容器/CI 用。
BRANCH=${LIGENT_BRANCH_OVERRIDE:-}
if [ -z "$BRANCH" ] && command -v git >/dev/null 2>&1 && [ -d "$ROOT/.git" ]; then
b=$(git -C "$ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
# detached HEAD(CI 上 checkout 某个 tag/commit 时就是这种状态)会返回
# 字面量 "HEAD" —— 那不是分支名,照实说成 unknown,别让读的人以为
# 有个叫 HEAD 的分支。
if [ -n "$b" ] && [ "$b" != "HEAD" ]; then
BRANCH=$b
fi
fi
[ -n "$BRANCH" ] || BRANCH=unknown
printf '%s\n' "$CALVER"
printf '%s\n' "$HASH"
printf '%s\n' "$BRANCH"
printf '%s\n' "$BUILD_DATE"
printf '%s\n' "$BUILD_TIME"
# 第 6 行只是给人看的完整串。哈希未知时**不要**拼出 "…-unknown" ——
# 那既丑又会被误读成一个真实的哈希。src/version.c 的拼接遵循同一规则,
# 两处必须一致,否则这个"调试输出"会误导人。
if [ "$HASH" = unknown ]; then
printf '%s\n' "$CALVER"
else
printf '%s-%s\n' "$CALVER" "$HASH"
fi