forked from Eeveid/lightOps
- 工作流发布步骤改为显式使用仓库密钥 RELEASE_TOKEN - 去掉发布脚本对 404 的吞错处理,改回明确暴露真实权限或接口错误 - 便于验证当前自定义 token 是否具备 Gitea Release 创建与附件上传权限
234 lines
6.1 KiB
Bash
Executable File
234 lines
6.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -Eeuo pipefail
|
||
|
||
GITEA_URL="${GITEA_URL:-https://gitea.kmux.cn}"
|
||
OWNER="${GITEA_OWNER:-Eeveid}"
|
||
REPO="${GITEA_REPO:-lightOps}"
|
||
TAG=""
|
||
TITLE=""
|
||
NOTES=""
|
||
TARGET="main"
|
||
CREATE_TAG="true"
|
||
PUSH_TAG="true"
|
||
PRERELEASE="false"
|
||
PACKAGES=()
|
||
|
||
usage() {
|
||
cat <<'EOF'
|
||
LightOps Gitea Release 自动发布脚本
|
||
|
||
用法:
|
||
GITEA_TOKEN=<token> bash scripts/publish-gitea-release.sh --tag v0.1.0 --package target/releases/lightops.tar.gz
|
||
|
||
选项:
|
||
--gitea-url <url> Gitea 地址,默认 https://gitea.kmux.cn
|
||
--owner <owner> 仓库所有者,默认 Eeveid
|
||
--repo <repo> 仓库名,默认 lightOps
|
||
--tag <tag> 发布标签,例如 v0.1.0,必填
|
||
--title <title> Release 标题,默认等于 tag
|
||
--notes <text> Release 说明
|
||
--target <ref> tag 指向的分支或提交,默认 main
|
||
--package <path> 要上传的发布包,可重复传入
|
||
--no-create-tag 不自动创建本地 tag
|
||
--no-push-tag 不自动推送 tag
|
||
--prerelease 标记为预发布
|
||
-h, --help 显示帮助
|
||
|
||
环境变量:
|
||
GITEA_TOKEN Gitea Access Token,必须具备仓库 Release 写入权限
|
||
EOF
|
||
}
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--gitea-url)
|
||
GITEA_URL="${2:?缺少 --gitea-url 参数值}"
|
||
shift 2
|
||
;;
|
||
--owner)
|
||
OWNER="${2:?缺少 --owner 参数值}"
|
||
shift 2
|
||
;;
|
||
--repo)
|
||
REPO="${2:?缺少 --repo 参数值}"
|
||
shift 2
|
||
;;
|
||
--tag)
|
||
TAG="${2:?缺少 --tag 参数值}"
|
||
shift 2
|
||
;;
|
||
--title)
|
||
TITLE="${2:?缺少 --title 参数值}"
|
||
shift 2
|
||
;;
|
||
--notes)
|
||
NOTES="${2:?缺少 --notes 参数值}"
|
||
shift 2
|
||
;;
|
||
--target)
|
||
TARGET="${2:?缺少 --target 参数值}"
|
||
shift 2
|
||
;;
|
||
--package)
|
||
PACKAGES+=("${2:?缺少 --package 参数值}")
|
||
shift 2
|
||
;;
|
||
--no-create-tag)
|
||
CREATE_TAG="false"
|
||
shift
|
||
;;
|
||
--no-push-tag)
|
||
PUSH_TAG="false"
|
||
shift
|
||
;;
|
||
--prerelease)
|
||
PRERELEASE="true"
|
||
shift
|
||
;;
|
||
-h|--help)
|
||
usage
|
||
exit 0
|
||
;;
|
||
*)
|
||
echo "未知参数:$1" >&2
|
||
usage >&2
|
||
exit 2
|
||
;;
|
||
esac
|
||
done
|
||
|
||
log() {
|
||
printf '\033[1;32m[LightOps]\033[0m %s\n' "$*"
|
||
}
|
||
|
||
fail() {
|
||
printf '\033[1;31m[LightOps]\033[0m %s\n' "$*" >&2
|
||
exit 1
|
||
}
|
||
|
||
require_cmd() {
|
||
command -v "$1" >/dev/null 2>&1 || fail "缺少命令:$1"
|
||
}
|
||
|
||
detect_json_runtime() {
|
||
if command -v node >/dev/null 2>&1; then
|
||
echo "node"
|
||
elif command -v python3 >/dev/null 2>&1; then
|
||
echo "python3"
|
||
elif command -v python >/dev/null 2>&1; then
|
||
echo "python"
|
||
else
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
json_escape() {
|
||
case "$JSON_RUNTIME" in
|
||
node)
|
||
node -e 'let s=""; process.stdin.setEncoding("utf8"); process.stdin.on("data", d => s += d); process.stdin.on("end", () => process.stdout.write(JSON.stringify(s)));'
|
||
;;
|
||
*)
|
||
"$JSON_RUNTIME" -c 'import json,sys; print(json.dumps(sys.stdin.read()))'
|
||
;;
|
||
esac
|
||
}
|
||
|
||
api_url() {
|
||
printf '%s/api/v1/repos/%s/%s/%s' "${GITEA_URL%/}" "$OWNER" "$REPO" "$1"
|
||
}
|
||
|
||
api_get() {
|
||
curl -fsS -H "Authorization: token ${GITEA_TOKEN}" "$1"
|
||
}
|
||
|
||
api_post_json() {
|
||
curl -fsS -X POST \
|
||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$2" \
|
||
"$1"
|
||
}
|
||
|
||
api_post_json_with_status() {
|
||
local output_file status
|
||
output_file="$(mktemp)"
|
||
status="$(curl -sS -o "$output_file" -w '%{http_code}' -X POST \
|
||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$2" \
|
||
"$1")" || {
|
||
cat "$output_file" >&2 || true
|
||
rm -f "$output_file"
|
||
return 1
|
||
}
|
||
cat "$output_file"
|
||
rm -f "$output_file"
|
||
printf '\n%s' "$status"
|
||
}
|
||
|
||
release_id_from_json() {
|
||
case "$JSON_RUNTIME" in
|
||
node)
|
||
node -e 'let s=""; process.stdin.setEncoding("utf8"); process.stdin.on("data", d => s += d); process.stdin.on("end", () => process.stdout.write(String(JSON.parse(s).id)));'
|
||
;;
|
||
*)
|
||
"$JSON_RUNTIME" -c 'import json,sys; print(json.load(sys.stdin)["id"])'
|
||
;;
|
||
esac
|
||
}
|
||
|
||
[[ -n "$TAG" ]] || fail "必须指定 --tag"
|
||
[[ -n "${GITEA_TOKEN:-}" ]] || fail "必须设置 GITEA_TOKEN"
|
||
[[ ${#PACKAGES[@]} -gt 0 ]] || fail "至少指定一个 --package"
|
||
[[ -n "$TITLE" ]] || TITLE="$TAG"
|
||
|
||
require_cmd curl
|
||
require_cmd git
|
||
JSON_RUNTIME="$(detect_json_runtime)" || fail "缺少命令:node、python 或 python3"
|
||
|
||
for package in "${PACKAGES[@]}"; do
|
||
[[ -f "$package" ]] || fail "发布包不存在:$package"
|
||
done
|
||
|
||
if [[ "$CREATE_TAG" == "true" ]] && ! git rev-parse "$TAG" >/dev/null 2>&1; then
|
||
log "创建本地 tag:$TAG"
|
||
git tag -a "$TAG" "$TARGET" -m "$TITLE"
|
||
fi
|
||
|
||
if [[ "$PUSH_TAG" == "true" ]]; then
|
||
log "推送 tag:$TAG"
|
||
git push origin "$TAG"
|
||
fi
|
||
|
||
release_url="$(api_url "releases/tags/${TAG}")"
|
||
release_json=""
|
||
release_status="$(curl -sS -o /dev/null -w '%{http_code}' -H "Authorization: token ${GITEA_TOKEN}" "$release_url" || true)"
|
||
if [[ "$release_status" == "200" ]] && release_json="$(api_get "$release_url" 2>/dev/null)"; then
|
||
log "Release 已存在:$TAG"
|
||
else
|
||
log "创建 Release:$TAG"
|
||
title_json="$(printf '%s' "$TITLE" | json_escape)"
|
||
notes_json="$(printf '%s' "$NOTES" | json_escape)"
|
||
payload="{\"tag_name\":\"${TAG}\",\"target_commitish\":\"${TARGET}\",\"name\":${title_json},\"body\":${notes_json},\"draft\":false,\"prerelease\":${PRERELEASE}}"
|
||
create_result="$(api_post_json_with_status "$(api_url releases)" "$payload")" || fail "创建 Release 请求失败"
|
||
create_status="${create_result##*$'\n'}"
|
||
release_json="${create_result%$'\n'*}"
|
||
[[ "$create_status" == "201" || "$create_status" == "200" ]] || fail "创建 Release 失败,HTTP 状态码:$create_status"
|
||
fi
|
||
|
||
release_id="$(printf '%s' "$release_json" | release_id_from_json)"
|
||
for package in "${PACKAGES[@]}"; do
|
||
name="$(basename "$package")"
|
||
log "上传附件:$name"
|
||
curl -fsS -X POST \
|
||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||
-F "attachment=@${package}" \
|
||
"$(api_url "releases/${release_id}/assets")?name=${name}" >/dev/null
|
||
done
|
||
|
||
cat <<EOF
|
||
|
||
Release 发布完成:
|
||
${GITEA_URL%/}/${OWNER}/${REPO}/releases/tag/${TAG}
|
||
EOF
|