142 lines
3.6 KiB
Go
142 lines
3.6 KiB
Go
package repo
|
|
|
|
import (
|
|
"path"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"go.yaml.in/yaml/v3"
|
|
)
|
|
|
|
// Docker Compose files: compose.yaml, docker-compose.yml and their
|
|
// variants (docker-compose.test.yml). Their keys are the services'
|
|
// environment variables, named service.VAR.
|
|
|
|
var composeType = configType{
|
|
name: "compose", is: IsCompose, format: func(string) string { return "yaml" }, parse: parseCompose,
|
|
jasyptSetting: func(name string) string {
|
|
service, _, _ := strings.Cut(name, ".")
|
|
return service + "." + jasyptEnv
|
|
},
|
|
placeholder: composePlaceholder,
|
|
}
|
|
|
|
var composeName = regexp.MustCompile(`^(docker-)?compose([.-][^/]+)?\.ya?ml$`)
|
|
|
|
// IsCompose reports whether f is named like a Docker Compose file.
|
|
func IsCompose(f string) bool {
|
|
return composeName.MatchString(strings.ToLower(path.Base(f)))
|
|
}
|
|
|
|
// composePlaceholderRe is compose's ${VAR}, ${VAR:-default}, ${VAR-default},
|
|
// ${VAR:?error}, ${VAR?error} or $VAR.
|
|
var composePlaceholderRe = regexp.MustCompile(`^\$(?:\{(\w+)(?:(:?-)([^}]*)|:?\?[^}]*)?\}|(\w+))$`)
|
|
|
|
func composePlaceholder(v string) (name, def string, hasDef, ok bool) {
|
|
m := composePlaceholderRe.FindStringSubmatch(v)
|
|
if m == nil {
|
|
return "", "", false, false
|
|
}
|
|
return m[1] + m[4], m[3], m[2] != "", true
|
|
}
|
|
|
|
func parseCompose(_ string, b []byte) ([]configKey, error) {
|
|
var doc yaml.Node
|
|
if err := yaml.Unmarshal(b, &doc); err != nil {
|
|
return nil, err
|
|
}
|
|
if len(doc.Content) == 0 {
|
|
return nil, nil
|
|
}
|
|
var out []configKey
|
|
for _, service := range pairs(field(doc.Content[0], "services")) {
|
|
for _, k := range environment(field(service.value, "environment")) {
|
|
k.name = service.key.Value + "." + k.key
|
|
out = append(out, k)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// environment reads a service's environment, a map (KEY: value) or a list
|
|
// (- KEY=value). A variable without a value (KEY:, - KEY) is taken from
|
|
// where compose runs: a reference.
|
|
func environment(n *yaml.Node) []configKey {
|
|
var out []configKey
|
|
switch n = deref(n); {
|
|
case n == nil:
|
|
case n.Kind == yaml.MappingNode:
|
|
for _, p := range pairs(n) {
|
|
v := deref(p.value)
|
|
ref := v.Tag == "!!null" || reference(composePlaceholder, v.Value)
|
|
out = append(out, configKey{key: p.key.Value, value: v.Value, ref: ref, line: p.key.Line})
|
|
}
|
|
case n.Kind == yaml.SequenceNode:
|
|
for _, item := range n.Content {
|
|
item = deref(item)
|
|
k, v, ok := strings.Cut(item.Value, "=")
|
|
out = append(out, configKey{key: k, value: v, ref: !ok || reference(composePlaceholder, v), line: item.Line})
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
type pair struct{ key, value *yaml.Node }
|
|
|
|
// pairs are a mapping's entries, with those merged in by << (anchors
|
|
// shared between services) first and its own over them.
|
|
func pairs(n *yaml.Node) []pair {
|
|
n = deref(n)
|
|
if n == nil || n.Kind != yaml.MappingNode {
|
|
return nil
|
|
}
|
|
var merged, own []pair
|
|
for i := 0; i+1 < len(n.Content); i += 2 {
|
|
k, v := n.Content[i], n.Content[i+1]
|
|
if k.Value != "<<" {
|
|
own = append(own, pair{k, v})
|
|
continue
|
|
}
|
|
from := []*yaml.Node{v}
|
|
if v.Kind == yaml.SequenceNode {
|
|
from = v.Content
|
|
}
|
|
for _, m := range from {
|
|
merged = append(merged, pairs(m)...)
|
|
}
|
|
}
|
|
var out []pair
|
|
for _, p := range append(merged, own...) {
|
|
if i := indexOf(out, p.key.Value); i >= 0 {
|
|
out[i] = p
|
|
} else {
|
|
out = append(out, p)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func indexOf(ps []pair, key string) int {
|
|
for i, p := range ps {
|
|
if p.key.Value == key {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func field(n *yaml.Node, key string) *yaml.Node {
|
|
ps := pairs(n)
|
|
if i := indexOf(ps, key); i >= 0 {
|
|
return ps[i].value
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func deref(n *yaml.Node) *yaml.Node {
|
|
for n != nil && n.Kind == yaml.AliasNode {
|
|
n = n.Alias
|
|
}
|
|
return n
|
|
}
|