github.com/saucelabs/saucectl@v0.175.1/internal/cmd/ini/common.go (about)

     1  package ini
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"regexp"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/rs/zerolog/log"
    13  	"github.com/saucelabs/saucectl/internal/cypress"
    14  	"github.com/saucelabs/saucectl/internal/espresso"
    15  	"github.com/saucelabs/saucectl/internal/xcuitest"
    16  
    17  	"github.com/AlecAivazis/survey/v2"
    18  	"github.com/fatih/color"
    19  	"gopkg.in/yaml.v2"
    20  )
    21  
    22  var configurators = map[string]func(cfg *initConfig) interface{}{
    23  	"cypress":     configureCypress,
    24  	"espresso":    configureEspresso,
    25  	"playwright":  configurePlaywright,
    26  	"testcafe":    configureTestcafe,
    27  	"xcuitest":    configureXCUITest,
    28  	"imagerunner": configureImageRunner,
    29  }
    30  
    31  var extraInfoDisplay = map[string]func(){
    32  	"imagerunner": displayExtraInfoImageRunner,
    33  }
    34  
    35  var sauceignores = map[string]string{
    36  	"cypress":    sauceignoreCypress,
    37  	"playwright": sauceignorePlaywright,
    38  	"testcafe":   sauceignoreTestcafe,
    39  }
    40  
    41  func saveConfigurationFiles(initCfg *initConfig) ([]string, error) {
    42  	var files []string
    43  
    44  	configFormatter, ok := configurators[initCfg.frameworkName]
    45  	if ok {
    46  		err := saveSauceConfig(configFormatter(initCfg))
    47  		if err != nil {
    48  			return []string{}, err
    49  		}
    50  		files = append(files, ".sauce/config.yml")
    51  	}
    52  
    53  	sauceignore, ok := sauceignores[initCfg.frameworkName]
    54  	if ok {
    55  		err := saveSauceIgnore(sauceignore)
    56  		if err != nil {
    57  			return []string{}, err
    58  		}
    59  		files = append(files, ".sauceignore")
    60  	}
    61  	return files, nil
    62  }
    63  
    64  func saveSauceConfig(p interface{}) error {
    65  	fi, err := os.Stat(".sauce")
    66  	if err != nil && os.IsNotExist(err) {
    67  		if err = os.Mkdir(".sauce", 0750); err != nil {
    68  			return err
    69  		}
    70  		fi, err = os.Stat(".sauce")
    71  		if err != nil {
    72  			return err
    73  		}
    74  	}
    75  	if !fi.IsDir() {
    76  		return fmt.Errorf(".sauce exists and is not a directory")
    77  	}
    78  	fd, err := os.Create(".sauce/config.yml")
    79  	if err != nil {
    80  		return err
    81  	}
    82  	defer fd.Close()
    83  	return yaml.NewEncoder(fd).Encode(p)
    84  }
    85  
    86  func saveSauceIgnore(content string) error {
    87  	fd, err := os.Create(".sauceignore")
    88  	if err != nil {
    89  		return err
    90  	}
    91  	defer fd.Close()
    92  	_, err = fd.WriteString(content)
    93  	return err
    94  }
    95  
    96  func displaySummary(files []string) {
    97  	fmt.Println()
    98  	color.HiGreen("The following files have been created:")
    99  	for _, f := range files {
   100  		color.Green("  %s", f)
   101  	}
   102  	fmt.Println()
   103  }
   104  
   105  func displayExtraInfo(framework string) {
   106  	fn, present := extraInfoDisplay[framework]
   107  	if !present {
   108  		return
   109  	}
   110  	fn()
   111  }
   112  
   113  func completeBasic(toComplete string) []string {
   114  	files, _ := filepath.Glob(fmt.Sprintf("%s%s", toComplete, "*"))
   115  	return files
   116  }
   117  
   118  func frameworkExtValidator(framework, frameworkVersion string) survey.Validator {
   119  	var exts []string
   120  	switch framework {
   121  	case espresso.Kind:
   122  		exts = []string{".apk", ".aab"}
   123  	case xcuitest.Kind:
   124  		exts = []string{".ipa", ".app"}
   125  	case cypress.Kind:
   126  		exts = []string{".js", ".ts", ".mjs", ".cjs"}
   127  		if getMajorVersion(frameworkVersion) < 10 {
   128  			exts = []string{".json"}
   129  		}
   130  	}
   131  
   132  	return extValidator(exts)
   133  }
   134  
   135  func extValidator(validExts []string) survey.Validator {
   136  	return func(s interface{}) error {
   137  		val := s.(string)
   138  		found := false
   139  		for _, ext := range validExts {
   140  			if strings.HasSuffix(val, ext) {
   141  				found = true
   142  			}
   143  		}
   144  		if !found {
   145  			return fmt.Errorf("invalid extension. must be one of the following: %s", strings.Join(validExts, ", "))
   146  		}
   147  		_, err := os.Stat(val)
   148  		if err != nil {
   149  			return fmt.Errorf("%s: %v", val, err)
   150  		}
   151  		return nil
   152  	}
   153  }
   154  
   155  func dockerImageValidator() survey.Validator {
   156  	re := regexp.MustCompile(`^([\w.\-_]+((:\d+|)(/[a-z0-9._-]+/[a-z0-9._-]+))|)(/|)([a-z0-9.\-_]+(/[a-z0-9.\-_]+|))(:([\w.\-_]{1,127})|)$`)
   157  	return func(s interface{}) error {
   158  		str := s.(string)
   159  		if re.MatchString(str) {
   160  			return nil
   161  		}
   162  		return fmt.Errorf("%s is not a valid docker image", str)
   163  	}
   164  }
   165  
   166  func getMajorVersion(frameworkVersion string) int {
   167  	version := strings.Split(frameworkVersion, ".")[0]
   168  	v, err := strconv.Atoi(version)
   169  	if err != nil {
   170  		log.Err(err).Msg("failed to get framework version.")
   171  		return 0
   172  	}
   173  	return v
   174  }
   175  
   176  func sortVersions(versions []string) {
   177  	sort.Slice(versions, func(i, j int) bool {
   178  		v1 := strings.Split(versions[i], ".")
   179  		v2 := strings.Split(versions[j], ".")
   180  		v1Major, _ := strconv.Atoi(v1[0])
   181  		v2Major, _ := strconv.Atoi(v2[0])
   182  
   183  		if v1Major == v2Major && len(v1) > 1 && len(v2) > 1 {
   184  			return strings.Compare(v1[1], v2[1]) == 1
   185  		}
   186  		return v1Major > v2Major
   187  	})
   188  }
   189  
   190  func sliceContainsString(slice []string, val string) bool {
   191  	for _, value := range slice {
   192  		if value == val {
   193  			return true
   194  		}
   195  	}
   196  	return false
   197  }