package main import ( "bytes" "fmt" "io/ioutil" "os" "sort" "strconv" "strings" "text/template" ) // generate looks for migration sql in a directory for the given dialect and // applies the templates below to the contents of the files, building up a // migrations map for the dialect func generate(dialect string) { baseDir := os.Getenv("GEN_BASEPATH") + "/internal/db/schema" srcDir := baseDir + "/migrations" dir, err := os.Open(fmt.Sprintf("%s/%s", srcDir, dialect)) if err != nil { fmt.Printf("error opening dir with dialect %s: %v\n", dialect, err) os.Exit(1) } versions, err := dir.Readdirnames(0) if err != nil { fmt.Printf("error reading dir names with dialect %s: %v\n", dialect, err) os.Exit(1) } sort.Strings(versions) type ContentValues struct { Name string Content string } var upContents []ContentValues var largestSchemaVersion int for _, ver := range versions { if strings.HasPrefix(ver, ".") { continue } var verVal int verVal, err := strconv.Atoi(ver) if err != nil { fmt.Printf("error reading major schema version directory %q. Must be a number: %v\n", ver, err) os.Exit(1) } dir, err := os.Open(fmt.Sprintf("%s/%s/%s", srcDir, dialect, ver)) if err != nil { fmt.Printf("error opening dir with dialect %s: %v\n", dialect, err) os.Exit(1) } names, err := dir.Readdirnames(0) if err != nil { fmt.Printf("error reading dir names with dialect %s: %v\n", dialect, err) os.Exit(1) } sort.Strings(names) for _, name := range names { if !strings.HasSuffix(name, ".sql") { continue } nameParts := strings.SplitN(name, "_", 2) if len(nameParts) != 2 { continue } v, err := strconv.Atoi(nameParts[0]) if err != nil { fmt.Printf("Unable to get file version from %q\n", name) continue } fullV := (verVal * 1000) + v if fullV > largestSchemaVersion { largestSchemaVersion = fullV } cbts, err := ioutil.ReadFile(fmt.Sprintf("%s/%s/%s/%s", srcDir, dialect, ver, name)) if err != nil { fmt.Printf("error opening file %s with dialect %s: %v", name, dialect, err) os.Exit(1) } contents := strings.TrimSpace(string(cbts)) if strings.ToLower(contents[:len("begin;")]) == "begin;" { contents = contents[len("begin;"):] } if strings.ToLower(contents[len(contents)-len("commit;"):]) == "commit;" { contents = contents[:len(contents)-len("commit;")] } contents = strings.TrimSpace(contents) cv := ContentValues{ Name: fmt.Sprint(fullV), Content: contents, } switch { case strings.Contains(nameParts[1], ".up."): upContents = append(upContents, cv) } } } outBuf := bytes.NewBuffer(nil) if err := migrationsTemplate.Execute(outBuf, struct { Type string UpValues []ContentValues BinarySchemaVersion int }{ Type: dialect, UpValues: upContents, BinarySchemaVersion: largestSchemaVersion, }); err != nil { fmt.Printf("error executing migrations value template for dialect %s: %s", dialect, err) os.Exit(1) } outFile := fmt.Sprintf("%s/%s_migration.gen.go", baseDir, dialect) if err := ioutil.WriteFile(outFile, outBuf.Bytes(), 0o644); err != nil { fmt.Printf("error writing file %q: %v\n", outFile, err) os.Exit(1) } } var migrationsTemplate = template.Must(template.Must(template.New("Content").Parse( `{{ .Name }}: []byte(` + "`\n{{ .Content }}\n`" + `), `)).New("MainPage").Parse(`package schema // Code generated by "make migrations"; DO NOT EDIT. func init() { migrationStates["{{ .Type }}"] = migrationState{ binarySchemaVersion: {{ .BinarySchemaVersion }}, upMigrations: map[int][]byte{ {{range .UpValues }}{{ template "Content" . }}{{end}} }, } } `))