feat(cli): kebab ingest progress display (p9-fb-02) + p9-fb-01 status flip
`kebab ingest` 가 진행 상황을 사용자에게 보여주는 두 surface 추가:
- **사람 모드 (TTY)**: indicatif `ProgressBar` on stderr — scan 중에는
spinner, ScanCompleted 후 bar 로 전환, 매 asset 마다 message 갱신.
- **사람 모드 (non-TTY, CI/pipe)**: indicatif draw target 을 hidden
으로 두고 stderr 에 한 줄씩 (`ingest: scanning`, `ingest: 1/N path`,
`ingest: complete (...)`).
- **`--json` 모드**: stderr 비우고 stdout 에 line-delimited
`ingest_progress.v1` JSON 을 emit. 마지막 줄은 기존
`ingest_report.v1` 그대로 (외부 wrapper backward-compat).
구현:
- 신규 `crates/kebab-cli/src/progress.rs` — `ProgressMode::{Json,
Human { tty }}`, `ProgressDisplay` (background thread 가 channel
drain + 모드별 render), `now_rfc3339` helper. mode 가 무엇이든 ts
는 wire emit 시점에 stamp.
- `crates/kebab-cli/src/wire.rs` 에 `wire_ingest_progress` 추가.
serde tag (`kind`) 위에 `schema_version` + `ts` 두 필드 더해 spec
§2.4a wire shape 완성.
- `Cmd::Ingest` 핸들러: mpsc channel 만들고 background thread 가
display 돌리는 동안 main 이 `ingest_with_config_progress` 호출.
ingest 반환 시 Sender drop → display thread 정상 종료. join 후
최종 ingest_report 출력.
- 새 dep: `indicatif` 0.17 (TTY 전용 진행 바, non-TTY/--json 에서는
hidden draw target).
Test:
- 3 lib unit (mode resolution + RFC 3339 round-trip).
- 3 integration (--json line-delimited / non-TTY stderr text /
ts+kind 검증). 16 PASS 전체 회귀 0.
Plan 갱신:
- p9-fb-01: status `in_progress` → `completed` (PR #52 머지 후속).
- p9-fb-02: status `planned` → `in_progress`. 머지 후 별도 한 줄
commit 으로 `completed` flip.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -114,6 +114,25 @@ pub fn wire_reset(r: &kebab_app::ResetReport) -> Value {
|
||||
tag_object(v, "reset_report.v1")
|
||||
}
|
||||
|
||||
/// Wrap an [`kebab_app::IngestEvent`] as `ingest_progress.v1`. Adds
|
||||
/// the `schema_version` discriminator on top of serde's existing
|
||||
/// `kind` discriminator, plus an `ts` field with the current
|
||||
/// wall-clock — the emit site is the only place that knows the moment
|
||||
/// of emission, so the timestamp is stamped here rather than carried
|
||||
/// on the event itself.
|
||||
pub fn wire_ingest_progress(
|
||||
event: &kebab_app::IngestEvent,
|
||||
) -> anyhow::Result<Value> {
|
||||
let mut v = serde_json::to_value(event)?;
|
||||
if let Value::Object(ref mut map) = v {
|
||||
map.insert(
|
||||
"ts".to_string(),
|
||||
Value::String(crate::progress::now_rfc3339()?),
|
||||
);
|
||||
}
|
||||
Ok(tag_object(v, "ingest_progress.v1"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user