at ccc40f5
use miette::{IntoDiagnostic, Result}; use serde::Deserialize; use sha1::{Digest, Sha1}; use std::path::PathBuf; pub mod archive; pub mod cargo; use archive::{ArchiveBuilder, ArchiveBuilderConfig}; use cargo::{CargoBuilder, CargoBuilderConfig, CargoDocBuilder, CargoDocBuilderConfig}; #[derive(Debug, Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum AnyBuilder { /// Creates a `.tar.gz` archive of the source tree, automatically excluding /// files and directories matched by any `.gitignore` found in the hierarchy. /// /// ```toml /// [[builders]] /// type = "archive" /// source_dir = "." # optional, defaults to CWD /// output = "myproject-1.0.0.tar.gz" # optional, defaults to source.tar.gz /// prefix = "myproject-1.0.0" # optional, defaults to source_dir name /// ``` Archive(ArchiveBuilderConfig), /// Compiles the crate in release mode with `cargo build --release`. /// One or more target triples can be specified for cross-compilation; /// omitting `targets` builds for the host platform. /// /// ```toml /// [[builders]] /// type = "cargo" /// targets = ["x86_64-unknown-linux-musl", "aarch64-unknown-linux-musl"] /// manifest_path = "Cargo.toml" # optional /// ``` Cargo(CargoBuilderConfig), /// Generates API documentation with `cargo doc`. /// Returns the per-crate doc directory (e.g. `target/doc/my_crate`) as an /// artifact so it can be published or archived by a later pipeline step. /// /// ```toml /// [[builders]] /// type = "cargo_doc" /// no_deps = true # optional, skip dependency docs /// manifest_path = "Cargo.toml" # optional /// ``` CargoDoc(CargoDocBuilderConfig), } impl AnyBuilder { pub async fn build(&self) -> Result<Vec<ArtifactPath>> { match self { Self::Archive(config) => ArchiveBuilder.build(config.clone()).await, Self::Cargo(config) => CargoBuilder.build(config.clone()).await, Self::CargoDoc(config) => CargoDocBuilder.build(config.clone()).await, } } } /// Read the file at `path` and return its SHA1 digest as a lowercase hex string. async fn hash_file(path: &PathBuf) -> Result<String> { let contents = tokio::fs::read(path).await.into_diagnostic()?; let mut hasher = Sha1::new(); hasher.update(&contents); Ok(hasher .finalize() .iter() .map(|b| format!("{b:02x}")) .collect()) } pub struct ArtifactPath { pub path: PathBuf, pub name: String, /// Lowercase hexadecimal SHA1 digest of the artifact's contents, if computed. pub hash: Option<String>, } #[allow(async_fn_in_trait)] pub trait Builder { type ConfigType: Default + for<'de> Deserialize<'de> + Clone; async fn build(&self, config: Self::ConfigType) -> Result<Vec<ArtifactPath>>; }