github.com/symfony-cli/symfony-cli@v0.0.0-20240514161054-ece2df437dfa/local/platformsh/generator/config.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"os"
    10  	"sort"
    11  	"strings"
    12  	"text/template"
    13  
    14  	"github.com/hashicorp/go-version"
    15  	"gopkg.in/yaml.v2"
    16  )
    17  
    18  type service struct {
    19  	Type     string          `json:"type"`
    20  	Runtime  bool            `json:"runtime"`
    21  	Versions serviceVersions `json:"versions"`
    22  }
    23  
    24  type serviceVersions struct {
    25  	Deprecated []string `json:"deprecated"`
    26  	Supported  []string `json:"supported"`
    27  }
    28  
    29  var configTemplate = template.Must(template.New("output").Parse(`// Code generated by platformsh/generator/main.go
    30  // DO NOT EDIT
    31  
    32  /*
    33   * Copyright (c) 2021-present Fabien Potencier <fabien@symfony.com>
    34   *
    35   * This file is part of Symfony CLI project
    36   *
    37   * This program is free software: you can redistribute it and/or modify
    38   * it under the terms of the GNU Affero General Public License as
    39   * published by the Free Software Foundation, either version 3 of the
    40   * License, or (at your option) any later version.
    41   *
    42   * This program is distributed in the hope that it will be useful,
    43   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    44   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    45   * GNU Affero General Public License for more details.
    46   *
    47   * You should have received a copy of the GNU Affero General Public License
    48   * along with this program. If not, see <http://www.gnu.org/licenses/>.
    49   */
    50  
    51  package platformsh
    52  
    53  var availablePHPExts = map[string][]string{
    54  {{ .Extensions -}}
    55  }
    56  
    57  var availableServices = []*service{
    58  {{ .Services -}}
    59  }
    60  `))
    61  
    62  func generateConfig() {
    63  	extsAsString, err := parsePHPExtensions()
    64  	if err != nil {
    65  		panic(err)
    66  	}
    67  
    68  	servicesAsString, err := parseServices()
    69  	if err != nil {
    70  		panic(err)
    71  	}
    72  
    73  	data := map[string]interface{}{
    74  		"Extensions": extsAsString,
    75  		"Services":   servicesAsString,
    76  	}
    77  	var buf bytes.Buffer
    78  	if err := configTemplate.Execute(&buf, data); err != nil {
    79  		panic(err)
    80  	}
    81  	f, err := os.Create("local/platformsh/config.go")
    82  	if err != nil {
    83  		panic(err)
    84  	}
    85  	f.Write(buf.Bytes())
    86  }
    87  
    88  func parseServices() (string, error) {
    89  	resp, err := http.Get("https://raw.githubusercontent.com/platformsh/platformsh-docs/master/shared/data/registry.json")
    90  	if err != nil {
    91  		return "", err
    92  	}
    93  	defer resp.Body.Close()
    94  	var services map[string]*service
    95  	body, err := io.ReadAll(resp.Body)
    96  	if err != nil {
    97  		return "", err
    98  	}
    99  	if err := json.Unmarshal(body, &services); err != nil {
   100  		return "", err
   101  	}
   102  	serviceNames := []string{}
   103  	for name := range services {
   104  		serviceNames = append(serviceNames, name)
   105  	}
   106  	sort.Strings(serviceNames)
   107  	servicesAsString := ""
   108  	for _, name := range serviceNames {
   109  		s := services[name]
   110  		if !s.Runtime {
   111  			deprecatedVersions, err := sortVersions(s.Versions.Deprecated)
   112  			if err != nil {
   113  				return "", err
   114  			}
   115  			supportedVersions, err := sortVersions(s.Versions.Supported)
   116  			if err != nil {
   117  				return "", err
   118  			}
   119  
   120  			servicesAsString += "\t{\n"
   121  			servicesAsString += fmt.Sprintf("\t\tType: \"%s\",\n", s.Type)
   122  			servicesAsString += "\t\tVersions: serviceVersions{\n"
   123  			if len(deprecatedVersions) > 0 {
   124  				servicesAsString += fmt.Sprintf("\t\t\tDeprecated: []string{\"%s\"},\n", strings.Join(deprecatedVersions, "\", \""))
   125  			} else {
   126  				servicesAsString += "\t\t\tDeprecated: []string{},\n"
   127  			}
   128  			if len(supportedVersions) > 0 {
   129  				servicesAsString += fmt.Sprintf("\t\t\tSupported:  []string{\"%s\"},\n", strings.Join(supportedVersions, "\", \""))
   130  			} else {
   131  				servicesAsString += "\t\t\tSupported: []string{},\n"
   132  			}
   133  			servicesAsString += "\t\t},\n"
   134  			servicesAsString += "\t},\n"
   135  		}
   136  	}
   137  	return servicesAsString, nil
   138  }
   139  
   140  func parsePHPExtensions() (string, error) {
   141  	resp, err := http.Get("https://raw.githubusercontent.com/platformsh/platformsh-docs/master/shared/data/php_extensions.yaml")
   142  	if err != nil {
   143  		return "", err
   144  	}
   145  	defer resp.Body.Close()
   146  	var versions []string
   147  	orderedExtensionNames := []string{}
   148  	extensions := make(map[string][]string)
   149  
   150  	body, err := io.ReadAll(resp.Body)
   151  	if err != nil {
   152  		return "", err
   153  	}
   154  	var fullConfig struct {
   155  		Grid map[string]struct {
   156  			Available []string
   157  			Default   []string
   158  		}
   159  	}
   160  	if err := yaml.Unmarshal(body, &fullConfig); err != nil {
   161  		return "", err
   162  	}
   163  	for version, cfg := range fullConfig.Grid {
   164  		for _, ext := range append(cfg.Available, cfg.Default...) {
   165  			name := strings.ToLower(ext)
   166  			if _, ok := extensions[name]; !ok {
   167  				orderedExtensionNames = append(orderedExtensionNames, name)
   168  			}
   169  			extensions[name] = append(extensions[name], version)
   170  		}
   171  	}
   172  
   173  	sort.Strings(orderedExtensionNames)
   174  	maxNameLen := 0
   175  	for name := range extensions {
   176  		if len(name) > maxNameLen {
   177  			maxNameLen = len(name)
   178  		}
   179  		sort.Strings(extensions[name])
   180  	}
   181  	extsAsString := ""
   182  
   183  	for _, name := range orderedExtensionNames {
   184  		versions = extensions[name]
   185  		line := fmt.Sprintf("\t\"%s\":%s{", name, strings.Repeat(" ", maxNameLen-len(name)+1))
   186  		for i, version := range versions {
   187  			line = line + fmt.Sprintf(`"%s"`, version)
   188  			if i != len(versions)-1 {
   189  				line = line + ", "
   190  			}
   191  		}
   192  		line = line + "},"
   193  		extsAsString = extsAsString + line + "\n"
   194  	}
   195  	return extsAsString, nil
   196  }
   197  
   198  func parseLine(line string) (string, []string) {
   199  	next := strings.Index(line[1:], "|") + 1
   200  	name := strings.TrimSpace(line[1:next])
   201  	var versions []string
   202  	for {
   203  		current := next + 1
   204  		nextIndex := strings.Index(line[current:], "|")
   205  		if nextIndex == -1 {
   206  			break
   207  		}
   208  		next = nextIndex + current
   209  		versions = append(versions, strings.TrimSpace(line[current:next]))
   210  		if next >= len(line) {
   211  			break
   212  		}
   213  	}
   214  	return name, versions
   215  }
   216  
   217  func sortVersions(versions []string) ([]string, error) {
   218  	parsedVersions := make([]*version.Version, len(versions))
   219  	for i, raw := range versions {
   220  		v, err := version.NewVersion(raw)
   221  		if err != nil {
   222  			return nil, err
   223  		}
   224  		parsedVersions[i] = v
   225  	}
   226  	sort.Sort(version.Collection(parsedVersions))
   227  	versionsAsStrings := make([]string, len(versions))
   228  	for i, version := range parsedVersions {
   229  		versionsAsStrings[i] = version.Original()
   230  	}
   231  	return versionsAsStrings, nil
   232  }