From 203aabb0a2cbafecfd68aaf56390774d66e4ed24 Mon Sep 17 00:00:00 2001 From: Orien Madgwick <497874+orien@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:34:16 +0700 Subject: [PATCH] Consistently treat default parameter values from the CloudFormation template as strings --- CHANGELOG.md | 5 +++ lib/stack_master/stack.rb | 2 +- spec/stack_master/stack_spec.rb | 63 +++++++++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 219a3d9d..22315ca4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,12 @@ The format is based on [Keep a Changelog], and this project adheres to ## [Unreleased] +### Changed + +- Always treat default parameter values from CloudFormation templates as strings. Avoids erroneous diffs being presented. ([#394]) + [Unreleased]: https://github.com/envato/stack_master/compare/v2.17.0...HEAD +[#394]: https://github.com/envato/stack_master/pull/394 ## [2.17.0] - 2025-07-11 diff --git a/lib/stack_master/stack.rb b/lib/stack_master/stack.rb index 7977ce03..5922bcd3 100644 --- a/lib/stack_master/stack.rb +++ b/lib/stack_master/stack.rb @@ -18,7 +18,7 @@ class Stack def template_default_parameters TemplateUtils.template_hash(template).fetch('Parameters', {}).inject({}) do |result, (parameter_name, description)| - result[parameter_name] = description['Default'] + result[parameter_name] = description['Default']&.to_s result end end diff --git a/spec/stack_master/stack_spec.rb b/spec/stack_master/stack_spec.rb index d3a799a7..5a147db4 100644 --- a/spec/stack_master/stack_spec.rb +++ b/spec/stack_master/stack_spec.rb @@ -95,7 +95,23 @@ let(:parameter_hash) { {template_parameters: {'DbPassword' => {'secret' => 'db_password'}}, compile_time_parameters: {}} } let(:resolved_compile_time_parameters) { {} } let(:template_file_name) { 'template.rb' } - let(:template_body) { '{"Parameters": { "VpcId": { "Description": "VPC ID" }, "InstanceType": { "Description": "Instance Type", "Default": "t2.micro" }} }' } + let(:template_body) { <<~JSON } + { + "Parameters": { + "VpcId": { + "Description": "VPC ID" + }, + "InstanceType": { + "Description": "Instance Type", + "Default": "t2.micro" + }, + "UltimateAnswer": { + "Description": "Life, The Universe, and Everything", + "Default": 42 + } + } + } + JSON let(:template_format) { :json } let(:stack_policy_body) { '{}' } @@ -146,11 +162,20 @@ end it 'extracts default template parameters' do - expect(stack.template_default_parameters).to eq('VpcId' => nil, 'InstanceType' => 't2.micro') + expect(stack.template_default_parameters).to include('VpcId' => nil, 'InstanceType' => 't2.micro') + end + + it 'treats parameter defaults as strings (as the CFN schema expects them to be)' do + expect(stack.template_default_parameters).to include('UltimateAnswer' => '42') end specify 'parameters_with_defaults does not resolve parameters (only defaults)' do - expect(stack.parameters_with_defaults).to eq('InstanceType' => 't2.micro', 'VpcId' => nil) + expect(stack.parameters_with_defaults) + .to eq( + 'VpcId' => nil, + 'InstanceType' => 't2.micro', + 'UltimateAnswer' => '42' + ) end end @@ -163,7 +188,23 @@ let(:resolved_template_parameters) { {'DbPassword' => 'sdfgjkdhlfjkghdflkjghdflkjg', 'InstanceType' => 't2.medium'} } let(:resolved_compile_time_parameters) { {} } let(:template_file_name) { 'template.rb' } - let(:template_body) { '{"Parameters": { "VpcId": { "Description": "VPC ID" }, "InstanceType": { "Description": "Instance Type", "Default": "t2.micro" }} }' } + let(:template_body) { <<~JSON } + { + "Parameters": { + "VpcId": { + "Description": "VPC ID" + }, + "InstanceType": { + "Description": "Instance Type", + "Default": "t2.micro" + }, + "UltimateAnswer": { + "Description": "Life, The Universe, and Everything", + "Default": 42 + } + } + } + JSON let(:template_format) { :json } let(:stack_policy_body) { '{}' } @@ -215,11 +256,21 @@ end it 'extracts default template parameters' do - expect(stack.template_default_parameters).to eq('VpcId' => nil, 'InstanceType' => 't2.micro') + expect(stack.template_default_parameters).to include('VpcId' => nil, 'InstanceType' => 't2.micro') + end + + it 'treats parameter defaults as strings (as the CFN schema expects them to be)' do + expect(stack.template_default_parameters).to include('UltimateAnswer' => '42') end it 'exposes parameters with defaults taken into account' do - expect(stack.parameters_with_defaults).to eq('DbPassword' => 'sdfgjkdhlfjkghdflkjghdflkjg', 'InstanceType' => 't2.medium', 'VpcId' => nil) + expect(stack.parameters_with_defaults) + .to eq( + 'DbPassword' => 'sdfgjkdhlfjkghdflkjghdflkjg', + 'InstanceType' => 't2.medium', + 'VpcId' => nil, + 'UltimateAnswer' => '42' + ) end end