1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
//! # Buildsrs Builder
//!
//! This crate implements the functionality of building crate artifacts. It receives jobs from the
//! backend, telling it which crates to build and which artifacts. It builds the crates using
//! whichever strategy it is configured to use, by default it will use Docker. It streams the
//! progress while the build is in progress, and finally signs and uploads the artifacts.
use anyhow::Result;
use async_trait::async_trait;
use bytes::Bytes;
use cargo_metadata::Metadata;
use futures::stream::{BoxStream, StreamExt};
use std::{path::Path, sync::Arc};
use url::Url;
#[cfg(feature = "docker")]
mod docker;
#[cfg(feature = "docker")]
pub use docker::{DockerBuilder, DockerStrategy};
#[cfg(feature = "options")]
mod options;
#[cfg(feature = "options")]
pub use options::StrategyOptions;
#[cfg(feature = "websocket")]
mod websocket;
#[cfg(feature = "websocket")]
pub use websocket::Connection;
/// Build logs and final file.
#[derive(Clone, Debug)]
pub enum Output<T> {
/// Logs while fetching
Fetch(Vec<u8>),
/// Logs while building
Build(Vec<u8>),
/// Final data
Data(T),
}
/// Stream file contents.
pub type FileStream = BoxStream<'static, Result<Bytes>>;
/// Stream build logs and final data.
pub type OutputStream<T> = BoxStream<'static, Result<Output<T>>>;
/// Trait to get the final data from an [`OutputStream`].
#[async_trait]
pub trait OutputStreamExt {
/// Type that this stream resolves out.
type Output;
/// Get final data, ignoring all log messages.
async fn resolve(self) -> Result<Self::Output>;
}
#[async_trait]
impl<T> OutputStreamExt for OutputStream<T> {
type Output = T;
async fn resolve(mut self) -> Result<Self::Output> {
while let Some(item) = self.next().await {
if let Output::Data(data) = item? {
return Ok(data);
}
}
return Err(anyhow::anyhow!("Did not find data"));
}
}
/// Represents a strategy for building artifacts.
///
/// A strategy is able to create a builder instance for a given crate. For example, building
/// binaries in Docker might be one strategy, while building binaries in a QEMU VM might be another
/// one.
#[async_trait]
pub trait Strategy: Send + Sync {
/// Create a builder instance from an extraced crate at the given path.
async fn builder_from_path(&self, path: &Path) -> Result<DynBuilder>;
/// Create a builder instance from a `.crate` file.
async fn builder_from_crate(&self, krate: &Path) -> Result<DynBuilder>;
/// Create a builder instance from a crate by download URL.
async fn builder_from_url(&self, url: &Url, checksum: &[u8]) -> Result<DynBuilder>;
}
/// Represents an instance of a builder with a single crate.
///
/// The builder is able to produce artifacts for the crate.
#[async_trait]
pub trait Builder: Send + Sync {
/// Build crate metadata.
async fn metadata(&self) -> Result<OutputStream<Metadata>>;
}
/// Dynamic builder.
pub type DynBuilder = Box<dyn Builder>;
/// Dynamic strategy.
pub type DynStrategy = Arc<dyn Strategy>;