feat(ansible): support public role vars

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 01:29:42 +00:00
parent ac8f7a36b5
commit 7f7cc55479
6 changed files with 74 additions and 2 deletions

View file

@ -481,7 +481,9 @@ func (e *Executor) runRole(ctx context.Context, hosts []string, roleRef *RoleRef
}
// Restore vars
e.vars = oldVars
if roleRef == nil || !roleRef.Public {
e.vars = oldVars
}
return nil
}
@ -1703,6 +1705,7 @@ func (e *Executor) resolveIncludeRoleRef(host string, task *Task) *RoleRef {
VarsFrom: e.templateString(varsFrom, host, task),
Vars: renderedVars,
Apply: apply,
Public: task.IncludeRole != nil && task.IncludeRole.Public || task.ImportRole != nil && task.ImportRole.Public,
When: task.When,
Tags: task.Tags,
}

View file

@ -786,6 +786,7 @@ func TestExecutorExtra_RunIncludeRole_Good_InheritsTaskVars(t *testing.T) {
VarsFrom string `yaml:"vars_from,omitempty"`
Vars map[string]any `yaml:"vars,omitempty"`
Apply *TaskApply `yaml:"apply,omitempty"`
Public bool `yaml:"public,omitempty"`
}{
Name: "demo",
},
@ -839,6 +840,7 @@ func TestExecutorExtra_RunIncludeRole_Good_AppliesRoleDefaults(t *testing.T) {
VarsFrom string `yaml:"vars_from,omitempty"`
Vars map[string]any `yaml:"vars,omitempty"`
Apply *TaskApply `yaml:"apply,omitempty"`
Public bool `yaml:"public,omitempty"`
}{
Name: "app",
Apply: &TaskApply{
@ -863,6 +865,65 @@ func TestExecutorExtra_RunIncludeRole_Good_AppliesRoleDefaults(t *testing.T) {
assert.Equal(t, "production|from-apply|from-task", e.results["localhost"]["role_result"].Stdout)
}
func TestExecutorExtra_RunIncludeRole_Good_PublicVarsPersist(t *testing.T) {
dir := t.TempDir()
require.NoError(t, writeTestFile(joinPath(dir, "roles", "shared", "tasks", "main.yml"), []byte(`---
- name: Shared role task
debug:
msg: "{{ shared_message }}"
register: shared_role_result
`), 0644))
e := NewExecutor(dir)
e.SetInventoryDirect(&Inventory{
All: &InventoryGroup{
Hosts: map[string]*Host{
"localhost": {},
},
},
})
gatherFacts := false
play := &Play{
Hosts: "localhost",
Connection: "local",
GatherFacts: &gatherFacts,
Tasks: []Task{
{
Name: "Load public role",
IncludeRole: &struct {
Name string `yaml:"name"`
TasksFrom string `yaml:"tasks_from,omitempty"`
DefaultsFrom string `yaml:"defaults_from,omitempty"`
VarsFrom string `yaml:"vars_from,omitempty"`
Vars map[string]any `yaml:"vars,omitempty"`
Apply *TaskApply `yaml:"apply,omitempty"`
Public bool `yaml:"public,omitempty"`
}{
Name: "shared",
Public: true,
},
Vars: map[string]any{"shared_message": "hello from public role"},
},
{
Name: "Use public role vars",
Module: "debug",
Args: map[string]any{
"msg": "{{ shared_message }}",
},
Register: "after_public_role",
},
},
}
require.NoError(t, e.runPlay(context.Background(), play))
require.NotNil(t, e.results["localhost"]["shared_role_result"])
assert.Equal(t, "hello from public role", e.results["localhost"]["shared_role_result"].Msg)
require.NotNil(t, e.results["localhost"]["after_public_role"])
assert.Equal(t, "hello from public role", e.results["localhost"]["after_public_role"].Msg)
}
func TestExecutorExtra_GetHostsIter_Good(t *testing.T) {
inv := &Inventory{
All: &InventoryGroup{

View file

@ -663,6 +663,7 @@ func TestExecutor_RunIncludeRole_Good_TemplatesRoleName(t *testing.T) {
VarsFrom string `yaml:"vars_from,omitempty"`
Vars map[string]any `yaml:"vars,omitempty"`
Apply *TaskApply `yaml:"apply,omitempty"`
Public bool `yaml:"public,omitempty"`
}{
Name: "{{ role_name }}",
},

View file

@ -356,7 +356,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
"retries": true, "delay": true, "until": true,
"action": true, "local_action": true,
"include_tasks": true, "import_tasks": true,
"include_role": true, "import_role": true,
"include_role": true, "import_role": true, "public": true,
"with_items": true, "with_dict": true, "with_indexed_items": true, "with_nested": true, "with_together": true, "with_subelements": true, "with_file": true, "with_fileglob": true, "with_sequence": true,
}

View file

@ -52,6 +52,7 @@ type RoleRef struct {
VarsFrom string `yaml:"vars_from,omitempty"`
Vars map[string]any `yaml:"vars,omitempty"`
Apply *TaskApply `yaml:"apply,omitempty"`
Public bool `yaml:"public,omitempty"`
When any `yaml:"when,omitempty"`
Tags []string `yaml:"tags,omitempty"`
}
@ -131,6 +132,7 @@ type Task struct {
VarsFrom string `yaml:"vars_from,omitempty"`
Vars map[string]any `yaml:"vars,omitempty"`
Apply *TaskApply `yaml:"apply,omitempty"`
Public bool `yaml:"public,omitempty"`
} `yaml:"include_role,omitempty"`
ImportRole *struct {
Name string `yaml:"name"`
@ -139,6 +141,7 @@ type Task struct {
VarsFrom string `yaml:"vars_from,omitempty"`
Vars map[string]any `yaml:"vars,omitempty"`
Apply *TaskApply `yaml:"apply,omitempty"`
Public bool `yaml:"public,omitempty"`
} `yaml:"import_role,omitempty"`
// Raw YAML for module extraction

View file

@ -69,6 +69,7 @@ name: web
tasks_from: setup.yml
defaults_from: custom-defaults.yml
vars_from: custom-vars.yml
public: true
`
var ref RoleRef
err := yaml.Unmarshal([]byte(input), &ref)
@ -78,6 +79,7 @@ vars_from: custom-vars.yml
assert.Equal(t, "setup.yml", ref.TasksFrom)
assert.Equal(t, "custom-defaults.yml", ref.DefaultsFrom)
assert.Equal(t, "custom-vars.yml", ref.VarsFrom)
assert.True(t, ref.Public)
}
// --- Task UnmarshalYAML ---
@ -580,6 +582,7 @@ include_role:
tasks_from: setup.yml
defaults_from: defaults.yml
vars_from: vars.yml
public: true
apply:
tags:
- deploy
@ -597,6 +600,7 @@ include_role:
assert.Equal(t, "setup.yml", task.IncludeRole.TasksFrom)
assert.Equal(t, "defaults.yml", task.IncludeRole.DefaultsFrom)
assert.Equal(t, "vars.yml", task.IncludeRole.VarsFrom)
assert.True(t, task.IncludeRole.Public)
require.NotNil(t, task.IncludeRole.Apply)
assert.Equal(t, []string{"deploy"}, task.IncludeRole.Apply.Tags)
require.NotNil(t, task.IncludeRole.Apply.Become)