InstallationProblems judges every value the mesh acts on for a name under a public top-level domain
or a public address, with prose, the world's registries, resolvers and certificate authorities
exempt, and a name a resource means on purpose declared with its reason (names-on-purpose). Run by
module check and a catalogue-wide test, not yet at registration, while the declared list shrinks.
${setting:<key>} fills a file from the assignment's settings and is refused when nothing set it.
A build context may live on the git seat; the request carries the seat's clone base (novox/hq ADR
0112, ADR 0155, issues 122 and 134).
110 lines
3.6 KiB
Go
110 lines
3.6 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// An operator's value, where a definition needs one (novox/hq ADR 0112, ADR 0155, design 27).
|
|
//
|
|
// A mail server's domain, a site's name, the address a proxy forwards from: values that are true of
|
|
// one installation and of no other, and that a module's software must be told. They had nowhere to
|
|
// live but the definition, which is how a catalogue meant for any mesh came to name this one
|
|
// (novox/hq issues 122, 134). ADR 0112 names the operator as one of the four providers; this is the
|
|
// operator answering.
|
|
//
|
|
// `${setting:<key>}` in a file's content is filled from the module's settings layers — the mesh's,
|
|
// then this node's — the same layers a mergeable JSON file and a contribution already take, so
|
|
// `settings set <module>` is the one place a person's values go. **Refused when no layer sets it**,
|
|
// naming the key and the remedy: a definition that carried a default for a mail domain would be
|
|
// carrying the very literal this removes, and a blank written silently would be a service that
|
|
// comes up wrong somewhere that names neither the module nor the key.
|
|
|
|
// settingRef is how a definition asks for an operator's value: ${setting:<key>}.
|
|
var settingRef = regexp.MustCompile(`\$\{setting:([a-z0-9][a-z0-9_.-]*)\}`)
|
|
|
|
// settingsUsed is every key a file's content asks for, once each, in order of first use.
|
|
func settingsUsed(content string) []string {
|
|
var keys []string
|
|
seen := map[string]bool{}
|
|
for _, m := range settingRef.FindAllStringSubmatch(content, -1) {
|
|
if !seen[m[1]] {
|
|
seen[m[1]] = true
|
|
keys = append(keys, m[1])
|
|
}
|
|
}
|
|
return keys
|
|
}
|
|
|
|
// settingInto fills a file's ${setting:…} placeholders from the layers over a module.
|
|
//
|
|
// The last layer setting a key wins, which is the node's over the mesh's — the same order settle
|
|
// applies to a mergeable file. A value that is not a string is written the way a program would read
|
|
// it (a number without a trailing .000000, a boolean as true/false).
|
|
func settingInto(resource map[string]any, layers []Layer, module string) error {
|
|
if fmt.Sprint(resource["type"]) != "file" {
|
|
return nil
|
|
}
|
|
content, ok := resource["content"].(string)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
for _, key := range settingsUsed(content) {
|
|
value, set := settingValue(layers, key)
|
|
if !set {
|
|
return fmt.Errorf(
|
|
"%s has a file that says ${setting:%s}, and nothing sets %q for it — an operator's "+
|
|
"value is the assignment's, never the definition's (novox/hq ADR 0112): "+
|
|
"`settings set %s <file>` with {%q: …}%s",
|
|
module, key, key, module, key, orNoSettings(layers))
|
|
}
|
|
content = strings.ReplaceAll(content, "${setting:"+key+"}", plainly(value))
|
|
}
|
|
resource["content"] = content
|
|
return nil
|
|
}
|
|
|
|
func settingValue(layers []Layer, key string) (any, bool) {
|
|
var value any
|
|
set := false
|
|
for _, layer := range layers {
|
|
if v, has := layer.Values[key]; has {
|
|
value, set = v, true
|
|
}
|
|
}
|
|
return value, set
|
|
}
|
|
|
|
func orNoSettings(layers []Layer) string {
|
|
var keys []string
|
|
for _, l := range layers {
|
|
for k := range l.Values {
|
|
keys = append(keys, k)
|
|
}
|
|
}
|
|
if len(keys) == 0 {
|
|
return "; no setting is set for this module"
|
|
}
|
|
sort.Strings(keys)
|
|
return "; set today: " + strings.Join(keys, ", ")
|
|
}
|
|
|
|
// settingKeysUsedBy is every key a module's files ask for, so a setting that lands in one is not
|
|
// called stray.
|
|
func settingKeysUsedBy(m Manifest) map[string]bool {
|
|
used := map[string]bool{}
|
|
for _, r := range m.Resources {
|
|
if fmt.Sprint(r["type"]) != "file" {
|
|
continue
|
|
}
|
|
if content, ok := r["content"].(string); ok {
|
|
for _, k := range settingsUsed(content) {
|
|
used[k] = true
|
|
}
|
|
}
|
|
}
|
|
return used
|
|
}
|