chub_cli/commands/
build.rs1use chub_core::build::builder::{build_registry, write_build_output_with_opts, BuildOptions};
2use chub_core::error::Result;
3use chub_core::team::analytics;
4use clap::Args;
5use indicatif::{ProgressBar, ProgressStyle};
6use std::path::PathBuf;
7
8use crate::output;
9
10#[derive(Args)]
11pub struct BuildArgs {
12 content_dir: PathBuf,
14
15 #[arg(short, long)]
17 output: Option<PathBuf>,
18
19 #[arg(long)]
21 base_url: Option<String>,
22
23 #[arg(long)]
25 validate_only: bool,
26
27 #[arg(long)]
29 no_incremental: bool,
30}
31
32pub fn run(args: BuildArgs, json: bool) -> Result<()> {
33 let content_dir = &args.content_dir;
34 let output_dir = args.output.unwrap_or_else(|| content_dir.join("dist"));
35
36 let opts = BuildOptions {
37 base_url: args.base_url,
38 validate_only: args.validate_only,
39 incremental: !args.no_incremental,
40 };
41
42 let pb = if !json {
44 let pb = ProgressBar::new_spinner();
45 pb.set_style(
46 ProgressStyle::with_template("{spinner:.cyan} {msg}")
47 .unwrap()
48 .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏-"),
49 );
50 pb.set_message("Scanning content directory...");
51 pb.enable_steady_tick(std::time::Duration::from_millis(80));
52 Some(pb)
53 } else {
54 None
55 };
56
57 let build_start = std::time::Instant::now();
58 let result = build_registry(content_dir, &opts)?;
59 let build_duration = build_start.elapsed().as_millis() as u64;
60
61 analytics::record_build(
62 result.docs_count + result.skills_count,
63 build_duration,
64 result.warnings.len(),
65 args.validate_only,
66 );
67
68 for w in &result.warnings {
70 output::warn(w);
71 }
72
73 if args.validate_only {
74 if let Some(pb) = pb {
75 pb.finish_and_clear();
76 }
77 if json {
78 println!(
79 "{}",
80 serde_json::json!({
81 "docs": result.docs_count,
82 "skills": result.skills_count,
83 "warnings": result.warnings.len(),
84 })
85 );
86 } else {
87 output::success(&format!(
88 "Valid: {} docs, {} skills, {} warnings",
89 result.docs_count,
90 result.skills_count,
91 result.warnings.len()
92 ));
93 }
94 return Ok(());
95 }
96
97 if let Some(ref pb) = pb {
98 pb.set_message("Writing output...");
99 }
100
101 write_build_output_with_opts(content_dir, &output_dir, &result, &opts)?;
103
104 if let Some(pb) = pb {
105 pb.finish_and_clear();
106 }
107
108 if json {
109 println!(
110 "{}",
111 serde_json::json!({
112 "docs": result.docs_count,
113 "skills": result.skills_count,
114 "warnings": result.warnings.len(),
115 "output": output_dir.display().to_string(),
116 })
117 );
118 } else {
119 output::success(&format!(
120 "Built: {} docs, {} skills → {}",
121 result.docs_count,
122 result.skills_count,
123 output_dir.display()
124 ));
125 }
126
127 Ok(())
128}