44 lines
1.7 KiB
Go
44 lines
1.7 KiB
Go
package lint
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"github.com/vladimir/goherence/internal/parser"
|
||
)
|
||
|
||
// SetFactStateRule ловит паттерн `set_fact: x="{{ .x + 1 }}"` — попытку
|
||
// завести изменяемое состояние (счётчик, накопитель) поверх декларативной
|
||
// модели, где каждый таск должен описывать конечное состояние, а не шаг
|
||
// вычисления, зависящий от предыдущего запуска этого же таска.
|
||
type SetFactStateRule struct{}
|
||
|
||
func (r *SetFactStateRule) Name() string { return "set-fact-self-reference" }
|
||
|
||
func (r *SetFactStateRule) Check(pb *parser.Playbook) []Violation {
|
||
var out []Violation
|
||
walkTasks(pb, func(loc string, t *parser.Task) {
|
||
if t.Module != "set_fact" {
|
||
return
|
||
}
|
||
for varName, val := range t.Args {
|
||
expr, ok := val.(string)
|
||
if !ok {
|
||
continue
|
||
}
|
||
if referencesVar(expr, varName) {
|
||
out = append(out, Violation{
|
||
Rule: r.Name(), Severity: Error, Location: loc,
|
||
Message: "set_fact: " + varName + " ссылается сам на себя — похоже на попытку " +
|
||
"завести изменяемое состояние между тасками, что нарушает идемпотентность плейбука",
|
||
})
|
||
}
|
||
}
|
||
})
|
||
return out
|
||
}
|
||
|
||
// referencesVar — грубая, но достаточная для первой версии проверка:
|
||
// содержит ли выражение шаблона `.varName` внутри себя же (self-reference).
|
||
func referencesVar(expr, varName string) bool {
|
||
return strings.Contains(expr, "."+varName)
|
||
}
|