diff --git a/internal/question/models/generate_versions.go b/internal/question/models/generate_versions.go new file mode 100644 index 0000000..9a54d76 --- /dev/null +++ b/internal/question/models/generate_versions.go @@ -0,0 +1,288 @@ +//go:build ignore + +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "go/format" + "io" + "log" + "net/http" + "os" + "path/filepath" + "sort" + "strings" + "text/template" +) + +const registryURL = "https://raw.githubusercontent.com/platformsh/platformsh-docs/main/shared/data/registry.json" + +type Registry map[string]Service + +type Service struct { + Description string `json:"description"` + Disk bool `json:"disk"` + Runtime bool `json:"runtime"` + Type string `json:"type"` + Versions map[string][]string `json:"versions"` +} + +// Mapping from registry service names to our ServiceName constants +var serviceMapping = map[string]string{ + "chrome-headless": "ChromeHeadless", + "influxdb": "InfluxDB", + "kafka": "Kafka", + "mariadb": "MariaDB", + "memcached": "Memcached", + "mysql": "MySQL", + "network-storage": "NetworkStorage", + "opensearch": "OpenSearch", + "oracle-mysql": "OracleMySQL", + "postgresql": "PostgreSQL", + "rabbitmq": "RabbitMQ", + "redis": "Redis", + "solr": "Solr", + "varnish": "Varnish", + "vault-kms": "VaultKMS", +} + +// Mapping from registry runtime names to our Runtime constants +var runtimeMapping = map[string]string{ + "dotnet": "DotNet", + "elixir": "Elixir", + "golang": "Golang", + "java": "Java", + "nodejs": "NodeJS", + "php": "PHP", + "python": "Python", + "ruby": "Ruby", + "rust": "Rust", +} + +const versionTemplate = `//go:generate go run generate_versions.go + +package models + +var ( + LanguageTypeVersions = map[Runtime][]string{ +{{- range .Languages }} + {{ .Name }}: {{ .Versions }}, +{{- end }} + } + + ServiceTypeVersions = map[ServiceName][]string{ +{{- range .Services }} + {{ .Name }}: {{ .Versions }}, +{{- end }} + } +) + +func DefaultVersionForRuntime(r Runtime) string { + versions := LanguageTypeVersions[r] + if len(versions) == 0 { + return "" + } + return versions[0] +} +` + +type TemplateData struct { + Languages []TypeVersion + Services []TypeVersion +} + +type TypeVersion struct { + Name string + Versions string +} + +func main() { + // Fetch the registry data + resp, err := http.Get(registryURL) + if err != nil { + log.Fatalf("Failed to fetch registry: %v", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatalf("Failed to read response: %v", err) + } + + var registry Registry + if err := json.Unmarshal(body, ®istry); err != nil { + log.Fatalf("Failed to parse JSON: %v", err) + } + + var languages []TypeVersion + var services []TypeVersion + + // Process runtimes (languages) + for serviceName, service := range registry { + if !service.Runtime { + continue + } + + constantName, exists := runtimeMapping[serviceName] + if !exists { + continue // Skip runtimes we don't support + } + + supported := service.Versions["supported"] + if len(supported) == 0 { + continue + } + + versions := formatVersionSlice(supported) + languages = append(languages, TypeVersion{ + Name: constantName, + Versions: versions, + }) + } + + // Process services + for serviceName, service := range registry { + if service.Runtime { + continue + } + + constantName, exists := serviceMapping[serviceName] + if !exists { + continue // Skip services we don't support + } + + supported := service.Versions["supported"] + if len(supported) == 0 { + continue + } + + versions := formatVersionSlice(supported) + services = append(services, TypeVersion{ + Name: constantName, + Versions: versions, + }) + + // Add RedisPersistent right after Redis + if serviceName == "redis" { + services = append(services, TypeVersion{ + Name: "RedisPersistent", + Versions: versions, + }) + } + } + + // Sort for consistent output + sort.Slice(languages, func(i, j int) bool { + return languages[i].Name < languages[j].Name + }) + sort.Slice(services, func(i, j int) bool { + return services[i].Name < services[j].Name + }) + + // Generate the Go code + tmpl, err := template.New("version").Parse(versionTemplate) + if err != nil { + log.Fatalf("Failed to parse template: %v", err) + } + + var buf bytes.Buffer + data := TemplateData{ + Languages: languages, + Services: services, + } + + if err := tmpl.Execute(&buf, data); err != nil { + log.Fatalf("Failed to execute template: %v", err) + } + + // Format the generated Go code + formatted, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatalf("Failed to format Go code: %v", err) + } + + // Write to version.go file + dir, err := os.Getwd() + if err != nil { + log.Fatalf("Failed to get working directory: %v", err) + } + + outputPath := filepath.Join(dir, "version.go") + if err := os.WriteFile(outputPath, formatted, 0644); err != nil { + log.Fatalf("Failed to write version.go: %v", err) + } + + fmt.Printf("Successfully generated %s\n", outputPath) +} + +func formatVersionSlice(versions []string) string { + if len(versions) == 0 { + return "{}" + } + + // Sort versions from latest to oldest + sortedVersions := make([]string, len(versions)) + copy(sortedVersions, versions) + + sort.Slice(sortedVersions, func(i, j int) bool { + return compareVersions(sortedVersions[i], sortedVersions[j]) + }) + + quoted := make([]string, len(sortedVersions)) + for i, v := range sortedVersions { + quoted[i] = fmt.Sprintf(`"%s"`, v) + } + + return fmt.Sprintf("{%s}", strings.Join(quoted, ", ")) +} + +// compareVersions returns true if version a is greater than version b +func compareVersions(a, b string) bool { + // Parse version parts + aParts := parseVersion(a) + bParts := parseVersion(b) + + // Compare each part + maxLen := len(aParts) + if len(bParts) > maxLen { + maxLen = len(bParts) + } + + for i := 0; i < maxLen; i++ { + aVal := 0 + bVal := 0 + + if i < len(aParts) { + aVal = aParts[i] + } + if i < len(bParts) { + bVal = bParts[i] + } + + if aVal > bVal { + return true + } + if aVal < bVal { + return false + } + } + + return false // versions are equal +} + +// parseVersion parses a version string into numeric parts +func parseVersion(version string) []int { + parts := strings.Split(version, ".") + nums := make([]int, 0, len(parts)) + + for _, part := range parts { + // Handle cases like "25.05" or "1.0" or just "1" + num := 0 + fmt.Sscanf(part, "%d", &num) + nums = append(nums, num) + } + + return nums +} diff --git a/internal/question/models/service_name.go b/internal/question/models/service_name.go index 923fe6e..f42c821 100644 --- a/internal/question/models/service_name.go +++ b/internal/question/models/service_name.go @@ -8,6 +8,7 @@ import ( const ( ChromeHeadless ServiceName = "chrome-headless" + ClickHouse ServiceName = "clickhouse" InfluxDB ServiceName = "influxdb" Kafka ServiceName = "kafka" MariaDB ServiceName = "mariadb" diff --git a/internal/question/models/version.go b/internal/question/models/version.go index 7cce54f..963030d 100644 --- a/internal/question/models/version.go +++ b/internal/question/models/version.go @@ -1,34 +1,37 @@ +//go:generate go run generate_versions.go + package models var ( LanguageTypeVersions = map[Runtime][]string{ - DotNet: {"6.0", "3.1"}, - Elixir: {"1.13", "1.12", "1.11"}, - Golang: {"1.22", "1.21", "1.20"}, - Java: {"19", "18", "17", "11", "8"}, - NodeJS: {"20", "18", "16"}, - PHP: {"8.2", "8.1", "8.0"}, - Python: {"3.11", "3.10", "3.9", "3.8", "3.7"}, - Ruby: {"3.3", "3.2", "3.1", "3.0", "2.7", "2.6", "2.5", "2.4", "2.3"}, + DotNet: {"8.0", "7.0", "6.0"}, + Elixir: {"1.18", "1.15", "1.14"}, + Golang: {"1.25", "1.24", "1.23", "1.22", "1.21", "1.20"}, + Java: {"21", "19", "18", "17", "11", "8"}, + NodeJS: {"24", "22", "20"}, + PHP: {"8.4", "8.3", "8.2", "8.1"}, + Python: {"3.13", "3.12", "3.11", "3.10", "3.9", "3.8"}, + Ruby: {"3.4", "3.3", "3.2", "3.1", "3.0"}, Rust: {"1"}, } ServiceTypeVersions = map[ServiceName][]string{ - ChromeHeadless: {"95", "91", "86", "84", "83", "81", "80", "73"}, - InfluxDB: {"2.3"}, - Kafka: {"3.2"}, - MariaDB: {"11.0", "10.11", "10.6", "10.5", "10.4", "10.3"}, + ChromeHeadless: {"120", "113", "95", "91"}, + ClickHouse: {"25.3", "24.3", "23.8"}, + InfluxDB: {"2.7", "2.3"}, + Kafka: {"3.7", "3.6", "3.4", "3.2"}, + MariaDB: {"11.8", "11.4", "10.11", "10.6"}, Memcached: {"1.6", "1.5", "1.4"}, - MySQL: {"10.6", "10.5", "10.4", "10.3"}, - NetworkStorage: {"2.0"}, - OpenSearch: {"2", "1.2", "1.1"}, + MySQL: {"11.8", "11.4", "10.11", "10.6"}, + NetworkStorage: {"1.0"}, + OpenSearch: {"3", "2"}, OracleMySQL: {"8.0", "5.7"}, - PostgreSQL: {"15", "14", "13", "12", "11"}, - RabbitMQ: {"3.11", "3.10", "3.9"}, - Redis: {"7.0", "6.2"}, - RedisPersistent: {"7.0", "6.2"}, - Solr: {"9.1", "8.11"}, - Varnish: {"7.2", "7.1", "6.3", "6.0"}, + PostgreSQL: {"18", "17", "16", "15", "14", "13", "12"}, + RabbitMQ: {"4.1", "4.0", "3.13", "3.12"}, + Redis: {"8.0", "7.2"}, + RedisPersistent: {"8.0", "7.2"}, + Solr: {"9.9", "9.6", "9.4", "9.2", "9.1", "8.11"}, + Varnish: {"7.6", "7.3", "7.2", "6.0"}, VaultKMS: {"1.12"}, } ) diff --git a/platformifier/platformifier_test.go b/platformifier/platformifier_test.go index 282ba84..4651e4d 100644 --- a/platformifier/platformifier_test.go +++ b/platformifier/platformifier_test.go @@ -21,7 +21,7 @@ var ( Name: "django", Type: "python", Stack: Django, - Runtime: "python-3.9", + Runtime: "python-3.13", ApplicationRoot: "app", Environment: map[string]string{ "DJANGO_SETTINGS_MODULE": "app.settings", @@ -81,14 +81,14 @@ var ( { Name: "db", Type: "postgres", - TypeVersions: []string{"13", "14", "15"}, + TypeVersions: []string{"17", "16", "15", "14", "13", "12"}, Disk: "1024", DiskSizes: []string{"1024", "2048"}, }, { Name: "mysql", Type: "mysql", - TypeVersions: []string{"13", "14", "15"}, + TypeVersions: []string{"11.0", "10.11", "10.6", "10.5", "10.4", "10.3"}, Disk: "1024", DiskSizes: []string{"1024", "2034"}, }, @@ -141,7 +141,7 @@ var ( Name: "Laravel", Type: "php", Stack: Laravel, - Runtime: "php-8.2", + Runtime: "php-8.4", ApplicationRoot: "app", Environment: map[string]string{}, Root: "app", diff --git a/platformifier/templates/generic/.platform/services.yaml b/platformifier/templates/generic/.platform/services.yaml index 6af6665..7704303 100644 --- a/platformifier/templates/generic/.platform/services.yaml +++ b/platformifier/templates/generic/.platform/services.yaml @@ -16,6 +16,6 @@ {{ else }} # relationships # db: -# type: postgresql:14 +# type: postgresql:17 # disk: 1024 {{ end -}} diff --git a/platformifier/templates/upsun/.upsun/config.yaml b/platformifier/templates/upsun/.upsun/config.yaml index 0b8450a..7ec361e 100644 --- a/platformifier/templates/upsun/.upsun/config.yaml +++ b/platformifier/templates/upsun/.upsun/config.yaml @@ -239,7 +239,7 @@ services: {{- else }} # services: # db: -# type: postgresql:14 +# type: postgresql:17 {{ end }} # The routes of the project. diff --git a/validator/validator_test.go b/validator/validator_test.go index b0b7d0c..8c06723 100644 --- a/validator/validator_test.go +++ b/validator/validator_test.go @@ -121,7 +121,7 @@ applications: Data: []byte(` services: redis: - type: redis:6.2 + type: redis:8.0 size: AUTO `, ), @@ -158,7 +158,7 @@ applications: Data: []byte(` services: redis: - type: redis:6.2 + type: redis:8.0 size: AUTO `, ), @@ -192,7 +192,7 @@ applications: Data: []byte(` services: redis: - type: redis:6.2 + type: redis:8.0 size: AUTO `, ),