190 lines
7.1 KiB
Zig
190 lines
7.1 KiB
Zig
//! Lambda Build Integration for Zig Build System
|
|
//!
|
|
//! This module provides build steps for packaging and deploying Lambda functions.
|
|
//! It builds the lambda-build CLI tool and invokes it for each operation.
|
|
|
|
const std = @import("std");
|
|
|
|
/// Configuration options for Lambda build integration.
|
|
///
|
|
/// These provide project-level defaults that can still be overridden
|
|
/// via command-line options (e.g., `-Dfunction-name=...`).
|
|
pub const Config = struct {
|
|
/// Default function name if not specified via -Dfunction-name.
|
|
/// This allows consuming projects to set their own default.
|
|
default_function_name: []const u8 = "zig-fn",
|
|
|
|
/// Default IAM role name if not specified via -Drole-name.
|
|
default_role_name: []const u8 = "lambda_basic_execution",
|
|
|
|
/// Default environment file if not specified via -Denv-file.
|
|
/// If the file doesn't exist, it's silently skipped.
|
|
default_env_file: ?[]const u8 = ".env",
|
|
|
|
/// Default AWS service principal to grant invoke permission.
|
|
/// For Alexa skills, use "alexa-appkit.amazon.com".
|
|
default_allow_principal: ?[]const u8 = null,
|
|
};
|
|
|
|
/// Information about the configured Lambda build steps.
|
|
///
|
|
/// Returned by `configureBuild` to allow consumers to depend on steps
|
|
/// and access deployment outputs.
|
|
pub const BuildInfo = struct {
|
|
/// Package step - creates the deployment zip
|
|
package_step: *std.Build.Step,
|
|
|
|
/// IAM step - creates/verifies the IAM role
|
|
iam_step: *std.Build.Step,
|
|
|
|
/// Deploy step - deploys the function to AWS Lambda
|
|
deploy_step: *std.Build.Step,
|
|
|
|
/// Invoke step - invokes the deployed function
|
|
invoke_step: *std.Build.Step,
|
|
|
|
/// LazyPath to JSON file with deployment info.
|
|
/// Contains: arn, function_name, region, account_id, role_arn, architecture, environment_keys
|
|
/// Available after deploy_step completes.
|
|
deploy_output: std.Build.LazyPath,
|
|
|
|
/// The function name used for deployment
|
|
function_name: []const u8,
|
|
};
|
|
|
|
/// Configure Lambda build steps for a Zig project.
|
|
///
|
|
/// Adds the following build steps:
|
|
/// - awslambda_package: Package the function into a zip file
|
|
/// - awslambda_iam: Create/verify IAM role
|
|
/// - awslambda_deploy: Deploy the function to AWS
|
|
/// - awslambda_run: Invoke the deployed function
|
|
///
|
|
/// The `config` parameter allows setting project-level defaults that can
|
|
/// still be overridden via command-line options.
|
|
///
|
|
/// Returns a `BuildInfo` struct containing references to all steps and
|
|
/// a `deploy_output` LazyPath to the deployment info JSON file.
|
|
pub fn configureBuild(
|
|
b: *std.Build,
|
|
lambda_build_dep: *std.Build.Dependency,
|
|
exe: *std.Build.Step.Compile,
|
|
config: Config,
|
|
) !BuildInfo {
|
|
// Get the lambda-build CLI artifact from the dependency
|
|
const cli = lambda_build_dep.artifact("lambda-build");
|
|
|
|
// Get configuration options (command-line overrides config defaults)
|
|
const function_name = b.option([]const u8, "function-name", "Function name for Lambda") orelse config.default_function_name;
|
|
const region = b.option([]const u8, "region", "AWS region") orelse null;
|
|
const profile = b.option([]const u8, "profile", "AWS profile") orelse null;
|
|
const role_name = b.option(
|
|
[]const u8,
|
|
"role-name",
|
|
"IAM role name (default: lambda_basic_execution)",
|
|
) orelse config.default_role_name;
|
|
const payload = b.option(
|
|
[]const u8,
|
|
"payload",
|
|
"Lambda invocation payload",
|
|
) orelse "{}";
|
|
const env_file = b.option(
|
|
[]const u8,
|
|
"env-file",
|
|
"Path to environment variables file (KEY=VALUE format)",
|
|
) orelse config.default_env_file;
|
|
const allow_principal = b.option(
|
|
[]const u8,
|
|
"allow-principal",
|
|
"AWS service principal to grant invoke permission (e.g., alexa-appkit.amazon.com)",
|
|
) orelse config.default_allow_principal;
|
|
|
|
// Determine architecture for Lambda
|
|
const target_arch = exe.root_module.resolved_target.?.result.cpu.arch;
|
|
const arch_str = blk: {
|
|
switch (target_arch) {
|
|
.aarch64 => break :blk "aarch64",
|
|
.x86_64 => break :blk "x86_64",
|
|
else => {
|
|
std.log.warn("Unsupported architecture for Lambda: {}, defaulting to x86_64", .{target_arch});
|
|
break :blk "x86_64";
|
|
},
|
|
}
|
|
};
|
|
|
|
// Package step - output goes to cache based on input hash
|
|
const package_cmd = b.addRunArtifact(cli);
|
|
package_cmd.step.name = try std.fmt.allocPrint(b.allocator, "{s} package", .{cli.name});
|
|
package_cmd.addArgs(&.{ "package", "--exe" });
|
|
package_cmd.addFileArg(exe.getEmittedBin());
|
|
package_cmd.addArgs(&.{"--output"});
|
|
const zip_output = package_cmd.addOutputFileArg("function.zip");
|
|
package_cmd.step.dependOn(&exe.step);
|
|
|
|
const package_step = b.step("awslambda_package", "Package the Lambda function");
|
|
package_step.dependOn(&package_cmd.step);
|
|
|
|
// IAM step
|
|
const iam_cmd = b.addRunArtifact(cli);
|
|
iam_cmd.step.name = try std.fmt.allocPrint(b.allocator, "{s} iam", .{cli.name});
|
|
if (profile) |p| iam_cmd.addArgs(&.{ "--profile", p });
|
|
if (region) |r| iam_cmd.addArgs(&.{ "--region", r });
|
|
iam_cmd.addArgs(&.{ "iam", "--role-name", role_name });
|
|
|
|
const iam_step = b.step("awslambda_iam", "Create/verify IAM role for Lambda");
|
|
iam_step.dependOn(&iam_cmd.step);
|
|
|
|
// Deploy step (depends on package)
|
|
const deploy_cmd = b.addRunArtifact(cli);
|
|
deploy_cmd.step.name = try std.fmt.allocPrint(b.allocator, "{s} deploy", .{cli.name});
|
|
if (profile) |p| deploy_cmd.addArgs(&.{ "--profile", p });
|
|
if (region) |r| deploy_cmd.addArgs(&.{ "--region", r });
|
|
deploy_cmd.addArgs(&.{
|
|
"deploy",
|
|
"--function-name",
|
|
function_name,
|
|
"--zip-file",
|
|
});
|
|
deploy_cmd.addFileArg(zip_output);
|
|
deploy_cmd.addArgs(&.{
|
|
"--role-name",
|
|
role_name,
|
|
"--arch",
|
|
arch_str,
|
|
});
|
|
if (env_file) |ef| deploy_cmd.addArgs(&.{ "--env-file", ef });
|
|
if (allow_principal) |ap| deploy_cmd.addArgs(&.{ "--allow-principal", ap });
|
|
// Add deploy output file for deployment info JSON
|
|
deploy_cmd.addArg("--deploy-output");
|
|
const deploy_output = deploy_cmd.addOutputFileArg("deploy-output.json");
|
|
deploy_cmd.step.dependOn(&package_cmd.step);
|
|
|
|
const deploy_step = b.step("awslambda_deploy", "Deploy the Lambda function");
|
|
deploy_step.dependOn(&deploy_cmd.step);
|
|
|
|
// Invoke/run step (depends on deploy)
|
|
const invoke_cmd = b.addRunArtifact(cli);
|
|
invoke_cmd.step.name = try std.fmt.allocPrint(b.allocator, "{s} invoke", .{cli.name});
|
|
if (profile) |p| invoke_cmd.addArgs(&.{ "--profile", p });
|
|
if (region) |r| invoke_cmd.addArgs(&.{ "--region", r });
|
|
invoke_cmd.addArgs(&.{
|
|
"invoke",
|
|
"--function-name",
|
|
function_name,
|
|
"--payload",
|
|
payload,
|
|
});
|
|
invoke_cmd.step.dependOn(&deploy_cmd.step);
|
|
|
|
const run_step = b.step("awslambda_run", "Invoke the deployed Lambda function");
|
|
run_step.dependOn(&invoke_cmd.step);
|
|
|
|
return .{
|
|
.package_step = package_step,
|
|
.iam_step = iam_step,
|
|
.deploy_step = deploy_step,
|
|
.invoke_step = run_step,
|
|
.deploy_output = deploy_output,
|
|
.function_name = function_name,
|
|
};
|
|
}
|