From 94c311749eb70a742e52b4fc311da282677c2369 Mon Sep 17 00:00:00 2001 From: Michael Moffett Date: Fri, 21 Aug 2026 13:34:16 -0600 Subject: [PATCH 1/2] feat(scaffold): expose deployed program ids as runbook outputs --- crates/cli/src/scaffold/mod.rs | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/crates/cli/src/scaffold/mod.rs b/crates/cli/src/scaffold/mod.rs index 33dffe4e2..bb9ec0737 100644 --- a/crates/cli/src/scaffold/mod.rs +++ b/crates/cli/src/scaffold/mod.rs @@ -161,6 +161,9 @@ pub fn scaffold_in_memory_iac( artifacts_path, ), ); + deployment_runbook_src.push_str(&get_interpolated_program_output_template( + &program_metadata.name, + )); } else { debug!( "Skipping program {} deployment in in-memory IaC since the .so file was not found", @@ -200,6 +203,57 @@ pub fn scaffold_in_memory_iac( Ok((runbook_id.into(), runbook_sources, manifest)) } +fn get_interpolated_program_output_template(program_name: &str) -> String { + format!( + r#" +output "{program_name}_program_id" {{ + description = "The program ID of the deployed {program_name} program" + value = action.deploy_{program_name}.program_id +}} +"# + ) +} + +#[allow(clippy::items_after_test_module)] +#[cfg(test)] +mod program_output_template_tests { + use super::*; + + #[test] + fn interpolated_program_output_for_one_program() { + let expected = r#" +output "hello_world_program_id" { + description = "The program ID of the deployed hello_world program" + value = action.deploy_hello_world.program_id +} +"#; + assert_eq!( + get_interpolated_program_output_template("hello_world").trim(), + expected.trim() + ); + } + + #[test] + fn interpolated_program_output_for_several_programs() { + let mut src = String::new(); + for name in ["counter", "vault"] { + src.push_str(&get_interpolated_program_output_template(name)); + } + let expected = r#" +output "counter_program_id" { + description = "The program ID of the deployed counter program" + value = action.deploy_counter.program_id +} + +output "vault_program_id" { + description = "The program ID of the deployed vault program" + value = action.deploy_vault.program_id +} +"#; + assert_eq!(src.trim(), expected.trim()); + } +} + pub fn scaffold_iac_layout( framework: &Framework, programs: &[ProgramMetadata], @@ -300,6 +354,9 @@ pub fn scaffold_iac_layout( deployment_runbook_src.push_str( &framework.get_interpolated_program_deployment_template(&program_metadata.name), ); + deployment_runbook_src.push_str(&get_interpolated_program_output_template( + &program_metadata.name, + )); } let runbook_name = "deployment"; From 681bd0069961e952e0a829665d6b56b64dc3239f Mon Sep 17 00:00:00 2001 From: Michael Moffett Date: Fri, 21 Aug 2026 15:01:54 -0600 Subject: [PATCH 2/2] test(scaffold): assert the program-output contract once, at end of file --- crates/cli/src/scaffold/mod.rs | 65 +++++++++++++--------------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/crates/cli/src/scaffold/mod.rs b/crates/cli/src/scaffold/mod.rs index bb9ec0737..824bd5250 100644 --- a/crates/cli/src/scaffold/mod.rs +++ b/crates/cli/src/scaffold/mod.rs @@ -214,46 +214,6 @@ output "{program_name}_program_id" {{ ) } -#[allow(clippy::items_after_test_module)] -#[cfg(test)] -mod program_output_template_tests { - use super::*; - - #[test] - fn interpolated_program_output_for_one_program() { - let expected = r#" -output "hello_world_program_id" { - description = "The program ID of the deployed hello_world program" - value = action.deploy_hello_world.program_id -} -"#; - assert_eq!( - get_interpolated_program_output_template("hello_world").trim(), - expected.trim() - ); - } - - #[test] - fn interpolated_program_output_for_several_programs() { - let mut src = String::new(); - for name in ["counter", "vault"] { - src.push_str(&get_interpolated_program_output_template(name)); - } - let expected = r#" -output "counter_program_id" { - description = "The program ID of the deployed counter program" - value = action.deploy_counter.program_id -} - -output "vault_program_id" { - description = "The program ID of the deployed vault program" - value = action.deploy_vault.program_id -} -"#; - assert_eq!(src.trim(), expected.trim()); - } -} - pub fn scaffold_iac_layout( framework: &Framework, programs: &[ProgramMetadata], @@ -571,3 +531,28 @@ pub fn scaffold_iac_layout( Ok(()) } + +#[cfg(test)] +mod program_output_template_tests { + use super::*; + + #[test] + fn every_program_gets_its_own_output_block_separated_by_a_blank_line() { + let mut src = String::new(); + for name in ["counter", "vault"] { + src.push_str(&get_interpolated_program_output_template(name)); + } + let expected = r#" +output "counter_program_id" { + description = "The program ID of the deployed counter program" + value = action.deploy_counter.program_id +} + +output "vault_program_id" { + description = "The program ID of the deployed vault program" + value = action.deploy_vault.program_id +} +"#; + assert_eq!(src.trim(), expected.trim()); + } +}