From b62bb2d37ac8c0866d18b43c5165971c5df43d8c Mon Sep 17 00:00:00 2001 From: Stavros Date: Mon, 13 Jul 2026 18:07:47 +0300 Subject: [PATCH] refactor: generate mdx docs with tabs for each config option (#996) --- .env.example | 6 ++-- gen/docs/gen_env.go | 3 +- gen/docs/gen_md.go | 78 ++++++++++++++++++++++++++++++---------- internal/model/config.go | 4 +-- 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/.env.example b/.env.example index 99b2c1cb..5d750968 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,10 @@ # The base URL where the app is hosted. TINYAUTH_APPURL= +# Path to config file. +TINYAUTH_CONFIGFILE= +# Label provider to use for ACLs (auto, docker, kubernetes or none to disable). auto detects the environment. +TINYAUTH_LABELPROVIDER="auto" # database config @@ -233,8 +237,6 @@ TINYAUTH_TAILSCALE_APITOKENFILE= TINYAUTH_TAILSCALE_TAILNET= # Cache duration for Tailscale device and user lists in seconds. TINYAUTH_TAILSCALE_CACHEDURATION=300 -# Label provider to use for ACLs (auto, docker, kubernetes or none to disable). auto detects the environment. -TINYAUTH_LABELPROVIDER="auto" # log config diff --git a/gen/docs/gen_env.go b/gen/docs/gen_env.go index 2f44611e..02182279 100644 --- a/gen/docs/gen_env.go +++ b/gen/docs/gen_env.go @@ -46,8 +46,9 @@ func generateExampleEnv() { func buildEnvEntry(child reflect.StructField, childValue reflect.Value, parentPath string, entries *[]EnvEntry) { desc := child.Tag.Get("description") tag := child.Tag.Get("yaml") + gen := child.Tag.Get("gen") - if tag == "-" { + if tag == "-" && gen != "include" { return } diff --git a/gen/docs/gen_md.go b/gen/docs/gen_md.go index ade916d4..60b14d25 100644 --- a/gen/docs/gen_md.go +++ b/gen/docs/gen_md.go @@ -1,7 +1,6 @@ package main import ( - "bytes" "errors" "fmt" "io/fs" @@ -13,9 +12,13 @@ import ( "github.com/tinyauthapp/tinyauth/internal/model" ) +type ConfigOptions struct { + Env string + Flag string + YAML string +} type MarkdownEntry struct { - Env string - Flag string + Options ConfigOptions Description string Default any } @@ -47,8 +50,9 @@ func generateMarkdown() { func buildMdEntry(child reflect.StructField, childValue reflect.Value, parentPath string, entries *[]MarkdownEntry) { desc := child.Tag.Get("description") tag := child.Tag.Get("yaml") + gen := child.Tag.Get("gen") - if tag == "-" { + if tag == "-" && gen != "include" { return } @@ -56,9 +60,17 @@ func buildMdEntry(child reflect.StructField, childValue reflect.Value, parentPat value := childValue.Interface() + configOptions := ConfigOptions{ + Env: strings.ToUpper(strings.ReplaceAll(parentPath, ".", "_")) + strings.ToUpper(child.Name), + Flag: fmt.Sprintf("--%s%s", strings.TrimPrefix(parentPath, "tinyauth."), strings.ToLower(child.Name)), + } + + if tag != "-" && tag != "" { + configOptions.YAML = strings.TrimPrefix(parentPath, "tinyauth.") + tag + } + entry := MarkdownEntry{ - Env: strings.ToUpper(strings.ReplaceAll(parentPath, ".", "_")) + strings.ToUpper(child.Name), - Flag: fmt.Sprintf("--%s%s", strings.TrimPrefix(parentPath, "tinyauth."), strings.ToLower(child.Name)), + Options: configOptions, Description: desc, } @@ -85,8 +97,9 @@ func buildMdMapEntry(child reflect.StructField, parentPath string, entries *[]Ma } tag := child.Tag.Get("yaml") + gen := child.Tag.Get("gen") - if tag == "-" { + if tag == "-" && gen != "include" { return } @@ -106,27 +119,54 @@ func buildMdChildPath(parent string, child string) string { } func compileMd(entries []MarkdownEntry) []byte { - buffer := bytes.Buffer{} + buffer := strings.Builder{} - buffer.WriteString("\n\n") - buffer.WriteString("# Tinyauth configuration reference\n\n") - buffer.WriteString("| Environment | Flag | Description | Default |\n") - buffer.WriteString("| - | - | - | - |\n") + buffer.WriteString("{/* This part is automatically generated by gen/gen_md.go in the main repository. DO NOT EDIT. */}\n\n") + buffer.WriteString("import { Tabs, TabItem } from '@astrojs/starlight/components';\n\n") + buffer.WriteString("\n") + renderTabItem(&buffer, entries, "Environment") + renderTabItem(&buffer, entries, "Flags") + renderTabItem(&buffer, entries, "YAML") + + buffer.WriteString("\n") + + return []byte(buffer.String()) +} + +func renderTabItem(buffer *strings.Builder, entries []MarkdownEntry, section string) { + buffer.WriteString(fmt.Sprintf(" \n", section)) + buffer.WriteString(" ### main\n\n") + buffer.WriteString(" | Option | Description | Default |\n") + buffer.WriteString(" | - | - | - |\n") + + configType := strings.ToLower(section) previousSection := "" for _, entry := range entries { - if strings.Count(entry.Env, "_") > 1 { - section := strings.Split(strings.TrimPrefix(entry.Env, "TINYAUTH_"), "_")[0] + section := strings.Split(entry.Options.YAML, ".")[0] + var option string + switch configType { + case "yaml": + option = entry.Options.YAML + case "flags": + option = entry.Options.Flag + case "environment": + option = entry.Options.Env + } + if option == "" { + continue + } + if strings.Count(entry.Options.YAML, ".") >= 1 { if section != previousSection { - buffer.WriteString("\n## " + strings.ToLower(section) + "\n\n") - buffer.WriteString("| Environment | Flag | Description | Default |\n") - buffer.WriteString("| - | - | - | - |\n") + buffer.WriteString("\n ### " + strings.ToLower(section) + "\n\n") + buffer.WriteString(" | Option | Description | Default |\n") + buffer.WriteString(" | - | - | - |\n") previousSection = section } } - fmt.Fprintf(&buffer, "| `%s` | `%s` | %s | %s |\n", entry.Env, entry.Flag, entry.Description, entry.Default) + fmt.Fprintf(buffer, " | `%s` | %s | %s |\n", option, entry.Description, entry.Default) } - return buffer.Bytes() + buffer.WriteString(" \n") } diff --git a/internal/model/config.go b/internal/model/config.go index 97c92510..c1a61156 100644 --- a/internal/model/config.go +++ b/internal/model/config.go @@ -103,6 +103,8 @@ func NewDefaultConfiguration(runtimeEnv RuntimeEnv) *Config { type Config struct { AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl,omitempty"` + ConfigFile string `description:"Path to config file." yaml:"-" gen:"include"` + LabelProvider string `description:"Label provider to use for ACLs (auto, docker, kubernetes or none to disable). auto detects the environment." yaml:"labelProvider,omitempty"` Database DatabaseConfig `description:"Database configuration." yaml:"database,omitempty"` Analytics AnalyticsConfig `description:"Analytics configuration." yaml:"analytics,omitempty"` Resources ResourcesConfig `description:"Resources configuration." yaml:"resources,omitempty"` @@ -115,9 +117,7 @@ type Config struct { LDAP LDAPConfig `description:"LDAP configuration." yaml:"ldap,omitempty"` Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental,omitempty"` Tailscale TailscaleConfig `description:"Tailscale configuration." yaml:"tailscale,omitempty"` - LabelProvider string `description:"Label provider to use for ACLs (auto, docker, kubernetes or none to disable). auto detects the environment." yaml:"labelProvider,omitempty"` Log LogConfig `description:"Logging configuration." yaml:"log,omitempty"` - ConfigFile string `description:"Path to config file." yaml:"-"` } type DatabaseConfig struct {