cargo-lambda-deploy 1.9.1

Cargo subcommand to work with AWS Lambda
Documentation
use aws_sdk_s3::{Client as S3Client, primitives::ByteStream};
use cargo_lambda_build::{BinaryArchive, BinaryModifiedAt};
use cargo_lambda_interactive::progress::Progress;
use cargo_lambda_metadata::cargo::deploy::Deploy;
use cargo_lambda_remote::{
    aws_sdk_config::SdkConfig,
    aws_sdk_lambda::{
        Client as LambdaClient,
        primitives::Blob,
        types::{LayerVersionContentInput, Runtime},
    },
};
use miette::{IntoDiagnostic, Result, WrapErr};
use serde::Serialize;
use tracing::debug;

#[derive(Serialize)]
pub(crate) struct DeployOutput {
    extension_arn: String,
    binary_modified_at: BinaryModifiedAt,
}

impl std::fmt::Display for DeployOutput {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "✅ extension uploaded successfully 🎉")?;
        writeln!(
            f,
            "🛠️ binary last compiled {}",
            self.binary_modified_at.humanize()
        )?;

        write!(f, "🔍 extension arn: {}", self.extension_arn)?;

        Ok(())
    }
}

#[allow(clippy::too_many_arguments)]
pub(crate) async fn deploy(
    config: &Deploy,
    name: &str,
    sdk_config: &SdkConfig,
    binary_archive: &BinaryArchive,
    progress: &Progress,
) -> Result<DeployOutput> {
    let lambda_client = LambdaClient::new(sdk_config);

    let compatible_runtimes = config
        .compatible_runtimes()
        .iter()
        .map(|runtime| Runtime::from(runtime.as_str()))
        .collect::<Vec<_>>();

    let input = match &config.s3_bucket {
        None => LayerVersionContentInput::builder()
            .zip_file(Blob::new(binary_archive.read()?))
            .build(),
        Some(bucket) => {
            progress.set_message("uploading binary to S3");

            let key = config.s3_key.as_deref().unwrap_or(name);
            debug!(bucket, key, "uploading zip to S3");

            let s3_client = S3Client::new(sdk_config);
            let mut operation = s3_client
                .put_object()
                .bucket(bucket)
                .key(key)
                .body(ByteStream::from(binary_archive.read()?));

            if let Some(tags) = config.s3_tags() {
                operation = operation.tagging(tags);
            }

            operation
                .send()
                .await
                .into_diagnostic()
                .wrap_err("failed to upload extension code to S3")?;

            LayerVersionContentInput::builder()
                .s3_bucket(bucket)
                .s3_key(key)
                .build()
        }
    };

    progress.set_message("publishing new layer version");

    let output = lambda_client
        .publish_layer_version()
        .layer_name(name)
        .compatible_architectures(binary_archive.architecture())
        .set_compatible_runtimes(Some(compatible_runtimes))
        .set_description(config.function_config.description.clone())
        .content(input)
        .send()
        .await
        .into_diagnostic()
        .wrap_err("failed to publish extension")?;

    Ok(DeployOutput {
        extension_arn: output.layer_version_arn.expect("missing ARN"),
        binary_modified_at: binary_archive.binary_modified_at.clone(),
    })
}