feat: update

This commit is contained in:
2026-08-25 15:26:29 +08:00
parent 03d6298145
commit 4423e7df1a
9 changed files with 661 additions and 21 deletions
+195
View File
@@ -0,0 +1,195 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
version.sh [-C GIT_DIR] [--docker] [--version VER]
Print one line: the canonical artifact version (Debian Version, no leading v).
With --docker, print the Docker tag rendering of that version.
Derivation (builder contract «版本号»):
official HEAD exact-match of vX.Y.Z → X.Y.Z
test nearest ancestor stable tag → X.Y.Z~branch.n+gSHA
no tag no stable tag reachable from HEAD → 0.0.0~branch+gSHA
Stable tags match v<major>.<minor>.<patch> only. Baseline is ancestry, not
the highest version in the repository. This script does not fetch tags.
--version VER overrides derivation. Official-shaped VER (X.Y.Z, optional
leading v) is accepted only when HEAD exact-matches that tag. Test-shaped
VER is used as-is.
Branch name: git symbolic-ref, or BUILD_BRANCH / CI_COMMIT_BRANCH /
GITHUB_REF_NAME when detached, else "detached". Sanitized to [a-z0-9-],
max 32 characters.
EOF
}
root=.
mode=canonical
override=
while (($#)); do
case "$1" in
-C) root=$2; shift 2 ;;
--docker) mode=docker; shift ;;
--version) override=$2; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "Error: unknown argument: $1" >&2; usage >&2; exit 2 ;;
esac
done
if ! git -C "$root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "Error: not a git repository: $root" >&2
exit 2
fi
root=$(git -C "$root" rev-parse --show-toplevel)
gitc() {
git -C "$root" "$@"
}
is_stable_tag() {
[[ "$1" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
}
is_official_version() {
[[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
}
# Strip one leading v if present.
strip_v() {
local v=$1
if [[ "$v" == v* ]]; then
v=${v#v}
fi
printf '%s' "$v"
}
sanitize_branch() {
local b=$1
b=$(printf '%s' "$b" | tr '[:upper:]' '[:lower:]')
b=${b//\//-}
b=${b//_/-}
b=$(printf '%s' "$b" | tr -cd 'a-z0-9-')
b=$(printf '%s' "$b" | tr -s '-')
b=${b#-}
b=${b%-}
if ((${#b} > 32)); then
b=${b:0:32}
b=${b%-}
fi
if [[ -z "$b" ]]; then
b=unknown
fi
printf '%s' "$b"
}
branch_slug() {
local b
b=$(gitc rev-parse --abbrev-ref HEAD)
if [[ "$b" == HEAD ]]; then
b=${BUILD_BRANCH:-${CI_COMMIT_BRANCH:-${GITHUB_REF_NAME:-detached}}}
fi
sanitize_branch "$b"
}
short_sha() {
gitc rev-parse HEAD | cut -c1-7
}
# Highest stable tag pointing at HEAD, or empty.
official_tag_at_head() {
local tag best=
while IFS= read -r tag; do
[[ -n "$tag" ]] || continue
is_stable_tag "$tag" || continue
if [[ -z "$best" ]]; then
best=$tag
elif printf '%s\n%s\n' "$best" "$tag" | sort -V | tail -n 1 | grep -qx "$tag"; then
best=$tag
fi
done < <(gitc tag --points-at HEAD)
printf '%s' "$best"
}
# Stable ancestor tag with the fewest commits to HEAD (not sort -V globally).
nearest_stable_tag() {
local tag dist best_dist="" best_tag=""
while IFS= read -r tag; do
[[ -n "$tag" ]] || continue
is_stable_tag "$tag" || continue
dist=$(gitc rev-list --count "${tag}..HEAD")
if [[ -z "$best_dist" ]] || ((dist < best_dist)); then
best_dist=$dist
best_tag=$tag
elif ((dist == best_dist)); then
if printf '%s\n%s\n' "$best_tag" "$tag" | sort -V | tail -n 1 | grep -qx "$tag"; then
best_tag=$tag
fi
fi
done < <(gitc tag --merged HEAD)
printf '%s' "$best_tag"
}
derive_canonical() {
local tag base n sha branch
tag=$(official_tag_at_head)
if [[ -n "$tag" ]]; then
printf '%s' "${tag#v}"
return
fi
sha=$(short_sha)
branch=$(branch_slug)
base=$(nearest_stable_tag)
if [[ -z "$base" ]]; then
printf '0.0.0~%s+g%s' "$branch" "$sha"
return
fi
n=$(gitc rev-list --count "${base}..HEAD")
printf '%s~%s.%s+g%s' "${base#v}" "$branch" "$n" "$sha"
}
to_docker() {
local v=$1
v=${v//\~/-}
v=${v//+g/.g}
printf '%s' "$v"
}
canonical=
if [[ -n "$override" ]]; then
canonical=$(strip_v "$override")
if [[ "$canonical" == *'_'* ]]; then
echo "Error: version must not contain '_': $canonical" >&2
exit 2
fi
if is_official_version "$canonical"; then
derived=$(derive_canonical)
if [[ "$derived" != "$canonical" ]]; then
echo "Error: --version $canonical looks official but HEAD is $derived" >&2
echo "Official X.Y.Z is allowed only when HEAD exact-matches vX.Y.Z." >&2
exit 2
fi
fi
else
canonical=$(derive_canonical)
fi
if [[ -z "$canonical" ]]; then
echo "Error: empty version" >&2
exit 1
fi
if [[ "$mode" == docker ]]; then
tag=$(to_docker "$canonical")
if [[ "$tag" == *:* || "$tag" == */* || "$tag" == *'~'* ]]; then
echo "Error: docker tag still contains illegal characters: $tag" >&2
exit 1
fi
printf '%s\n' "$tag"
else
printf '%s\n' "$canonical"
fi