config: migrate to new yaml format

config-base.yaml is almost identical to json version, except there's no "order" field, as
"sections" and "settings" fields are now lists themselves and so Go can
parse the correct order. As such, removed enumerate_config.py. Also,
rewrote scripts/generate_ini.py in Go as scripts/ini/. Config structure
in Go form is now in common/config.go, and is used by jfa-go and the ini
script. app.configBase is now untouched once read from config-base.yaml,
and instead copied to and patched in app.patchedConfig. Patching occurs
at program start and config modification, so GetConfig is now just a
couple of lines. Discord role patching still occurs in GetConfig, as the
available roles can change regularly. Also added new "Disabled" field to
sections, to avoid the nightmare of deleting from an array.
This commit is contained in:
Harvey Tindall
2024-08-26 15:43:28 +01:00
parent 711b817cff
commit f063b970b4
17 changed files with 2180 additions and 2566 deletions
-29
View File
@@ -1,29 +0,0 @@
# Since go doesn't order its json, this script adds ordered lists
# of section/setting names for the settings tab to use.
import json, argparse
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", help="input config base from jf-accounts")
parser.add_argument("-o", "--output", help="output config base for jfa-go")
args = parser.parse_args()
with open(args.input, 'r') as f:
config = json.load(f)
newconfig = {"sections": {}, "order": []}
for sect in config["sections"]:
newconfig["order"].append(sect)
newconfig["sections"][sect] = {}
newconfig["sections"][sect]["order"] = []
newconfig["sections"][sect]["meta"] = config["sections"][sect]["meta"]
newconfig["sections"][sect]["settings"] = {}
for setting in config["sections"][sect]["settings"]:
newconfig["sections"][sect]["order"].append(setting)
newconfig["sections"][sect]["settings"][setting] = config["sections"][sect]["settings"][setting]
with open(args.output, 'w') as f:
f.write(json.dumps(newconfig, indent=4))
-47
View File
@@ -1,47 +0,0 @@
# Generates config file
import configparser
import json
import argparse
from pathlib import Path
def fix_description(desc):
return "; " + desc.replace("\n", "\n; ")
def generate_ini(base_file, ini_file):
"""
Generates .ini file from config-base file.
"""
with open(Path(base_file), "r") as f:
config_base = json.load(f)
ini = configparser.RawConfigParser(allow_no_value=True)
for section in config_base["sections"]:
ini.add_section(section)
if "meta" in config_base["sections"][section]:
ini.set(section, fix_description(config_base["sections"][section]["meta"]["description"]))
for entry in config_base["sections"][section]["settings"]:
if config_base["sections"][section]["settings"][entry]["type"] == "note":
continue
if "description" in config_base["sections"][section]["settings"][entry]:
ini.set(section, fix_description(config_base["sections"][section]["settings"][entry]["description"]))
value = config_base["sections"][section]["settings"][entry]["value"]
if isinstance(value, bool):
value = str(value).lower()
else:
value = str(value)
ini.set(section, entry, value)
with open(Path(ini_file), "w") as config_file:
ini.write(config_file)
return True
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", help="input config base from jf-accounts")
parser.add_argument("-o", "--output", help="output ini")
args = parser.parse_args()
print(generate_ini(base_file=args.input, ini_file=args.output))
+12
View File
@@ -0,0 +1,12 @@
module github.com/hrfee/jfa-go/scripts/ini
replace github.com/hrfee/jfa-go/common => ../../common
go 1.22.4
require (
github.com/hrfee/jfa-go/common v0.0.0-20240824141650-fcdd4e451882 // indirect
github.com/hrfee/jfa-go/logmessages v0.0.0-20240806200606-6308db495a0a // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+7
View File
@@ -0,0 +1,7 @@
github.com/hrfee/jfa-go/logmessages v0.0.0-20240806200606-6308db495a0a h1:qbXZgCqb9eaPSJfLEXczQD2lxTv6jb6silMPIWW9j6o=
github.com/hrfee/jfa-go/logmessages v0.0.0-20240806200606-6308db495a0a/go.mod h1:c5HKkLayo0GrEUDlJwT12b67BL9cdPjP271Xlv/KDRQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+130
View File
@@ -0,0 +1,130 @@
package main
import (
"errors"
"flag"
"fmt"
"os"
"strings"
"github.com/hrfee/jfa-go/common"
"gopkg.in/ini.v1"
"gopkg.in/yaml.v3"
)
func fixDescription(desc string) string {
return "; " + strings.ReplaceAll(desc, "\n", "\n; ")
}
func generateIni(yamlPath string, iniPath string) {
yamlFile, err := os.ReadFile(yamlPath)
if err != nil {
panic(err)
}
configBase := common.Config{}
err = yaml.Unmarshal(yamlFile, &configBase)
if err != nil {
panic(err)
}
conf := ini.Empty()
for _, section := range configBase.Sections {
cSection, err := conf.NewSection(section.Section)
if err != nil {
panic(err)
}
if section.Meta.Description != "" {
cSection.Comment = fixDescription(section.Meta.Description)
}
for _, setting := range section.Settings {
if setting.Type == common.NoteType {
continue
}
val := ""
if setting.Value != nil {
// Easy way to convert bools and numbers to strings,
// Instead of checking setting.Type
val = fmt.Sprintf("%v", setting.Value)
}
cKey, err := cSection.NewKey(setting.Setting, val)
if err != nil {
panic(err)
}
if setting.Description != "" {
cKey.Comment = fixDescription(setting.Description)
}
// Explain how to use list type
if setting.Type == common.ListType {
if cKey.Comment != "" {
cKey.Comment += "\n"
}
cKey.Comment += `List type: duplicate and edit the line to add more entries.`
}
}
}
err = conf.SaveTo(iniPath)
if err != nil {
panic(err)
}
}
// Compares two inis, used to check this script does the equivalent of the old generate_ini.py.
func compareInis(p1, p2 string) {
cA, err := ini.ShadowLoad(p1)
if err != nil {
panic(err)
}
cB, err := ini.ShadowLoad(p2)
if err != nil {
panic(err)
}
for _, pair := range [][2]*ini.File{{cA, cB}, {cB, cA}} {
s1 := pair[0].Sections()
s2 := pair[1].Sections()
for i := range s1 {
if s1[i].Name() != s2[i].Name() {
panic(fmt.Errorf("mismatching section order: s0[i]=%s, s1[i]=%s", s1[i].Name(), s2[i].Name()))
}
// fmt.Println("Section order matches")
st1 := s1[i].Keys()
st2 := s2[i].Keys()
for i := range st1 {
if st1[i].Name() != st2[i].Name() {
panic(fmt.Errorf("mismatching setting order: st1[i]=%s, st2[i]=%s", st1[i].Name(), st2[i].Name()))
}
if st1[i].Value() != st2[i].Value() {
panic(fmt.Errorf("mismatching setting values: st1[i]=%s, st2[i]=%s", st1[i].Value(), st2[i].Value()))
}
// fmt.Println("Setting matches")
}
}
}
}
func main() {
var yamlPath string
var iniPath string
var comparePath string
flag.StringVar(&yamlPath, "in", "", "Input of the config base in yaml.")
flag.StringVar(&iniPath, "out", "", "Output path of an ini file.")
flag.StringVar(&comparePath, "comp", "", "Path to ini file to compare against.")
flag.Parse()
if yamlPath == "" {
panic(errors.New("invalid yaml path"))
}
if iniPath == "" {
panic(errors.New("invalid ini path"))
}
generateIni(yamlPath, iniPath)
if comparePath != "" {
compareInis(iniPath, comparePath)
fmt.Println("Passed.")
}
}