diff --git a/examples/module-phases-inheritance/calculator/togomak.hcl b/examples/module-phases-inheritance/calculator/togomak.hcl new file mode 100644 index 0000000..95c9add --- /dev/null +++ b/examples/module-phases-inheritance/calculator/togomak.hcl @@ -0,0 +1,30 @@ +togomak { + version = 2 +} + +variable "a" { + type = number + description = "first variable" +} +variable "b" { + type = number + description = "second variable" +} + +variable "operation" { + type = string + description = "Operation to perform, any of: [add, subtract, multiply, divide]" +} + +stage "add" { + if = var.operation == "add" + script = "echo ${var.a} + ${var.b} is ${var.a + var.b}" +} + +stage "paths" { + script = <<-EOT + echo path.module: ${path.module} + echo path.cwd: ${path.cwd} + echo path.root: ${path.root} + EOT +} diff --git a/examples/module-phases-inheritance/togomak.hcl b/examples/module-phases-inheritance/togomak.hcl new file mode 100644 index 0000000..99f08b5 --- /dev/null +++ b/examples/module-phases-inheritance/togomak.hcl @@ -0,0 +1,28 @@ + togomak { + version = 2 +} + +locals { + a = 99 + b = 1 +} + +stage "paths" { + script = <<-EOT + echo path.module: ${path.module} + echo path.cwd: ${path.cwd} + echo path.root: ${path.root} + EOT +} + +module "add" { + source = "./calculator" + a = local.a + b = local.b + operation = "add" + + lifecycle { + phase = ["add"] + } +} + diff --git a/internal/behavior/models.go b/internal/behavior/models.go index 3649166..7bc6151 100644 --- a/internal/behavior/models.go +++ b/internal/behavior/models.go @@ -7,6 +7,8 @@ type Child struct { // Parent is the flag to indicate whether the program is running in parent mode Parent string + ParentLifecycles []string + // ParentParams is the list of parameters to be passed to the parent ParentParams []string } diff --git a/internal/ci/module_run.go b/internal/ci/module_run.go index 29ecffd..ba6c7f1 100644 --- a/internal/ci/module_run.go +++ b/internal/ci/module_run.go @@ -163,13 +163,28 @@ func (m *Module) run(conductor *Conductor, source string, evalCtx *hcl.EvalConte }) } + var parentLifecycles []string + if cfg.Behavior.Child.ParentLifecycles != nil { + parentLifecycles = cfg.Behavior.Child.ParentLifecycles + } + if m.Lifecycle != nil { + conductor.Eval().Mutex().RLock() + lifecyclePhases, d := m.Lifecycle.Phase.Value(evalCtx) + conductor.Eval().Mutex().RUnlock() + diags = diags.Extend(d) + for _, phase := range lifecyclePhases.AsValueSlice() { + parentLifecycles = append(parentLifecycles, phase.AsString()) + } + } + b := &behavior.Behavior{ Unattended: conductor.Config.Behavior.Unattended, Ci: conductor.Config.Behavior.Ci, Child: behavior.Child{ - Enabled: true, - Parent: "", - ParentParams: nil, + Enabled: true, + Parent: "", + ParentParams: nil, + ParentLifecycles: parentLifecycles, }, DryRun: false, } @@ -194,8 +209,7 @@ func (m *Module) run(conductor *Conductor, source string, evalCtx *hcl.EvalConte conductorOptions = append(conductorOptions, ConductorWithParser(conductor.Parser)) // populate input variables for the child conductor, which would be passed to the module - attrs, d := m.Body.JustAttributes() - diags = diags.Extend(d) + attrs, _ := m.Body.JustAttributes() for _, attr := range attrs { //we need to evaluate the values first within the parent's evaluation context //before sending it to the child goroutine and child conductor diff --git a/internal/ci/run.go b/internal/ci/run.go index 6f0c8fe..9ee8aa1 100644 --- a/internal/ci/run.go +++ b/internal/ci/run.go @@ -112,10 +112,16 @@ func BlockCanRun(runnable Block, conductor *Conductor, runnableId string, depGra ok = false overridden = false - // if the list is empty, we will assume that the runnable is not overridden + // if the list is empty, we will assume that the runnable is not overridden, // and we will run all module blocks. This is so that the child processoe var phases []cty.Value - var phasesDefined bool + + // if the parent config has lifecycles defined, we will append it to the child + for _, phase := range conductor.Config.Behavior.Child.ParentLifecycles { + phases = append(phases, cty.StringVal(phase)) + } + + var phasesDefined bool = len(phases) > 0 stage := runnable.(PhasedBlock) if stage.LifecycleConfig() != nil { evalContext := conductor.Eval().Context() @@ -126,8 +132,8 @@ func BlockCanRun(runnable Block, conductor *Conductor, runnableId string, depGra diags = diags.Extend(d) return false, false, diags } - phasesDefined = !phaseHcl.IsNull() - phases = phaseHcl.AsValueSlice() + phasesDefined = !phaseHcl.IsNull() || len(phases) > 0 + phases = append(phases, phaseHcl.AsValueSlice()...) } if runnable.Type() == blocks.ModuleBlock && len(phases) == 0 && !phasesDefined { diff --git a/tests/togomak.hcl b/tests/togomak.hcl index 0a944d4..fddc97b 100644 --- a/tests/togomak.hcl +++ b/tests/togomak.hcl @@ -49,6 +49,36 @@ stage "tests" { } } +stage "module_phase_test" { + pre_hook { + stage { + script = "echo ${ansi.fg.green}${each.key}${ansi.reset}: full" + } + } + + for_each = toset([ + "../examples/module-phases-inheritance/togomak.hcl", + ]) + + depends_on = [stage.build, stage.coverage_prepare] + args = [ + "./togomak_coverage", + "-C", dirname(each.key), + "--ci", "-v", "-v", "-v", + "add" + ] + + env { + name = "GOCOVERDIR" + value = local.coverage_data_dir + } + env { + name = "TOGOMAK_VAR_name" + value = "bot" + } + +} + stage "tests_dry_run" { pre_hook { @@ -82,7 +112,7 @@ stage "fmt" { } stage "cache" { - depends_on = [stage.fmt, stage.tests, stage.tests_dry_run] + depends_on = [stage.fmt, stage.tests, stage.tests_dry_run, stage.module_phase_test] script = "./togomak_coverage cache clean --recursive" }