Files
geniusrun/backend/cmd/dumpschema/main.go
Christophe Vila 431315bac3 refactor(log): replace stdlib log with the application JSON logger
Every log.Printf/Fatalf becomes a structured slog call through
internal/log: request handlers use the request-scoped logger
(applog.FromContext, carrying request_id), startup failures exit via a
fatal() helper that still emits JSON, and the seedsample/dumpschema CLIs
bootstrap the same JSON logger. Stale client_test expectations aligned
with the refactored wrapper-call logging (message casing, result attr,
ERROR level).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 16:33:26 +02:00

105 lines
2.6 KiB
Go

// Command dumpschema regenerates docs/DATABASE.md from the real, live
// database schema -- it opens a fresh temp database through store.Open
// (the exact same code path geniusrund itself uses) and introspects
// sqlite_master, so the generated documentation can never drift from what
// the app actually creates. Run it after any change to
// internal/store/schema.sql:
//
// go run ./cmd/dumpschema
package main
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"strings"
applog "geniusrun/backend/internal/log"
"geniusrun/backend/internal/store"
)
type schemaEntry struct {
typ string // "table" or "view"
name string
tblName string
sql string
}
func main() {
slog.SetDefault(applog.NewLogger("info", os.Stdout))
tmpDir, err := os.MkdirTemp("", "geniusrun-dumpschema")
must(err)
defer os.RemoveAll(tmpDir)
db, err := store.Open(filepath.Join(tmpDir, "schema-check.db"))
must(err)
defer db.Close()
rows, err := db.Query(`
SELECT type, name, tbl_name, sql FROM sqlite_master
WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%'
ORDER BY rowid`)
must(err)
defer rows.Close()
var tables, views []schemaEntry
indexesByTable := map[string][]schemaEntry{}
for rows.Next() {
var e schemaEntry
must(rows.Scan(&e.typ, &e.name, &e.tblName, &e.sql))
switch e.typ {
case "table":
tables = append(tables, e)
case "view":
views = append(views, e)
case "index":
indexesByTable[e.tblName] = append(indexesByTable[e.tblName], e)
}
}
must(rows.Err())
var b strings.Builder
b.WriteString("# geniusrun database schema\n\n")
b.WriteString("Generated from the live schema via `go run ./cmd/dumpschema` -- do not hand-edit. ")
b.WriteString("The source of truth is `backend/internal/store/schema.sql`; regenerate this file after changing it.\n\n")
b.WriteString("## Tables\n\n")
for _, t := range tables {
fmt.Fprintf(&b, "- [`%s`](#%s)\n", t.name, t.name)
}
b.WriteString("\n")
for _, t := range tables {
fmt.Fprintf(&b, "## `%s`\n\n", t.name)
fmt.Fprintf(&b, "```sql\n%s;\n```\n\n", t.sql)
if idxs := indexesByTable[t.name]; len(idxs) > 0 {
b.WriteString("Indexes:\n\n```sql\n")
for _, idx := range idxs {
fmt.Fprintf(&b, "%s;\n", idx.sql)
}
b.WriteString("```\n\n")
}
}
if len(views) > 0 {
b.WriteString("## Views\n\n")
for _, v := range views {
fmt.Fprintf(&b, "### `%s`\n\n", v.name)
fmt.Fprintf(&b, "```sql\n%s;\n```\n\n", v.sql)
}
}
outPath := "../docs/DATABASE.md"
must(os.WriteFile(outPath, []byte(b.String()), 0644))
slog.Info("wrote schema doc", "path", outPath)
}
func must(err error) {
if err != nil {
slog.Error("dumpschema", "error", err)
os.Exit(1)
}
}