const std = @import("std"); const lambda_zig = @import("lambda_zig"); pub fn build(b: *std.Build) !void { // Default to aarch64-linux for Lambda Graviton deployment const target = b.standardTargetOptions(.{ .default_target = .{ .cpu_arch = .aarch64, .os_tag = .linux, }, }); const optimize = b.standardOptimizeOption(.{}); // Native target for build tools const native_target = b.resolveTargetQuery(.{}); // Get lambda-zig dependency const lambda_zig_dep = b.dependency("lambda_zig", .{ .target = target, .optimize = optimize, }); // Get controlr dependency for rinnai module const controlr_dep = b.dependency("controlr", .{ .target = target, .optimize = optimize, }); // Create the main module const main_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); // Add lambda_runtime import main_module.addImport("lambda_runtime", lambda_zig_dep.module("lambda_runtime")); // Add rinnai import from controlr main_module.addImport("rinnai", controlr_dep.module("rinnai")); // Create executable const exe = b.addExecutable(.{ .name = "house-control", .root_module = main_module, }); b.installArtifact(exe); // Configure Lambda build steps and get deployment info // Function name defaults to exe.name ("house-control") const lambda = try lambda_zig.configureBuild(b, lambda_zig_dep, exe, .{}); // Get AWS profile option (already declared by lambda-zig) const profile = b.user_input_options.get("profile"); const profile_str: ?[]const u8 = if (profile) |p| switch (p.value) { .scalar => |s| s, else => null, } else null; // Get AWS region option (already declared by lambda-zig) const region = b.user_input_options.get("region"); const region_str: ?[]const u8 = if (region) |r| switch (r.value) { .scalar => |s| s, else => null, } else null; // Build the gen-skill-json tool (runs on host) const gen_skill_json_module = b.createModule(.{ .root_source_file = b.path("tools/gen-skill-json.zig"), .target = native_target, .optimize = .ReleaseSafe, }); const gen_skill_json_exe = b.addExecutable(.{ .name = "gen-skill-json", .root_module = gen_skill_json_module, }); // Generate skill.json from template using Lambda ARN const gen_skill_cmd = b.addRunArtifact(gen_skill_json_exe); gen_skill_cmd.addFileArg(lambda.deploy_output); gen_skill_cmd.addFileArg(b.path("skill.template.json")); gen_skill_cmd.step.dependOn(lambda.deploy_step); // Capture generated skill.json const skill_json = gen_skill_cmd.captureStdOut(); // Write skill.json to skill-package directory (updates source files, necessary for the ask deploy) const write_skill_json = b.addUpdateSourceFiles(); write_skill_json.addCopyFileToSource(skill_json, "skill-package/skill.json"); const gen_skill_step = b.step("gen_skill_json", "Generate skill.json from template (will deploy function)"); gen_skill_step.dependOn(&write_skill_json.step); // ASK CLI deploy step for Alexa skill metadata const ask_deploy_cmd = b.addSystemCommand(&.{ "bun", "x", "ask", "deploy", "--target", "skill-metadata", }); // ASK deploy depends on skill.json being generated ask_deploy_cmd.step.dependOn(&write_skill_json.step); // Add Alexa skill-specific Lambda permission // // Alexa requires a skill-specific Lambda permission with the skill ID as an // event_source_token condition. This is more secure than a generic principal-based // permission (--allow-principal) and restricts invocation to only our specific skill. // // We use our own tool instead of lambda-zig's built-in --allow-principal because: // 1. The skill ID isn't known until after ASK deploy runs // 2. event_source_token is Alexa-specific (not supported by lambda-zig) // // Pipeline: Lambda deploy -> gen-skill-json -> ASK deploy -> add-alexa-permission const aws_dep = b.dependency("aws", .{ .target = native_target, .optimize = .ReleaseSafe, }); const add_alexa_perm_module = b.createModule(.{ .root_source_file = b.path("tools/add-alexa-permission.zig"), .target = native_target, .optimize = .ReleaseSafe, }); add_alexa_perm_module.addImport("aws", aws_dep.module("aws")); const add_alexa_perm_exe = b.addExecutable(.{ .name = "add-alexa-permission", .root_module = add_alexa_perm_module, }); const add_alexa_perm_cmd = b.addRunArtifact(add_alexa_perm_exe); add_alexa_perm_cmd.addFileArg(lambda.deploy_output); add_alexa_perm_cmd.addFileArg(b.path(".ask/ask-states.json")); if (profile_str) |p| { add_alexa_perm_cmd.addArgs(&.{ "--profile", p }); } if (region_str) |r| { add_alexa_perm_cmd.addArgs(&.{ "--region", r }); } // Must run after ASK deploy (which creates/updates skill ID) and Lambda deploy add_alexa_perm_cmd.step.dependOn(&ask_deploy_cmd.step); const ask_deploy_step = b.step("ask_deploy", "Deploy Alexa skill metadata via ASK CLI"); ask_deploy_step.dependOn(&add_alexa_perm_cmd.step); // Full deploy step - deploys Lambda, generates skill.json, deploys Alexa skill, adds permission const full_deploy_step = b.step("deploy", "Deploy Lambda function and Alexa skill"); full_deploy_step.dependOn(&add_alexa_perm_cmd.step); // Test step - use native target for tests (not cross-compiled Lambda target) const lambda_zig_dep_native = b.dependency("lambda_zig", .{ .target = native_target, .optimize = optimize, }); const controlr_dep_native = b.dependency("controlr", .{ .target = native_target, .optimize = optimize, }); const test_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = native_target, .optimize = optimize, }); test_module.addImport("lambda_runtime", lambda_zig_dep_native.module("lambda_runtime")); test_module.addImport("rinnai", controlr_dep_native.module("rinnai")); const main_tests = b.addTest(.{ .name = "test", .root_module = test_module, }); const run_main_tests = b.addRunArtifact(main_tests); const test_step = b.step("test", "Run unit tests"); test_step.dependOn(&run_main_tests.step); // Also verify tools compile test_step.dependOn(&gen_skill_json_exe.step); test_step.dependOn(&add_alexa_perm_exe.step); // Run step for local testing (uses native target) const run_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = native_target, .optimize = optimize, }); run_module.addImport("lambda_runtime", lambda_zig_dep_native.module("lambda_runtime")); run_module.addImport("rinnai", controlr_dep_native.module("rinnai")); const run_exe = b.addExecutable(.{ .name = exe.name, .root_module = run_module, }); const run_cmd = b.addRunArtifact(run_exe); if (b.args) |args| { run_cmd.addArgs(args); } const run_step = b.step("run", "Run locally for testing"); run_step.dependOn(&run_cmd.step); }