1use std::fs;
2use std::path::{Path, PathBuf};
3use std::process::{Command, Stdio};
4
5use anyhow::{Context, Result, bail};
6use tempfile::TempDir;
7
8pub const PACKC_ENV: &str = "GREENTIC_PACK_PLAN_PACKC";
9
10pub fn materialize_pack_path(input: &Path, verbose: bool) -> Result<(Option<TempDir>, PathBuf)> {
11 let metadata =
12 fs::metadata(input).with_context(|| format!("unable to read input {}", input.display()))?;
13 if metadata.is_file() {
14 Ok((None, input.to_path_buf()))
15 } else if metadata.is_dir() {
16 let (temp, path) = build_pack_from_source(input, verbose)?;
17 Ok((Some(temp), path))
18 } else {
19 bail!(
20 "input {} is neither a file nor a directory",
21 input.display()
22 );
23 }
24}
25
26fn build_pack_from_source(source: &Path, verbose: bool) -> Result<(TempDir, PathBuf)> {
27 let temp = TempDir::new().context("failed to create temporary directory for pack build")?;
28 let gtpack_path = temp.path().join("pack.gtpack");
29 let wasm_path = temp.path().join("pack.wasm");
30 let manifest_path = temp.path().join("manifest.cbor");
31 let sbom_path = temp.path().join("sbom.cdx.json");
32 let component_data = temp.path().join("data.rs");
33
34 let packc_bin = std::env::var(PACKC_ENV).unwrap_or_else(|_| "packc".to_string());
35 let mut cmd = Command::new(packc_bin);
36 cmd.arg("build")
37 .arg("--in")
38 .arg(source)
39 .arg("--out")
40 .arg(&wasm_path)
41 .arg("--manifest")
42 .arg(&manifest_path)
43 .arg("--sbom")
44 .arg(&sbom_path)
45 .arg("--gtpack-out")
46 .arg(>pack_path)
47 .arg("--component-data")
48 .arg(&component_data)
49 .arg("--log")
50 .arg(if verbose { "info" } else { "warn" });
51
52 if !verbose {
53 cmd.stdout(Stdio::null()).stderr(Stdio::null());
54 }
55
56 let status = cmd
57 .status()
58 .context("failed to spawn packc to build temporary .gtpack")?;
59 if !status.success() {
60 bail!("packc build failed with status {}", status);
61 }
62
63 Ok((temp, gtpack_path))
64}