Skip to main content

harmont_cli/cli/
pipelines.rs

1use std::path::PathBuf;
2
3use anyhow::{Context, Result};
4use clap::Parser;
5use hm_dsl_engine::{detect, engine_for};
6
7#[derive(Debug, Clone, Parser)]
8pub struct PipelinesArgs {
9    /// Source root containing `.hm/` (defaults to cwd).
10    #[arg(short, long)]
11    pub dir: Option<PathBuf>,
12}
13
14/// Empty discovery envelope, emitted when a repo declares no pipelines. Mirrors
15/// the shape of `harmont.dump_registry_json()` so backend discovery parses it
16/// the same way (it reads only the `pipelines` array).
17const EMPTY_ENVELOPE: &str = r#"{"schema_version":"1","pipelines":[]}"#;
18
19/// Print the discovery envelope JSON (all pipelines) to stdout.
20///
21/// A repo with no `.hm/` directory (or one with no `.py`/`.ts` files)
22/// declares no pipelines and yields the empty envelope rather than an error —
23/// the backend fans discovery out across every repo in an installation, most of
24/// which carry no pipelines. Both Python and TypeScript pipelines emit the same
25/// discovery envelope; a repo declaring both languages resolves to Python via
26/// [`detect::detect_language_python_first`] (the fully-supported backend path),
27/// matching `hm render`.
28///
29/// # Errors
30///
31/// Returns an error if the engine can't start or the DSL runtime fails to
32/// evaluate the pipelines.
33pub async fn run(args: PipelinesArgs) -> Result<()> {
34    let repo_root = match args.dir {
35        Some(d) => d,
36        None => std::env::current_dir().context("cannot determine current directory")?,
37    };
38
39    if !detect::has_pipeline_files(&repo_root) {
40        print!("{EMPTY_ENVELOPE}");
41        return Ok(());
42    }
43
44    let lang =
45        detect::detect_language_python_first(&repo_root).context("detecting pipeline language")?;
46    let engine = engine_for(lang).context("initializing DSL engine")?;
47    let json = engine
48        .registry_json(&repo_root)
49        .await
50        .context("dumping pipeline registry")?;
51
52    // Machine-facing: raw envelope JSON on stdout, nothing else.
53    print!("{json}");
54    Ok(())
55}