gitea 0.1.3 發佈:wiki 轉議題、HTML 匯出與範本風格設定 #19

Merged
admin merged 9 commits from develop into master 2026-08-26 01:34:01 +00:00
4 changed files with 142 additions and 64 deletions
Showing only changes of commit 36fc4de73c - Show all commits
+8 -9
View File
@@ -1,18 +1,17 @@
---
name: repo-sync
description: Batch-sync all readable repos of a chosen Gitea owner into the working directory. List owners, let the user pick, then clone or update each repo; local changes become a branch, commit, push, and PR to develop or master. Use for workspace bootstrap or bulk refresh; not for a single repo.
description: Batch-sync all readable repos of a chosen Gitea owner into the working directory. List owners, let the user pick, then clone or update each repo through tools/repo-sync.sh; a repo reported dirty goes to jsc-git:pr against the base branch that same script reports. Use for workspace bootstrap or bulk refresh; not for a single repo.
---
# repo-sync — batch-sync repositories
## Steps
1. Before calling `tools/gitea.sh`, resolve `GITEA_HOST` and `GITEA_TOKEN` from the current shell environment (also check `tea login list` for a usable login token when `GITEA_TOKEN` is unset). If `GITEA_HOST` is unresolvable, or `GITEA_TOKEN` is unset and no tea login token exists either, ask the user for the missing value per the `jsc-ask:ask` rules before proceeding to any `tools/gitea.sh` call.
2. Run `tools/gitea.sh owners` to list every `{owner}` the user can read.
3. Ask the user which `{owner}` to sync, per the `jsc-ask:ask` rules. Every option states the owner's repo count and impact scope.
4. Run `tools/gitea.sh repos {owner}` to list every readable `{repo}` under that owner.
1. Before calling `tools/gitea.sh`, resolve `GITEA_HOST` and `GITEA_TOKEN` from the current shell environment (also check `tea login list` for a usable login token when `GITEA_TOKEN` is unset). If `GITEA_HOST` is unresolvable, or `GITEA_TOKEN` is unset and no tea login token exists either, ask the user for the missing value per the `jsc-ask:ask` rules before proceeding to any `tools/gitea.sh` call. Done when `GITEA_HOST` holds a value and either `GITEA_TOKEN` or a tea login token is available.
2. Run `tools/gitea.sh owners` to list every `{owner}` the user can read. Done when the command has printed at least one `{owner}`.
3. Ask the user which `{owner}` to sync, per the `jsc-ask:ask` rules. Every option states the owner's repo count and impact scope. Done when the user has named exactly one `{owner}` from that list.
4. Run `tools/gitea.sh repos {owner}` to list every readable `{repo}` under that owner. Done when the command has printed the full `{owner}/{repo}` list for the chosen owner.
5. Sync each `{repo}` one by one. This step **MUST run as a sub agent** (one sub agent per repo):
1. Missing locally → `git clone` into the working directory (clone URL from `tools/gitea.sh clone-url`).
2. Present locally → switch to `develop`, else `master` (or the result of `tools/gitea.sh default-branch`), then `git pull`.
3. Local file changes → create a branch from develop or master, commit via `jsc-git:commit`, push, then open a PR back to develop or master via `jsc-git:pr`.
6. Report the sync result for every repo: cloned, updated, PR created, or the failure reason.
1. Run `tools/repo-sync.sh {owner}/{repo}`. The script owns the clone-versus-pull decision and the base-branch precedence, so run no `git clone`, `git checkout` or `git pull` by hand, and derive no branch name yourself. Route on its single line of output: `cloned` or `updated` → this repo is done; `dirty {branch}` → go to substep 2; `failed {reason}` → record that reason and stop this repo. Done when exactly one of those four outcomes is recorded for this repo.
2. `dirty {branch}` → call `jsc-git:pr` with `{branch}` from that same output line as the base, passed through verbatim. It commits, branches, pushes and opens the PR itself, so add none of those steps. Done when `jsc-git:pr` returns the PR URL.
6. Report the sync result for every repo: cloned, updated, PR URL, or the failure reason. Done when every `{repo}` from step 4 carries one of those four results.
+8 -21
View File
@@ -1,6 +1,6 @@
---
name: wiki
description: Read or write a Gitea wiki page through tools/gitea.sh and tools/hash-id. Resolve the wiki repo per page type with JSC_WIKI_REPO_{TYPE} first, then JSC_WIKI_REPO, and ask only when neither is set. Page content is chart-first - prefer mermaid diagrams and markdown tables over plain prose. Used by jsc-ask, jsc-sdlc, and jsc-log for wiki pages, including ERROR pages, not for repo code files.
description: Read or write a Gitea wiki page through tools/gitea.sh and tools/hash-id. Resolve the wiki repo per page type with JSC_WIKI_REPO_{TYPE} first, then JSC_WIKI_REPO, and ask only when neither is set. Page content is chart-first - prefer mermaid diagrams and markdown tables over plain prose. Callers are jsc-ask, jsc-sdlc, jsc-log, and jsc-hooks for its ERROR pages. Use for any wiki page in the skill set; not for repo code files.
---
# wiki — read and write Gitea wiki pages
@@ -9,11 +9,11 @@ Every wiki operation in the jsc skill set goes through this skill. One entry poi
## Resolve the wiki location
Different page types can live in different `{owner}/{repo}` repos, classified by page-name prefix: `QUESTION`, `PLAN`, `ANALYZE`, `DELIVER`, `MAINTAIN`, `REPO`, `LOG`, `LEARN`, `ERROR`.
Different page types can live in different `{owner}/{repo}` repos, classified by the page-name prefix.
1. Before asking the user, inspect the current shell environment for the needed repo variables and Gitea connection variables: `JSC_WIKI_REPO_{TYPE}`, `JSC_WIKI_REPO`, `GITEA_HOST`, and `GITEA_TOKEN` (also check `tea login list` for a usable login token when `GITEA_TOKEN` is unset). Use inherited shell values first; only ask when the needed repo or connection value cannot be resolved after that check. `GITEA_HOST` and `GITEA_TOKEN` are both covered by this ask-if-unresolvable rule, the same as the wiki-repo variables below.
2. Run `tools/gitea.sh wiki-repo {TYPE}` (TYPE = the page-name prefix). Allowed types are `QUESTION`, `PLAN`, `ANALYZE`, `DELIVER`, `MAINTAIN`, `REPO`, `LOG`, `LEARN`, and `ERROR`. Resolution order is `JSC_WIKI_REPO_{TYPE}` first, then `JSC_WIKI_REPO`. Never borrow another type's repo.
3. On exit 3 (neither is set after env inspection), ask the user for that page type's `{owner}/{repo}` per the `jsc-ask:ask` rules, and suggest setting `JSC_WIKI_REPO_{TYPE}` (can differ per type) or `JSC_WIKI_REPO` (shared default).
1. Before asking the user, inspect the current shell environment for the needed repo variables and Gitea connection variables: `JSC_WIKI_REPO_{TYPE}`, `JSC_WIKI_REPO`, `GITEA_HOST`, and `GITEA_TOKEN` (also check `tea login list` for a usable login token when `GITEA_TOKEN` is unset). Use inherited shell values first; only ask when the needed repo or connection value cannot be resolved after that check. Done when every one of those variables is either resolved from the environment or listed as missing.
2. Run `tools/gitea.sh wiki-repo {TYPE}` (TYPE = the page-name prefix). Allowed types are `QUESTION`, `PLAN`, `ANALYZE`, `DELIVER`, `MAINTAIN`, `REPO`, `LOG`, `LEARN`, and `ERROR`. Resolution order is `JSC_WIKI_REPO_{TYPE}` first, then `JSC_WIKI_REPO`. Never borrow another type's repo. Done when the command has printed exactly one `{owner}/{repo}`, or exited 3 and sent this page type to step 3.
3. On exit 3 (neither is set after env inspection), ask the user for that page type's `{owner}/{repo}` per the `jsc-ask:ask` rules, and suggest setting `JSC_WIKI_REPO_{TYPE}` (can differ per type) or `JSC_WIKI_REPO` (shared default). Done when the user has supplied one `{owner}/{repo}` for that page type.
## Operations
@@ -24,20 +24,7 @@ Different page types can live in different `{owner}/{repo}` repos, classified by
| write page | write the content to a temp file first, then `tools/gitea.sh wiki-put {owner}/{repo} {page} {file}` (creates or updates automatically) |
| page URL | `tools/gitea.sh wiki-url {owner}/{repo} {page}` — the page's absolute URL, taken from the API's `html_url` (exit 4 when the page is missing) |
## Linking between wiki pages
Two rules, and getting either wrong produces a link that silently points at a page that does not exist.
**Direction**: Gitea uses the GitHub/Gollum convention — `[[display text|page name]]`, **display text on the LEFT, page name on the RIGHT**. This is the opposite of MediaWiki. Gitea's own source says so (`modules/markup/html_link.go`): *"MediaWiki uses [[link|text]], while GitHub uses [[text|link]] … we prefer GitHub syntax"*. So `[[PLAN_H1234567|我的計畫]]` renders as the text `PLAN_H1234567` linking to a page named 我的計畫 — broken. Write `[[我的計畫|PLAN_H1234567]]`. When display text and page name are the same, use the no-pipe form `[[PLAN_H1234567]]`, which cannot be got wrong.
**Scope**: `[[...]]` and relative markdown links both resolve **only inside the current wiki**. There is no cross-repo wiki-link syntax.
| Link | Same wiki? | Use |
| --- | --- | --- |
| Same page type (e.g. `PLAN_CONTENTS` → `PLAN_{HASH}`) | Always — one type, one repo | `[[display\|page]]` or `[[page]]` |
| Different page type (e.g. `LOG_{HASH}` → `PLAN_{HASH}`) | **Only when both types resolve to the same repo** | Absolute URL from `wiki-url`: `[display](https://…/wiki/PLAN_…)` |
Because each type resolves its own `JSC_WIKI_REPO_{TYPE}`, a cross-type link **must** use the absolute URL — it stays correct whether or not the two types happen to share a repo, so never branch on that. Get the URL from `wiki-url`, never hand-assemble the path.
When writing a page, link same-type pages with the `[[display|page]]` form (display text on the LEFT) and cross-type pages with the absolute URL from `wiki-url`, because `[[...]]` resolves only inside one wiki. Full rules and the direction trap: `references/wiki-links.md`.
## Rules
@@ -45,5 +32,5 @@ Because each type resolves its own `JSC_WIKI_REPO_{TYPE}`, a cross-type link **m
2. Use `tools/hash-id` for `{HASH}` values. It returns the first 8 uppercase SHA-1 hex chars, or `H` plus the first 7 chars when the raw hash starts with `0-9`, `A`, `B`, or `C`.
3. To update a contents page (`*_CONTENTS`): `wiki-get` it first, apply the template to append or modify, then `wiki-put` the whole page back. Never overwrite entries owned by others.
4. Write all wiki content in UTF-8 Traditional Chinese, per the STE100 output rule.
5. Prefer visual forms for page content: use mermaid diagrams (flowchart, sequence, gantt, pie) and markdown tables wherever the information allows. Plain running text is the last resort, kept short.
6. Authentication fallback is built into `tools/gitea.sh`: on a missing GITEA_TOKEN or a 401/403 response it retries with the tea CLI login token automatically. But before calling it, resolve `GITEA_HOST` and `GITEA_TOKEN` per rule 1: if `GITEA_HOST` is unresolvable, or `GITEA_TOKEN` is unset and no tea login token exists either, ask the user for the missing value per the `jsc-ask:ask` rules — same decision-tree pattern as the missing-wiki-repo case above — before calling `tools/gitea.sh`. Only report a genuine failure when the user has no answer to give or Gitea itself rejects the request (e.g. a 401/403 even after the tea fallback).
5. Prefer visual forms for page content: use mermaid diagrams (flowchart, sequence, gantt, pie) and markdown tables wherever the information allows. Prose is capped at 3 sentences per section, and a sentence stays only when neither a mermaid diagram nor a markdown table can carry the same information.
6. `tools/gitea.sh` retries once with the tea CLI login token when `GITEA_TOKEN` is missing or the response is 401/403. Report a failure only after that retry also fails.
+46 -34
View File
@@ -1,11 +1,15 @@
#!/usr/bin/env sh
# check-wiki-rules — 驗證 wiki repo 解析與 hash-id 規則。
# 涵蓋全部九種頁面類型,每種三項:專用變數優先、退回共用變數、不得跨類型代用。
# 全部通過印 OK 並 exit 0;任一項不符印出差異並 exit 1。
set -eu
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
gitea="$dir/gitea.sh"
hash_id="$dir/hash-id"
TYPES='QUESTION PLAN ANALYZE DELIVER MAINTAIN REPO LOG LEARN ERROR'
fail() {
printf '%s\n' "$1" >&2
exit 1
@@ -18,7 +22,7 @@ expect_eq() {
[ "$got" = "$want" ] || fail "$label: want=$want got=$got"
}
check_repo() {
check_repo() { # type label want [VAR=值...]
type=$1
label=$2
want=$3
@@ -27,6 +31,19 @@ check_repo() {
expect_eq "$got" "$want" "$label"
}
check_no_repo() { # type label [VAR=值...]:期望 exit 3 且不印出任何 {owner}/{repo}
type=$1
label=$2
shift 2
if got=$(env -i PATH="${PATH:-/usr/bin:/bin}" "$@" "$gitea" wiki-repo "$type" 2>/dev/null); then
code=0
else
code=$?
fi
[ "$code" -eq 3 ] || fail "$label: want exit=3 got exit=$code output=$got"
[ -z "$got" ] || fail "$label: want no output got=$got"
}
check_hash() {
input=$1
want=$2
@@ -34,47 +51,42 @@ check_hash() {
expect_eq "$got" "$want" "hash-id $input"
}
check_repo REPO 'REPO specific wins' 'records/REPO' \
JSC_WIKI_REPO_REPO='records/REPO' \
JSC_WIKI_REPO_ANALYZE='knowledges/ANALYZE' \
JSC_WIKI_REPO='shared/wiki'
# 其他八個類型的誘餌值。跨類型代用一旦發生,回傳的就會是 decoy/{別的類型}。
decoys() { # $1=要排除的類型
for t in $TYPES; do
[ "$t" = "$1" ] || printf 'JSC_WIKI_REPO_%s=decoy/%s ' "$t" "$t"
done
}
check_repo REPO 'REPO falls back to shared only' 'shared/wiki' \
JSC_WIKI_REPO_ANALYZE='knowledges/ANALYZE' \
JSC_WIKI_REPO='shared/wiki'
for ty in $TYPES; do
# 1. 自己的變數贏過共用變數,也贏過其他類型的誘餌。
check_repo "$ty" "$ty specific wins" "own/$ty" \
$(decoys "$ty") "JSC_WIKI_REPO_$ty=own/$ty" JSC_WIKI_REPO='shared/wiki'
check_repo ANALYZE 'ANALYZE specific wins' 'knowledges/ANALYZE' \
JSC_WIKI_REPO_REPO='records/REPO' \
JSC_WIKI_REPO_ANALYZE='knowledges/ANALYZE' \
JSC_WIKI_REPO='shared/wiki'
# 2. 自己的變數未設定時,只退回共用變數。
check_repo "$ty" "$ty falls back to shared only" 'shared/wiki' \
$(decoys "$ty") JSC_WIKI_REPO='shared/wiki'
check_repo ANALYZE 'ANALYZE falls back to shared only' 'shared/wiki' \
JSC_WIKI_REPO_REPO='records/REPO' \
JSC_WIKI_REPO='shared/wiki'
# 3. 自己的變數與共用變數都沒有時,exit 3,不借用別的類型。
check_no_repo "$ty" "$ty never borrows another type" $(decoys "$ty")
done
check_repo LEARN 'LEARN specific wins' 'lessons/LEARN' \
JSC_WIKI_REPO_LEARN='lessons/LEARN' \
JSC_WIKI_REPO='shared/wiki'
check_repo LEARN 'LEARN falls back to shared only' 'shared/wiki' \
JSC_WIKI_REPO_LOG='records/LOG' \
JSC_WIKI_REPO='shared/wiki'
check_repo DELIVER 'DELIVER specific wins' 'handover/DELIVER' \
JSC_WIKI_REPO_DELIVER='handover/DELIVER' \
JSC_WIKI_REPO='shared/wiki'
check_repo DELIVER 'DELIVER falls back to shared only' 'shared/wiki' \
JSC_WIKI_REPO_ANALYZE='knowledges/ANALYZE' \
JSC_WIKI_REPO='shared/wiki'
check_repo ERROR 'ERROR uses its own repo' 'errors/wiki' \
JSC_WIKI_REPO_ERROR='errors/wiki' \
JSC_WIKI_REPO='shared/wiki'
# 未知類型 exit 2,與「設定不足」的 exit 3 分開。
if got=$(env -i PATH="${PATH:-/usr/bin:/bin}" JSC_WIKI_REPO='shared/wiki' \
"$gitea" wiki-repo NOSUCH 2>/dev/null); then
code=0
else
code=$?
fi
[ "$code" -eq 2 ] || fail "unknown type: want exit=2 got exit=$code"
[ -z "$got" ] || fail "unknown type: want no output got=$got"
# hash-id:首碼 0-9/A/B/C 改成 H 加前 7 碼;其餘首碼原樣輸出 8 碼大寫。
check_hash 'case-2' 'H5172CB7'
check_hash 'case-11' 'HA9A6662'
check_hash 'case-1' 'HB6EC7FD'
check_hash 'case-12' 'HCCA8D42'
check_hash 'case-3' 'D3E5AA27'
check_hash 'case-7' 'D794C002'
printf '%s\n' 'OK'
+80
View File
@@ -0,0 +1,80 @@
#!/usr/bin/env sh
# repo-sync.sh — 把單一存取庫同步到本機。
# 用法: repo-sync.sh <owner>/<repo> [target-dir] # target-dir 預設為 <repo>
# 目錄不存在 → git clone(clone URL 取自 gitea.sh clone-url)
# 目錄已存在 → git fetch、切到基準分支、git pull --ff-only
# 有未提交變更 → 只回報 dirty 與基準分支,不動分支也不 pull
# 基準分支的唯一優先序(呼叫端不必再判斷,也不要自己再推導一次):
# 1. gitea.sh default-branch <owner>/<repo>(該分支要在遠端存在)
# 2. 查不到時依序取遠端的 develop、main、master
# 3. 都沒有 → failed no default branch
# 輸出: 恰好一行,cloned、updated、dirty {分支} 或 failed {原因}。
# 前三種 exit 0;failed exit 1。
# dirty 會把解析好的基準分支一起帶出來,呼叫端直接拿去當 PR 的 base。
set -u
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
gitea="$script_dir/gitea.sh"
fail() { # 原因壓成一行,避免呼叫端解析多行輸出
printf 'failed %s\n' "$(printf '%s' "$1" | tr '\n\r\t' ' ')"
exit 1
}
full="${1:-}"
[ -n "$full" ] || fail "usage: repo-sync.sh <owner>/<repo> [target-dir]"
case "$full" in
*/*/*|/*|*/) fail "not owner/repo: $full" ;;
*/*) ;;
*) fail "not owner/repo: $full" ;;
esac
repo="${full#*/}"
target="${2:-$repo}"
remote_has() { # 遠端是否有這個分支
git -C "$target" rev-parse --verify --quiet "refs/remotes/origin/$1" >/dev/null 2>&1
}
if [ ! -e "$target" ]; then
# clone URL 只收 stdout:把 stderr 併進來會讓任何雜訊變成網址的一部分
err=$(mktemp)
url=$("$gitea" clone-url "$full" 2>"$err") || url=''
if [ -z "$url" ]; then
reason=$(cat "$err"); rm -f "$err"
fail "clone-url failed for $full: ${reason:-no clone URL}"
fi
rm -f "$err"
out=$(git clone "$url" "$target" 2>&1) || fail "clone: $out"
printf 'cloned\n'
exit 0
fi
git -C "$target" rev-parse --git-dir >/dev/null 2>&1 || fail "$target is not a git repo"
dirty=0
[ -z "$(git -C "$target" status --porcelain 2>/dev/null)" ] || dirty=1
# dirty 的存取庫也要解析基準分支:呼叫端拿它當 PR 的 base,自己再推導一次就會
# 出現第二套優先序。fetch 只更新 refs,不動工作目錄,dirty 時照樣安全;但 dirty
# 時網路失敗不算致命,還是要把 dirty 回報出去,改用本機既有的 remote refs 判斷。
if ! out=$(git -C "$target" fetch --prune origin 2>&1); then
[ "$dirty" -eq 1 ] || fail "fetch: $out"
fi
branch=$("$gitea" default-branch "$full" 2>/dev/null) || branch=''
if [ -z "$branch" ] || ! remote_has "$branch"; then
branch=''
for b in develop main master; do
if remote_has "$b"; then branch="$b"; break; fi
done
fi
[ -n "$branch" ] || fail "no default branch"
if [ "$dirty" -eq 1 ]; then
printf 'dirty %s\n' "$branch"
exit 0
fi
out=$(git -C "$target" checkout "$branch" 2>&1) || fail "checkout $branch: $out"
out=$(git -C "$target" pull --ff-only origin "$branch" 2>&1) || fail "pull $branch: $out"
printf 'updated\n'