feat(kebab-config + kebab-app): p9-fb-05 workspace.root path policy
도그푸딩 item 3 — `workspace.root` 의 허용 형식이 명문화 안 돼 사용자가
\"상대 경로면 어디 기준?\" 가 불명확. 이제 절대/tilde/env/상대 모두
지원하되, 상대 경로의 base 는 **config.toml 자체가 위치한 디렉토리**
(사용자의 cwd 와 무관) 로 일관 정책.
## 핵심 변경
- **`kebab_config::expand_path_with_base(raw, data_dir, base_dir)`**
신규. 기존 `expand_path` (tilde + env 만) 위에 relative-path
resolution 추가:
- tilde / 절대 / `${VAR}` 입력은 base_dir 무시 (이미 absolute)
- relative 입력만 `base_dir.join(...)` 로 절대화
- **`Config.source_dir: Option<PathBuf>`** 신규 (`#[serde(skip)]`).
`Config::from_file` / `load` 가 `path.parent()` 로 stamp. defaults
는 None (cwd fallback).
- **`Config::resolve_workspace_root()`** helper: source_dir 있으면
그것 기준, 없으면 cwd 기준.
- **callsite 정리**:
- `kebab-app::lib.rs` 의 3 군데 `expand_tilde(&app.config.workspace
.root)` → `app.config.resolve_workspace_root()`
- `kebab-app::init_workspace` 도 동일
- `kebab-source-fs::FsSourceConnector::new` → 동일
- kebab-source-fs 의 fork 된 local `expand_tilde` + `dirs_home`
헬퍼 제거 (kebab-config 가 canonical)
- **`kebab init`** 가 생성하는 `config.toml` 위에 path policy 안내
헤더 코멘트 prepend (절대/tilde/env/상대 + 상대 base = config dir).
기존 `expand_tilde` 가 kebab-app/lib.rs 에 한 군데 (storage.data_dir)
남음 — spec out-of-scope (\"expand_tilde 통일 P+\") 라 보류.
## 테스트
- `expand_path_with_base` 에 신규 4 unit (relative→base, absolute
ignores base, tilde ignores base, ${XDG} ignores base)
- 기존 27 kebab-config tests + workspace 전체 (`cargo test --workspace
--no-fail-fast -j 1` exit 0) 모두 통과
- `cargo clippy --workspace --all-targets -- -D warnings` clean
## 문서
- README Configuration 절: workspace.root 형식 + relative base 규칙
한 줄 추가
- HANDOFF: 2026-05-03 entry
- spec status planned → in_progress
## 영향
기존 사용자: 영향 없음 (defaults 의 `~/KnowledgeBase` 는 tilde-rooted,
relative path 분기 안 탐). 새 사용자가 `--config /tmp/cfg.toml` +
`root = "kb"` 같이 쓰면 cwd 무관하게 `/tmp/kb` 가 워크스페이스가 됨 —
이전엔 이 케이스가 cwd 기준이라 invisible foot-gun.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,11 +44,11 @@ pub struct FsSourceConnector {
|
||||
|
||||
impl FsSourceConnector {
|
||||
pub fn new(config: &Config) -> Result<Self> {
|
||||
// `config.workspace.root` is a String that may contain `~` or env
|
||||
// expansions. P0-* did not yet provide a path-expansion helper in
|
||||
// kb-config; for P1-1 we expand `~` ourselves and leave `${VAR}`
|
||||
// for a follow-up. The vast majority of users hit the `~` case.
|
||||
let root = expand_tilde(&config.workspace.root);
|
||||
// p9-fb-05: tilde / env / `${VAR}` substitutions plus
|
||||
// relative-path resolution against the config file's
|
||||
// directory (Config.source_dir) — so `--config /tmp/cfg.toml`
|
||||
// + `root = "kb"` reads `/tmp/kb`, not the user's cwd.
|
||||
let root = config.resolve_workspace_root();
|
||||
|
||||
let copy_threshold_bytes = config
|
||||
.storage
|
||||
@@ -166,30 +166,11 @@ impl SourceConnector for FsSourceConnector {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kb-config): hoist tilde + ${VAR} expansion into a kb-config helper
|
||||
// once that crate gains a path-expansion API. Today this duplicates logic
|
||||
// that P1-6 (store-sqlite) and future crates will also need.
|
||||
/// Expand a leading `~` to the current user's home directory. No-op for
|
||||
/// any other shape (absolute, relative, `${VAR}`-style).
|
||||
fn expand_tilde(s: &str) -> PathBuf {
|
||||
if let Some(rest) = s.strip_prefix("~/") {
|
||||
if let Some(home) = dirs_home() {
|
||||
return home.join(rest);
|
||||
}
|
||||
} else if s == "~" {
|
||||
if let Some(home) = dirs_home() {
|
||||
return home;
|
||||
}
|
||||
}
|
||||
PathBuf::from(s)
|
||||
}
|
||||
|
||||
/// Tiny `dirs::home_dir`-compat shim that does NOT add the `dirs` crate to
|
||||
/// our dep set (we explicitly enumerate allowed deps in the task spec).
|
||||
/// Reads `$HOME` directly.
|
||||
fn dirs_home() -> Option<PathBuf> {
|
||||
std::env::var_os("HOME").map(PathBuf::from)
|
||||
}
|
||||
// p9-fb-05: removed local `expand_tilde` + `dirs_home` shim. The
|
||||
// canonical helper now lives in `kebab-config::resolve_workspace_root`
|
||||
// (calling `expand_path_with_base`), so this crate just delegates via
|
||||
// `Config::resolve_workspace_root` above. Keeps tilde / `${VAR}` /
|
||||
// relative path semantics consistent with kebab-app and kebab-cli.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
Reference in New Issue
Block a user