Files
arcrun-collector/templateuntouched.go

88 lines
4.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// templateuntouched.go — 「範本空殼」與「真內容」怎麼分辨
// inkstone/Arcrun#180 驗收條件 2 與 3leo 2026-08-28)。
//
// ── 題目 ─────────────────────────────────────────────────────────────────────
//
// leo 原話:「**cards 在 system-dev 下,wiki 就在裡面,所以不允許跳過它**」
//
// 實據(`~/Desktop/youlinhsieh-test1`,同一棵 `system-dev/` 底下兩種東西並存):
//
// system-dev/wiki/status.md ← 安裝器鋪的空殼,內容是「(初始化後填入)」
// system-dev/wiki/cards/*.md ← 9 張真的卡(火星座標/Leo-Hsieh-填答/小果被AFTEE詐貸…)
//
// 舊判準是**路徑前綴**`TemplateOwns``system-dev/` 與 `scripts/` 整棵不是知識),
// 它分不出這兩層——把空殼和真內容一起丟掉。
//
// 🔴 而修法不准是「把 system-dev 放行」(票上紅線①):那會讓所有裝過 template 的 repo
// 開始收一堆一模一樣的空殼,變成另一個方向的錯。**要的是分辨。**
//
// ── 判準 ─────────────────────────────────────────────────────────────────────
//
// daemon **自己就是那個安裝器**template_install.go 的 `//go:embed all:templatefs`
// 把整份範本快照打進二進位)⇒ 原版就在手上,可以逐字比對,不必猜:
//
// 快照裡有同名檔,**且 sha256 逐字相同** ⇒ 空殼:安裝器鋪的,他一個字都沒動 ⇒ 不收
// 快照裡沒有,或內容不一樣 ⇒ 他寫的/他放的 ⇒ 照常判斷
//
// **為什麼它不會再誤殺一份現成的 wiki**:範本的 `status.md` 寫的是「(初始化後填入)」,
// **使用者只要寫過一個字,雜湊就對不上,判斷立刻翻面**。
// 判準不看名字、不看深度、不看模式 ⇒ 換個資料夾名、換個安裝位置,答案都一樣
// (這也是紅線:不准用白名單,「下一個叫別的名字的資料夾照樣中」)。
//
// ⚠️ 這支只回答「是不是原封不動的範本」。**其餘的收檔判斷一條都沒有動**
// (副檔名白名單、`.gitignore`、模式範圍、隱藏檔)——它只取代 `TemplateOwns`
// 在掃描時的那一格。tidy.go 仍然用 `TemplateOwns`:它問的是另一題
// (「哪些是範本殘留、該搬進收容處」),不是「這是不是知識」。
package collector
import (
"crypto/sha256"
"encoding/hex"
"io/fs"
"os"
"path/filepath"
"sync"
)
// templateSnapshotHashes:內嵌範本快照裡每個檔的 sha256(key=斜線相對路徑)。
// 只算一次——快照是編譯期就固定的東西,執行期不會變。
var templateSnapshotHashes = sync.OnceValue(func() map[string]string {
out := map[string]string{}
_ = fs.WalkDir(templateFS, templateFSRoot, func(p string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return nil
}
rel, rerr := filepath.Rel(templateFSRoot, p)
if rerr != nil {
return nil
}
data, rerr := templateFS.ReadFile(p)
if rerr != nil {
return nil
}
sum := sha256.Sum256(data)
out[filepath.ToSlash(rel)] = hex.EncodeToString(sum[:])
return nil
})
return out
})
// TemplateUntouched 回答「這個檔是不是安裝器鋪下去、而且使用者一個字都沒動過的空殼」。
//
// relSlash=相對監看根的路徑(斜線分隔);absPath=那個檔在磁碟上的位置。
//
// 🔴 讀不到檔一律回 false——**認不出來就不要排除**。誤留一份空殼只是雜訊,
// 誤丟一份使用者的知識是資料遺失,兩邊的代價不對等(同 cleanup.go 的規矩②)。
func TemplateUntouched(relSlash, absPath string) bool {
want, ok := templateSnapshotHashes()[filepath.ToSlash(relSlash)]
if !ok {
return false // 快照裡沒有這個路徑 ⇒ 不是安裝器鋪的
}
data, err := os.ReadFile(absPath)
if err != nil {
return false
}
sum := sha256.Sum256(data)
return hex.EncodeToString(sum[:]) == want
}