package postgresadmin import ( "embed" "fmt" "path/filepath" "regexp" "sort" "strconv" "strings" ) type Migration struct { Version int Name string SQL string } //go:embed migrations/*.sql var migrationFiles embed.FS var loadedMigrations = mustLoadMigrations() var migrationNamePattern = regexp.MustCompile(`^(\d{4})_([a-z][a-z0-9_]*)\.sql$`) func Migrations() []Migration { return append([]Migration(nil), loadedMigrations...) } func mustLoadMigrations() []Migration { entries, err := migrationFiles.ReadDir("migrations") if err != nil { panic(fmt.Sprintf("postgresadmin: read embedded migrations: %v", err)) } result := make([]Migration, 0, len(entries)) for _, entry := range entries { if entry.IsDir() { continue } matches := migrationNamePattern.FindStringSubmatch(entry.Name()) if len(matches) != 3 { panic("postgresadmin: invalid migration filename " + entry.Name()) } version, err := strconv.Atoi(matches[1]) if err != nil || version <= 0 { panic("postgresadmin: invalid migration version " + entry.Name()) } payload, err := migrationFiles.ReadFile(filepath.ToSlash("migrations/" + entry.Name())) if err != nil { panic(fmt.Sprintf("postgresadmin: read migration %s: %v", entry.Name(), err)) } if strings.TrimSpace(string(payload)) == "" { panic("postgresadmin: empty migration " + entry.Name()) } result = append(result, Migration{Version: version, Name: matches[2], SQL: string(payload)}) } sort.Slice(result, func(left, right int) bool { return result[left].Version < result[right].Version }) for index := 1; index < len(result); index++ { if result[index-1].Version == result[index].Version { panic(fmt.Sprintf("postgresadmin: duplicate migration version %d", result[index].Version)) } } if len(result) == 0 { panic("postgresadmin: no embedded migrations") } return result }