Enhanced the argument handling system to support stage-specific CLI arguments for Terraform tasks. This allows providing different arguments for different Terraform stages (init, plan, apply) which is essential for complex Terraform workflows.
Unified to use map-based arguments with "default" key for backward compatibility:
type LocalAppRunningArgs struct {
CliArgs map[string][]string // Stage-specific args (e.g., "init", "apply", "default")
EnvironmentVars []string
Inputs map[string]string
TaskParams any
TemplateParams any
Callback func(*os.Process)
}Key Change: Array format arguments are automatically converted to map format with key "default".
Added convertArgsJSONIfArray() and getCLIArgsMap() functions that:
convertArgsJSONIfArray(): Checks JSON format and converts array format to map with "default" key in-placegetCLIArgsMap(): Parses arguments as map format (after conversion)- Array format is automatically converted to map format at runtime
- Supports both Template and Task level arguments
- Ensures consistent map-based interface throughout the system
Updated getTerraformArgs() to:
- Return map format only (unified interface)
- Merge template and task arguments at the stage level
- Apply common args (destroy, vars, secrets) to all stages
- Ensure at least "default" stage exists with common args
Modified Terraform execution to:
- Accept stage-specific init args during installation
- Use different args for plan and apply stages
- Fall back to "default" key when specific stage not defined
- New method
InstallRequirementsWithInitArgs()for init customization
Enhanced Run() method to:
- Get args before prepareRun for Terraform apps
- Pass init-specific args during installation
- Provide plan/apply-specific args during execution
- Convert all args to unified map format with "default" key for non-Terraform apps
Array format arguments are automatically converted to map with "default" key:
{
"arguments": ["-var", "environment=production"]
}Internally converted to:
{
"arguments": {
"default": ["-var", "environment=production"]
}
}Stage-specific arguments for different Terraform operations:
{
"arguments": {
"init": ["-upgrade"],
"plan": ["-var", "foo=bar"],
"apply": ["-var", "foo=baz"]
}
}Template with stage-specific configurations:
{
"template": {
"arguments": {
"init": ["-backend-config=bucket=my-bucket"],
"plan": ["-out=tfplan"],
"apply": ["tfplan"]
}
}
}Task override combining with template args:
{
"task": {
"arguments": {
"init": ["-reconfigure"],
"apply": ["-auto-approve"]
}
}
}Result: Arguments are merged per stage
- init:
-backend-config=bucket=my-bucket,-reconfigure - plan:
-out=tfplan - apply:
tfplan,-auto-approve
✅ 100% Backward Compatible
- Existing array format continues to work
- No changes required to existing templates/tasks
- Array format arguments are used for all stages when no map is provided
- Gradual migration path available
- Parse Phase: Arguments parsed as array or map from JSON
- Merge Phase: Template and task args merged at stage level
- Common Args: Environment vars, secrets, and destroy flag added to all stages
- Execution Phase: Appropriate args used for each stage (init, plan, apply)
getCLIArgsMap(): Parses both formats from JSONgetTerraformArgs(): Builds stage-specific argument mapsprepareRunTerraform(): Passes init args to Terraform installationTerraformApp.Run(): Uses plan/apply-specific args during execution
- init: Used during
terraform init(via InstallRequirements) - plan: Used during
terraform plan - apply: Used during
terraform apply - default: Used as fallback when specific stage not defined
For each stage, arguments are resolved in this order:
- Stage-specific key (e.g., "init", "plan", "apply")
- Fall back to "default" key if stage-specific not found
- Empty array if neither exists
Array Format → Map Conversion:
- Runtime Conversion: Array
["-var", "foo=bar"]is converted to{"default": ["-var", "foo=bar"]}in-place when the task runs - No Database Changes: Original JSON remains stored as array, conversion happens only during task execution
- Transparent: Users don't see the conversion, it happens automatically
- Ansible and Shell apps: Always use "default" key
- Terraform apps: Use stage-specific keys, fall back to "default"
The implementation has been validated with:
- Successful build of entire project
- No linter errors
- Backward compatibility verified
- Both array and map formats tested
- Flexibility: Different args for different Terraform stages
- Security: Keep sensitive args only in specific stages
- Efficiency: Optimize each stage independently
- Clarity: Clear separation of stage-specific configurations
- Compatibility: Works alongside existing array format
{"arguments": ["-var", "foo=bar"]}Result: Automatically converted to {"default": ["-var", "foo=bar"]} internally
{"arguments": {"init": ["-upgrade"], "apply": ["-var", "foo=bar"]}}Result: Uses stage-specific args for init and apply
{
"arguments": {
"default": ["-var", "common=value"],
"init": ["-upgrade"],
"apply": ["-parallelism=20"]
}
}Result: plan stage uses "default" args, init and apply use their specific args
{
"arguments": {
"init": ["-backend-config=..."],
"plan": ["-out=tfplan", "-var-file=prod.tfvars"],
"apply": ["tfplan", "-parallelism=20"]
}
}Result: Complete control over each stage independently