diff --git a/cmd/tree.go b/cmd/tree.go index bfba63d7..d2f907cb 100644 --- a/cmd/tree.go +++ b/cmd/tree.go @@ -33,5 +33,6 @@ func init() { treeGroupCmd.AddCommand( NewTreePkglistCommand(), NewTreeValidateCommand(), + NewTreeBumpCommand(), ) } diff --git a/cmd/tree/bump.go b/cmd/tree/bump.go new file mode 100644 index 00000000..71eceeed --- /dev/null +++ b/cmd/tree/bump.go @@ -0,0 +1,67 @@ +// Copyright © 2020 Ettore Di Giacinto +// Daniele Rondina +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, see . + +package cmd_tree + +import ( + "fmt" + //"os" + //"sort" + + . "github.com/mudler/luet/pkg/logger" + tree "github.com/mudler/luet/pkg/tree" + + "github.com/spf13/cobra" +) + +func NewTreeBumpCommand() *cobra.Command { + + var ans = &cobra.Command{ + Use: "bump [OPTIONS]", + Short: "Bump a new package build version.", + Args: cobra.OnlyValidArgs, + PreRun: func(cmd *cobra.Command, args []string) { + df, _ := cmd.Flags().GetString("definition-file") + if df == "" { + Fatal("Mandatory definition.yaml path missing.") + } + }, + Run: func(cmd *cobra.Command, args []string) { + spec, _ := cmd.Flags().GetString("definition-file") + pack, err := tree.ReadDefinitionFile(spec) + if err != nil { + Fatal(err.Error()) + } + + // Retrieve version build section with Gentoo parser + err = pack.BumpBuildVersion() + if err != nil { + Fatal("Error on increment build version: " + err.Error()) + } + + err = tree.WriteDefinitionFile(&pack, spec) + if err != nil { + Fatal("Error on write definition file: " + err.Error()) + } + + fmt.Printf("Bumped package %s/%s-%s.\n", pack.Category, pack.Name, pack.Version) + }, + } + + ans.Flags().StringP("definition-file", "f", "", "Path of the definition to bump.") + + return ans +} diff --git a/pkg/package/package.go b/pkg/package/package.go index 8e72066d..4dc48e1c 100644 --- a/pkg/package/package.go +++ b/pkg/package/package.go @@ -22,16 +22,17 @@ import ( "fmt" "io" "path/filepath" + "regexp" "sort" + "strconv" "strings" - // . "github.com/mudler/luet/pkg/logger" - + gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo" "github.com/crillab/gophersat/bf" + "github.com/ghodss/yaml" version "github.com/hashicorp/go-version" "github.com/jinzhu/copier" - - "github.com/ghodss/yaml" + "github.com/pkg/errors" ) // Package is a package interface (TBD) @@ -62,6 +63,7 @@ type Package interface { GetVersion() string RequiresContains(PackageDatabase, Package) (bool, error) Matches(m Package) bool + BumpBuildVersion() error AddUse(use string) RemoveUse(use string) @@ -334,7 +336,6 @@ func (p *DefaultPackage) Matches(m Package) bool { return false } - func (p *DefaultPackage) Expand(definitiondb PackageDatabase) ([]Package, error) { var versionsInWorld []Package @@ -608,3 +609,89 @@ func (p *DefaultPackage) Explain() { fmt.Println("====================") } + +func (p *DefaultPackage) BumpBuildVersion() error { + cat := p.Category + if cat == "" { + // Use fake category for parse package + cat = "app" + } + gp, err := gentoo.ParsePackageStr( + fmt.Sprintf("%s/%s-%s", cat, + p.Name, p.GetVersion())) + if err != nil { + return errors.Wrap(err, "Error on parser version") + } + + buildPrefix := "" + buildId := 0 + + if gp.VersionBuild != "" { + // Check if version build is a number + buildId, err = strconv.Atoi(gp.VersionBuild) + if err == nil { + goto end + } + // POST: is not only a number + + // TODO: check if there is a better way to handle all use cases. + + r1 := regexp.MustCompile(`^r[0-9]*$`) + if r1 == nil { + return errors.New("Error on create regex for -r[0-9]") + } + if r1.MatchString(gp.VersionBuild) { + buildId, err = strconv.Atoi(strings.ReplaceAll(gp.VersionBuild, "r", "")) + if err == nil { + buildPrefix = "r" + goto end + } + } + + p1 := regexp.MustCompile(`^p[0-9]*$`) + if p1 == nil { + return errors.New("Error on create regex for -p[0-9]") + } + if p1.MatchString(gp.VersionBuild) { + buildId, err = strconv.Atoi(strings.ReplaceAll(gp.VersionBuild, "p", "")) + if err == nil { + buildPrefix = "p" + goto end + } + } + + rc1 := regexp.MustCompile(`^rc[0-9]*$`) + if rc1 == nil { + return errors.New("Error on create regex for -rc[0-9]") + } + if rc1.MatchString(gp.VersionBuild) { + buildId, err = strconv.Atoi(strings.ReplaceAll(gp.VersionBuild, "rc", "")) + if err == nil { + buildPrefix = "rc" + goto end + } + } + + // Check if version build contains a dot + dotIdx := strings.LastIndex(gp.VersionBuild, ".") + if dotIdx > 0 { + buildPrefix = gp.VersionBuild[0 : dotIdx+1] + bVersion := gp.VersionBuild[dotIdx+1:] + buildId, err = strconv.Atoi(bVersion) + if err == nil { + goto end + } + } + + buildPrefix = gp.VersionBuild + "." + buildId = 0 + } + +end: + + buildId++ + p.Version = fmt.Sprintf("%s%s+%s%d", + gp.Version, gp.VersionSuffix, buildPrefix, buildId) + + return nil +} diff --git a/pkg/package/package_test.go b/pkg/package/package_test.go index dd65bc50..d33c10b4 100644 --- a/pkg/package/package_test.go +++ b/pkg/package/package_test.go @@ -252,4 +252,69 @@ var _ = Describe("Package", func() { Expect(len(a1.GetUses())).To(Equal(0)) }) }) + + Context("Check Bump build Version", func() { + It("Bump without build version", func() { + a1 := NewPackage("A", "1.0", []*DefaultPackage{}, []*DefaultPackage{}) + err := a1.BumpBuildVersion() + Expect(err).ToNot(HaveOccurred()) + Expect(a1.GetVersion()).To(Equal("1.0+1")) + }) + It("Bump 2", func() { + p := NewPackage("A", "1.0+1", []*DefaultPackage{}, []*DefaultPackage{}) + err := p.BumpBuildVersion() + Expect(err).ToNot(HaveOccurred()) + Expect(p.GetVersion()).To(Equal("1.0+2")) + }) + + It("Bump 3", func() { + p := NewPackage("A", "1.0+100", []*DefaultPackage{}, []*DefaultPackage{}) + err := p.BumpBuildVersion() + Expect(err).ToNot(HaveOccurred()) + Expect(p.GetVersion()).To(Equal("1.0+101")) + }) + + It("Bump 4", func() { + p := NewPackage("A", "1.0+r1", []*DefaultPackage{}, []*DefaultPackage{}) + err := p.BumpBuildVersion() + Expect(err).ToNot(HaveOccurred()) + Expect(p.GetVersion()).To(Equal("1.0+r2")) + }) + + It("Bump 5", func() { + p := NewPackage("A", "1.0+p1", []*DefaultPackage{}, []*DefaultPackage{}) + err := p.BumpBuildVersion() + Expect(err).ToNot(HaveOccurred()) + Expect(p.GetVersion()).To(Equal("1.0+p2")) + }) + + It("Bump 6", func() { + p := NewPackage("A", "1.0+pre20200315", []*DefaultPackage{}, []*DefaultPackage{}) + err := p.BumpBuildVersion() + Expect(err).ToNot(HaveOccurred()) + Expect(p.GetVersion()).To(Equal("1.0+pre20200315.1")) + }) + + It("Bump 7", func() { + p := NewPackage("A", "1.0+pre20200315.1", []*DefaultPackage{}, []*DefaultPackage{}) + err := p.BumpBuildVersion() + Expect(err).ToNot(HaveOccurred()) + Expect(p.GetVersion()).To(Equal("1.0+pre20200315.2")) + }) + + It("Bump 8", func() { + p := NewPackage("A", "1.0+d-r1", []*DefaultPackage{}, []*DefaultPackage{}) + err := p.BumpBuildVersion() + Expect(err).ToNot(HaveOccurred()) + Expect(p.GetVersion()).To(Equal("1.0+d-r1.1")) + }) + + It("Bump 9", func() { + p := NewPackage("A", "1.0+p20200315.1", []*DefaultPackage{}, []*DefaultPackage{}) + err := p.BumpBuildVersion() + Expect(err).ToNot(HaveOccurred()) + Expect(p.GetVersion()).To(Equal("1.0+p20200315.2")) + }) + }) + }) diff --git a/pkg/tree/compiler_recipe.go b/pkg/tree/compiler_recipe.go index bea03a3b..d3c9f7fe 100644 --- a/pkg/tree/compiler_recipe.go +++ b/pkg/tree/compiler_recipe.go @@ -38,6 +38,20 @@ func NewCompilerRecipe(d pkg.PackageDatabase) Builder { return &CompilerRecipe{Recipe: Recipe{Database: d}} } +func ReadDefinitionFile(path string) (pkg.DefaultPackage, error) { + empty := pkg.DefaultPackage{} + dat, err := ioutil.ReadFile(path) + if err != nil { + return empty, errors.Wrap(err, "Error reading file "+path) + } + pack, err := pkg.DefaultPackageFromYaml(dat) + if err != nil { + return empty, errors.Wrap(err, "Error reading yaml "+path) + } + + return pack, nil +} + // Recipe is the "general" reciper for Trees type CompilerRecipe struct { Recipe @@ -64,13 +78,9 @@ func (r *CompilerRecipe) Load(path string) error { return nil // Skip with no errors } - dat, err := ioutil.ReadFile(currentpath) + pack, err := ReadDefinitionFile(currentpath) if err != nil { - return errors.Wrap(err, "Error reading file "+currentpath) - } - pack, err := pkg.DefaultPackageFromYaml(dat) - if err != nil { - return errors.Wrap(err, "Error reading yaml "+currentpath) + return err } // Path is set only internally when tree is loaded from disk pack.SetPath(filepath.Dir(currentpath)) @@ -78,7 +88,7 @@ func (r *CompilerRecipe) Load(path string) error { // Instead of rdeps, have a different tree for build deps. compileDefPath := pack.Rel(CompilerDefinitionFile) if helpers.Exists(compileDefPath) { - dat, err = ioutil.ReadFile(compileDefPath) + dat, err := ioutil.ReadFile(compileDefPath) if err != nil { return errors.Wrap(err, "Error reading file "+CompilerDefinitionFile+" from "+ diff --git a/pkg/tree/recipes.go b/pkg/tree/recipes.go index 052cfcd6..93b3d02c 100644 --- a/pkg/tree/recipes.go +++ b/pkg/tree/recipes.go @@ -41,20 +41,28 @@ type Recipe struct { Database pkg.PackageDatabase } -func (r *Recipe) Save(path string) error { +func WriteDefinitionFile(p pkg.Package, definitionFilePath string) error { + data, err := p.Yaml() + if err != nil { + return err + } + err = ioutil.WriteFile(definitionFilePath, data, 0644) + if err != nil { + return err + } + return nil +} + +func (r *Recipe) Save(path string) error { for _, p := range r.Database.World() { dir := filepath.Join(path, p.GetCategory(), p.GetName(), p.GetVersion()) os.MkdirAll(dir, os.ModePerm) - data, err := p.Yaml() - if err != nil { - return err - } - err = ioutil.WriteFile(filepath.Join(dir, DefinitionFile), data, 0644) - if err != nil { - return err - } + err := WriteDefinitionFile(p, filepath.Join(dir, DefinitionFile)) + if err != nil { + return err + } } return nil }