// CheckLatest checks whether this version of Helm is the latest version. // // This does not ensure that this is the latest. If a newer version is found, // this generates a message indicating that. // // The passed-in version is the base version that will be checked against the // remote release list. func CheckLatest(version string) { ver, err := release.LatestVersion() if err != nil { log.Warn("Skipped Helm version check: %s", err) return } current, err := semver.NewVersion(version) if err != nil { log.Warn("Local version %s is not well-formed", version) return } remote, err := semver.NewVersion(ver) if err != nil { log.Warn("Remote version %s is not well-formed", ver) return } if remote.GreaterThan(current) { log.Warn("A new version of Helm is available. You have %s. The latest is %v", version, ver) if dl, err := release.LatestDownloadURL(); err == nil { log.Info("Download version %s here: %s", ver, dl) } } }
func (m *memCache) put(name, version string) { m.Lock() defer m.Unlock() m.t[name] = true sv, err := semver.NewVersion(version) if err != nil { msg.Debug("Ignoring %s version %s: %s", name, version, err) return } latest, found := m.latest[name] if found { lv, err := semver.NewVersion(latest) if err == nil { if sv.GreaterThan(lv) { m.latest[name] = version } } } else { m.latest[name] = version } found = false for _, v := range m.versions[name] { if v == version { found = true } } if !found { m.versions[name] = append(m.versions[name], version) } }
func versionEquals(v1, v2 string) bool { sv1, err := semver.NewVersion(v1) if err != nil { // Fallback to string comparison. return v1 == v2 } sv2, err := semver.NewVersion(v2) if err != nil { return false } return sv1.Equal(sv2) }
// Less returns true if the version of entry a is less than the version of entry b. func (c ChartVersions) Less(a, b int) bool { // Failed parse pushes to the back. i, err := semver.NewVersion(c[a].Version) if err != nil { return true } j, err := semver.NewVersion(c[b].Version) if err != nil { return false } return i.LessThan(j) }
func getWizard(dep *cfg.Dependency) { var remote string if dep.Repository != "" { remote = dep.Repository } else { remote = "https://" + dep.Name } // Lookup dependency info and store in cache. msg.Info("--> Gathering release information for %s", dep.Name) wizardFindVersions(dep) memlatest := cache.MemLatest(remote) if memlatest != "" { dres := wizardAskLatest(memlatest, dep) if dres { dep.Reference = memlatest sv, err := semver.NewVersion(dep.Reference) if err == nil { res := wizardAskRange(sv, dep) if res == "m" { dep.Reference = "^" + sv.String() } else if res == "p" { dep.Reference = "~" + sv.String() } } } } }
// Resolve resolves dependencies and returns a lock file with the resolution. func (r *Resolver) Resolve(reqs *chartutil.Requirements) (*chartutil.RequirementsLock, error) { d, err := HashReq(reqs) if err != nil { return nil, err } // Now we clone the dependencies, locking as we go. locked := make([]*chartutil.Dependency, len(reqs.Dependencies)) for i, d := range reqs.Dependencies { // Right now, we're just copying one entry to another. What we need to // do here is parse the requirement as a SemVer range, and then look up // whether a version in index.yaml satisfies this constraint. If so, // we need to clone the dep, setting Version appropriately. // If not, we need to error out. if _, err := semver.NewVersion(d.Version); err != nil { return nil, fmt.Errorf("dependency %q has an invalid version: %s", d.Name, err) } locked[i] = &chartutil.Dependency{ Name: d.Name, Repository: d.Repository, Version: d.Version, } } return &chartutil.RequirementsLock{ Generated: time.Now(), Digest: d, Dependencies: locked, }, nil }
func benchCheckVersion(c, v string, b *testing.B) { version, _ := semver.NewVersion(v) constraint, _ := semver.NewConstraint(c) for i := 0; i < b.N; i++ { constraint.Check(version) } }
func benchValidateVersion(c, v string, b *testing.B) { version, _ := semver.NewVersion(v) constraint, _ := semver.NewConstraint(c) for i := 0; i < b.N; i++ { constraint.Validate(version) } }
func (m *memCache) setCurrent(name, version string) { m.Lock() defer m.Unlock() if m.c[name] == "" { m.c[name] = version } else { // If we already have a version try to see if the new or old one is // semver and use that one. _, err := semver.NewVersion(m.c[name]) if err != nil { _, err2 := semver.NewVersion(version) if err2 == nil { m.c[name] = version } } } }
// Resolve resolves dependencies and returns a lock file with the resolution. func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]string) (*chartutil.RequirementsLock, error) { d, err := HashReq(reqs) if err != nil { return nil, err } // Now we clone the dependencies, locking as we go. locked := make([]*chartutil.Dependency, len(reqs.Dependencies)) missing := []string{} for i, d := range reqs.Dependencies { constraint, err := semver.NewConstraint(d.Version) if err != nil { return nil, fmt.Errorf("dependency %q has an invalid version/constraint format: %s", d.Name, err) } repoIndex, err := repo.LoadIndexFile(r.helmhome.CacheIndex(repoNames[d.Name])) if err != nil { return nil, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err) } vs, ok := repoIndex.Entries[d.Name] if !ok { return nil, fmt.Errorf("%s chart not found in repo %s", d.Name, d.Repository) } locked[i] = &chartutil.Dependency{ Name: d.Name, Repository: d.Repository, } found := false // The version are already sorted and hence the first one to satisfy the constraint is used for _, ver := range vs { v, err := semver.NewVersion(ver.Version) if err != nil || len(ver.URLs) == 0 { // Not a legit entry. continue } if constraint.Check(v) { found = true locked[i].Version = v.Original() break } } if !found { missing = append(missing, d.Name) } } if len(missing) > 0 { return nil, fmt.Errorf("Can't get a valid version for repositories %s. Try changing the version constraint in requirements.yaml", strings.Join(missing, ", ")) } return &chartutil.RequirementsLock{ Generated: time.Now(), Digest: d, Dependencies: locked, }, nil }
// Filter a list of versions to only included semantic versions. The response // is a mapping of the original version to the semantic version. func getSemVers(refs []string) []*semver.Version { sv := []*semver.Version{} for _, r := range refs { v, err := semver.NewVersion(r) if err == nil { sv = append(sv, v) } } return sv }
// VersionOK returns true if the given version meets the constraints. // // It returns false if the version string or constraint is unparsable or if the // version does not meet the constraint. func (d *Dependency) VersionOK(version string) bool { c, err := semver.NewConstraint(d.Version) if err != nil { return false } v, err := semver.NewVersion(version) if err != nil { return false } return c.Check(v) }
// Less compares a to b, and returns true if a is less than b. func (s scoreSorter) Less(a, b int) bool { first := s[a] second := s[b] if first.Score > second.Score { return false } if first.Score < second.Score { return true } if first.Name == second.Name { v1, err := semver.NewVersion(first.Chart.Version) if err != nil { return true } v2, err := semver.NewVersion(second.Chart.Version) if err != nil { return true } return v1.GreaterThan(v2) } return first.Name < second.Name }
// CheckLatest checks whether this version of Helm Classic is the latest version. // // This does not ensure that this is the latest. If a newer version is found, // this generates a message indicating that. // // The passed-in version is the base version that will be checked against the // remote release list. func CheckLatest(version string) { ver, err := release.LatestVersion() if err != nil { log.Warn("Skipped Helm Classic version check: %s", err) return } current, err := semver.NewVersion(version) if err != nil { log.Warn("Local version %s is not well-formed", version) return } remote, err := semver.NewVersion(ver) if err != nil { log.Warn("Remote version %s is not well-formed", ver) return } if remote.GreaterThan(current) { log.Warn("A new version of Helm Classic is available. You have %s. The latest is %v", version, ver) log.Info("Download version %s by running: %s", ver, "curl -s https://get.helm.sh | bash") } }
func getSemverTags() []*semver.Version { rowTags := git("tag") tags := bytes.Split(rowTags, []byte("\n")) tags = tags[:len(tags)-1] vs := make([]*semver.Version, 0) for _, r := range tags { v, err := semver.NewVersion(string(r)) if err == nil { vs = append(vs, v) } } sort.Sort(sort.Reverse(semver.Collection(vs))) return vs }
func validateChartVersion(cf *chart.Metadata) error { if cf.Version == "" { return errors.New("version is required") } version, err := semver.NewVersion(cf.Version) if err != nil { return fmt.Errorf("version '%s' is not a valid SemVer", cf.Version) } c, err := semver.NewConstraint("> 0") valid, msg := c.Validate(version) if !valid && len(msg) > 0 { return fmt.Errorf("version %v", msg[0]) } return nil }
func validateChartVersion(cf *chart.Metadata) (lintError support.LintError) { if cf.Version == "" { lintError = fmt.Errorf("Chart.yaml: 'version' value is required") return } version, err := semver.NewVersion(cf.Version) if err != nil { lintError = fmt.Errorf("Chart.yaml: version '%s' is not a valid SemVer", cf.Version) return } c, err := semver.NewConstraint("> 0") valid, msg := c.Validate(version) if !valid && len(msg) > 0 { lintError = fmt.Errorf("Chart.yaml: 'version' %v", msg[0]) } return }
// ConfigWizard reads configuration from a glide.yaml file and attempts to suggest // improvements. The wizard is interactive. func ConfigWizard(base string) { _, err := gpath.Glide() glidefile := gpath.GlideFile if err != nil { msg.Info("Unable to find a glide.yaml file. Would you like to create one now? Yes (Y) or No (N)") bres := msg.PromptUntilYorN() if bres { // Guess deps conf := guessDeps(base, false) // Write YAML if err := conf.WriteFile(glidefile); err != nil { msg.Die("Could not save %s: %s", glidefile, err) } } else { msg.Err("Unable to find configuration file. Please create configuration information to continue.") } } conf := EnsureConfig() err = cache.Setup() if err != nil { msg.Die("Problem setting up cache: %s", err) } msg.Info("Looking for dependencies to make suggestions on") msg.Info("--> Scanning for dependencies not using version ranges") msg.Info("--> Scanning for dependencies using commit ids") var deps []*cfg.Dependency for _, dep := range conf.Imports { if wizardLookInto(dep) { deps = append(deps, dep) } } for _, dep := range conf.DevImports { if wizardLookInto(dep) { deps = append(deps, dep) } } msg.Info("Gathering information on each dependency") msg.Info("--> This may take a moment. Especially on a codebase with many dependencies") msg.Info("--> Gathering release information for dependencies") msg.Info("--> Looking for dependency imports where versions are commit ids") for _, dep := range deps { wizardFindVersions(dep) } var changes int for _, dep := range deps { var remote string if dep.Repository != "" { remote = dep.Repository } else { remote = "https://" + dep.Name } // First check, ask if the tag should be used instead of the commit id for it. cur := cache.MemCurrent(remote) if cur != "" && cur != dep.Reference { wizardSugOnce() var dres bool asked, use, val := wizardOnce("current") if !use { dres = wizardAskCurrent(cur, dep) } if !asked { as := wizardRemember() wizardSetOnce("current", as, dres) } if asked && use { dres = val.(bool) } if dres { msg.Info("Updating %s to use the tag %s instead of commit id %s", dep.Name, cur, dep.Reference) dep.Reference = cur changes++ } } // Second check, if no version is being used and there's a semver release ask about latest. memlatest := cache.MemLatest(remote) if dep.Reference == "" && memlatest != "" { wizardSugOnce() var dres bool asked, use, val := wizardOnce("latest") if !use { dres = wizardAskLatest(memlatest, dep) } if !asked { as := wizardRemember() wizardSetOnce("latest", as, dres) } if asked && use { dres = val.(bool) } if dres { msg.Info("Updating %s to use the release %s instead of no release", dep.Name, memlatest) dep.Reference = memlatest changes++ } } // Third check, if the version is semver offer to use a range instead. sv, err := semver.NewVersion(dep.Reference) if err == nil { wizardSugOnce() var res string asked, use, val := wizardOnce("range") if !use { res = wizardAskRange(sv, dep) } if !asked { as := wizardRemember() wizardSetOnce("range", as, res) } if asked && use { res = val.(string) } if res == "m" { r := "^" + sv.String() msg.Info("Updating %s to use the range %s instead of commit id %s", dep.Name, r, dep.Reference) dep.Reference = r changes++ } else if res == "p" { r := "~" + sv.String() msg.Info("Updating %s to use the range %s instead of commit id %s", dep.Name, r, dep.Reference) dep.Reference = r changes++ } } } if changes > 0 { msg.Info("Configuration changes have been made. Would you like to write these") msg.Info("changes to your configuration file? Yes (Y) or No (N)") dres := msg.PromptUntilYorN() if dres { msg.Info("Writing updates to configuration file (%s)", glidefile) if err := conf.WriteFile(glidefile); err != nil { msg.Die("Could not save %s: %s", glidefile, err) } msg.Info("You can now edit the glide.yaml file.:") msg.Info("--> For more information on versions and ranges see https://glide.sh/docs/versions/") msg.Info("--> For details on additional metadata see https://glide.sh/docs/glide.yaml/") } else { msg.Warn("Change not written to configuration file") } } else { msg.Info("No proposed changes found. Have a nice day.") } }
func determineDependency(v, dep *cfg.Dependency, dest string) *cfg.Dependency { repo, err := v.GetRepo(dest) if err != nil { singleWarn("Unable to access repo for %s\n", v.Name) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } vIsRef := repo.IsReference(v.Reference) depIsRef := repo.IsReference(dep.Reference) // Both are references and they are different ones. if vIsRef && depIsRef { singleWarn("Conflict: %s ref is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } else if vIsRef { // The current one is a reference and the suggestion is a SemVer constraint. con, err := semver.NewConstraint(dep.Reference) if err != nil { singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", dep.Name, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } ver, err := semver.NewVersion(v.Reference) if err != nil { // The existing version is not a semantic version. singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } if con.Check(ver) { singleInfo("Keeping %s %s because it fits constraint '%s'", v.Name, v.Reference, dep.Reference) return v } singleWarn("Conflict: %s version is %s but does not meet constraint '%s'\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } else if depIsRef { con, err := semver.NewConstraint(v.Reference) if err != nil { singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", v.Name, v.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } ver, err := semver.NewVersion(dep.Reference) if err != nil { singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } if con.Check(ver) { v.Reference = dep.Reference singleInfo("Using %s %s because it fits constraint '%s'", v.Name, v.Reference, v.Reference) return v } singleWarn("Conflict: %s semantic version constraint is %s but '%s' does not meet the constraint\n", v.Name, v.Reference, v.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } // Neither is a vcs reference and both could be semantic version // constraints that are different. _, err = semver.NewConstraint(dep.Reference) if err != nil { // dd.Reference is not a reference or a valid constraint. singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", dep.Name, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } _, err = semver.NewConstraint(v.Reference) if err != nil { // existing.Reference is not a reference or a valid constraint. // We really should never end up here. singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", v.Name, v.Reference) v.Reference = dep.Reference v.Pin = "" singleInfo("Using %s %s because it is a valid version", v.Name, v.Reference) return v } // Both versions are constraints. Try to merge them. // If either comparison has an || skip merging. That's complicated. ddor := strings.Index(dep.Reference, "||") eor := strings.Index(v.Reference, "||") if ddor == -1 && eor == -1 { // Add the comparisons together. newRef := v.Reference + ", " + dep.Reference v.Reference = newRef v.Pin = "" singleInfo("Combining %s semantic version constraints %s and %s", v.Name, v.Reference, dep.Reference) return v } singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v }
// mergeDeps merges any dependency array into deps. func mergeDeps(orig map[string]*yaml.Dependency, add []*yaml.Dependency, vend string) []string { mod := []string{} for _, dd := range add { // Add it unless it's already there. if existing, ok := orig[dd.Name]; !ok { orig[dd.Name] = dd Debug("Adding %s to the scan list", dd.Name) mod = append(mod, dd.Name) } else if existing.Reference == "" && dd.Reference != "" { // If a nested dep has finer dependency references than outside, // set the reference. existing.Reference = dd.Reference mod = append(mod, dd.Name) } else if dd.Reference != "" && existing.Reference != "" && dd.Reference != existing.Reference { // Check if one is a version and the other is a constraint. If the // version is in the constraint use that. dest := path.Join(vend, dd.Name) repo, err := existing.GetRepo(dest) if err != nil { Warn("Unable to access repo for %s\n", existing.Name) Info("Keeping %s %s", existing.Name, existing.Reference) continue } eIsRef := repo.IsReference(existing.Reference) ddIsRef := repo.IsReference(dd.Reference) // Both are references and different ones. if eIsRef && ddIsRef { Warn("Conflict: %s ref is %s, but also asked for %s\n", existing.Name, existing.Reference, dd.Reference) Info("Keeping %s %s", existing.Name, existing.Reference) } else if eIsRef { // Test ddIsRef is a constraint and if eIsRef is a semver // within that con, err := semver.NewConstraint(dd.Reference) if err != nil { Warn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", dd.Name, dd.Reference) Info("Keeping %s %s", existing.Name, existing.Reference) continue } ver, err := semver.NewVersion(existing.Reference) if err != nil { // The existing version is not a semantic version. Warn("Conflict: %s version is %s, but also asked for %s\n", existing.Name, existing.Reference, dd.Reference) Info("Keeping %s %s", existing.Name, existing.Reference) continue } if con.Check(ver) { Info("Keeping %s %s because it fits constraint '%s'", existing.Name, existing.Reference, dd.Reference) } else { Warn("Conflict: %s version is %s but does not meet constraint '%s'\n", existing.Name, existing.Reference, dd.Reference) Info("Keeping %s %s", existing.Name, existing.Reference) } } else if ddIsRef { // Test eIsRef is a constraint and if ddIsRef is a semver // within that con, err := semver.NewConstraint(existing.Reference) if err != nil { Warn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", existing.Name, existing.Reference) Info("Keeping %s %s", existing.Name, existing.Reference) continue } ver, err := semver.NewVersion(dd.Reference) if err != nil { // The dd version is not a semantic version. Warn("Conflict: %s version is %s, but also asked for %s\n", existing.Name, existing.Reference, dd.Reference) Info("Keeping %s %s", existing.Name, existing.Reference) continue } if con.Check(ver) { // Use the specific version if noted instead of the existing // constraint. existing.Reference = dd.Reference mod = append(mod, dd.Name) Info("Using %s %s because it fits constraint '%s'", existing.Name, dd.Reference, existing.Reference) } else { Warn("Conflict: %s semantic version constraint is %s but '%s' does not meet the constraint\n", existing.Name, existing.Reference, dd.Reference) Info("Keeping %s %s", existing.Name, existing.Reference) } } else { // Neither is a vcs reference and both could be semantic version // constraints that are different. _, err := semver.NewConstraint(dd.Reference) if err != nil { // dd.Reference is not a reference or a valid constraint. Warn("Version %s %s is not a reference or valid semantic version constraint\n", dd.Name, dd.Reference) Info("Keeping %s %s", existing.Name, existing.Reference) continue } _, err = semver.NewConstraint(existing.Reference) if err != nil { // existing.Reference is not a reference or a valid constraint. // We really should never end up here. Warn("Version %s %s is not a reference or valid semantic version constraint\n", existing.Name, existing.Reference) existing.Reference = dd.Reference mod = append(mod, dd.Name) Info("Using %s %s because it is a valid version", existing.Name, existing.Reference) continue } // Both versions are constraints. Try to merge them. // If either comparison has an || skip merging. That's complicated. ddor := strings.Index(dd.Reference, "||") eor := strings.Index(existing.Reference, "||") if ddor == -1 && eor == -1 { // Add the comparisons together. newRef := existing.Reference + ", " + dd.Reference existing.Reference = newRef mod = append(mod, dd.Name) Info("Combining %s semantic version constraints %s and %s", existing.Name, existing.Reference, dd.Reference) } else { Warn("Conflict: %s version is %s, but also asked for %s\n", existing.Name, existing.Reference, dd.Reference) Info("Keeping %s %s", existing.Name, existing.Reference) } } } } return mod }
func benchNewVersion(v string, b *testing.B) { for i := 0; i < b.N; i++ { semver.NewVersion(v) } }