Merge pull request '釋出:監控頁三塊的接法收進腳本,組頁那一步不再由呼叫端臨場挑工具' (#35) from develop into master
Reviewed-on: #35 Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
This commit was merged in pull request #35.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-assist",
|
"name": "jsc-assist",
|
||||||
"version": "0.2.6",
|
"version": "0.2.7",
|
||||||
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
||||||
"skills": "./skills",
|
"skills": "./skills",
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-assist",
|
"name": "jsc-assist",
|
||||||
"version": "0.2.6",
|
"version": "0.2.7",
|
||||||
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
||||||
"skills": "./skills",
|
"skills": "./skills",
|
||||||
"jsc": {
|
"jsc": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-assist",
|
"name": "jsc-assist",
|
||||||
"version": "0.2.6",
|
"version": "0.2.7",
|
||||||
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
||||||
"skills": "./skills/",
|
"skills": "./skills/",
|
||||||
"jsc": {
|
"jsc": {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -71,7 +71,7 @@ Every tool below is addressed through `{CURRENT}/{plugin}`, with `{CURRENT}` sta
|
|||||||
|
|
||||||
| What it does | Path to run |
|
| What it does | Path to run |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| one patrol round | `{CURRENT}/jsc-assist/tools/patrol.sh` |
|
| one patrol round, and joining the monitor page's three blocks | `{CURRENT}/jsc-assist/tools/patrol.sh` |
|
||||||
| the system scheduler | `{CURRENT}/jsc-assist/tools/schedule.sh` |
|
| the system scheduler | `{CURRENT}/jsc-assist/tools/schedule.sh` |
|
||||||
| the task book, the only writer there is | `{CURRENT}/jsc-assist/tools/tasks.sh` |
|
| the task book, the only writer there is | `{CURRENT}/jsc-assist/tools/tasks.sh` |
|
||||||
| the built-in check items, reconciled against the delegation list | `{CURRENT}/jsc-assist/tools/seed-tasks.sh` |
|
| the built-in check items, reconciled against the delegation list | `{CURRENT}/jsc-assist/tools/seed-tasks.sh` |
|
||||||
@@ -356,15 +356,21 @@ One round: read five sources, record the result, then beat. Everything before th
|
|||||||
|
|
||||||
3. **Rebuild `MONITOR_{HASH}` from its three blocks.** Hand it to `jsc-gitea:wiki` with page type `MONITOR`, so the repo is the one `gitea.sh wiki-repo MONITOR` resolves — `JSC_WIKI_REPO_MONITOR`, then `JSC_WIKI_REPO`, then exit 3. This is the content page and it stays in that repo; only the directory page of step 4 moved to the `CONTENTS` repo, and neither chain ever falls back to the other. Read the page back first, then build the new body out of what came back plus this round's files, in this order and with nothing else on the page:
|
3. **Rebuild `MONITOR_{HASH}` from its three blocks.** Hand it to `jsc-gitea:wiki` with page type `MONITOR`, so the repo is the one `gitea.sh wiki-repo MONITOR` resolves — `JSC_WIKI_REPO_MONITOR`, then `JSC_WIKI_REPO`, then exit 3. This is the content page and it stays in that repo; only the directory page of step 4 moved to the `CONTENTS` repo, and neither chain ever falls back to the other. Read the page back first, then build the new body out of what came back plus this round's files, in this order and with nothing else on the page:
|
||||||
|
|
||||||
|
**The three blocks are joined by `{CURRENT}/jsc-assist/tools/patrol.sh rebuild --old {the file the old page was read into}`, never by hand.** It prints `rebuilt_file=` — put that file. It also prints `old_format=`, `blocks=`, `summary_rows=` and `old_rows_kept=`; carry those into the report. Exit 6 means an argument was rejected or the old-page file is not there, exit 5 a scratch file could not be written; both take step 3's abort row.
|
||||||
|
|
||||||
|
Joining them by hand is what this replaces, and the reason is worth keeping. The join is a fixed shape, so leaving it to the caller meant a fresh agent picked its own tools for it every round: one round reached for `awk`, was refused by the permission gate twice, worked around it because somebody was there to watch, and said in its own report that the same refusal would hang an unattended round — with the round lock already taken. The scratch directory had accumulated 124 files by then, named `newpage.md`, `new-page.md`, `new_page.md`, `page-new.md` and so on: the same join, redone every round, under a different set of names. **A step with one correct shape does not belong in a prompt.** What the script joins:
|
||||||
|
|
||||||
| Block | Where it comes from |
|
| Block | Where it comes from |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 本頁基本資料 | the old page, byte for byte from its heading to the line before 最新一輪. Never rewritten, never re-derived |
|
| 本頁基本資料 | the old page, byte for byte from its heading to the line before 最新一輪. Never rewritten, never re-derived — the script does not even normalise its trailing blank lines |
|
||||||
| 最新一輪 | the whole content of `latest_file`, replacing the old block entirely |
|
| 最新一輪 | the whole content of `latest_file`, replacing the old block entirely |
|
||||||
| 近 24 輪摘要 | `summary_file`, which already holds the heading, the five-column table header (`巡檢時間`、`本輪判定`、`各項成敗`、`待人處理`、`警示來源`) and this round's row; then the old table's data rows in their old order underneath, cut so the table holds at most 24 rows |
|
| 近 24 輪摘要 | `summary_file`, which already holds the heading, the five-column table header (`巡檢時間`、`本輪判定`、`各項成敗`、`待人處理`、`警示來源`) and this round's row; then the old table's data rows in their old order underneath, cut so the table holds at most 24 rows |
|
||||||
|
|
||||||
|
An old-format page — per-round sections stacked up, no 最新一輪 heading — is detected by the script itself, which keeps the basic-data block, drops the stacked sections, starts the table with this round's row, and prints `old_format=yes converted=1`. Report that the page was converted.
|
||||||
|
|
||||||
**Verify the page's links before the write.** List every link the rebuilt body carries — the ones the latest-round block brought in, and any that survived in the block carried over from the old page — and run `{CURRENT}/jsc-gitea/tools/link-check.sh` over the whole list. Exit 0 is the only result that permits the write. On exit 1 report the `DEAD` lines verbatim, then run `{CURRENT}/jsc-assist/tools/patrol.sh abort --round {round}` and stop: a round that writes a dead link records a false trail nobody can follow back. Exits 2, 3 and 7 take the same abort, each reported by the rule B table above. A body carrying no link at all needs no call — say so in the report rather than claiming a check that never ran.
|
**Verify the page's links before the write.** List every link the rebuilt body carries — the ones the latest-round block brought in, and any that survived in the block carried over from the old page — and run `{CURRENT}/jsc-gitea/tools/link-check.sh` over the whole list. Exit 0 is the only result that permits the write. On exit 1 report the `DEAD` lines verbatim, then run `{CURRENT}/jsc-assist/tools/patrol.sh abort --round {round}` and stop: a round that writes a dead link records a false trail nobody can follow back. Exits 2, 3 and 7 take the same abort, each reported by the rule B table above. A body carrying no link at all needs no call — say so in the report rather than claiming a check that never ran.
|
||||||
|
|
||||||
Put the whole page. An old-format page — per-round sections stacked up, no summary table — has no rows to carry over: keep its `本頁基本資料` block, drop the stacked sections, let the table start with this round's row, and say in the report that the page was converted. Only exit 4 from the read permits creating the page instead, and then the body is the whole content of `newpage_file`, which already carries all three blocks. Exit 7 and exit 8 mean the old content is unknown: create nothing, write nothing — rebuilding a page from an unknown original throws the summary table away. On any write failure — including exit 3 with no wiki repo configured for `MONITOR`, which the patrol cannot ask about — run `{CURRENT}/jsc-assist/tools/patrol.sh abort --round {round}`, report the code, and stop. **No record, no heartbeat**, and that verdict belongs to this step alone: the round's result lives on this page, so a repo this step cannot resolve leaves the round with nowhere to be recorded. Step 4 is judged on its own terms. Completion condition: `link-check.sh` exited 0 over the body's links or the body carried none, the put or the create returned success, and the page holds exactly three blocks with the summary table at 24 rows or fewer and this round's row on top, or the abort ran and the round was reported as unrecorded with its exit code.
|
Put the whole page. Only exit 4 from the read permits creating the page instead, and then the body is the whole content of `newpage_file`, which already carries all three blocks. Exit 7 and exit 8 mean the old content is unknown: create nothing, write nothing — rebuilding a page from an unknown original throws the summary table away. On any write failure — including exit 3 with no wiki repo configured for `MONITOR`, which the patrol cannot ask about — run `{CURRENT}/jsc-assist/tools/patrol.sh abort --round {round}`, report the code, and stop. **No record, no heartbeat**, and that verdict belongs to this step alone: the round's result lives on this page, so a repo this step cannot resolve leaves the round with nowhere to be recorded. Step 4 is judged on its own terms. Completion condition: `link-check.sh` exited 0 over the body's links or the body carried none, the put or the create returned success, and the page holds exactly three blocks with the summary table at 24 rows or fewer and this round's row on top, or the abort ran and the round was reported as unrecorded with its exit code.
|
||||||
|
|
||||||
4. **Update this machine's block in `MONITOR_CONTENTS`, through `jsc-gitea/tools/wiki-contents.sh`.** That page is a directory every machine writes to, and it lives in the repo `gitea.sh wiki-repo CONTENTS` resolves — `JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`, then exit 3, and never a fallback to `JSC_WIKI_REPO_MONITOR`. The page carries no table: it is an H1, a `>` preamble, and then one H2 block per machine — the heading is that machine's monitor page name, and the fields are one `- {name}:{value}` bullet each underneath. The script owns the read-match-write of one block, so never read this page and rebuild it by hand, never write it through `jsc-gitea:wiki`, and never rebuild it the way step 3 rebuilds the content page — every other block here belongs to a machine that is not this one, and one careless whole-page write deletes their records.
|
4. **Update this machine's block in `MONITOR_CONTENTS`, through `jsc-gitea/tools/wiki-contents.sh`.** That page is a directory every machine writes to, and it lives in the repo `gitea.sh wiki-repo CONTENTS` resolves — `JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`, then exit 3, and never a fallback to `JSC_WIKI_REPO_MONITOR`. The page carries no table: it is an H1, a `>` preamble, and then one H2 block per machine — the heading is that machine's monitor page name, and the fields are one `- {name}:{value}` bullet each underneath. The script owns the read-match-write of one block, so never read this page and rebuild it by hand, never write it through `jsc-gitea:wiki`, and never rebuild it the way step 3 rebuilds the content page — every other block here belongs to a machine that is not this one, and one careless whole-page write deletes their records.
|
||||||
|
|
||||||
|
|||||||
+54
-1
@@ -237,6 +237,7 @@ usage() {
|
|||||||
usage: patrol.sh collect [--out 目錄] [--trigger 排程|事件|手動]
|
usage: patrol.sh collect [--out 目錄] [--trigger 排程|事件|手動]
|
||||||
patrol.sh finish --round 輪次代號 [--out 目錄] [--dry-run]
|
patrol.sh finish --round 輪次代號 [--out 目錄] [--dry-run]
|
||||||
patrol.sh abort --round 輪次代號 [--out 目錄]
|
patrol.sh abort --round 輪次代號 [--out 目錄]
|
||||||
|
patrol.sh rebuild --old 舊頁檔 [--out 目錄]
|
||||||
EOF
|
EOF
|
||||||
exit 6
|
exit 6
|
||||||
}
|
}
|
||||||
@@ -1078,7 +1079,7 @@ compose() {
|
|||||||
CMD="${1:-}"
|
CMD="${1:-}"
|
||||||
[ -n "$CMD" ] || usage
|
[ -n "$CMD" ] || usage
|
||||||
shift
|
shift
|
||||||
case "$CMD" in collect|finish|abort) ;; *) usage ;; esac
|
case "$CMD" in collect|finish|abort|rebuild) ;; *) usage ;; esac
|
||||||
|
|
||||||
OUT=''
|
OUT=''
|
||||||
while [ "$#" -gt 0 ]; do
|
while [ "$#" -gt 0 ]; do
|
||||||
@@ -1087,9 +1088,11 @@ while [ "$#" -gt 0 ]; do
|
|||||||
--trigger) [ "$#" -ge 2 ] || usage; TRIGGER="$2"; shift 2 ;;
|
--trigger) [ "$#" -ge 2 ] || usage; TRIGGER="$2"; shift 2 ;;
|
||||||
--round) [ "$#" -ge 2 ] || usage; ROUND="$2"; shift 2 ;;
|
--round) [ "$#" -ge 2 ] || usage; ROUND="$2"; shift 2 ;;
|
||||||
--dry-run) DRYRUN=1; shift ;;
|
--dry-run) DRYRUN=1; shift ;;
|
||||||
|
--old) [ "$#" -ge 2 ] || usage; OLD_PAGE="$2"; shift 2 ;;
|
||||||
*) usage ;;
|
*) usage ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
OLD_PAGE="${OLD_PAGE:-}"
|
||||||
[ -n "$OUT" ] && RD="$OUT"
|
[ -n "$OUT" ] && RD="$OUT"
|
||||||
|
|
||||||
case "$CMD" in
|
case "$CMD" in
|
||||||
@@ -1204,4 +1207,54 @@ case "$CMD" in
|
|||||||
printf 'heartbeat=not-written\n'
|
printf 'heartbeat=not-written\n'
|
||||||
lock_release
|
lock_release
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
|
|
||||||
|
# 拿舊頁重組出要寫回去的整頁。三塊的接法固定,所以由這一支做完,呼叫端只要「讀回舊頁、
|
||||||
|
# 叫這一支、把結果寫回去」三步。
|
||||||
|
#
|
||||||
|
# 為什麼要有這個子命令:接三塊這件事本來留給呼叫端,而呼叫端是一個代理人,於是它每一輪
|
||||||
|
# 臨場挑工具——實測某一輪挑了 awk,兩次都被權限擋掉;那一輪有人在場所以它改用別的方式繞
|
||||||
|
# 過去了,並且自己指出無人值守那一輪遇到同一個擋法會直接卡住,而鎖在第一步就取走了。
|
||||||
|
# 更根本的是暫存目錄實測累積了 124 個檔案,名字是 newpage.md、new-page.md、new_page.md、
|
||||||
|
# page-new.md 這種——同一件事每輪重做一次,每輪換一組檔名。規格說那裡只該有五個檔案。
|
||||||
|
# 接法寫死在這裡之後,那一步不再需要任何通用文字工具,也就沒有東西可以被擋。
|
||||||
|
rebuild)
|
||||||
|
[ -n "$OLD_PAGE" ] || usage
|
||||||
|
[ -f "$OLD_PAGE" ] || die 6 "舊頁檔讀不到:$OLD_PAGE。這一支不自己讀 wiki,舊頁要由呼叫端先取下來。"
|
||||||
|
for _f in "$RD/latest.md" "$RD/summary.md"; do
|
||||||
|
[ -f "$_f" ] || die 6 "$_f 不在。rebuild 要接的是同一輪 collect 產出的那幾塊,先跑 collect。"
|
||||||
|
done
|
||||||
|
_out="$RD/rebuilt.md"
|
||||||
|
# 舊頁那一塊「本頁基本資料」原樣留著,從它的標題到「最新一輪」前一行為止。整頁沒有
|
||||||
|
# 「最新一輪」那個標題時視為舊格式:基本資料留著、堆疊的各輪小節丟掉,摘要表從本輪開始。
|
||||||
|
if grep -q '^## 最新一輪' "$OLD_PAGE" 2>/dev/null; then
|
||||||
|
awk '/^## 最新一輪/{exit} {print}' "$OLD_PAGE" >"$RD/rb-head.md" 2>/dev/null || die 5 "暫存檔寫不進去:$RD/rb-head.md。"
|
||||||
|
printf 'old_format=no\n'
|
||||||
|
else
|
||||||
|
awk '/^## /{n++} n<=1{print}' "$OLD_PAGE" >"$RD/rb-head.md" 2>/dev/null || die 5 "暫存檔寫不進去:$RD/rb-head.md。"
|
||||||
|
printf 'old_format=yes converted=1\n'
|
||||||
|
fi
|
||||||
|
# 舊表的資料列照原順序留在本輪那一列底下,整張表截到 24 列。摘要那一塊已經含標題、
|
||||||
|
# 表頭與本輪那一列,所以這裡只補舊列。
|
||||||
|
awk '/^## 近 24 輪摘要/{f=1;next} f && /^\| 2/{print}' "$OLD_PAGE" >"$RD/rb-rows.md" 2>/dev/null || die 5 "暫存檔寫不進去:$RD/rb-rows.md。"
|
||||||
|
_new_rows=$(awk '/^\| 2/{n++} END{print n+0}' "$RD/summary.md")
|
||||||
|
_keep=$((24 - _new_rows))
|
||||||
|
[ "$_keep" -lt 0 ] && _keep=0
|
||||||
|
# 基本資料那一塊逐字照抄,連尾端空行都不動——技能本文寫的是「逐字保留」,剝掉再補一個
|
||||||
|
# 雖然內容沒少,那句話就變成假的了。只在它沒有以空行收尾時補一個分隔,免得下一塊的
|
||||||
|
# 標題黏在表格最後一列後面。
|
||||||
|
_tail=$(awk 'END{print (NF==0 ? "blank" : "text")}' "$RD/rb-head.md" 2>/dev/null)
|
||||||
|
{
|
||||||
|
cat "$RD/rb-head.md"
|
||||||
|
[ "$_tail" = "blank" ] || printf '\n'
|
||||||
|
cat "$RD/latest.md"
|
||||||
|
printf '\n'
|
||||||
|
cat "$RD/summary.md"
|
||||||
|
[ "$_keep" -gt 0 ] && head -n "$_keep" "$RD/rb-rows.md"
|
||||||
|
} >"$_out" 2>/dev/null || die 5 "暫存檔寫不進去:$_out。"
|
||||||
|
printf 'rebuilt_file=%s\n' "$_out"
|
||||||
|
printf 'blocks=%s summary_rows=%s old_rows_kept=%s\n' \
|
||||||
|
"$(grep -c '^## ' "$_out")" \
|
||||||
|
"$(awk '/^## 近 24 輪摘要/{f=1;next} f && /^\| 2/{n++} END{print n+0}' "$_out")" \
|
||||||
|
"$([ "$_keep" -gt 0 ] && awk 'END{print NR+0}' "$RD/rb-rows.md" | awk -v k="$_keep" '{print ($1<k?$1:k)}' || echo 0)"
|
||||||
|
exit 0 ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
Reference in New Issue
Block a user