Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .trunk/configs/.hadolint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Following source doesn't work in most setups
ignored:
- SC1090
- SC1091
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was generated by trunk when I ran trunk upgrade

2 changes: 2 additions & 0 deletions aqua.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ packages:
tags: [terraform]
- name: opentofu/opentofu@v1.9.1
tags: [tofu]
- name: trunk-io/launcher
version: 1.0.0
42 changes: 41 additions & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,46 @@ locals {

_root_module_yaml_decoded = merge(local._multi_instance_root_module_yaml_decoded, local._single_instance_root_module_yaml_decoded)

## Stack Name Template Resolution
# Determine the default template based on structure type
# MultiInstance: "${workspace}-${module_path}" (e.g., "dev-network")
# SingleInstance: "${module_name}" (e.g., "rds-cluster")
default_stack_name_template = local._multi_instance_structure ? "$${workspace}-$${module_path}" : "$${module_name}"

# Use provided template or fall back to default
stack_name_template = coalesce(var.stack_name_template, local.default_stack_name_template)

# Generate stack names using the template
# Iterates over all modules and their stack files, applying the template with available parameters
stack_names = {
for module, files in local._root_module_yaml_decoded :
module => {
for file, content in files :
file => (
file == var.common_config_file ? null :
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need this check if you have the if file != var.common_config_file on the for?

templatestring(
local.stack_name_template,
{
module_name = basename(module)
workspace = trimsuffix(file, ".yaml")
module_path = module
}
)
) if file != var.common_config_file
}
}

# Clean up stack names to handle edge cases
# Removes double hyphens, leading/trailing hyphens, and applies trimspace
# This handles cases where template parameters may be empty (e.g., SingleInstance with ${workspace})
cleaned_stack_names = {
for module, names in local.stack_names :
module => {
for file, name in names :
file => name != null ? trimspace(replace(replace(replace(name, "--", "-"), "/^-/", ""), "/-$/", "")) : null
}
}

## Common Stack configurations
# Retrieve common Stack configurations for each root module.
# SingleInstance root_module_structure does not support common configs today.
Expand Down Expand Up @@ -155,7 +195,7 @@ locals {
# }
_root_module_stack_configs = merge([for module, files in local._root_module_yaml_decoded : {
for file, content in files :
local._multi_instance_structure ? "${module}-${trimsuffix(file, ".yaml")}" : module =>
local.cleaned_stack_names[module][file] =>
merge(
{
# Use specified project_root, if not, build it using the root_modules_path and module name
Expand Down
5 changes: 3 additions & 2 deletions tests/main.tftest.hcl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
variables {
root_modules_path = "./tests/fixtures/multi-instance"
common_config_file = "common.yaml"
root_modules_path = "./tests/fixtures/multi-instance"
stack_name_template = "$${module_path}-$${workspace}"
common_config_file = "common.yaml"
github_enterprise = {
namespace = "masterpointio"
}
Expand Down
7 changes: 4 additions & 3 deletions tests/nested-directories.tftest.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
variables {
repository = "terraform-spacelift-automation"
repository = "terraform-spacelift-automation"
stack_name_template = "$${module_path}-$${workspace}"
github_enterprise = {
namespace = "masterpointio"
}
Expand Down Expand Up @@ -55,7 +56,7 @@ run "test_nested_directory_multi_instance_support" {
}

assert {
condition = spacelift_stack.default["parent/nested-prod"].branch == "main"
condition = spacelift_stack.default["parent/nested-prod"].branch == "main"
error_message = "Nested directory prod stack should have main branch: ${spacelift_stack.default["parent/nested-prod"].branch}"
}
}
Expand Down Expand Up @@ -185,4 +186,4 @@ run "test_deeply_nested_directory_support" {
condition = contains(spacelift_stack.default["nested-multi-instance/parent/nested-dev"].labels, "folder:nested-multi-instance/parent/nested/dev")
error_message = "Deeply nested folder label incorrect: ${jsonencode(spacelift_stack.default["nested-multi-instance/parent/nested-dev"].labels)}"
}
}
}
13 changes: 7 additions & 6 deletions tests/resource-id-resolver.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ mock_provider "jsonschema" {
}

variables {
root_modules_path = "./tests/fixtures/multi-instance"
common_config_file = "common.yaml"
stack_name_template = "$${module_path}-$${workspace}"
root_modules_path = "./tests/fixtures/multi-instance"
common_config_file = "common.yaml"
github_enterprise = {
namespace = "masterpointio"
}
Expand Down Expand Up @@ -108,8 +109,8 @@ run "test_global_space_id_variable_is_used" {
command = plan

variables {
space_id = "global-space-id-from-variable"
root_modules_path = "./tests/fixtures/single-instance"
space_id = "global-space-id-from-variable"
root_modules_path = "./tests/fixtures/single-instance"
root_module_structure = "SingleInstance"
}

Expand All @@ -124,7 +125,7 @@ run "test_default_space_id_is_used_when_no_values_provided" {
command = plan

variables {
root_modules_path = "./tests/fixtures/single-instance"
root_modules_path = "./tests/fixtures/single-instance"
root_module_structure = "SingleInstance"
}

Expand All @@ -139,7 +140,7 @@ run "test_single_instance_space_id_from_stack_yaml" {
command = plan

variables {
root_modules_path = "./tests/fixtures/single-instance"
root_modules_path = "./tests/fixtures/single-instance"
root_module_structure = "SingleInstance"
}

Expand Down
3 changes: 2 additions & 1 deletion tests/single-instance.tftest.hcl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

variables {
root_modules_path = "./tests/fixtures/single-instance"
stack_name_template = "$${module_path}-$${workspace}"
root_modules_path = "./tests/fixtures/single-instance"
github_enterprise = {
namespace = "masterpointio"
}
Expand Down
7 changes: 4 additions & 3 deletions tests/vcs_integrations.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mock_provider "jsonschema" {
}

variables {
stack_name_template = "$${module_path}-$${workspace}"
root_modules_path = "./tests/fixtures/multi-instance"
common_config_file = "common.yaml"
repository = "terraform-spacelift-automation"
Expand Down Expand Up @@ -149,9 +150,9 @@ run "test_vcs_blocks_empty_when_null" {

variables {
github_enterprise = null
raw_git = null
gitlab = null
bitbucket_cloud = null
raw_git = null
gitlab = null
bitbucket_cloud = null
bitbucket_datacenter = null
}

Expand Down
19 changes: 19 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,25 @@ variable "worker_pool_name" {
default = null
}

variable "stack_name_template" {
type = string
description = <<-EOT
Template for generating stack names. Supports the following parameters:
- $${module_name}: The root module directory name (e.g., 'network', 'delegated-tf')
- $${workspace}: The workspace/environment name from YAML filename (e.g., 'dev', 'prod')
- $${module_path}: The root module path for S3 backend workspace isolation (e.g., 'aws-iam/delegated-tf')
Examples:
- "$${workspace}-$${module_path}" (default for MultiInstance)
- "$${module_name}" (default for SingleInstance)
EOT
default = null

validation {
condition = var.stack_name_template == null || can(regex("\\$\\{(module_name|workspace|module_path)\\}", var.stack_name_template))
error_message = "stack_name_template must contain at least one of: $${module_name}, $${workspace}, or $${module_path}"
}
}

variable "spaces" {
description = "A map of Spacelift Spaces to create"
type = map(object({
Expand Down
Loading