diff --git a/tooling/go/cmd/site-consistency/hugo_toml_structs.go b/tooling/go/cmd/site-consistency/hugo_toml_structs.go new file mode 100644 index 000000000..5b000be91 --- /dev/null +++ b/tooling/go/cmd/site-consistency/hugo_toml_structs.go @@ -0,0 +1,34 @@ +package main + +type hugoToml struct { + Markup *hugoTomlMarkup `toml:"markup"` +} + +type hugoTomlMarkup struct { + TableOfContents *hugoTomlTableOfContents `toml:"tableOfContents"` + Goldmark *hugoTomlGoldmark `toml:"goldmark"` +} + +type hugoTomlTableOfContents struct { + Endlevel *int `toml:"endLevel"` + Ordered *bool `toml:"ordered"` + StartLevel *int `toml:"startLevel"` +} + +type hugoTomlGoldmark struct { + Renderer *hugoTomlGoldmarkRenderer `toml:"renderer"` + Parser *hugoTomlGoldmarkParser `toml:"parser"` +} + +type hugoTomlGoldmarkRenderer struct { + Unsafe *bool `toml:"unsafe"` +} + +type hugoTomlGoldmarkParser struct { + Attribute *hugoTomlGoldmarkParserAttribute `toml:"attribute"` +} + +type hugoTomlGoldmarkParserAttribute struct { + Block *bool `toml:"block"` + Title *bool `toml:"title"` +} diff --git a/tooling/go/cmd/site-consistency/main.go b/tooling/go/cmd/site-consistency/main.go index d1d4a3857..91fec84ac 100644 --- a/tooling/go/cmd/site-consistency/main.go +++ b/tooling/go/cmd/site-consistency/main.go @@ -9,11 +9,15 @@ import ( "os" "path/filepath" "sort" + + "github.com/BurntSushi/toml" ) func main() { var rootDirectory string + var fix bool flag.StringVar(&rootDirectory, "root-dir", ".", "Root directory to search for hugo.toml files in") + flag.BoolVar(&fix, "fix", false, "Fix problems, rather than reporting them") flag.Parse() var directoriesToCheck []string @@ -35,10 +39,14 @@ func main() { var errors []string for _, directoryToCheck := range directoriesToCheck { for _, expectedSymlink := range []string{"config", "deploy-netlify.sh", "netlify.toml"} { - if errorToReport, ok := checkForParentSymlink(directoryToCheck, expectedSymlink); !ok { + if errorToReport, ok := checkForParentSymlink(directoryToCheck, expectedSymlink, fix); !ok { errors = append(errors, errorToReport) } } + + if errorToReport := checkForMarkupConfig(filepath.Join(directoryToCheck, "hugo.toml"), fix); errorToReport != nil { + errors = append(errors, errorToReport.Error()) + } } for _, errorToReport := range errors { @@ -51,13 +59,19 @@ func main() { // checkForParentSymlink checks that a symlink exists in the directory with expectedLinkName pointing at something in the parent directory with the same name. // It returns a bool indicating whether things were correct, and if the bool is false, returns a non-empty string describing the problem suitable for displaying to a user. -func checkForParentSymlink(directoryToCheck, expectedLinkName string) (string, bool) { +func checkForParentSymlink(directoryToCheck, expectedLinkName string, fix bool) (string, bool) { expectedDestination := filepath.Join("..", "tooling", "common-config", expectedLinkName) path := filepath.Join(directoryToCheck, expectedLinkName) fileInfo, err := os.Lstat(path) if err != nil { reason := fmt.Sprintf("an error occurred looking it up: %v", err) if errors.Is(err, fs.ErrNotExist) { + if fix { + if err := os.Symlink(expectedDestination, path); err != nil { + panic(fmt.Sprintf("Failed to create symlink %s: %v", path, err)) + } + return "", true + } reason = "it didn't exist" } return formatError(path, expectedDestination, reason), false @@ -78,3 +92,58 @@ func checkForParentSymlink(directoryToCheck, expectedLinkName string) (string, b func formatError(path, expectedDestination, reason string) string { return fmt.Sprintf("Expected %s to be a symlink pointing at %s but %s", path, expectedDestination, reason) } + +func checkForMarkupConfig(path string, fix bool) error { + var config hugoToml + bytes, err := os.ReadFile(path) + if err != nil { + return fmt.Errorf("failed to read %s: %w", path, err) + } + if err := toml.Unmarshal(bytes, &config); err != nil { + return fmt.Errorf("failed to decode %s as toml: %w", path, err) + } + + want := &hugoTomlMarkup{ + TableOfContents: &hugoTomlTableOfContents{ + Endlevel: ptr(2), + Ordered: ptr(true), + StartLevel: ptr(2), + }, + Goldmark: &hugoTomlGoldmark{ + Renderer: &hugoTomlGoldmarkRenderer{ + Unsafe: ptr(true), + }, + Parser: &hugoTomlGoldmarkParser{ + Attribute: &hugoTomlGoldmarkParserAttribute{ + Block: ptr(true), + Title: ptr(true), + }, + }, + }, + } + if config.Markup != want { + wantConfig := hugoToml{ + Markup: want, + } + marshalledWant, err := toml.Marshal(wantConfig) + if err != nil { + panic(fmt.Sprintf("failed to marshal known-good toml: %v", err)) + } + if fix && config.Markup == nil { + var out []byte + out = append(out, bytes...) + out = append(out, '\n', '\n') + out = append(out, marshalledWant...) + if err := os.WriteFile(path, out, 0644); err != nil { + panic(fmt.Sprintf("failed to fix %s: %v", path, err)) + } + return nil + } + return fmt.Errorf("%s had wrong or missing [markup] section. Add this:\n%s", path, string(marshalledWant)) + } + return nil +} + +func ptr[T any](v T) *T { + return &v +} diff --git a/tooling/go/go.mod b/tooling/go/go.mod index 05fb562a2..0e61fd6ff 100644 --- a/tooling/go/go.mod +++ b/tooling/go/go.mod @@ -3,13 +3,13 @@ module github.com/CodeYourFuture/curriculum/tooling/go go 1.21.5 require ( + github.com/BurntSushi/toml v1.4.0 + github.com/adrg/frontmatter v0.2.0 github.com/stretchr/testify v1.8.4 golang.org/x/mod v0.14.0 ) require ( - github.com/BurntSushi/toml v0.3.1 // indirect - github.com/adrg/frontmatter v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect diff --git a/tooling/go/go.sum b/tooling/go/go.sum index 51a6c4938..36ba7a3fd 100644 --- a/tooling/go/go.sum +++ b/tooling/go/go.sum @@ -1,5 +1,7 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/adrg/frontmatter v0.2.0 h1:/DgnNe82o03riBd1S+ZDjd43wAmC6W35q67NHeLkPd4= github.com/adrg/frontmatter v0.2.0/go.mod h1:93rQCj3z3ZlwyxxpQioRKC1wDLto4aXHrbqIsnH9wmE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=