Initial commit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
timmy
2026-04-21 13:04:09 +08:00
commit faee15bc78
18 changed files with 3278 additions and 0 deletions

140
scripts/release.sh Executable file
View File

@@ -0,0 +1,140 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/common.sh
source "${SCRIPT_DIR}/lib/common.sh"
usage() {
cat >&2 <<'EOF'
Usage: release.sh <tag> [--name NAME] [--notes TEXT] [--target BRANCH]
[--draft] [--prerelease] [--asset PATH]... [--repo NAME]
<tag> tag_name若 tag 不存在 Gitea 會在 target 自動建
--name release 標題(預設同 tag
--notes release 說明
--target target_commitish預設 HEAD 所在分支
--draft 建成 draft
--prerelease 標記 prerelease
--asset PATH 要上傳的附件(可重複)
--repo NAME 指定 repo省略則從 cwd origin 推斷
EOF
exit 1
}
[[ $# -ge 1 ]] || usage
TAG=""
NAME=""
NOTES=""
TARGET=""
DRAFT=false
PRERELEASE=false
ASSETS=()
REPO_OVERRIDE=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) [[ $# -ge 2 ]] || { log_err "--name 需要值"; exit 1; }; NAME="$2"; shift 2 ;;
--notes) [[ $# -ge 2 ]] || { log_err "--notes 需要值"; exit 1; }; NOTES="$2"; shift 2 ;;
--target) [[ $# -ge 2 ]] || { log_err "--target 需要值"; exit 1; }; TARGET="$2"; shift 2 ;;
--draft) DRAFT=true; shift ;;
--prerelease) PRERELEASE=true; shift ;;
--asset) [[ $# -ge 2 ]] || { log_err "--asset 需要值"; exit 1; }; ASSETS+=("$2"); shift 2 ;;
--repo) [[ $# -ge 2 ]] || { log_err "--repo 需要值"; exit 1; }; REPO_OVERRIDE="$2"; shift 2 ;;
-h|--help) usage ;;
-*) log_err "未知參數:$1"; usage ;;
*)
if [[ -z "$TAG" ]]; then TAG="$1"; else log_err "多餘參數:$1"; usage; fi
shift ;;
esac
done
[[ -n "$TAG" ]] || usage
NAME="${NAME:-$TAG}"
load_config
require_cmd jq
if [[ -n "$REPO_OVERRIDE" ]]; then
OWNER_REPO="${GITEA_USER}/${REPO_OVERRIDE}"
else
if ! OWNER_REPO="$(infer_repo_from_cwd)"; then
log_err "無法從 cwd 推斷 repo請加 --repo"
exit 1
fi
fi
if [[ -z "$TARGET" ]]; then
if git rev-parse --git-dir >/dev/null 2>&1; then
TARGET="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
fi
fi
# 驗證 asset 路徑
for a in "${ASSETS[@]+"${ASSETS[@]}"}"; do
if [[ ! -f "$a" ]]; then
log_err "找不到 asset 檔案:$a"
exit 1
fi
done
# 組 release body
BODY="$(jq -n \
--arg tag "$TAG" \
--arg name "$NAME" \
--arg body "$NOTES" \
--arg target "$TARGET" \
--argjson draft "$DRAFT" \
--argjson prerelease "$PRERELEASE" \
'{tag_name:$tag, name:$name, body:$body, draft:$draft, prerelease:$prerelease}
+ (if $target == "" then {} else {target_commitish:$target} end)')"
log_info "建立 release ${OWNER_REPO} / ${TAG}"
gitea_api POST "/repos/${OWNER_REPO}/releases" "$BODY"
case "$HTTP_CODE" in
201) log_ok "release 已建立" ;;
404) log_err "找不到 repo${OWNER_REPO}"; exit 1 ;;
*) log_err "建立 release 失敗 (HTTP ${HTTP_CODE})"; { cat "$GITEA_API_RESP_FILE"; echo ""; } >&2; exit 1 ;;
esac
RELEASE_ID="$(jq -r '.id' < "$GITEA_API_RESP_FILE")"
RELEASE_URL="$(jq -r '.html_url' < "$GITEA_API_RESP_FILE")"
# stdout 先印 release URL
echo "$RELEASE_URL"
# 上傳 asset
FAILED=()
for a in "${ASSETS[@]+"${ASSETS[@]}"}"; do
local_name="$(basename "$a")"
log_info "上傳 asset${local_name}"
UP_RESP="$(mktemp)"
UP_CODE="$(curl -sS -o "$UP_RESP" -w '%{http_code}' \
-X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@${a};filename=${local_name}" \
"${GITEA_URL}/api/v1/repos/${OWNER_REPO}/releases/${RELEASE_ID}/assets?name=${local_name}")"
case "$UP_CODE" in
201)
ASSET_URL="$(jq -r '.browser_download_url' < "$UP_RESP")"
log_ok "$local_name"
echo "$ASSET_URL"
;;
*)
log_err "上傳 ${local_name} 失敗 (HTTP ${UP_CODE})"
cat "$UP_RESP" >&2
echo "" >&2
FAILED+=("$local_name")
;;
esac
rm -f "$UP_RESP"
done
if [[ ${#FAILED[@]} -gt 0 ]]; then
log_err "部分 asset 上傳失敗:${FAILED[*]}"
exit 1
fi
log_done "完成"