From f2cc325cf38ae67a7e04478a60f1a2907ae2e353 Mon Sep 17 00:00:00 2001 From: altair823 Date: Sun, 31 May 2026 12:09:31 +0000 Subject: [PATCH] =?UTF-8?q?feat(cli):=20kebab=20config=20migrate=20?= =?UTF-8?q?=EC=84=9C=EB=B8=8C=EC=BB=A4=EB=A7=A8=EB=93=9C=20+=20wire=20conf?= =?UTF-8?q?ig=5Fmigration.v1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Cmd::Config { Migrate { --dry-run } }, --json 시 config_migration.v1. - wire_config_migration (ConfigMigrationReport 가 schema_version 자체 보유). - schema.rs WIRE_SCHEMAS 에 config_migration.v1 등록 + JSON schema 파일. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/kebab-app/src/schema.rs | 1 + crates/kebab-cli/src/main.rs | 52 +++++++++++++++++++ crates/kebab-cli/src/wire.rs | 6 +++ .../v1/config_migration.v1.schema.json | 38 ++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 docs/wire-schema/v1/config_migration.v1.schema.json diff --git a/crates/kebab-app/src/schema.rs b/crates/kebab-app/src/schema.rs index d2bc396..1712cb3 100644 --- a/crates/kebab-app/src/schema.rs +++ b/crates/kebab-app/src/schema.rs @@ -108,6 +108,7 @@ const WIRE_SCHEMAS: &[&str] = &[ "doc_summary.v1", "chunk_inspection.v1", "doctor.v1", + "config_migration.v1", "ingest_report.v1", "ingest_progress.v1", "reset_report.v1", diff --git a/crates/kebab-cli/src/main.rs b/crates/kebab-cli/src/main.rs index 3568c32..e1c1960 100644 --- a/crates/kebab-cli/src/main.rs +++ b/crates/kebab-cli/src/main.rs @@ -60,6 +60,12 @@ enum Cmd { force: bool, }, + /// config.toml 관리 (스키마 마이그레이션 등). + Config { + #[command(subcommand)] + what: ConfigWhat, + }, + /// Scan the workspace and ingest new/updated documents. Ingest { /// Workspace root override. @@ -346,6 +352,16 @@ enum Cmd { }, } +#[derive(Subcommand, Debug)] +enum ConfigWhat { + /// 기존 config.toml 을 새 스키마로 마이그레이션(빠진 섹션 추가 + 멱등 + .bak 백업). + Migrate { + /// 변경만 출력하고 파일은 수정하지 않는다. + #[arg(long)] + dry_run: bool, + }, +} + #[derive(Subcommand, Debug)] enum ListWhat { /// List documents currently indexed. @@ -1310,6 +1326,42 @@ fn run(cli: &Cli) -> anyhow::Result<()> { Ok(()) } + Cmd::Config { what } => match what { + ConfigWhat::Migrate { dry_run } => { + let report = + kebab_app::config_migrate_with_config_path(cli.config.as_deref(), *dry_run)?; + if cli.json { + println!( + "{}", + serde_json::to_string(&wire::wire_config_migration(&report))? + ); + } else if !report.changed { + println!( + "config 이미 최신입니다 (schema v{}).", + report.to_schema_version + ); + } else { + let verb = if report.dry_run { "변경 예정" } else { "적용됨" }; + println!( + "config 마이그레이션 {verb}: v{} → v{} ({} changes)", + report.from_schema_version, + report.to_schema_version, + report.changes.len() + ); + for c in &report.changes { + println!(" - [{:?}] {} — {}", c.kind, c.path, c.detail); + } + if let Some(bak) = &report.backup_path { + println!("백업: {bak}"); + } + if report.dry_run { + println!("(--dry-run: 파일 미수정. 적용하려면 --dry-run 없이 재실행)"); + } + } + Ok(()) + } + }, + Cmd::Doctor => { let report = kebab_app::doctor_with_config_path(cli.config.as_deref())?; if cli.json { diff --git a/crates/kebab-cli/src/wire.rs b/crates/kebab-cli/src/wire.rs index d866a94..fc882f9 100644 --- a/crates/kebab-cli/src/wire.rs +++ b/crates/kebab-cli/src/wire.rs @@ -225,6 +225,12 @@ pub fn wire_bulk_search_item(item: &kebab_core::BulkSearchItem) -> Value { v } +/// `config_migration.v1` 직렬화. `ConfigMigrationReport` 가 `schema_version` +/// 필드를 자체 보유하므로(doctor 와 동일) 그대로 직렬화한다. +pub fn wire_config_migration(r: &kebab_app::ConfigMigrationReport) -> Value { + serde_json::to_value(r).expect("ConfigMigrationReport serializes") +} + #[cfg(test)] mod tests { use super::*; diff --git a/docs/wire-schema/v1/config_migration.v1.schema.json b/docs/wire-schema/v1/config_migration.v1.schema.json new file mode 100644 index 0000000..c5e6ea0 --- /dev/null +++ b/docs/wire-schema/v1/config_migration.v1.schema.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "config_migration.v1", + "description": "Result of `kebab config migrate` — schema reconciliation of a user's config.toml.", + "type": "object", + "required": [ + "schema_version", + "config_path", + "dry_run", + "from_schema_version", + "to_schema_version", + "changed", + "changes" + ], + "properties": { + "schema_version": { "const": "config_migration.v1" }, + "config_path": { "type": "string" }, + "dry_run": { "type": "boolean" }, + "from_schema_version": { "type": "integer" }, + "to_schema_version": { "type": "integer" }, + "changed": { "type": "boolean" }, + "backup_path": { "type": ["string", "null"] }, + "changes": { + "type": "array", + "items": { + "type": "object", + "required": ["kind", "path", "detail"], + "properties": { + "kind": { + "enum": ["added_section", "added_key", "removed_deprecated"] + }, + "path": { "type": "string" }, + "detail": { "type": "string" } + } + } + } + } +}