Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
288 changes: 288 additions & 0 deletions internal/question/models/generate_versions.go
Original file line number Diff line number Diff line change
@@ -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, &registry); 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
}
44 changes: 23 additions & 21 deletions internal/question/models/version.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
//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"},
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.0", "10.11", "10.6", "10.5", "10.4", "10.3"},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are deprecated version based on https://docs.upsun.com/add-services/mysql.html#deprecated-versions could we maybe not include them to avoid user to use them on new projects?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem here is that MySQL needs to be treated as a synonym of MariaDB.

(The generation code is correctly only using the supported key from the registry, but the registry has old versions listed for mysql and the correct versions listed for mariadb)

NetworkStorage: {"1.0"},
OpenSearch: {"3", "2", "1"},
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: {"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"},
}
)
Expand Down
8 changes: 4 additions & 4 deletions platformifier/platformifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"},
},
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion platformifier/templates/generic/.platform/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
{{ else }}
# relationships
# db:
# type: postgresql:14
# type: postgresql:17
# disk: 1024
{{ end -}}
2 changes: 1 addition & 1 deletion platformifier/templates/upsun/.upsun/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ services:
{{- else }}
# services:
# db:
# type: postgresql:14
# type: postgresql:17
{{ end }}

# The routes of the project.
Expand Down
Loading
Loading