Gitea 的 wiki 此前是空的。本次把百科的**源文件版本化进仓库**(docs/wiki/), 再用脚本发布到 wiki——文档因此与代码同源、可 review、可回溯,而不是只存在于网页上。 八页(Home 作为导航与项目速览): Home 项目速览 / 两条版本线的宿主兼容性差异 / 文档地图 / 谱系说明 01-需求分析 五条核心需求(现象·根因·验收标准·状态)+ 非目标与约束 + 需求→实现→锚点 02-系统架构 host/client 分层与产物、三层接口面、面板宿主层叠契约与 isolate 陷阱、关键不变量 03-功能说明 面板/预览器总表、信息层三 tab 数据来源、HTML 预览交付方式、i18n 与令牌、配置面 04-部署指南 宿主版本矩阵与选线决策、profile 接线(file: + bundles)、构建、验收清单、卸载与切换 05-运维手册 分支/tag 策略与回滚剧本、发布纪律(版本三元组/非确定性产物)、巡检、备份铁律、诊断 06-故障排查 8 类症状 → 根因 → 处置(含本会话实机定位过的全部案例)+ 定位手法 07-开发与测试 工程约定、目录、门禁基线、测试分层与契约 spec、扩展点、测量式 re-base 方法 脚本 scripts/push-wiki.sh:幂等发布(POST 新建 / PATCH 覆盖),凭据走 git credential helper (临时 netrc,600,退出即删),不落盘、不进 argv。已实测:8 页全部发布成功,复推为更新。 内容纪律:只写**本次会话实证过**的事实(提交号、行数、层级取值、产物大小、宿主版本矩阵、 静默禁用机制、re-base 步骤),未验证的部分明确标注为待验证或已知限制;并如实说明本仓库 历代承载过 v1 / v2 / 当前 fork 三代实现,本百科描述的是 main 上的 fork 线。
114 lines
4.3 KiB
Bash
Executable File
114 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# =============================================================================
|
||
# push-wiki.sh — 把 docs/wiki/*.md 推送/更新到本仓库的 Gitea wiki
|
||
#
|
||
# 为什么需要它:本项目的百科源文件**版本化在仓库里**(docs/wiki/),
|
||
# Gitea wiki 只是它的发布面。改文档 = 改仓库文件 + 跑本脚本,日志与回溯都在 git 里。
|
||
#
|
||
# 行为:
|
||
# · 幂等:页面不存在则 POST 新建,已存在则 PATCH 覆盖(标题 = 文件名去掉 .md)
|
||
# · 凭据来自 `git credential fill`(与推代码同一套),脚本自身不存任何 token
|
||
# · 凭据经临时 netrc 文件传给 curl(600 权限,退出即删),不进入 argv
|
||
#
|
||
# 用法:
|
||
# bash scripts/push-wiki.sh # 推送全部页面
|
||
# bash scripts/push-wiki.sh 05-运维手册 # 只推指定页面(按文件名/片段匹配)
|
||
# WIKI_REMOTE=origin bash scripts/push-wiki.sh # 指定 remote(默认 gitea,缺失则 origin)
|
||
# =============================================================================
|
||
set -euo pipefail
|
||
|
||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
cd "$ROOT"
|
||
|
||
WIKI_DIR="$ROOT/docs/wiki"
|
||
FILTER="${1:-}"
|
||
|
||
# ---- 解析 remote / owner / repo -------------------------------------------
|
||
pick_remote() {
|
||
for r in "${WIKI_REMOTE:-}" gitea origin; do
|
||
[ -z "$r" ] && continue
|
||
if git remote get-url "$r" >/dev/null 2>&1; then echo "$r"; return 0; fi
|
||
done
|
||
echo "无法找到可用的 git remote(试过: ${WIKI_REMOTE:-<空>} gitea origin)" >&2
|
||
return 1
|
||
}
|
||
REMOTE="$(pick_remote)"
|
||
URL="$(git remote get-url "$REMOTE")"
|
||
case "$URL" in
|
||
https://*) ;;
|
||
*) echo "本脚本只支持 https remote(当前: $URL)" >&2; exit 1 ;;
|
||
esac
|
||
HOST="$(printf '%s' "$URL" | sed -E 's#^https://([^/]+)/.*#\1#')"
|
||
SLUG="$(printf '%s' "$URL" | sed -E 's#^https://[^/]+/##; s#\.git$##')"
|
||
OWNER="${SLUG%%/*}"
|
||
REPO="${SLUG#*/}"
|
||
API="https://$HOST/api/v1/repos/$OWNER/$REPO/wiki"
|
||
|
||
echo "wiki 目标 : $OWNER/$REPO @ $HOST (remote: $REMOTE)"
|
||
echo "源目录 : docs/wiki"
|
||
echo
|
||
|
||
# ---- 凭据 -----------------------------------------------------------------
|
||
CRED="$(printf 'protocol=https\nhost=%s\n\n' "$HOST" | git credential fill 2>/dev/null || true)"
|
||
USER="$(printf '%s\n' "$CRED" | sed -n 's/^username=//p')"
|
||
PASS="$(printf '%s\n' "$CRED" | sed -n 's/^password=//p')"
|
||
if [ -z "$PASS" ]; then
|
||
echo "未从 git credential helper 取到 $HOST 的凭据(先确保 git push 能成功)" >&2
|
||
exit 1
|
||
fi
|
||
NETRC="$(mktemp)"
|
||
chmod 600 "$NETRC"
|
||
trap 'rm -f "$NETRC"' EXIT
|
||
printf 'machine %s\nlogin %s\npassword %s\n' "$HOST" "$USER" "$PASS" > "$NETRC"
|
||
|
||
json_body() { # $1 = file
|
||
python3 - "$1" <<'PY'
|
||
import base64, json, sys, pathlib
|
||
p = pathlib.Path(sys.argv[1])
|
||
print(json.dumps({
|
||
"title": p.stem,
|
||
"content_base64": base64.b64encode(p.read_text(encoding="utf-8").encode("utf-8")).decode("ascii"),
|
||
}, ensure_ascii=False))
|
||
PY
|
||
}
|
||
|
||
api() { # $1 = method, $2 = path, $3 = body(optional) -> 输出 HTTP 码
|
||
local method="$1" path="$2" body="${3:-}"
|
||
if [ -n "$body" ]; then
|
||
curl -s --netrc-file "$NETRC" -o /tmp/push-wiki-resp.json -w '%{http_code}' \
|
||
-X "$method" -H 'Content-Type: application/json' --data-binary "$body" "$API$path"
|
||
else
|
||
curl -s --netrc-file "$NETRC" -o /tmp/push-wiki-resp.json -w '%{http_code}' -X "$method" "$API$path"
|
||
fi
|
||
}
|
||
|
||
# ---- 逐页推送 -------------------------------------------------------------
|
||
shopt -s nullglob
|
||
files=("$WIKI_DIR"/*.md)
|
||
[ ${#files[@]} -gt 0 ] || { echo "docs/wiki 下没有 .md 文件" >&2; exit 1; }
|
||
|
||
ok=0; fail=0
|
||
for f in "${files[@]}"; do
|
||
title="$(basename "$f" .md)"
|
||
if [ -n "$FILTER" ] && [[ "$title" != *"$FILTER"* ]]; then continue; fi
|
||
body="$(json_body "$f")"
|
||
|
||
code="$(api POST /new "$body")"
|
||
if [ "$code" = "201" ]; then
|
||
echo " + $title (新建)"; ok=$((ok+1)); continue
|
||
fi
|
||
enc="$(python3 -c 'import sys,urllib.parse;print(urllib.parse.quote(sys.argv[1], safe=""))' "$title")"
|
||
code="$(api PATCH "/page/$enc" "$body")"
|
||
if [ "$code" = "200" ]; then
|
||
echo " ~ $title (更新)"; ok=$((ok+1))
|
||
else
|
||
echo " ! $title 失败(HTTP $code): $(head -c 200 /tmp/push-wiki-resp.json 2>/dev/null)" >&2
|
||
fail=$((fail+1))
|
||
fi
|
||
done
|
||
|
||
echo
|
||
echo "完成:成功 $ok 页,失败 $fail 页"
|
||
[ "$fail" -eq 0 ] || exit 1
|
||
echo "查看: https://$HOST/$OWNER/$REPO/wiki"
|