feat(job): support templ command

This commit is contained in:
SG Command
2026-09-08 06:17:45 +02:00
parent cdd066dafe
commit 3d57c947cd
4 changed files with 170 additions and 12 deletions
+72 -10
View File
@@ -33,10 +33,11 @@ const (
CommandConvert = "convert" // read one or more schema files, optionally merge, write one output
CommandMerge = "merge" // additive merge of two or more schema files into one output
CommandScriptsList = "scripts-list" // deterministically list SQL scripts across one or more directories
CommandTempl = "templ" // apply a custom Go text template to one or more schemas
)
// SupportedCommands lists every accepted command, in help order.
var SupportedCommands = []string{CommandConvert, CommandMerge, CommandScriptsList}
var SupportedCommands = []string{CommandConvert, CommandMerge, CommandScriptsList, CommandTempl}
// readerFormats are the file-based input formats a job may declare (path).
var readerFormats = map[string]bool{
@@ -72,14 +73,17 @@ type Job struct {
Name string `yaml:"-"`
SourceFile string `yaml:"-"`
Command string `yaml:"command"`
Description string `yaml:"description"`
DependsOn []string `yaml:"depends_on"`
Inputs []Input `yaml:"inputs"`
ScriptDirs []string `yaml:"script_dirs"`
Output *Output `yaml:"output"`
Options Options `yaml:"options"`
Logfile string `yaml:"logfile"`
Command string `yaml:"command"`
Description string `yaml:"description"`
DependsOn []string `yaml:"depends_on"`
Inputs []Input `yaml:"inputs"`
ScriptDirs []string `yaml:"script_dirs"`
Template string `yaml:"template"`
Mode string `yaml:"mode"`
FilenamePattern string `yaml:"filename_pattern"`
Output *Output `yaml:"output"`
Options Options `yaml:"options"`
Logfile string `yaml:"logfile"`
}
// Input is one declared input schema.
@@ -258,7 +262,7 @@ func (j *Job) validate() []string {
var e []string
switch j.Command {
case CommandConvert, CommandMerge, CommandScriptsList:
case CommandConvert, CommandMerge, CommandScriptsList, CommandTempl:
case "":
e = append(e, "missing command")
return e
@@ -277,6 +281,7 @@ func (j *Job) validate() []string {
}
}
checkPath("logfile", j.Logfile)
checkPath("template", j.Template)
for _, in := range j.Inputs {
checkPath("input path", in.Path)
}
@@ -317,6 +322,63 @@ func (j *Job) validate() []string {
if j.Output != nil {
e = append(e, "output is not valid for command \"scripts-list\"")
}
case CommandTempl:
if len(j.Inputs) < 1 {
e = append(e, "command \"templ\" requires at least 1 input")
}
for i, in := range j.Inputs {
e = append(e, validateTemplInput(i, in)...)
}
if j.Template == "" {
e = append(e, "command \"templ\" requires template")
}
mode := strings.ToLower(j.Mode)
if mode == "" {
mode = "database"
}
switch mode {
case "database", "schema", "script", "table":
default:
e = append(e, fmt.Sprintf("command \"templ\" has unsupported mode %q (supported: database, schema, script, table)", j.Mode))
}
if len(j.ScriptDirs) > 0 {
e = append(e, "script_dirs is not valid for command \"templ\"")
}
if j.Output != nil && j.Output.ConnEnv != "" {
e = append(e, "command \"templ\" does not support database output")
}
if j.Output != nil && j.Output.Format != "" {
e = append(e, "output.format is not valid for command \"templ\"")
}
}
return e
}
func validateTemplInput(i int, in Input) []string {
var e []string
if in.Format == "" {
return []string{fmt.Sprintf("input[%d]: missing format", i)}
}
f := strings.ToLower(in.Format)
if f == "pgsql" {
if in.ConnEnv == "" {
e = append(e, fmt.Sprintf("input[%d]: format %q requires conn_env (an environment variable name)", i, in.Format))
}
if in.Path != "" {
e = append(e, fmt.Sprintf("input[%d]: format %q takes conn_env, not path", i, in.Format))
}
} else if readerFormats[f] {
if in.Path == "" {
e = append(e, fmt.Sprintf("input[%d]: missing path", i))
}
if in.ConnEnv != "" {
e = append(e, fmt.Sprintf("input[%d]: format %q does not use conn_env", i, in.Format))
}
} else {
e = append(e, fmt.Sprintf("input[%d]: unsupported templ input format %q", i, in.Format))
}
if looksLikeSecret(in.ConnEnv) {
e = append(e, fmt.Sprintf("input[%d]: conn_env must be an environment variable name, not a connection string", i))
}
return e
}