stacks: Make path.module and path.root relative, to match documentation (#37982)

* stacks: Make path.module and path.root relative, to match documentation

Previously, we were just handing off the absolute path produced by the
sourcebundle struct. But that's no good, because the value doesn't stay
consistent between plan and apply when running under tfc-agent. (It uses working
directories named after the unique job ID.) It also doesn't match the
documentation, which describes these as relative paths. This was preventing
people from uploading module-provided files to create aws lambda functions, for
example.

This commit addresses that by converting the source-bundle provided module path
to a relative path (relative to Terraform's working directory). In tfc-agent for
stacks runs, that ends up being the directory directly above the sourcebundle
directory, and all the paths below that are consistent between plan and apply.

* I think this is how the changelog thing works?
pull/38009/head
Nick Fagerlund 4 months ago committed by GitHub
parent f591872699
commit b2aad914f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
kind: BUG FIXES
body: 'stacks: change absolute paths in path.module/path.root to be relative, as documented'
time: 2025-12-09T23:00:00.316597+00:00
custom:
Issue: "37982"

@ -67,7 +67,31 @@ func (p *SourceBundleParser) LoadConfigDir(source sourceaddrs.FinalSource) (*Mod
})
return nil, diags
}
mod.SourceDir = sourceDir
// The result of sources.LocalPathForSource is an absolute path, but we
// don't actually want to pass an absolute path for a module's SourceDir;
// doing so will cause the value of `path.module` in Terraform configs to
// differ across plans and applies, since tfc-agent performs plans and
// applies in temporary directories. Instead, we try to resolve a relative
// path from Terraform's working directory, which should always be a
// reasonable SourceDir value.
workDir, err := os.Getwd()
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Cannot resolve working directory",
Detail: fmt.Sprintf("Failed to resolve current working directory: %s. This is a bug in Terraform - please report it.", err),
})
}
relativeSourceDir, err := filepath.Rel(workDir, sourceDir)
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Cannot resolve relative path",
Detail: fmt.Sprintf("Failed to resolve relative path to module directory: %s. This is a bug in Terraform - please report it.", err),
})
}
mod.SourceDir = relativeSourceDir
return mod, diags
}

@ -7,7 +7,6 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"time"
@ -852,17 +851,9 @@ func TestPlanning_PathValues(t *testing.T) {
t.Fatalf("failed to get current working directory: %s", err)
}
normalizePath := func(path string) string {
rel, err := filepath.Rel(cwd, path)
if err != nil {
t.Errorf("rel(%s,%s): %s", cwd, path, err)
return path
}
return rel
}
// path.cwd should be absolute and all others should be relative, as per documentation.
expected := map[string]string{
"cwd": ".",
"cwd": cwd,
"root": "testdata/sourcebundle/planning/path_values/module", // this is the root module of the component
"module": "testdata/sourcebundle/planning/path_values/module", // this is the root module
"child_root": "testdata/sourcebundle/planning/path_values/module", // should be the same for all modules
@ -870,11 +861,11 @@ func TestPlanning_PathValues(t *testing.T) {
}
actual := map[string]string{
"cwd": normalizePath(component.PlannedOutputValues[addrs.OutputValue{Name: "cwd"}].AsString()),
"root": normalizePath(component.PlannedOutputValues[addrs.OutputValue{Name: "root"}].AsString()),
"module": normalizePath(component.PlannedOutputValues[addrs.OutputValue{Name: "module"}].AsString()),
"child_root": normalizePath(component.PlannedOutputValues[addrs.OutputValue{Name: "child_root"}].AsString()),
"child_module": normalizePath(component.PlannedOutputValues[addrs.OutputValue{Name: "child_module"}].AsString()),
"cwd": component.PlannedOutputValues[addrs.OutputValue{Name: "cwd"}].AsString(),
"root": component.PlannedOutputValues[addrs.OutputValue{Name: "root"}].AsString(),
"module": component.PlannedOutputValues[addrs.OutputValue{Name: "module"}].AsString(),
"child_root": component.PlannedOutputValues[addrs.OutputValue{Name: "child_root"}].AsString(),
"child_module": component.PlannedOutputValues[addrs.OutputValue{Name: "child_module"}].AsString(),
}
if cmp.Diff(expected, actual) != "" {

Loading…
Cancel
Save