From 96a0772d65e29c9471d4b303b611dd3dc93f460c Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 8 Sep 2026 10:32:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(gitea):=20=E7=82=BA=20hash-id=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20key=20=E5=AD=90=E5=91=BD=E4=BB=A4=E4=B8=A6=E6=8B=86?= =?UTF-8?q?=E5=87=BA=E7=8D=A8=E7=AB=8B=E7=9A=84=E5=90=8D=E7=A8=B1=E6=AD=A3?= =?UTF-8?q?=E8=A6=8F=E5=8C=96=E6=A8=A1=E7=B5=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 為什麼:多個技能各自手動拼接計畫名稱與 owner/repo 字串再算雜湊,重複邏輯容易長出分歧的正規化寫法,也讓 hash-id 混雜了業務規則,職責變得不單純。 做了什麼: - hash-id 新增 key 子命令(hash-id key <計畫名稱> ),將計畫名稱正規化後以 | 分隔接上 owner/repo 再計算雜湊,提供統一的組法入口,讓其他技能不必各自接字串。 - 將正規化規則(Unicode NFC、去除頭尾空白、內部連續空白壓成一個、全形英數轉半形,大小寫維持不變)獨立成 tools/normalize-name.sh,hash-id 只呼叫它,不再內嵌業務規則,保持雜湊職責單純,也方便日後其他工具重用同一套正規化邏輯。 Co-Authored-By: Claude Sonnet 5 --- tools/hash-id | 21 ++++++++++++++++++- tools/normalize-name.sh | 46 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100755 tools/normalize-name.sh diff --git a/tools/hash-id b/tools/hash-id index c09f52f..d080508 100755 --- a/tools/hash-id +++ b/tools/hash-id @@ -2,17 +2,36 @@ # hash-id — 產生完整 40 碼大寫 SHA-1 hash。 # 用法: # hash-id +# hash-id key <計畫名稱> # printf '%s' | hash-id +# key 子命令的參數: +# 計畫名稱: 交給 normalize-name.sh 正規化後再接字串,同名不同打法算出同一把 key。 +# owner/repo: 存取庫識別,原樣接上,不經正規化。 # 規則: # 取完整 SHA-1 四十碼,a-f 轉大寫,不截短、不加前綴。 # 輸入為空即 exit 2。空字串本身也有合法的 SHA-1,靜靜回傳它就會生出 # 一個看起來正常的頁名,把內容寫到沒有人讀的那一頁。 +# key 子命令把多個欄位接成雜湊輸入的組法收在這裡當單一入口:固定用 `|` +# 分隔,避免各技能各自接字串各接各的。正規化規則本身不是雜湊職責,收在 +# 獨立那支工具,這裡只管接字串。 # 結束碼: 0=成功 1=這台機器沒有 sha1sum 也沒有 shasum,算不出雜湊 # 2=用法錯誤:沒有給文字,或給的是空字串 set -eu +dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) + input='' -if [ "$#" -gt 0 ]; then +if [ "${1:-}" = 'key' ]; then + shift + name="${1:-}"; repo="${2:-}" + if [ -z "$name" ] || [ -z "$repo" ]; then + echo 'usage: hash-id key <計畫名稱> ' >&2 + exit 2 + fi + # 正規化計畫名稱,讓「我的 計畫」與「我的 計畫」這類同名不同打法算出同一把 key。 + name=$(sh "$dir/normalize-name.sh" "$name") + input="${name}|${repo}" +elif [ "$#" -gt 0 ]; then input=$* elif [ -t 0 ]; then input='' diff --git a/tools/normalize-name.sh b/tools/normalize-name.sh new file mode 100755 index 0000000..c1c5d4f --- /dev/null +++ b/tools/normalize-name.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env sh +# normalize-name — 正規化一個名稱字串,讓同一個名稱的不同打法算出同一個結果。 +# 用法: +# normalize-name.sh +# 規則(依序套用): +# 1. Unicode NFC 正規化 +# 2. 去除頭尾空白 +# 3. 內部連續空白(含全形空白)壓成一個半形空白 +# 4. 英數全形字元轉半形(只轉英數,其他全形字元如標點不動) +# 大小寫不在正規化範圍內,維持原樣、視為不同名稱。 +# 例子: +# normalize-name.sh "我的 計畫" -> 我的 計畫 +# normalize-name.sh " Abc1 " -> Abc1 +# 結束碼: 0=成功 1=這台機器沒有 python3,算不出正規化結果 2=用法錯誤:沒有給文字 +set -eu + +if [ "$#" -lt 1 ]; then + echo 'usage: normalize-name.sh ' >&2 + exit 2 +fi + +if ! command -v python3 >/dev/null 2>&1; then + echo 'no python3 found' >&2 + exit 1 +fi + +printf '%s' "$1" | python3 -c ' +import sys, re, unicodedata + +FULLWIDTH_OFFSET = 0xFEE0 # Unicode 全形英數區塊相對半形區塊的固定位移量 + + +def to_halfwidth_alnum(ch): + if ("0" <= ch <= "9") or ("A" <= ch <= "Z") or ("a" <= ch <= "z"): + return chr(ord(ch) - FULLWIDTH_OFFSET) + return ch + + +name = unicodedata.normalize("NFC", sys.stdin.read()) +name = name.strip() +name = re.sub(r"[ \t ]+", " ", name).strip() +# 只轉英數的全形字元,其他全形字元(例如全形標點)不動——只轉英數是規則本身 +# 的範圍,不是整段跑 NFKC;NFKC 連標點都會改寫,會讓名稱的視覺呈現跟著變。 +name = "".join(to_halfwidth_alnum(c) for c in name) +sys.stdout.write(name) +' -- 2.53.0