github.com/jenkins-x/jx/v2@v2.1.155/pkg/quickstarts/model.go (about)

     1  package quickstarts
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/jenkins-x/jx-logging/pkg/log"
     9  	"github.com/jenkins-x/jx/v2/pkg/gits"
    10  	"github.com/jenkins-x/jx/v2/pkg/util"
    11  	"github.com/jenkins-x/jx/v2/pkg/versionstream"
    12  	"github.com/pkg/errors"
    13  	"gopkg.in/AlecAivazis/survey.v1"
    14  )
    15  
    16  const (
    17  	// JenkinsXQuickstartsOwner default quickstart owner
    18  	JenkinsXQuickstartsOwner = "jenkins-x-quickstarts"
    19  )
    20  
    21  // GitQuickstart returns a github based quickstart
    22  func GitQuickstart(provider gits.GitProvider, owner string, repo string, version string, downloadURL string, language string, framework string, tags ...string) *Quickstart {
    23  	return &Quickstart{
    24  		ID:             owner + "/" + repo,
    25  		Owner:          owner,
    26  		Name:           repo,
    27  		Version:        version,
    28  		Language:       language,
    29  		Framework:      framework,
    30  		Tags:           tags,
    31  		DownloadZipURL: downloadURL,
    32  		GitProvider:    provider,
    33  	}
    34  }
    35  
    36  func toGitHubQuickstart(provider gits.GitProvider, owner string, repo *gits.GitRepository) *Quickstart {
    37  	language := repo.Language
    38  	// TODO find this from GitHub???
    39  	framework := ""
    40  	tags := []string{}
    41  
    42  	branch := "master"
    43  	repoName := repo.Name
    44  	gitCommits, err := provider.ListCommits(owner, repoName, &gits.ListCommitsArguments{
    45  		SHA:     branch,
    46  		Page:    1,
    47  		PerPage: 1,
    48  	})
    49  	version := ""
    50  	u := ""
    51  	if err != nil {
    52  		log.Logger().Warnf("failed to load commits on branch %s for repo %s/%s due to: %s", branch, owner, repoName, err.Error())
    53  	} else if len(gitCommits) > 0 {
    54  		commit := gitCommits[0]
    55  		sha := commit.ShortSha()
    56  		version = QuickStartVersion(sha)
    57  		u = provider.BranchArchiveURL(owner, repoName, sha)
    58  	}
    59  	if u == "" {
    60  		u = provider.BranchArchiveURL(owner, repoName, "master")
    61  	}
    62  	return GitQuickstart(provider, owner, repoName, version, u, language, framework, tags...)
    63  }
    64  
    65  // QuickStartVersion creates a quickstart version string
    66  func QuickStartVersion(sha string) string {
    67  	return "1.0.0+" + sha
    68  }
    69  
    70  // LoadGithubQuickstarts Loads quickstarts from github
    71  func (model *QuickstartModel) LoadGithubQuickstarts(provider gits.GitProvider, owner string, includes []string, excludes []string) error {
    72  	repos, err := provider.ListRepositories(owner)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	for _, repo := range repos {
    77  		name := repo.Name
    78  		if util.StringMatchesAny(name, includes, excludes) {
    79  			model.Add(toGitHubQuickstart(provider, owner, repo))
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  // NewQuickstartModel creates a new quickstart model
    86  func NewQuickstartModel() *QuickstartModel {
    87  	return &QuickstartModel{
    88  		Quickstarts: map[string]*Quickstart{},
    89  	}
    90  }
    91  
    92  // SortedNames returns the sorted names of the quickstarts
    93  func (model *QuickstartModel) SortedNames() []string {
    94  	names := []string{}
    95  	for name := range model.Quickstarts {
    96  		names = append(names, name)
    97  	}
    98  	sort.Strings(names)
    99  	return names
   100  }
   101  
   102  // Add adds the given quickstart to this mode. Returns true if it was added
   103  func (model *QuickstartModel) Add(q *Quickstart) bool {
   104  	if q != nil {
   105  		id := q.ID
   106  		if id != "" {
   107  			model.Quickstarts[id] = q
   108  			return true
   109  		}
   110  	}
   111  	return false
   112  }
   113  
   114  // CreateSurvey creates a survey to query pick a quickstart
   115  func (model *QuickstartModel) CreateSurvey(filter *QuickstartFilter, batchMode bool, handles util.IOFileHandles) (*QuickstartForm, error) {
   116  	surveyOpts := survey.WithStdio(handles.In, handles.Out, handles.Err)
   117  	language := filter.Language
   118  	if language != "" {
   119  		languages := model.Languages()
   120  		if len(languages) == 0 {
   121  			// lets ignore this filter as there are none available
   122  			filter.Language = ""
   123  		} else {
   124  			lower := strings.ToLower(language)
   125  			lowerLanguages := util.StringArrayToLower(languages)
   126  			if util.StringArrayIndex(lowerLanguages, lower) < 0 {
   127  				return nil, util.InvalidOption("language", language, languages)
   128  			}
   129  		}
   130  	}
   131  	quickstarts := model.Filter(filter)
   132  	names := []string{}
   133  	m := map[string]*Quickstart{}
   134  	for _, q := range quickstarts {
   135  		name := q.SurveyName()
   136  		m[name] = q
   137  		names = append(names, name)
   138  	}
   139  	sort.Strings(names)
   140  
   141  	if len(names) == 0 {
   142  		return nil, fmt.Errorf("No quickstarts match filter")
   143  	}
   144  	answer := ""
   145  	if len(names) == 1 {
   146  		// if there's only a single option, use it
   147  		answer = names[0]
   148  	} else if batchMode {
   149  		// should not prompt for selection in batch mode so return an error
   150  		return nil, fmt.Errorf("More than one quickstart matches the current filter options. Try filtering based on other criteria (eg. Owner or Text): %v", names)
   151  	} else {
   152  		// if no single answer after filtering and we're not in batch mode then prompt
   153  		prompt := &survey.Select{
   154  			Message: "select the quickstart you wish to create",
   155  			Options: names,
   156  		}
   157  		err := survey.AskOne(prompt, &answer, survey.Required, surveyOpts)
   158  		if err != nil {
   159  			return nil, err
   160  		}
   161  	}
   162  
   163  	if answer == "" {
   164  		return nil, fmt.Errorf("No quickstart chosen")
   165  	}
   166  	q := m[answer]
   167  	if q == nil {
   168  		return nil, fmt.Errorf("Could not find chosen quickstart for %s", answer)
   169  	}
   170  	name := filter.ProjectName
   171  	form := &QuickstartForm{
   172  		Quickstart: q,
   173  		Name:       name,
   174  	}
   175  	return form, nil
   176  }
   177  
   178  // Filter filters all the available quickstarts with the filter and return the matches
   179  func (model *QuickstartModel) Filter(filter *QuickstartFilter) []*Quickstart {
   180  	answer := []*Quickstart{}
   181  	for _, name := range model.SortedNames() {
   182  		q := model.Quickstarts[name]
   183  		if filter.Matches(q) {
   184  			// If the filter matches a quickstart name exactly, return only that quickstart
   185  			if q.Name == filter.Text {
   186  				return []*Quickstart{q}
   187  			}
   188  			answer = append(answer, q)
   189  		}
   190  	}
   191  	return answer
   192  }
   193  
   194  // Languages returns all the languages in the quickstarts sorted
   195  func (model *QuickstartModel) Languages() []string {
   196  	m := map[string]string{}
   197  	for _, q := range model.Quickstarts {
   198  		l := q.Language
   199  		if l != "" {
   200  			m[l] = l
   201  		}
   202  	}
   203  	return util.SortedMapKeys(m)
   204  }
   205  
   206  func (model *QuickstartModel) LoadQuickStarts(quickstarts *versionstream.QuickStarts) error {
   207  	for _, from := range quickstarts.QuickStarts {
   208  		id := from.ID
   209  		if id == "" {
   210  			log.Logger().Warnf("no ID available for quickstart in version stream %#v", from)
   211  			continue
   212  		}
   213  		to := model.Quickstarts[id]
   214  		if to == nil {
   215  			to = &Quickstart{}
   216  		}
   217  		err := model.convertToQuickStart(from, to)
   218  		if err != nil {
   219  			return errors.Wrapf(err, "failed to convert quickstart from the version stream %s", id)
   220  		}
   221  		model.Quickstarts[id] = to
   222  	}
   223  	return nil
   224  }
   225  
   226  func (model *QuickstartModel) convertToQuickStart(from *versionstream.QuickStart, to *Quickstart) error {
   227  	s := func(text string, override string) string {
   228  		if override != "" {
   229  			return override
   230  		}
   231  		return text
   232  	}
   233  	ss := func(texts []string, overrides []string) []string {
   234  		answer := append([]string{}, texts...)
   235  		for _, o := range overrides {
   236  			if util.StringArrayIndex(answer, o) < 0 {
   237  				answer = append(answer, o)
   238  			}
   239  		}
   240  		return answer
   241  	}
   242  
   243  	to.ID = s(to.ID, from.ID)
   244  	to.Owner = s(to.Owner, from.Owner)
   245  	to.Name = s(to.Name, from.Name)
   246  	to.Version = s(to.Version, from.Version)
   247  	to.DownloadZipURL = s(to.DownloadZipURL, from.DownloadZipURL)
   248  	to.Framework = s(to.Framework, from.Framework)
   249  	to.Language = s(to.Language, from.Language)
   250  	to.Tags = ss(to.Tags, from.Tags)
   251  	return nil
   252  }