fix(t39): daemon 端展開 config 的 ~/ 路徑
安裝器成功頁下載的 config.json 把 manifest 寫成 ~/.arcrun-rag/manifest.json——那是給人 看的寫法,Go 不展開波浪號,os.ReadFile 會去找一個字面上叫 "~" 的資料夾。 修在 daemon 端而非前端:用戶自己手打 config、或把設定搬到另一台機器時 ~/ 都該會動, 而前端也拿不到用戶家目錄。manifest 與 watch_folder(s) 全展開;只認 ~/ 與單獨的 ~, ~user 形式不猜(Go 無法解析他人家目錄);拿不到家目錄就原樣返回讓錯誤誠實浮現。 測試 +2(config 展開含 CJK 路徑與絕對路徑不動、expandHome 六種邊界),collector 全套綠。
This commit is contained in:
@@ -56,6 +56,26 @@ type DirectConfig struct {
|
||||
MaxRemoved float64 `json:"max_removed_ratio"` // 大量刪除防呆門檻(空/0=0.4)
|
||||
}
|
||||
|
||||
// expandHome 把開頭的 `~/`(或單獨的 `~`)展開成使用者家目錄的絕對路徑。
|
||||
//
|
||||
// t39(07-24 安裝器實案):成功頁下載的 config.json 把 manifest 寫成 `~/.arcrun-rag/manifest.json`——
|
||||
// 那是給人看的寫法,**Go 不會展開波浪號**,os.ReadFile 會去找一個字面上叫 "~" 的資料夾。
|
||||
// 修在 daemon 端而不是前端:用戶自己手打 config、或把設定搬到別台機器時,`~/` 都該會動;
|
||||
// 前端也拿不到用戶的家目錄。展開失敗(拿不到家目錄)就原樣返回,讓後續錯誤照常誠實浮現。
|
||||
func expandHome(p string) string {
|
||||
if p != "~" && !strings.HasPrefix(p, "~/") {
|
||||
return p
|
||||
}
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil || home == "" {
|
||||
return p
|
||||
}
|
||||
if p == "~" {
|
||||
return home
|
||||
}
|
||||
return filepath.Join(home, strings.TrimPrefix(p, "~/"))
|
||||
}
|
||||
|
||||
// LoadDirectConfig 讀設定檔並補預設值 + 基本驗證。
|
||||
func LoadDirectConfig(path string) (*DirectConfig, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
@@ -110,6 +130,12 @@ func LoadDirectConfig(path string) (*DirectConfig, error) {
|
||||
c.MaxRemoved = DefaultMaxRemovedRatio
|
||||
}
|
||||
c.CypherURL = strings.TrimSuffix(c.CypherURL, "/")
|
||||
// t39:路徑欄位一律展開 `~/`(config 是給人填/人讀的,波浪號是人的寫法)
|
||||
c.Manifest = expandHome(c.Manifest)
|
||||
c.WatchFolder = expandHome(c.WatchFolder)
|
||||
for i, p := range c.WatchFolders {
|
||||
c.WatchFolders[i] = expandHome(p)
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -122,3 +122,59 @@ func TestRunDirectOnceMultiRootDryRun(t *testing.T) {
|
||||
t.Fatalf("rootB 事件錯:%v", byRoot[rootB])
|
||||
}
|
||||
}
|
||||
|
||||
// t39:config 的 `~/` 由 daemon 端展開——安裝器成功頁下載的 config.json 用
|
||||
// `~/.arcrun-rag/manifest.json` 這種人看的寫法,Go 不展開波浪號=會去找字面上叫 "~" 的資料夾。
|
||||
func TestLoadDirectConfigExpandsHome(t *testing.T) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil || home == "" {
|
||||
t.Skip("拿不到家目錄,跳過")
|
||||
}
|
||||
dir := t.TempDir()
|
||||
cfgPath := filepath.Join(dir, "config.json")
|
||||
body := `{
|
||||
"watch_folders": ["~/Documents/知識庫", "/tmp/絕對路徑不動"],
|
||||
"manifest": "~/.arcrun-rag/manifest.json",
|
||||
"cypher_url": "https://cypher.example.workers.dev",
|
||||
"namespace": "ns1"
|
||||
}`
|
||||
if err := os.WriteFile(cfgPath, []byte(body), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg, err := LoadDirectConfig(cfgPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadDirectConfig: %v", err)
|
||||
}
|
||||
wantManifest := filepath.Join(home, ".arcrun-rag/manifest.json")
|
||||
if cfg.Manifest != wantManifest {
|
||||
t.Errorf("manifest 未展開:got %q want %q", cfg.Manifest, wantManifest)
|
||||
}
|
||||
folders := cfg.Folders()
|
||||
wantFolder := filepath.Join(home, "Documents/知識庫")
|
||||
if folders[0] != wantFolder {
|
||||
t.Errorf("watch_folders[0] 未展開:got %q want %q", folders[0], wantFolder)
|
||||
}
|
||||
if folders[1] != "/tmp/絕對路徑不動" {
|
||||
t.Errorf("絕對路徑不該被動:got %q", folders[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandHomeEdgeCases(t *testing.T) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil || home == "" {
|
||||
t.Skip("拿不到家目錄,跳過")
|
||||
}
|
||||
cases := map[string]string{
|
||||
"~": home,
|
||||
"~/a/b": filepath.Join(home, "a/b"),
|
||||
"/abs/path": "/abs/path",
|
||||
"relative/dir": "relative/dir",
|
||||
"": "",
|
||||
"~notme/x": "~notme/x", // 只認 `~/`;`~user` 形式不猜(Go 無法解析他人家目錄)
|
||||
}
|
||||
for in, want := range cases {
|
||||
if got := expandHome(in); got != want {
|
||||
t.Errorf("expandHome(%q)=%q,want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user