Resolve local paths from playbook base

This commit is contained in:
Virgil 2026-04-01 22:03:43 +00:00
parent 3a4118ada8
commit d969cc9205
4 changed files with 55 additions and 0 deletions

View file

@ -1258,6 +1258,16 @@ func (e *Executor) getClient(host string, play *Play) (sshExecutorClient, error)
return client, nil
}
// resolveLocalPath resolves a local file path relative to the executor's base
// path when possible.
func (e *Executor) resolveLocalPath(path string) string {
if path == "" || e == nil || e.parser == nil {
return path
}
return e.parser.resolvePath(path)
}
// gatherFacts collects facts from a host.
func (e *Executor) gatherFacts(ctx context.Context, host string, play *Play) error {
client, err := e.getClient(host, play)

View file

@ -339,6 +339,7 @@ func (e *Executor) moduleScript(ctx context.Context, client sshExecutorClient, a
}
// Read local script
script = e.resolveLocalPath(script)
data, err := coreio.Local.Read(script)
if err != nil {
return nil, coreerr.E("Executor.moduleScript", "read script", err)
@ -370,6 +371,7 @@ func (e *Executor) moduleCopy(ctx context.Context, client sshExecutorClient, arg
var err error
if src := getStringArg(args, "src", ""); src != "" {
src = e.resolveLocalPath(src)
content, err = coreio.Local.Read(src)
if err != nil {
return nil, coreerr.E("Executor.moduleCopy", "read src", err)
@ -424,6 +426,7 @@ func (e *Executor) moduleTemplate(ctx context.Context, client sshExecutorClient,
}
// Process template
src = e.resolveLocalPath(src)
content, err := e.TemplateFile(src, host, task)
if err != nil {
return nil, coreerr.E("Executor.moduleTemplate", "template", err)
@ -1576,6 +1579,7 @@ func (e *Executor) moduleUnarchive(ctx context.Context, client sshExecutorClient
var cmd string
if !remote {
// Upload local file first
src = e.resolveLocalPath(src)
data, err := coreio.Local.Read(src)
if err != nil {
return nil, coreerr.E("Executor.moduleUnarchive", "read src", err)
@ -1984,6 +1988,7 @@ func (e *Executor) moduleIncludeVars(args map[string]any) (*TaskResult, error) {
}
if file != "" {
file = e.resolveLocalPath(file)
sources = append(sources, file)
if err := loadFile(file); err != nil {
return nil, err
@ -1991,6 +1996,7 @@ func (e *Executor) moduleIncludeVars(args map[string]any) (*TaskResult, error) {
}
if dir != "" {
dir = e.resolveLocalPath(dir)
entries, err := os.ReadDir(dir)
if err != nil {
return nil, coreerr.E("Executor.moduleIncludeVars", "read vars dir", err)

View file

@ -858,6 +858,26 @@ func TestModulesAdv_ModuleIncludeVars_Good_LoadDirectoryWithMerge(t *testing.T)
assert.Equal(t, 2, nested["b"])
}
func TestModulesAdv_ModuleIncludeVars_Good_ResolvesRelativePathsAgainstBasePath(t *testing.T) {
dir := t.TempDir()
require.NoError(t, writeTestFile(joinPath(dir, "vars.yml"), []byte("app_name: demo\n"), 0644))
require.NoError(t, writeTestFile(joinPath(dir, "vars", "01-extra.yaml"), []byte("app_port: 8080\n"), 0644))
e := NewExecutor(dir)
result, err := e.moduleIncludeVars(map[string]any{
"file": "vars.yml",
"dir": "vars",
})
require.NoError(t, err)
assert.True(t, result.Changed)
assert.Contains(t, result.Msg, "vars.yml")
assert.Contains(t, result.Msg, joinPath(dir, "vars", "01-extra.yaml"))
assert.Equal(t, "demo", e.vars["app_name"])
assert.Equal(t, 8080, e.vars["app_port"])
}
// --- sysctl module ---
func TestModulesAdv_ModuleSysctl_Good_ReloadsAfterPersisting(t *testing.T) {

View file

@ -127,6 +127,25 @@ func TestModulesCmd_MockSSHClient_Good_BecomeTracking(t *testing.T) {
assert.Equal(t, "secret", mock.becomePass)
}
func TestModulesCmd_ModuleScript_Good_RelativePathResolvedAgainstBasePath(t *testing.T) {
dir := t.TempDir()
scriptPath := joinPath(dir, "scripts", "deploy.sh")
require.NoError(t, writeTestFile(scriptPath, []byte("echo deploy"), 0755))
e := NewExecutor(dir)
mock := NewMockSSHClient()
mock.expectCommand("echo deploy", "deploy\n", "", 0)
result, err := e.moduleScript(context.Background(), mock, map[string]any{
"_raw_params": "scripts/deploy.sh",
})
require.NoError(t, err)
assert.True(t, result.Changed)
assert.Equal(t, "deploy\n", result.Stdout)
assert.True(t, mock.hasExecuted("echo deploy"))
}
func TestModulesCmd_MockSSHClient_Good_HasExecuted(t *testing.T) {
mock := NewMockSSHClient()
_, _, _, _ = mock.Run(nil, "systemctl restart nginx")