Skip to content

Commit

Permalink
fix: change detection of Terragrunt module source.
Browse files Browse the repository at this point in the history
Signed-off-by: i4k <[email protected]>
  • Loading branch information
i4ki committed Nov 27, 2024
1 parent e469ee4 commit 355ee9a
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Given a version number `MAJOR.MINOR.PATCH`, we increment the:

## Unreleased

### Fixed

- Fix Terragrunt modules change detection.

## v0.11.2

### Added
Expand Down
60 changes: 60 additions & 0 deletions stack/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,17 @@ func TestListChangedStacks(t *testing.T) {
changed: []string{"/tg-stack"},
},
},
{
name: "single Terragrunt stack with single local Terraform module changed referenced with abspath",
repobuilder: func(t *testing.T) repository {
t.Helper()
return singleTerragruntStackWithSingleTerraformModuleChangedFromAbsPathSourceRepo(t, "force")
},
want: listTestResult{
list: []string{"/tg-stack"},
changed: []string{"/tg-stack"},
},
},
{
name: "Terragrunt stack changed due to referenced file changed",
repobuilder: terragruntStackChangedDueToReferencedFileChangedRepo,
Expand Down Expand Up @@ -765,6 +776,55 @@ func singleTerragruntStackWithSingleTerraformModuleChangedRepo(t *testing.T, ena
return repo
}

func singleTerragruntStackWithSingleTerraformModuleChangedFromAbsPathSourceRepo(t *testing.T, enabledOption string) repository {
repo := singleMergeCommitRepoNoStack(t)
test.WriteFile(t, repo.Dir, "terramate.tm.hcl", Doc(
Block("terramate",
Block("config",
Block("change_detection",
Block("terragrunt",
Str("enabled", enabledOption),
),
),
),
),
).String())
modules := test.Mkdir(t, repo.Dir, "modules")
module1 := test.Mkdir(t, modules, "module1")

repo.modules = append(repo.modules, module1)

stack := test.Mkdir(t, repo.Dir, "tg-stack")
root, err := config.LoadRoot(repo.Dir)
assert.NoError(t, err)
createStack(t, root, stack)

g := test.NewGitWrapper(t, repo.Dir, []string{})
assert.NoError(t, g.Checkout("testbranch", true), "create branch failed")

test.WriteFile(t, stack, "terragrunt.hcl", Doc(
Block("terraform",
Str("source", "${get_repo_root()}/modules/module1"),
),
).String())

test.WriteFile(t, module1, "main.tf", `# empty file`)

assert.NoError(t, g.Add(repo.Dir), "add files")
assert.NoError(t, g.Commit("files"), "commit files")

addMergeCommit(t, repo.Dir, "testbranch")
assert.NoError(t, g.DeleteBranch("testbranch"), "delete testbranch")

// now we branch again and modify the module
assert.NoError(t, g.Checkout("testbranch2", true), "create branch testbranch2 failed")

test.WriteFile(t, module1, "main.tf", `# changed file`)
assert.NoError(t, g.Add(module1), "add files")
assert.NoError(t, g.Commit("module changed"), "commit files")
return repo
}

func terragruntStackChangedDueToDependencyChangedRepo(t *testing.T) repository {
repo := singleMergeCommitRepoNoStack(t)
stack := test.Mkdir(t, repo.Dir, "tg-stack")
Expand Down
6 changes: 5 additions & 1 deletion test/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package test

import (
"path/filepath"
"testing"

"github.com/madlambda/spells/assert"
Expand Down Expand Up @@ -48,7 +49,10 @@ func EmptyRepo(t testing.TB, bare bool) string {
gw := NewGitWrapper(t, "", []string{})

repodir := TempDir(t)
err := gw.Init(repodir, DefBranch, bare)
// resolve symlinks
repodir, err := filepath.EvalSymlinks(repodir)
assert.NoError(t, err, "evaluating symlinks")
err = gw.Init(repodir, DefBranch, bare)
assert.NoError(t, err, "git init")

return repodir
Expand Down
17 changes: 17 additions & 0 deletions tg/tg_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,23 @@ func ScanModules(rootdir string, dir project.Path, trackDependencies bool) (Modu
tgMod := stack.Modules[0]
mod.Source = *tgConfig.Terraform.Source
dependsOn := map[project.Path]struct{}{}
st, err := os.Lstat(mod.Source)
if err == nil && st.IsDir() {
// if the source is a directory, we assume it is a local module.
if filepath.IsAbs(mod.Source) {
src, err := filepath.EvalSymlinks(mod.Source)
if err != nil {
return nil, errors.E(err, "evaluating symlinks in %q", mod.Source)
}
// we normalize local paths as relative to the module.
// so this is compatible with Terraform module sources.
rel, err := filepath.Rel(cfgOpts.WorkingDir, src)
if err != nil {
return nil, errors.E(err, "normalizing local path %q", mod.Source)
}
mod.Source = rel
}
}
for _, path := range mod.DependsOn {
dependsOn[path] = struct{}{}
}
Expand Down
66 changes: 65 additions & 1 deletion tg/tg_module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,10 +450,74 @@ func TestTerragruntScanModules(t *testing.T) {
},
},
},
{
name: "local module directory also tracked as dependency",
layout: []string{
`f:some/dir/terragrunt.hcl:` + Doc(
Block("terraform",
Str("source", "${get_repo_root()}/modules/some"),
),
Block("include",
Labels("root"),
Expr("path", `find_in_parent_folders()`),
),
Block("dependency",
Labels("other2"), // other2 declared before other: result must be sorted.
Str("config_path", "../other2/dir"),
),
Block("dependency",
Labels("other"),
Str("config_path", "../other/dir"),
),
).String(),
`f:modules/some/main.tf:# empty file`,
`f:some/other/dir/terragrunt.hcl:` + Doc(
Bool("skip", true),
Block("terraform",
Str("source", "https://some.etc/prj"),
)).String(),
`f:some/other2/dir/terragrunt.hcl:` + Block("terraform",
Str("source", "https://some.etc/prj"),
).String(),
`f:terragrunt.hcl:` + Doc(
Block("terraform"),
).String(),
`f:common.tfvars:a = "1"`,
`f:regional.tfvars:b = "2"`,
},
want: want{
modules: tg.Modules{
{
Path: project.NewPath("/some/dir"),
Source: "../../modules/some",
ConfigFile: project.NewPath("/some/dir/terragrunt.hcl"),
DependsOn: project.Paths{
project.NewPath("/some/other/dir"),
project.NewPath("/some/other2/dir"),
project.NewPath("/terragrunt.hcl"),
},
After: project.Paths{
project.NewPath("/some/other/dir"),
project.NewPath("/some/other2/dir"),
},
},
{
Path: project.NewPath("/some/other/dir"),
Source: "https://some.etc/prj",
ConfigFile: project.NewPath("/some/other/dir/terragrunt.hcl"),
},
{
Path: project.NewPath("/some/other2/dir"),
Source: "https://some.etc/prj",
ConfigFile: project.NewPath("/some/other2/dir/terragrunt.hcl"),
},
},
},
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
s := sandbox.NoGit(t, false)
s := sandbox.New(t)
s.BuildTree(tc.layout)
basedir := tc.basedir
if basedir.String() == "" {
Expand Down

0 comments on commit 355ee9a

Please sign in to comment.