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 }