#!/usr/bin/env bash # gitea-publish helper: 在自托管 Gitea 上建仓(若不存在)并推送一个本地目录。 # 凭据通过参数或环境变量传入,不写入任何文件;推送时用一次性鉴权 URL, # 本地 .git/config 只保存不含口令的干净 remote,避免口令落盘。 set -euo pipefail REPO="" PATH_="" URL="${GITEA_URL:-}" USER="${GITEA_USER:-}" TOKEN="${GITEA_TOKEN:-}" DISPLAY_URL="" PRIVATE="false" BRANCH="" while [[ $# -gt 0 ]]; do case "$1" in --repo) REPO="$2"; shift 2;; --path) PATH_="$2"; shift 2;; --url) URL="$2"; shift 2;; --display-url) DISPLAY_URL="$2"; shift 2;; --user) USER="$2"; shift 2;; --token) TOKEN="$2"; shift 2;; --private) PRIVATE="true"; shift;; --branch) BRANCH="$2"; shift 2;; *) echo "未知参数: $1" >&2; exit 1;; esac done # ---- 校验必填项 ---- [[ -z "$REPO" ]] && { echo "缺少 --repo" >&2; exit 1; } [[ -z "$PATH_" ]] && { echo "缺少 --path" >&2; exit 1; } [[ -d "$PATH_" ]] || { echo "目录不存在: $PATH_" >&2; exit 1; } [[ -z "$URL" ]] && { echo "缺少 --url 或环境变量 GITEA_URL" >&2; exit 1; } [[ -z "$USER" ]] && { echo "缺少 --user 或环境变量 GITEA_USER" >&2; exit 1; } [[ -z "$TOKEN" ]] && { echo "缺少 --token 或环境变量 GITEA_TOKEN" >&2; exit 1; } # 规范化:去掉结尾斜杠 URL="${URL%/}" # 对外透出的 Web 地址:默认使用公开域名 gitea.apescale.com。 # 即使 git 推送 / API 调用走的是内网地址或 IP,最终展示给用户的链接 # 也始终落在公开域名下,避免把内网主机名 / IP 直接暴露出来。 DISPLAY_URL="${DISPLAY_URL:-${GITEA_DISPLAY_URL:-https://gitea.apescale.com}}" DISPLAY_URL="${DISPLAY_URL%/}" WEB_URL="$DISPLAY_URL/$USER/$REPO" # 对凭据做 URL 百分号编码(口令可能含 # $ @ 等特殊字符) urlencode() { python3 -c 'import sys,urllib.parse;print(urllib.parse.quote(sys.argv[1],safe=""))' "$1"; } U_ENC="$(urlencode "$USER")" P_ENC="$(urlencode "$TOKEN")" AUTH_ENC="$U_ENC:$P_ENC" # 仅用于嵌入 git remote URL AUTH_RAW="$USER:$TOKEN" # 仅用于 curl -u(走 Basic Auth 头,base64 安全) # 推断默认分支 if [[ -z "$BRANCH" ]]; then if [[ -d "$PATH_/.git" ]]; then BRANCH="$(git -C "$PATH_" symbolic-ref --short HEAD 2>/dev/null || true)" fi fi [[ -z "$BRANCH" ]] && BRANCH="main" API="$URL/api/v1" PROTO="${URL%%://*}" # 从 URL 提取协议 (http 或 https) HOST="${URL#*//}" # 提取主机+端口 REMOTE_CLEAN="$PROTO://$HOST/$USER/$REPO.git" REMOTE_AUTH="$PROTO://$AUTH_ENC@$HOST/$USER/$REPO.git" echo "==> 检查仓库 $USER/$REPO 是否存在 ..." HTTP="$(curl -sS -o /dev/null -w '%{http_code}' -u "$AUTH_RAW" "$API/repos/$USER/$REPO")" if [[ "$HTTP" == "200" ]]; then echo " 仓库已存在,跳过创建。" elif [[ "$HTTP" == "404" ]]; then echo " 仓库不存在,创建中 ..." CREATE_JSON="$(printf '{"name":"%s","private":%s,"auto_init":false,"default_branch":"%s"}' "$REPO" "$PRIVATE" "$BRANCH")" CREATE_HTTP="$(curl -sS -o /tmp/gitea_create.json -w '%{http_code}' -u "$AUTH_RAW" \ -H 'Content-Type: application/json' -X POST "$API/user/repos" -d "$CREATE_JSON")" if [[ "$CREATE_HTTP" != "201" && "$CREATE_HTTP" != "200" ]]; then echo "创建仓库失败 (HTTP $CREATE_HTTP):" >&2 cat /tmp/gitea_create.json >&2 exit 1 fi echo " 仓库已创建。" else echo "检查仓库时返回意外状态: $HTTP" >&2 exit 1 fi # ---- git 操作 ---- cd "$PATH_" [[ ! -d .git ]] && git init -q -b "$BRANCH" git config user.email "${GITEA_USER:-gitea-publish}@local" 2>/dev/null || true git config user.name "${GITEA_USER:-gitea-publish}" 2>/dev/null || true # remote 只保存不含口令的干净地址,方便后续查看/手动更新 if git remote get-url origin >/dev/null 2>&1; then git remote set-url origin "$REMOTE_CLEAN" else git remote add origin "$REMOTE_CLEAN" fi git add -A if git diff --cached --quiet; then echo " 没有需要提交的变更。" else git commit -q -m "Publish $REPO via gitea-publish skill" echo " 已提交。" fi # 若远程已有该分支,先用带口令的 URL rebase,避免冲突(不落盘口令) if git ls-remote --exit-code "$REMOTE_AUTH" "$BRANCH" >/dev/null 2>&1; then git pull -q --rebase "$REMOTE_AUTH" "$BRANCH" || true fi # 用一次性带口令 URL 推送,不设置含口令的 upstream git push "$REMOTE_AUTH" "$BRANCH" git branch --set-upstream-to="origin/$BRANCH" 2>/dev/null || true echo "==> 完成。仓库地址: $WEB_URL"