Compare commits

...

64 Commits
0.6 ... 0.7

Author SHA1 Message Date
Ettore Di Giacinto
3c5e144b8d Tag 0.7 2020-03-21 21:39:12 +01:00
Ettore Di Giacinto
44aa69928a Merge pull request #69 from mudler/add-pkg-labels
Added `labels` to package definition
2020-03-21 21:37:42 +01:00
Daniele Rondina
c7652c8a70 Added labels to package definition
* cmd/search: Add support for search of the packages
  with a specific label.

* review Search method of Repositories for permit
  different search modes.

* labels are k/v attributes and could be matched
  through label key (with HasLabel method) or through
  regex that use "$key" + "=" + "$value"
2020-03-21 19:24:27 +01:00
Ettore Di Giacinto
fab2d21ac1 Drop unused function 2020-03-19 22:39:22 +01:00
Ettore Di Giacinto
487239334f Treat base case with same algorithm of dependencies 2020-03-19 22:37:39 +01:00
Ettore Di Giacinto
69477a0c36 Make uniform build of seeds and packages 2020-03-19 22:34:26 +01:00
Ettore Di Giacinto
67362a3bc1 Drop unused condition 2020-03-19 22:29:34 +01:00
Ettore Di Giacinto
302d18e749 Tag the target image with the computed hash 2020-03-19 22:28:33 +01:00
geaaru
9047e13d21 Merge pull request #68 from mudler/tree-bump
Tree bump
2020-03-16 22:57:14 +01:00
Daniele Rondina
0249c0fa4a version: Add temporary workaround for handle build string with _ 2020-03-16 22:18:48 +01:00
Daniele Rondina
fc40c770ab Add 'tree bump' command 2020-03-16 22:18:48 +01:00
Ettore Di Giacinto
4357ee45e9 Drop unused functions in package 2020-03-16 18:08:46 +01:00
Daniele Rondina
6ce9a86245 Update vendor github.com/Sabayon/pkgs-checker@b6efed54b4b1 2020-03-16 00:37:31 +01:00
Daniele Rondina
853a1995b4 cmd/tree/validate: Now support multiple tree paths 2020-03-15 19:16:22 +01:00
Daniele Rondina
039b77fdfd cmd/tree/validate: Add counter of broken deps 2020-03-15 17:40:20 +01:00
Ettore Di Giacinto
050d9b3095 Adapt to image based on debian instead of alpine for fixture 2020-03-15 15:30:27 +01:00
Ettore Di Giacinto
4985ff7b5a Use standard golang image for fixture (alpine doesn't contain gcc) 2020-03-15 15:42:24 +01:00
Ettore Di Giacinto
6f138811dd Make recipes idempotent, allowing to load multiple trees 2020-03-15 13:31:12 +01:00
Daniele Rondina
f02c54a274 cmd/tree/validate: Improve scripting integration 2020-03-14 17:38:57 +01:00
Daniele Rondina
d6182cba3b Add cli command 'tree validate' 2020-03-14 11:14:14 +01:00
Daniele Rondina
bc5c0fa0cf cmd/tree/pkglist: Cleanup code 2020-03-14 11:14:14 +01:00
Ettore Di Giacinto
dc64dbff75 Don't try to solve finalizers with --nodeps 2020-03-13 19:32:00 +01:00
Daniele Rondina
a94e430a3b cmd/tree/pkglist: Add --matches/-m flag 2020-03-13 18:08:49 +01:00
Daniele Rondina
93f8f0dd0f solver: Add category to tests 2020-03-12 00:26:53 +01:00
Daniele Rondina
7bdcc72dd3 Update vendor github.com/Sabayon/pkgs-checker@f4e0aec412f0 2020-03-11 08:38:15 +01:00
Daniele Rondina
e47c07d438 Update vendor github.com/Sabayon/pkgs-checker@935615ba9d27 2020-03-10 09:12:22 +01:00
Daniele Rondina
6fc5d1a97b Upgrade vendor github.com/Sabayon/pkgs-checker@v0.6.1 2020-03-09 23:35:01 +01:00
Daniele Rondina
f4a5a97cff tree/compiler_recipe: Return right error message 2020-03-08 16:28:12 +01:00
Daniele Rondina
8c972403a3 tree/compiler_recipe: Handle invalid tree path
If instead of abs path is used bash annotation
now return an error related to wrong directory
supply.
2020-03-08 15:47:40 +01:00
Daniele Rondina
82a17a1a22 Drop cfgFile print to improve bash integration 2020-03-08 00:13:45 +01:00
Daniele Rondina
17238d187a Change 'Skip dir' message to debug 2020-03-08 00:07:20 +01:00
Daniele Rondina
ad011e937f cmd/tree/pkglist: Fix init of regExcludes 2020-03-06 17:54:26 +01:00
Daniele Rondina
25c796c430 Add 'tree pkglist' command cli 2020-03-06 17:51:04 +01:00
Daniele Rondina
df5499393f --verbose/-v is now --debug/-d 2020-03-06 16:53:36 +01:00
Ettore Di Giacinto
026ddf6fc9 Add dev version tag 2020-03-05 18:37:16 +01:00
Ettore Di Giacinto
ec0b83a811 Prepare for 0.6.4 tag 2020-03-05 18:36:51 +01:00
Ettore Di Giacinto
765261f233 Add Swap() to Installer. Refactor code in Upgrade() to consume it 2020-02-27 23:48:14 +01:00
Ettore Di Giacinto
6c7e24fadf Sync repositories once (and at beginning) during upgrade 2020-02-27 23:25:29 +01:00
Ettore Di Giacinto
de23e0d5b1 Download dependencies before installing
This enables smoother upgrades, and uses cache to pre-download
packages before uninstall/install

Allows also from cli to download deps only
2020-02-27 23:15:31 +01:00
Ettore Di Giacinto
8572aa5222 Preserve cache data from deletion during uninstall 2020-02-27 23:14:36 +01:00
Ettore Di Giacinto
0e0c2f21a6 Add dev version tag 2020-02-27 18:57:41 +01:00
Ettore Di Giacinto
575079bb77 Prepare for 0.6.3 tag 2020-02-27 18:57:13 +01:00
Ettore Di Giacinto
5bcc8d112a Enforce solver constraints
- Don't sign installed packages during finalizer execution
- Enforce solver constraints: build ALO and AMO rules taking into account
  that the current package might not be selected at all.
- Force uninstalls on upgrade
- Enable option to tell uninstall to ignore conflict with the analized system state,
  as we don't want any conflict with the installed to raise during the upgrade.
  In this way we both force uninstalls and we avoid to check with conflicts
  against the current system state which is pending to deletion.
  This is due to the fact that now the solver enforces the constraints
  and explictly denies two packages of the same version installed.
- Adapt test as now we generate more constraints, which makes the solver more
  noisy on the package that are explictly selected or not
2020-02-27 18:38:31 +01:00
Ettore Di Giacinto
65b17f5283 Add simple upgrade integration test 2020-02-26 23:59:48 +01:00
Ettore Di Giacinto
a0618107a8 Adapt tests 2020-02-26 23:56:21 +01:00
Ettore Di Giacinto
25f2abf103 Do conflict with same package versions while building formula 2020-02-26 23:01:08 +01:00
Ettore Di Giacinto
e3ebfd6bfe Respect ImageRepository with build --nodeps 2020-02-26 19:00:22 +01:00
Ettore Di Giacinto
48df98bcae Add dev version tag 2020-02-23 22:42:02 +01:00
Ettore Di Giacinto
0ce1baa448 Prepare for 0.6.2 tag 2020-02-23 22:41:33 +01:00
Ettore Di Giacinto
07610bc216 Add build option to remove backend artifacts
Add keep-exported-images to build to instruct to remove backend artifacts (docker images)
2020-02-21 22:33:18 +01:00
Daniele Rondina
a04dadf100 Add support for parsing version with build 2020-02-21 21:56:32 +01:00
Daniele Rondina
2f4ce42472 Update vendor github.com/Sabayon/pkgs-checker 2020-02-21 21:55:42 +01:00
Daniele Rondina
def04724d4 Add support for build identifier on version 2020-02-20 17:46:47 +01:00
Ettore Di Giacinto
38296bc5d7 Add test for HashFingerprint() 2020-02-19 18:33:14 +01:00
Ettore Di Giacinto
7e16e0cdb6 Add HashFingerprint() to Package and consume it while generating image names
In this way we don't depend on the package fingerpint characters while generating images, allowing
to support completely semver (e.g. + character is not allowed in docker images tag).

This change is not backward compatible with images currently already built and will invalidate the cache
2020-02-19 18:28:29 +01:00
Ettore Di Giacinto
647ea35983 Add a way to extend the CLI with external binaries
This lets luet to be extended like git does (just by having luet-subcommand binaries under $PATH)
following the UNIX filosophy. It has the downside that we silence the errors from cobra and we handle
them by ourselves.

Also the Exec syscall is not portable, and it should have implementation for each platform (hence why in helpers)
2020-02-18 23:08:14 +01:00
Ettore Di Giacinto
4648dedb55 Add --nodeps, --onlydeps and --force to Installer
This allows also to use it on upgrade and uninstall as it exposes the Installer options to the CLI.

See #48, #49
2020-02-18 18:37:56 +01:00
Ettore Di Giacinto
2ce427d601 Add Nodeps, Onlydeps and Force options to Compiler
See #41
2020-02-18 18:36:44 +01:00
Ettore Di Giacinto
b01c017507 Add HumanReadableString() to Package 2020-02-18 18:20:45 +01:00
Ettore Di Giacinto
189c042fea Add dev version tag 2020-02-17 18:04:36 +01:00
Ettore Di Giacinto
4e675723e8 Prepare for 0.6.1 tag 2020-02-17 18:04:05 +01:00
Ettore Di Giacinto
dd00d491b9 Skip repository if no candidate is found
FindPackageCandidate is idempotent and returns the same definition if no new is found.
This prevents installs from multiple-repos
2020-02-17 17:21:31 +01:00
Ettore Di Giacinto
26b94888c3 Skip image building if we pulled them successfully 2020-02-15 22:53:00 +01:00
Ettore Di Giacinto
01e635bd78 Add dev version tag 2020-02-15 16:52:30 +01:00
58 changed files with 2202 additions and 523 deletions

View File

@@ -15,18 +15,17 @@
package cmd
import (
"fmt"
"io/ioutil"
"os"
"github.com/mudler/luet/pkg/compiler"
"github.com/mudler/luet/pkg/compiler/backend"
. "github.com/mudler/luet/pkg/config"
helpers "github.com/mudler/luet/pkg/helpers"
. "github.com/mudler/luet/pkg/logger"
pkg "github.com/mudler/luet/pkg/package"
tree "github.com/mudler/luet/pkg/tree"
_gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@@ -45,12 +44,16 @@ var buildCmd = &cobra.Command{
viper.BindPFlag("revdeps", cmd.Flags().Lookup("revdeps"))
viper.BindPFlag("all", cmd.Flags().Lookup("all"))
viper.BindPFlag("compression", cmd.Flags().Lookup("compression"))
viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps"))
viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps"))
viper.BindPFlag("image-repository", cmd.Flags().Lookup("image-repository"))
viper.BindPFlag("push", cmd.Flags().Lookup("push"))
viper.BindPFlag("pull", cmd.Flags().Lookup("pull"))
viper.BindPFlag("keep-images", cmd.Flags().Lookup("keep-images"))
LuetCfg.Viper.BindPFlag("keep-exported-images", cmd.Flags().Lookup("keep-exported-images"))
LuetCfg.Viper.BindPFlag("solver.type", cmd.Flags().Lookup("solver-type"))
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
@@ -72,6 +75,9 @@ var buildCmd = &cobra.Command{
push := viper.GetBool("push")
pull := viper.GetBool("pull")
keepImages := viper.GetBool("keep-images")
nodeps := viper.GetBool("nodeps")
onlydeps := viper.GetBool("onlydeps")
keepExportedImages := viper.GetBool("keep-exported-images")
compilerSpecs := compiler.NewLuetCompilationspecs()
var compilerBackend compiler.CompilerBackend
@@ -126,31 +132,21 @@ var buildCmd = &cobra.Command{
opts.PullFirst = pull
opts.KeepImg = keepImages
opts.Push = push
opts.OnlyDeps = onlydeps
opts.NoDeps = nodeps
opts.KeepImageExport = keepExportedImages
luetCompiler := compiler.NewLuetCompiler(compilerBackend, generalRecipe.GetDatabase(), opts)
luetCompiler.SetConcurrency(concurrency)
luetCompiler.SetCompressionType(compiler.CompressionImplementation(compressionType))
if !all {
for _, a := range args {
gp, err := _gentoo.ParsePackageStr(a)
pack, err := helpers.ParsePackageStr(a)
if err != nil {
Fatal("Invalid package string ", a, ": ", err.Error())
}
if gp.Version == "" {
gp.Version = "0"
gp.Condition = _gentoo.PkgCondGreaterEqual
}
pack := &pkg.DefaultPackage{
Name: gp.Name,
Version: fmt.Sprintf("%s%s%s",
pkg.PkgSelectorConditionFromInt(gp.Condition.Int()).String(),
gp.Version,
gp.VersionSuffix,
),
Category: gp.Category,
Uri: make([]string, 0),
}
spec, err := luetCompiler.FromPackage(pack)
if err != nil {
Fatal("Error: " + err.Error())
@@ -212,6 +208,9 @@ func init() {
buildCmd.Flags().Bool("push", false, "Push images to a hub")
buildCmd.Flags().Bool("pull", false, "Pull images from a hub")
buildCmd.Flags().Bool("keep-images", true, "Keep built docker images in the host")
buildCmd.Flags().Bool("nodeps", false, "Build only the target packages, skipping deps (it works only if you already built the deps locally, or by using --pull) ")
buildCmd.Flags().Bool("onlydeps", false, "Build only package dependencies")
buildCmd.Flags().Bool("keep-exported-images", false, "Keep exported images used during building")
buildCmd.Flags().String("solver-type", "", "Solver strategy")
buildCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")

View File

@@ -15,17 +15,16 @@
package cmd
import (
"fmt"
"os"
"path/filepath"
installer "github.com/mudler/luet/pkg/installer"
. "github.com/mudler/luet/pkg/config"
helpers "github.com/mudler/luet/pkg/helpers"
. "github.com/mudler/luet/pkg/logger"
pkg "github.com/mudler/luet/pkg/package"
_gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo"
"github.com/spf13/cobra"
)
@@ -39,6 +38,11 @@ var installCmd = &cobra.Command{
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
LuetCfg.Viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps"))
LuetCfg.Viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps"))
LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force"))
LuetCfg.Viper.BindPFlag("download-only", cmd.Flags().Lookup("download-only"))
},
Long: `Install packages in parallel`,
Run: func(cmd *cobra.Command, args []string) {
@@ -46,26 +50,10 @@ var installCmd = &cobra.Command{
var systemDB pkg.PackageDatabase
for _, a := range args {
gp, err := _gentoo.ParsePackageStr(a)
pack, err := helpers.ParsePackageStr(a)
if err != nil {
Fatal("Invalid package string ", a, ": ", err.Error())
}
if gp.Version == "" {
gp.Version = "0"
gp.Condition = _gentoo.PkgCondGreaterEqual
}
pack := &pkg.DefaultPackage{
Name: gp.Name,
Version: fmt.Sprintf("%s%s%s",
pkg.PkgSelectorConditionFromInt(gp.Condition.Int()).String(),
gp.Version,
gp.VersionSuffix,
),
Category: gp.Category,
Uri: make([]string, 0),
}
toInstall = append(toInstall, pack)
}
@@ -83,7 +71,10 @@ var installCmd = &cobra.Command{
discount := LuetCfg.Viper.GetFloat64("solver.discount")
rate := LuetCfg.Viper.GetFloat64("solver.rate")
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
force := LuetCfg.Viper.GetBool("force")
nodeps := LuetCfg.Viper.GetBool("nodeps")
onlydeps := LuetCfg.Viper.GetBool("onlydeps")
downloadOnly := LuetCfg.Viper.GetBool("download-only")
LuetCfg.GetSolverOptions().Type = stype
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
LuetCfg.GetSolverOptions().Discount = float32(discount)
@@ -91,7 +82,14 @@ var installCmd = &cobra.Command{
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()})
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{
Concurrency: LuetCfg.GetGeneral().Concurrency,
SolverOptions: *LuetCfg.GetSolverOptions(),
NoDeps: nodeps,
Force: force,
OnlyDeps: onlydeps,
PreserveSystemEssentialData: true,
})
inst.Repositories(repos)
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
@@ -101,7 +99,7 @@ var installCmd = &cobra.Command{
systemDB = pkg.NewInMemoryDatabase(true)
}
system := &installer.System{Database: systemDB, Target: LuetCfg.GetSystem().Rootfs}
err := inst.Install(toInstall, system)
err := inst.Install(toInstall, system, downloadOnly)
if err != nil {
Fatal("Error: " + err.Error())
}
@@ -119,6 +117,10 @@ func init() {
installCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
installCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
installCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
installCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful!)")
installCmd.Flags().Bool("onlydeps", false, "Consider **only** package dependencies")
installCmd.Flags().Bool("force", false, "Skip errors and keep going (potentially harmful)")
installCmd.Flags().Bool("download-only", false, "Download dependencies only")
RootCmd.AddCommand(installCmd)
}

View File

@@ -16,6 +16,7 @@
package cmd
import (
"fmt"
"os"
"os/user"
"path/filepath"
@@ -24,6 +25,7 @@ import (
"github.com/marcsauter/single"
config "github.com/mudler/luet/pkg/config"
helpers "github.com/mudler/luet/pkg/helpers"
. "github.com/mudler/luet/pkg/logger"
repo "github.com/mudler/luet/pkg/repository"
@@ -35,7 +37,7 @@ var cfgFile string
var Verbose bool
const (
LuetCLIVersion = "0.6"
LuetCLIVersion = "0.7"
LuetEnvPrefix = "LUET"
)
@@ -51,12 +53,13 @@ var RootCmd = &cobra.Command{
Fatal("failed to load configuration:", err.Error())
}
},
SilenceErrors: true,
}
func LoadConfig(c *config.LuetConfig) error {
// If a config file is found, read it in.
if err := c.Viper.ReadInConfig(); err != nil {
Warning(err)
Debug(err)
}
err := c.Viper.Unmarshal(&config.LuetCfg)
@@ -99,8 +102,18 @@ func Execute() {
}
defer s.TryUnlock()
}
if err := RootCmd.Execute(); err != nil {
Error(err)
if len(os.Args) > 0 {
for _, c := range RootCmd.Commands() {
if c.Name() == os.Args[1] {
os.Exit(-1) // Something failed
}
}
// Try to load a bin from path.
helpers.Exec("luet-"+os.Args[1], os.Args[1:], os.Environ())
}
fmt.Println(err)
os.Exit(-1)
}
}
@@ -109,7 +122,7 @@ func init() {
cobra.OnInitialize(initConfig)
pflags := RootCmd.PersistentFlags()
pflags.StringVar(&cfgFile, "config", "", "config file (default is $HOME/.luet.yaml)")
pflags.BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
pflags.BoolP("debug", "d", false, "verbose output")
pflags.Bool("fatal", false, "Enables Warnings to exit")
u, err := user.Current()
@@ -124,7 +137,7 @@ func init() {
pflags.Int("concurrency", runtime.NumCPU(), "Concurrency")
config.LuetCfg.Viper.BindPFlag("general.same_owner", pflags.Lookup("same-owner"))
config.LuetCfg.Viper.BindPFlag("general.debug", pflags.Lookup("verbose"))
config.LuetCfg.Viper.BindPFlag("general.debug", pflags.Lookup("debug"))
config.LuetCfg.Viper.BindPFlag("general.concurrency", pflags.Lookup("concurrency"))
config.LuetCfg.Viper.BindPFlag("general.fatal_warnings", pflags.Lookup("fatal"))
}
@@ -141,7 +154,6 @@ func initConfig() {
viper.SetConfigType("yaml")
viper.SetConfigName(".luet") // name of config file (without extension)
if cfgFile != "" { // enable ability to specify config file via flag
Info(">>> cfgFile: ", cfgFile)
viper.SetConfigFile(cfgFile)
} else {
viper.AddConfigPath(dir)

View File

@@ -17,7 +17,6 @@ package cmd
import (
"os"
"path/filepath"
"regexp"
. "github.com/mudler/luet/pkg/config"
installer "github.com/mudler/luet/pkg/installer"
@@ -51,6 +50,8 @@ var searchCmd = &cobra.Command{
discount := LuetCfg.Viper.GetFloat64("solver.discount")
rate := LuetCfg.Viper.GetFloat64("solver.rate")
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
searchWithLabel, _ := cmd.Flags().GetBool("by-label")
searchWithLabelMatch, _ := cmd.Flags().GetBool("by-label-regex")
LuetCfg.GetSolverOptions().Type = stype
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
@@ -70,8 +71,12 @@ var searchCmd = &cobra.Command{
repos = append(repos, r)
}
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()})
inst := installer.NewLuetInstaller(
installer.LuetInstallerOptions{
Concurrency: LuetCfg.GetGeneral().Concurrency,
SolverOptions: *LuetCfg.GetSolverOptions(),
},
)
inst.Repositories(repos)
synced, err := inst.SyncRepositories(false)
if err != nil {
@@ -80,7 +85,14 @@ var searchCmd = &cobra.Command{
Info("--- Search results: ---")
matches := synced.Search(args[0])
matches := []installer.PackageMatch{}
if searchWithLabel {
matches = synced.SearchLabel(args[0])
} else if searchWithLabelMatch {
matches = synced.SearchLabelMatch(args[0])
} else {
matches = synced.Search(args[0])
}
for _, m := range matches {
Info(":package:", m.Package.GetCategory(), m.Package.GetName(),
m.Package.GetVersion(), "repository:", m.Repo.GetName())
@@ -94,14 +106,25 @@ var searchCmd = &cobra.Command{
systemDB = pkg.NewInMemoryDatabase(true)
}
system := &installer.System{Database: systemDB, Target: LuetCfg.GetSystem().Rootfs}
var term = regexp.MustCompile(args[0])
for _, k := range system.Database.GetPackages() {
pack, err := system.Database.GetPackage(k)
if err == nil && term.MatchString(pack.GetName()) {
Info(":package:", pack.GetCategory(), pack.GetName(), pack.GetVersion())
}
var err error
iMatches := []pkg.Package{}
if searchWithLabel {
iMatches, err = system.Database.FindPackageLabel(args[0])
} else if searchWithLabelMatch {
iMatches, err = system.Database.FindPackageLabelMatch(args[0])
} else {
iMatches, err = system.Database.FindPackageMatch(args[0])
}
if err != nil {
Fatal("Error: " + err.Error())
}
for _, pack := range iMatches {
Info(":package:", pack.GetCategory(), pack.GetName(), pack.GetVersion())
}
}
},
@@ -119,5 +142,7 @@ func init() {
searchCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
searchCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
searchCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
searchCmd.Flags().Bool("by-label", false, "Search packages through label")
searchCmd.Flags().Bool("by-label-regex", false, "Search packages through label regex")
RootCmd.AddCommand(searchCmd)
}

38
cmd/tree.go Normal file
View File

@@ -0,0 +1,38 @@
// Copyright © 2020 Ettore Di Giacinto <mudler@gentoo.org>
// Daniele Rondina <geaaru@sabayonlinux.org>
//
// 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 <http://www.gnu.org/licenses/>.
package cmd
import (
. "github.com/mudler/luet/cmd/tree"
"github.com/spf13/cobra"
)
var treeGroupCmd = &cobra.Command{
Use: "tree [command] [OPTIONS]",
Short: "Tree operations",
}
func init() {
RootCmd.AddCommand(treeGroupCmd)
treeGroupCmd.AddCommand(
NewTreePkglistCommand(),
NewTreeValidateCommand(),
NewTreeBumpCommand(),
)
}

67
cmd/tree/bump.go Normal file
View File

@@ -0,0 +1,67 @@
// Copyright © 2020 Ettore Di Giacinto <mudler@gentoo.org>
// Daniele Rondina <geaaru@sabayonlinux.org>
//
// 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 <http://www.gnu.org/licenses/>.
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
}

140
cmd/tree/pkglist.go Normal file
View File

@@ -0,0 +1,140 @@
// Copyright © 2020 Ettore Di Giacinto <mudler@gentoo.org>
// Daniele Rondina <geaaru@sabayonlinux.org>
//
// 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 <http://www.gnu.org/licenses/>.
package cmd_tree
import (
"fmt"
"sort"
//. "github.com/mudler/luet/pkg/config"
helpers "github.com/mudler/luet/pkg/helpers"
. "github.com/mudler/luet/pkg/logger"
pkg "github.com/mudler/luet/pkg/package"
tree "github.com/mudler/luet/pkg/tree"
"github.com/spf13/cobra"
)
func pkgDetail(pkg pkg.Package) string {
ans := fmt.Sprintf(`
@@ Package: %s/%s-%s
Description: %s
License: %s`,
pkg.GetCategory(), pkg.GetName(), pkg.GetVersion(),
pkg.GetDescription(), pkg.GetLicense())
for idx, u := range pkg.GetURI() {
if idx == 0 {
ans += fmt.Sprintf(" URLs: %s", u)
} else {
ans += fmt.Sprintf(" %s", u)
}
}
return ans
}
func NewTreePkglistCommand() *cobra.Command {
var excludes []string
var matches []string
var ans = &cobra.Command{
Use: "pkglist [OPTIONS]",
Short: "List of the packages found in tree.",
Args: cobra.OnlyValidArgs,
PreRun: func(cmd *cobra.Command, args []string) {
t, _ := cmd.Flags().GetString("tree")
if t == "" {
Fatal("Mandatory tree param missing.")
}
},
Run: func(cmd *cobra.Command, args []string) {
treePath, _ := cmd.Flags().GetString("tree")
verbose, _ := cmd.Flags().GetBool("verbose")
full, _ := cmd.Flags().GetBool("full")
reciper := tree.NewInstallerRecipe(pkg.NewInMemoryDatabase(false))
err := reciper.Load(treePath)
if err != nil {
Fatal("Error on load tree ", err)
}
regExcludes, err := helpers.CreateRegexArray(excludes)
if err != nil {
Fatal(err.Error())
}
regMatches, err := helpers.CreateRegexArray(matches)
if err != nil {
Fatal(err.Error())
}
plist := make([]string, 0)
for _, p := range reciper.GetDatabase().World() {
pkgstr := ""
addPkg := true
if full {
pkgstr = pkgDetail(p)
} else if verbose {
pkgstr = fmt.Sprintf("%s/%s-%s", p.GetCategory(), p.GetName(), p.GetVersion())
} else {
pkgstr = fmt.Sprintf("%s/%s", p.GetCategory(), p.GetName())
}
if len(matches) > 0 {
matched := false
for _, rgx := range regMatches {
if rgx.MatchString(pkgstr) {
matched = true
break
}
}
if !matched {
addPkg = false
}
}
if len(excludes) > 0 && addPkg {
for _, rgx := range regExcludes {
if rgx.MatchString(pkgstr) {
addPkg = false
break
}
}
}
if addPkg {
plist = append(plist, pkgstr)
}
}
sort.Strings(plist)
for _, p := range plist {
fmt.Println(p)
}
},
}
ans.Flags().BoolP("verbose", "v", false, "Add package version")
ans.Flags().BoolP("full", "f", false, "Show package detail")
ans.Flags().StringP("tree", "t", "", "Path of the tree to use.")
ans.Flags().StringSliceVarP(&matches, "matches", "m", []string{},
"Include only matched packages from list. (Use string as regex).")
ans.Flags().StringSliceVarP(&excludes, "exclude", "e", []string{},
"Exclude matched packages from list. (Use string as regex).")
return ans
}

212
cmd/tree/validate.go Normal file
View File

@@ -0,0 +1,212 @@
// Copyright © 2020 Ettore Di Giacinto <mudler@gentoo.org>
// Daniele Rondina <geaaru@sabayonlinux.org>
//
// 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 <http://www.gnu.org/licenses/>.
package cmd_tree
import (
"fmt"
"os"
"sort"
//. "github.com/mudler/luet/pkg/config"
helpers "github.com/mudler/luet/pkg/helpers"
. "github.com/mudler/luet/pkg/logger"
pkg "github.com/mudler/luet/pkg/package"
"github.com/mudler/luet/pkg/solver"
tree "github.com/mudler/luet/pkg/tree"
"github.com/spf13/cobra"
)
func NewTreeValidateCommand() *cobra.Command {
var excludes []string
var matches []string
var treePaths []string
var ans = &cobra.Command{
Use: "validate [OPTIONS]",
Short: "Validate a tree or a list of packages",
Args: cobra.OnlyValidArgs,
PreRun: func(cmd *cobra.Command, args []string) {
if len(treePaths) < 1 {
Fatal("Mandatory tree param missing.")
}
},
Run: func(cmd *cobra.Command, args []string) {
var depSolver solver.PackageSolver
var errstr string
errors := make([]string, 0)
brokenPkgs := 0
brokenDeps := 0
withSolver, _ := cmd.Flags().GetBool("with-solver")
reciper := tree.NewInstallerRecipe(pkg.NewInMemoryDatabase(false))
for _, treePath := range treePaths {
err := reciper.Load(treePath)
if err != nil {
Fatal("Error on load tree ", err)
}
}
emptyInstallationDb := pkg.NewInMemoryDatabase(false)
if withSolver {
depSolver = solver.NewSolver(pkg.NewInMemoryDatabase(false),
reciper.GetDatabase(),
emptyInstallationDb)
}
regExcludes, err := helpers.CreateRegexArray(excludes)
if err != nil {
Fatal(err.Error())
}
regMatches, err := helpers.CreateRegexArray(matches)
if err != nil {
Fatal(err.Error())
}
for _, p := range reciper.GetDatabase().World() {
pkgstr := fmt.Sprintf("%s/%s-%s", p.GetCategory(), p.GetName(),
p.GetVersion())
validpkg := true
if len(matches) > 0 {
matched := false
for _, rgx := range regMatches {
if rgx.MatchString(pkgstr) {
matched = true
break
}
}
if !matched {
continue
}
}
if len(excludes) > 0 {
excluded := false
for _, rgx := range regExcludes {
if rgx.MatchString(pkgstr) {
excluded = true
break
}
}
if excluded {
continue
}
}
Info("Checking package "+fmt.Sprintf("%s/%s-%s", p.GetCategory(), p.GetName(), p.GetVersion()), "with", len(p.GetRequires()), "dependencies.")
for _, r := range p.GetRequires() {
deps, err := reciper.GetDatabase().FindPackages(
&pkg.DefaultPackage{
Name: r.GetName(),
Category: r.GetCategory(),
Version: r.GetVersion(),
},
)
if err != nil || len(deps) < 1 {
if err != nil {
errstr = err.Error()
} else {
errstr = "No packages"
}
Error(fmt.Sprintf("%s/%s-%s: Broken Dep %s/%s-%s - %s",
p.GetCategory(), p.GetName(), p.GetVersion(),
r.GetCategory(), r.GetName(), r.GetVersion(),
errstr,
))
errors = append(errors,
fmt.Sprintf("%s/%s-%s: Broken Dep %s/%s-%s - %s",
p.GetCategory(), p.GetName(), p.GetVersion(),
r.GetCategory(), r.GetName(), r.GetVersion(),
errstr))
brokenDeps++
validpkg = false
} else {
Debug("Find packages for dep",
fmt.Sprintf("%s/%s-%s", r.GetCategory(), r.GetName(), r.GetVersion()))
if withSolver {
Spinner(32)
_, err := depSolver.Install([]pkg.Package{r})
SpinnerStop()
if err != nil {
Error(fmt.Sprintf("%s/%s-%s: solver broken for dep %s/%s-%s - %s",
p.GetCategory(), p.GetName(), p.GetVersion(),
r.GetCategory(), r.GetName(), r.GetVersion(),
err.Error(),
))
errors = append(errors,
fmt.Sprintf("%s/%s-%s: solver broken for Dep %s/%s-%s - %s",
p.GetCategory(), p.GetName(), p.GetVersion(),
r.GetCategory(), r.GetName(), r.GetVersion(),
err.Error()))
brokenDeps++
validpkg = false
}
}
}
}
if !validpkg {
brokenPkgs++
}
}
sort.Strings(errors)
for _, e := range errors {
fmt.Println(e)
}
fmt.Println("Broken packages:", brokenPkgs, "(", brokenDeps, "deps ).")
if brokenPkgs > 0 {
os.Exit(1)
} else {
os.Exit(0)
}
},
}
ans.Flags().BoolP("with-solver", "s", false,
"Enable check of requires also with solver.")
ans.Flags().StringSliceVarP(&treePaths, "tree", "t", []string{},
"Path of the tree to use.")
ans.Flags().StringSliceVarP(&excludes, "exclude", "e", []string{},
"Exclude matched packages from analysis. (Use string as regex).")
ans.Flags().StringSliceVarP(&matches, "matches", "m", []string{},
"Analyze only matched packages. (Use string as regex).")
return ans
}

View File

@@ -15,16 +15,15 @@
package cmd
import (
"fmt"
"os"
"path/filepath"
. "github.com/mudler/luet/pkg/config"
helpers "github.com/mudler/luet/pkg/helpers"
installer "github.com/mudler/luet/pkg/installer"
. "github.com/mudler/luet/pkg/logger"
pkg "github.com/mudler/luet/pkg/package"
_gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo"
"github.com/spf13/cobra"
)
@@ -39,34 +38,25 @@ var uninstallCmd = &cobra.Command{
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
LuetCfg.Viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps"))
LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force"))
},
Run: func(cmd *cobra.Command, args []string) {
var systemDB pkg.PackageDatabase
for _, a := range args {
gp, err := _gentoo.ParsePackageStr(a)
pack, err := helpers.ParsePackageStr(a)
if err != nil {
Fatal("Invalid package string ", a, ": ", err.Error())
}
if gp.Version == "" {
gp.Version = "0"
gp.Condition = _gentoo.PkgCondGreaterEqual
}
pack := &pkg.DefaultPackage{
Name: gp.Name,
Version: fmt.Sprintf("%s%s%s",
pkg.PkgSelectorConditionFromInt(gp.Condition.Int()).String(),
gp.Version,
gp.VersionSuffix,
),
Category: gp.Category,
Uri: make([]string, 0),
}
stype := LuetCfg.Viper.GetString("solver.type")
discount := LuetCfg.Viper.GetFloat64("solver.discount")
rate := LuetCfg.Viper.GetFloat64("solver.rate")
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
force := LuetCfg.Viper.GetBool("force")
nodeps := LuetCfg.Viper.GetBool("nodeps")
LuetCfg.GetSolverOptions().Type = stype
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
@@ -75,7 +65,12 @@ var uninstallCmd = &cobra.Command{
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()})
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{
Concurrency: LuetCfg.GetGeneral().Concurrency,
SolverOptions: *LuetCfg.GetSolverOptions(),
NoDeps: nodeps,
Force: force,
})
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
systemDB = pkg.NewBoltDatabase(
@@ -103,5 +98,8 @@ func init() {
uninstallCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
uninstallCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
uninstallCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
uninstallCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful!)")
uninstallCmd.Flags().Bool("force", false, "Force uninstall")
RootCmd.AddCommand(uninstallCmd)
}

View File

@@ -36,6 +36,7 @@ var upgradeCmd = &cobra.Command{
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force"))
},
Long: `Upgrades packages in parallel`,
Run: func(cmd *cobra.Command, args []string) {
@@ -55,6 +56,7 @@ var upgradeCmd = &cobra.Command{
discount := LuetCfg.Viper.GetFloat64("solver.discount")
rate := LuetCfg.Viper.GetFloat64("solver.rate")
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
force := LuetCfg.Viper.GetBool("force")
LuetCfg.GetSolverOptions().Type = stype
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
@@ -63,7 +65,11 @@ var upgradeCmd = &cobra.Command{
Debug("Solver", LuetCfg.GetSolverOptions().String())
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()})
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{
Concurrency: LuetCfg.GetGeneral().Concurrency,
SolverOptions: *LuetCfg.GetSolverOptions(),
Force: force,
})
inst.Repositories(repos)
_, err := inst.SyncRepositories(false)
if err != nil {
@@ -95,5 +101,7 @@ func init() {
upgradeCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
upgradeCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
upgradeCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
upgradeCmd.Flags().Bool("force", false, "Force upgrade by ignoring errors")
RootCmd.AddCommand(upgradeCmd)
}

5
go.mod
View File

@@ -4,7 +4,8 @@ go 1.12
require (
github.com/DataDog/zstd v1.4.4 // indirect
github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7
github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56 // indirect
github.com/Sabayon/pkgs-checker v0.6.2-0.20200315232328-b6efed54b4b1
github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154
github.com/briandowns/spinner v1.7.0
github.com/cavaliercoder/grab v2.0.0+incompatible
@@ -12,6 +13,7 @@ require (
github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23
github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd
github.com/ghodss/yaml v1.0.0
github.com/go-yaml/yaml v2.1.0+incompatible // indirect
github.com/hashicorp/go-version v1.2.0
github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3
github.com/klauspost/pgzip v1.2.1
@@ -20,6 +22,7 @@ require (
github.com/logrusorgru/aurora v0.0.0-20190417123914-21d75270181e
github.com/marcsauter/single v0.0.0-20181104081128-f8bf46f26ec0
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/mudler/docker-companion v0.4.6-0.20191110154655-b8b364100616
github.com/onsi/ginkgo v1.10.1
github.com/onsi/gomega v1.7.0

17
go.sum
View File

@@ -9,11 +9,24 @@ github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56 h1:XCZM9J5KqLsr5NqtrZuXiD3X5fe5IfgU7IIUZzpeFBk=
github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56/go.mod h1:+Gbv6dg6TPHWq4oDjZY1vn978PLCEZ2hOu8kvn+S7t4=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7 h1:Vf80sSLu1ZWjjMmUKhw0FqM43lEOvT8O5B22NaHB6AQ=
github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
github.com/Sabayon/pkgs-checker v0.5.0 h1:VRyyAxo6ox41Dytyl+K+QC+Tps0IxvqYbidu+AH+HUQ=
github.com/Sabayon/pkgs-checker v0.5.1-0.20200221202320-073693f2c657 h1:VK5S2Gh9kPUxX81zCFUgKVQn+hFy6VgyZMD3QLm76u8=
github.com/Sabayon/pkgs-checker v0.5.1-0.20200221202320-073693f2c657/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
github.com/Sabayon/pkgs-checker v0.6.1 h1:7HIrrAQujfEQ0+vPjb1Px4AdUY7KWQPn8W0NjKMoGLI=
github.com/Sabayon/pkgs-checker v0.6.1/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
github.com/Sabayon/pkgs-checker v0.6.2-0.20200310081024-935615ba9d27 h1:+5UCxj8bQHrcjuZumsX+E4mvol2F2dW8lXb1K+A/VlM=
github.com/Sabayon/pkgs-checker v0.6.2-0.20200310081024-935615ba9d27/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
github.com/Sabayon/pkgs-checker v0.6.2-0.20200311072754-f4e0aec412f0 h1:C8pSo4uPvXcb5X2ai5oxuABdsOI1mlNNBaw2oxLbzy4=
github.com/Sabayon/pkgs-checker v0.6.2-0.20200311072754-f4e0aec412f0/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
github.com/Sabayon/pkgs-checker v0.6.2-0.20200315232328-b6efed54b4b1 h1:0hkt4Na3ZDZO6e9hCKATOki+zBKOdRa0LxUtYlm9oyE=
github.com/Sabayon/pkgs-checker v0.6.2-0.20200315232328-b6efed54b4b1/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
github.com/Sereal/Sereal v0.0.0-20181211220259-509a78ddbda3 h1:Xu7z47ZiE/J+sKXHZMGxEor/oY2q6dq51fkO0JqdSwY=
github.com/Sereal/Sereal v0.0.0-20181211220259-509a78ddbda3/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -91,6 +104,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
@@ -170,6 +185,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=

View File

@@ -265,17 +265,12 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
}
}
fp := p.GetPackage().HashFingerprint()
if buildertaggedImage == "" {
buildertaggedImage = cs.ImageRepository + "-" + p.GetPackage().GetFingerPrint() + "-builder"
buildertaggedImage = cs.ImageRepository + "-" + fp + "-builder"
}
if packageImage == "" {
packageImage = cs.ImageRepository + "-" + p.GetPackage().GetFingerPrint()
}
if cs.Options.PullFirst {
//Best effort pull
cs.Backend.DownloadImage(CompilerBackendOptions{ImageName: buildertaggedImage})
cs.Backend.DownloadImage(CompilerBackendOptions{ImageName: packageImage})
packageImage = cs.ImageRepository + "-" + fp
}
Info(pkgTag, "Generating :whale: definition for builder image from", image)
@@ -289,19 +284,29 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
Destination: p.Rel(p.GetPackage().GetFingerPrint() + "-builder.image.tar"),
}
err = cs.Backend.BuildImage(builderOpts)
if err != nil {
return nil, errors.Wrap(err, "Could not build image: "+image+" "+builderOpts.DockerFileName)
buildBuilderImage := true
if cs.Options.PullFirst {
if err := cs.Backend.DownloadImage(builderOpts); err == nil {
buildBuilderImage = false
}
}
err = cs.Backend.ExportImage(builderOpts)
if err != nil {
if buildBuilderImage {
if err = cs.Backend.BuildImage(builderOpts); err != nil {
return nil, errors.Wrap(err, "Could not build image: "+image+" "+builderOpts.DockerFileName)
}
}
if err = cs.Backend.ExportImage(builderOpts); err != nil {
return nil, errors.Wrap(err, "Could not export image")
}
if cs.Options.Push {
err = cs.Backend.Push(builderOpts)
if err != nil {
if !cs.Options.KeepImageExport {
defer os.Remove(builderOpts.Destination)
}
if cs.Options.Push && buildBuilderImage {
if err = cs.Backend.Push(builderOpts); err != nil {
return nil, errors.Wrap(err, "Could not push image: "+image+" "+builderOpts.DockerFileName)
}
}
@@ -320,14 +325,29 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
// return nil, errors.Wrap(err, "Could not export image to tar")
// }
// } else {
if err := cs.Backend.BuildImage(runnerOpts); err != nil {
return nil, errors.Wrap(err, "Failed building image for "+runnerOpts.ImageName+" "+runnerOpts.DockerFileName)
buildPackageImage := true
if cs.Options.PullFirst {
//Best effort pull
if err := cs.Backend.DownloadImage(runnerOpts); err == nil {
buildPackageImage = false
}
}
if buildPackageImage {
if err := cs.Backend.BuildImage(runnerOpts); err != nil {
return nil, errors.Wrap(err, "Failed building image for "+runnerOpts.ImageName+" "+runnerOpts.DockerFileName)
}
}
if err := cs.Backend.ExportImage(runnerOpts); err != nil {
return nil, errors.Wrap(err, "Failed exporting image")
}
if cs.Options.Push {
if !cs.Options.KeepImageExport {
defer os.Remove(runnerOpts.Destination)
}
if cs.Options.Push && buildPackageImage {
err = cs.Backend.Push(runnerOpts)
if err != nil {
return nil, errors.Wrap(err, "Could not push image: "+image+" "+builderOpts.DockerFileName)
@@ -409,85 +429,13 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
return artifact, nil
}
func (cs *LuetCompiler) packageFromImage(p CompilationSpec, tag string, keepPermissions, keepImg bool, concurrency int) (Artifact, error) {
if !cs.Clean {
if art, err := LoadArtifactFromYaml(p); err == nil {
Debug("Artifact reloaded. Skipping build")
return art, err
}
}
pkgTag := ":package: " + p.GetPackage().GetName()
Info(pkgTag, " 🍩 Build starts 🔨 🔨 🔨 ")
builderOpts := CompilerBackendOptions{
ImageName: p.GetImage(),
Destination: p.Rel(p.GetPackage().GetFingerPrint() + ".image.tar"),
}
err := cs.Backend.DownloadImage(builderOpts)
if err != nil {
return nil, errors.Wrap(err, "Could not download image")
}
if tag != "" {
err = cs.Backend.CopyImage(p.GetImage(), tag)
if err != nil {
return nil, errors.Wrap(err, "Could not download image")
}
}
err = cs.Backend.ExportImage(builderOpts)
if err != nil {
return nil, errors.Wrap(err, "Could not export image")
}
rootfs, err := ioutil.TempDir(p.GetOutputPath(), "rootfs")
if err != nil {
return nil, errors.Wrap(err, "Could not create tempdir")
}
defer os.RemoveAll(rootfs) // clean up
// TODO: Compression and such
err = cs.Backend.ExtractRootfs(CompilerBackendOptions{
ImageName: p.GetImage(),
SourcePath: builderOpts.Destination, Destination: rootfs}, keepPermissions)
if err != nil {
return nil, errors.Wrap(err, "Could not extract rootfs")
}
artifact := NewPackageArtifact(p.Rel(p.GetPackage().GetFingerPrint() + ".package.tar"))
artifact.SetCompileSpec(p)
artifact.SetCompressionType(cs.CompressionType)
err = artifact.Compress(rootfs, concurrency)
if err != nil {
return nil, errors.Wrap(err, "Error met while creating package archive")
}
if !keepImg {
// We keep them around, so to not reload them from the tar (which should be the "correct way") and we automatically share the same layers
// TODO: Handle caching and optionally do not remove things
err = cs.Backend.RemoveImage(builderOpts)
if err != nil {
Warning("Could not remove image ", builderOpts.ImageName)
// return nil, errors.Wrap(err, "Could not remove image")
}
}
Info(pkgTag, " :white_check_mark: Done")
err = artifact.WriteYaml(p.GetOutputPath())
if err != nil {
return artifact, err
}
return artifact, nil
}
func (cs *LuetCompiler) ComputeDepTree(p CompilationSpec) (solver.PackagesAssertions, error) {
s := solver.NewResolver(pkg.NewInMemoryDatabase(false), cs.Database, pkg.NewInMemoryDatabase(false), cs.Options.SolverOptions.Resolver())
solution, err := s.Install([]pkg.Package{p.GetPackage()})
if err != nil {
return nil, errors.Wrap(err, "While computing a solution for "+p.GetPackage().GetName())
return nil, errors.Wrap(err, "While computing a solution for "+p.GetPackage().HumanReadableString())
}
dependencies := solution.Order(cs.Database, p.GetPackage().GetFingerPrint())
@@ -519,21 +467,20 @@ func (cs *LuetCompiler) Compile(keepPermissions bool, p CompilationSpec) (Artifa
}
func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p CompilationSpec) (Artifact, error) {
Info(":package: Compiling", p.GetPackage().GetName(), "version", p.GetPackage().GetVersion(), ".... :coffee:")
Info(":package: Compiling", p.GetPackage().HumanReadableString(), ".... :coffee:")
if len(p.GetPackage().GetRequires()) == 0 && p.GetImage() == "" {
Error("Package with no deps and no seed image supplied, bailing out")
return nil, errors.New("Package " + p.GetPackage().GetFingerPrint() + "with no deps and no seed image supplied, bailing out")
}
targetAssertion := p.GetSourceAssertion().Search(p.GetPackage().GetFingerPrint())
targetPackageHash := cs.ImageRepository + ":" + targetAssertion.Hash.PackageHash
// - If image is set we just generate a plain dockerfile
// Treat last case (easier) first. The image is provided and we just compute a plain dockerfile with the images listed as above
if p.GetImage() != "" {
if p.ImageUnpack() { // If it is just an entire image, create a package from it
return cs.packageFromImage(p, "", keepPermissions, cs.KeepImg, concurrency)
}
return cs.compileWithImage(p.GetImage(), "", "", concurrency, keepPermissions, cs.KeepImg, p)
return cs.compileWithImage(p.GetImage(), "", targetPackageHash, concurrency, keepPermissions, cs.KeepImg, p)
}
// - If image is not set, we read a base_image. Then we will build one image from it to kick-off our build based
@@ -547,74 +494,68 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
depsN := 0
currentN := 0
Info(":deciduous_tree: Build dependencies for " + p.GetPackage().GetName())
for _, assertion := range dependencies { //highly dependent on the order
depsN++
Info(" :arrow_right_hook:", assertion.Package.GetName(), ":leaves:", assertion.Package.GetVersion(), "(", assertion.Package.GetCategory(), ")")
}
for _, assertion := range dependencies { //highly dependent on the order
currentN++
pkgTag := fmt.Sprintf(":package: %d/%d %s ⤑ %s", currentN, depsN, p.GetPackage().GetName(), assertion.Package.GetName())
Info(pkgTag, " :zap: Building dependency")
compileSpec, err := cs.FromPackage(assertion.Package)
if err != nil {
return nil, errors.Wrap(err, "Error while generating compilespec for "+assertion.Package.GetName())
if !cs.Options.NoDeps {
Info(":deciduous_tree: Build dependencies for " + p.GetPackage().HumanReadableString())
for _, assertion := range dependencies { //highly dependent on the order
depsN++
Info(" :arrow_right_hook:", assertion.Package.HumanReadableString(), ":leaves:")
}
compileSpec.SetOutputPath(p.GetOutputPath())
buildImageHash := cs.ImageRepository + ":" + assertion.Hash.BuildHash
currentPackageImageHash := cs.ImageRepository + ":" + assertion.Hash.PackageHash
Debug(pkgTag, " :arrow_right_hook: :whale: Builder image from", buildImageHash)
Debug(pkgTag, " :arrow_right_hook: :whale: Package image name", currentPackageImageHash)
for _, assertion := range dependencies { //highly dependent on the order
currentN++
pkgTag := fmt.Sprintf(":package: %d/%d %s ⤑ %s", currentN, depsN, p.GetPackage().HumanReadableString(), assertion.Package.HumanReadableString())
Info(pkgTag, " :zap: Building dependency")
compileSpec, err := cs.FromPackage(assertion.Package)
if err != nil {
return nil, errors.Wrap(err, "Error while generating compilespec for "+assertion.Package.GetName())
}
compileSpec.SetOutputPath(p.GetOutputPath())
lastHash = currentPackageImageHash
if compileSpec.GetImage() != "" {
// TODO: Refactor this
if compileSpec.ImageUnpack() { // If it is just an entire image, create a package from it
if compileSpec.GetImage() == "" {
return nil, errors.New("No image defined for package: " + assertion.Package.GetName())
}
Info(pkgTag, ":whale: Sourcing package from image", compileSpec.GetImage())
artifact, err := cs.packageFromImage(compileSpec, currentPackageImageHash, keepPermissions, cs.KeepImg, concurrency)
buildImageHash := cs.ImageRepository + ":" + assertion.Hash.BuildHash
currentPackageImageHash := cs.ImageRepository + ":" + assertion.Hash.PackageHash
Debug(pkgTag, " :arrow_right_hook: :whale: Builder image from", buildImageHash)
Debug(pkgTag, " :arrow_right_hook: :whale: Package image name", currentPackageImageHash)
lastHash = currentPackageImageHash
if compileSpec.GetImage() != "" {
Debug(pkgTag, " :wrench: Compiling "+compileSpec.GetPackage().HumanReadableString()+" from image")
artifact, err := cs.compileWithImage(compileSpec.GetImage(), buildImageHash, currentPackageImageHash, concurrency, keepPermissions, cs.KeepImg, compileSpec)
if err != nil {
return nil, errors.Wrap(err, "Failed compiling "+compileSpec.GetPackage().GetName())
return nil, errors.Wrap(err, "Failed compiling "+compileSpec.GetPackage().HumanReadableString())
}
departifacts = append(departifacts, artifact)
Info(pkgTag, ":white_check_mark: Done")
continue
}
Debug(pkgTag, " :wrench: Compiling "+compileSpec.GetPackage().GetFingerPrint()+" from image")
artifact, err := cs.compileWithImage(compileSpec.GetImage(), buildImageHash, currentPackageImageHash, concurrency, keepPermissions, cs.KeepImg, compileSpec)
Debug(pkgTag, " :wrench: Compiling "+compileSpec.GetPackage().HumanReadableString()+" from tree")
artifact, err := cs.compileWithImage(buildImageHash, "", currentPackageImageHash, concurrency, keepPermissions, cs.KeepImg, compileSpec)
if err != nil {
return nil, errors.Wrap(err, "Failed compiling "+compileSpec.GetPackage().GetName())
return nil, errors.Wrap(err, "Failed compiling "+compileSpec.GetPackage().HumanReadableString())
// deperrs = append(deperrs, err)
// break // stop at first error
}
departifacts = append(departifacts, artifact)
Info(pkgTag, ":white_check_mark: Done")
continue
Info(pkgTag, ":collision: Done")
}
Debug(pkgTag, " :wrench: Compiling "+compileSpec.GetPackage().GetFingerPrint()+" from tree")
artifact, err := cs.compileWithImage(buildImageHash, "", currentPackageImageHash, concurrency, keepPermissions, cs.KeepImg, compileSpec)
} else if len(dependencies) > 0 {
lastHash = cs.ImageRepository + ":" + dependencies[len(dependencies)-1].Hash.PackageHash
}
if !cs.Options.OnlyDeps {
Info(":package:", p.GetPackage().HumanReadableString(), ":cyclone: Building package target from:", lastHash)
artifact, err := cs.compileWithImage(lastHash, "", targetPackageHash, concurrency, keepPermissions, cs.KeepImg, p)
if err != nil {
return nil, errors.Wrap(err, "Failed compiling "+compileSpec.GetPackage().GetName())
// deperrs = append(deperrs, err)
// break // stop at first error
return artifact, err
}
departifacts = append(departifacts, artifact)
Info(pkgTag, ":collision: Done")
}
artifact.SetDependencies(departifacts)
artifact.SetSourceAssertion(p.GetSourceAssertion())
Info(":package:", p.GetPackage().GetName(), ":cyclone: Building package target from:", lastHash)
artifact, err := cs.compileWithImage(lastHash, "", "", concurrency, keepPermissions, cs.KeepImg, p)
if err != nil {
return artifact, err
} else {
return departifacts[len(departifacts)-1], nil
}
artifact.SetDependencies(departifacts)
artifact.SetSourceAssertion(p.GetSourceAssertion())
return artifact, err
}
func (cs *LuetCompiler) FromPackage(p pkg.Package) (CompilationSpec, error) {

View File

@@ -49,7 +49,10 @@ type CompilerOptions struct {
Concurrency int
CompressionType CompressionImplementation
Clean bool
KeepImageExport bool
OnlyDeps bool
NoDeps bool
SolverOptions config.LuetSolverOptions
}
@@ -62,6 +65,8 @@ func NewDefaultCompilerOptions() *CompilerOptions {
KeepImg: true,
Concurrency: runtime.NumCPU(),
Clean: true,
OnlyDeps: false,
NoDeps: false,
}
}

77
pkg/helpers/cli.go Normal file
View File

@@ -0,0 +1,77 @@
// Copyright © 2020 Ettore Di Giacinto <mudler@gentoo.org>
// Daniele Rondina <geaaru@sabayonlinux.org>
//
// 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 <http://www.gnu.org/licenses/>.
package helpers
import (
"errors"
"fmt"
"regexp"
_gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo"
pkg "github.com/mudler/luet/pkg/package"
)
func CreateRegexArray(rgx []string) ([]*regexp.Regexp, error) {
ans := make([]*regexp.Regexp, len(rgx))
if len(rgx) > 0 {
for idx, reg := range rgx {
re := regexp.MustCompile(reg)
if re == nil {
return nil, errors.New("Invalid regex " + reg + "!")
}
ans[idx] = re
}
}
return ans, nil
}
func ParsePackageStr(p string) (*pkg.DefaultPackage, error) {
gp, err := _gentoo.ParsePackageStr(p)
if err != nil {
return nil, err
}
if gp.Version == "" {
gp.Version = "0"
gp.Condition = _gentoo.PkgCondGreaterEqual
}
pkgVersion := ""
if gp.VersionBuild != "" {
pkgVersion = fmt.Sprintf("%s%s%s+%s",
pkg.PkgSelectorConditionFromInt(gp.Condition.Int()).String(),
gp.Version,
gp.VersionSuffix,
gp.VersionBuild,
)
} else {
pkgVersion = fmt.Sprintf("%s%s%s",
pkg.PkgSelectorConditionFromInt(gp.Condition.Int()).String(),
gp.Version,
gp.VersionSuffix,
)
}
pack := &pkg.DefaultPackage{
Name: gp.Name,
Category: gp.Category,
Version: pkgVersion,
Uri: make([]string, 0),
}
return pack, nil
}

32
pkg/helpers/sys.go Normal file
View File

@@ -0,0 +1,32 @@
// Copyright © 2020 Ettore Di Giacinto <mudler@gentoo.org>
//
// 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 <http://www.gnu.org/licenses/>.
package helpers
import (
"os/exec"
"syscall"
"github.com/pkg/errors"
)
// This allows a multi-platform switch in the future
func Exec(cmd string, args []string, env []string) error {
path, err := exec.LookPath(cmd)
if err != nil {
return errors.Wrap(err, "Could not find binary in path: "+cmd)
}
return syscall.Exec(path, args, env)
}

View File

@@ -21,6 +21,7 @@ import (
"os/exec"
"path/filepath"
"sort"
"strings"
"sync"
"github.com/ghodss/yaml"
@@ -36,8 +37,12 @@ import (
)
type LuetInstallerOptions struct {
SolverOptions config.LuetSolverOptions
Concurrency int
SolverOptions config.LuetSolverOptions
Concurrency int
NoDeps bool
OnlyDeps bool
Force bool
PreserveSystemEssentialData bool
}
type LuetInstaller struct {
@@ -103,23 +108,15 @@ func (l *LuetInstaller) Upgrade(s *System) error {
return err
}
// First match packages against repositories by priority
// matches := syncedRepos.PackageMatches(p)
allRepos := pkg.NewInMemoryDatabase(false)
syncedRepos.SyncDatabase(allRepos)
// compute a "big" world
solv := solver.NewResolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
uninstall, solution, err := solv.Upgrade()
uninstall, solution, err := solv.Upgrade(false)
if err != nil {
return errors.Wrap(err, "Failed solving solution for upgrade")
}
for _, u := range uninstall {
err := l.Uninstall(u, s)
if err != nil {
Warning("Failed uninstall for ", u.GetFingerPrint())
}
}
toInstall := []pkg.Package{}
for _, assertion := range solution {
if assertion.Value {
@@ -127,7 +124,7 @@ func (l *LuetInstaller) Upgrade(s *System) error {
}
}
return l.Install(toInstall, s)
return l.swap(syncedRepos, uninstall, toInstall, s)
}
func (l *LuetInstaller) SyncRepositories(inMemory bool) (Repositories, error) {
@@ -152,7 +149,62 @@ func (l *LuetInstaller) SyncRepositories(inMemory bool) (Repositories, error) {
return syncedRepos, nil
}
func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
func (l *LuetInstaller) Swap(toRemove []pkg.Package, toInstall []pkg.Package, s *System) error {
syncedRepos, err := l.SyncRepositories(true)
if err != nil {
return err
}
return l.swap(syncedRepos, toRemove, toInstall, s)
}
func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove []pkg.Package, toInstall []pkg.Package, s *System) error {
// First match packages against repositories by priority
allRepos := pkg.NewInMemoryDatabase(false)
syncedRepos.SyncDatabase(allRepos)
toInstall = syncedRepos.ResolveSelectors(toInstall)
if err := l.install(syncedRepos, toInstall, s, true); err != nil {
return errors.Wrap(err, "Pre-downloading packages")
}
// We don't want any conflict with the installed to raise during the upgrade.
// In this way we both force uninstalls and we avoid to check with conflicts
// against the current system state which is pending to deletion
// E.g. you can't check for conflicts for an upgrade of a new version of A
// if the old A results installed in the system. This is due to the fact that
// now the solver enforces the constraints and explictly denies two packages
// of the same version installed.
forced := false
if l.Options.Force {
forced = true
}
l.Options.Force = true
for _, u := range toRemove {
Info(":package: Marked for deletion", u.HumanReadableString())
err := l.Uninstall(u, s)
if err != nil && !l.Options.Force {
Error("Failed uninstall for ", u.HumanReadableString())
return errors.Wrap(err, "uninstalling "+u.HumanReadableString())
}
}
l.Options.Force = forced
return l.install(syncedRepos, toInstall, s, false)
}
func (l *LuetInstaller) Install(cp []pkg.Package, s *System, downloadOnly bool) error {
syncedRepos, err := l.SyncRepositories(true)
if err != nil {
return err
}
return l.install(syncedRepos, cp, s, downloadOnly)
}
func (l *LuetInstaller) install(syncedRepos Repositories, cp []pkg.Package, s *System, downloadOnly bool) error {
var p []pkg.Package
// Check if the package is installed first
@@ -161,7 +213,7 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
vers, _ := s.Database.FindPackageVersions(pi)
if len(vers) >= 1 {
Warning("Filtering out package " + pi.GetFingerPrint() + ", it has other versions already installed. Uninstall one of them first ")
Warning("Filtering out package " + pi.HumanReadableString() + ", it has other versions already installed. Uninstall one of them first ")
continue
//return errors.New("Package " + pi.GetFingerPrint() + " has other versions already installed. Uninstall one of them first: " + strings.Join(vers, " "))
@@ -175,10 +227,6 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
}
// First get metas from all repos (and decodes trees)
syncedRepos, err := l.SyncRepositories(true)
if err != nil {
return err
}
// First match packages against repositories by priority
// matches := syncedRepos.PackageMatches(p)
@@ -186,111 +234,181 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
allRepos := pkg.NewInMemoryDatabase(false)
syncedRepos.SyncDatabase(allRepos)
p = syncedRepos.ResolveSelectors(p)
solv := solver.NewResolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
solution, err := solv.Install(p)
if err != nil {
return errors.Wrap(err, "Failed solving solution for package")
}
// Gathers things to install
toInstall := map[string]ArtifactMatch{}
for _, assertion := range solution {
if assertion.Value {
matches := syncedRepos.PackageMatches([]pkg.Package{assertion.Package})
if len(matches) == 0 {
return errors.New("Failed matching solutions against repository - where are definitions coming from?!")
}
A:
for _, artefact := range matches[0].Repo.GetIndex() {
if artefact.GetCompileSpec().GetPackage() == nil {
return errors.New("Package in compilespec empty")
var packagesToInstall []pkg.Package
var err error
var solution solver.PackagesAssertions
}
if matches[0].Package.Matches(artefact.GetCompileSpec().GetPackage()) {
// Filter out already installed
if _, err := s.Database.FindPackage(assertion.Package); err != nil {
toInstall[assertion.Package.GetFingerPrint()] = ArtifactMatch{Package: assertion.Package, Artifact: artefact, Repository: matches[0].Repo}
}
break A
}
if !l.Options.NoDeps {
solv := solver.NewResolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
solution, err = solv.Install(p)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Failed solving solution for package")
}
// Gathers things to install
for _, assertion := range solution {
if assertion.Value {
packagesToInstall = append(packagesToInstall, assertion.Package)
}
}
} else if !l.Options.OnlyDeps {
for _, currentPack := range p {
packagesToInstall = append(packagesToInstall, currentPack)
}
}
// Gathers things to install
for _, currentPack := range packagesToInstall {
matches := syncedRepos.PackageMatches([]pkg.Package{currentPack})
if len(matches) == 0 {
return errors.New("Failed matching solutions against repository for " + currentPack.HumanReadableString() + " where are definitions coming from?!")
}
A:
for _, artefact := range matches[0].Repo.GetIndex() {
if artefact.GetCompileSpec().GetPackage() == nil {
return errors.New("Package in compilespec empty")
}
if matches[0].Package.Matches(artefact.GetCompileSpec().GetPackage()) {
// Filter out already installed
if _, err := s.Database.FindPackage(currentPack); err != nil {
toInstall[currentPack.GetFingerPrint()] = ArtifactMatch{Package: currentPack, Artifact: artefact, Repository: matches[0].Repo}
}
break A
}
}
}
// Install packages into rootfs in parallel.
all := make(chan ArtifactMatch)
var wg = new(sync.WaitGroup)
for i := 0; i < l.Options.Concurrency; i++ {
wg.Add(1)
go l.installerWorker(i, wg, all, s)
if !downloadOnly {
// Download first
for i := 0; i < l.Options.Concurrency; i++ {
wg.Add(1)
go l.installerWorker(i, wg, all, s, true)
}
for _, c := range toInstall {
all <- c
}
close(all)
wg.Wait()
all = make(chan ArtifactMatch)
wg = new(sync.WaitGroup)
// Do the real install
for i := 0; i < l.Options.Concurrency; i++ {
wg.Add(1)
go l.installerWorker(i, wg, all, s, false)
}
for _, c := range toInstall {
all <- c
}
close(all)
wg.Wait()
} else {
for i := 0; i < l.Options.Concurrency; i++ {
wg.Add(1)
go l.installerWorker(i, wg, all, s, downloadOnly)
}
for _, c := range toInstall {
all <- c
}
close(all)
wg.Wait()
}
if downloadOnly {
return nil
}
for _, c := range toInstall {
all <- c
// Annotate to the system that the package was installed
_, err := s.Database.CreatePackage(c.Package)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Failed creating package")
}
}
close(all)
wg.Wait()
executedFinalizer := map[string]bool{}
// TODO: Lower those errors as warning
for _, w := range p {
// Finalizers needs to run in order and in sequence.
ordered := solution.Order(allRepos, w.GetFingerPrint())
for _, ass := range ordered {
if ass.Value {
// Annotate to the system that the package was installed
// TODO: Annotate also files that belong to the package, somewhere to uninstall
if _, err := s.Database.FindPackage(ass.Package); err == nil {
err := s.Database.UpdatePackage(ass.Package)
if err != nil {
return errors.Wrap(err, "Failed updating package")
}
} else {
_, err := s.Database.CreatePackage(ass.Package)
if err != nil {
return errors.Wrap(err, "Failed creating package")
}
}
installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok {
return errors.New("Couldn't find ArtifactMatch for " + ass.Package.GetFingerPrint())
}
if !l.Options.NoDeps {
// TODO: Lower those errors as warning
for _, w := range p {
// Finalizers needs to run in order and in sequence.
ordered := solution.Order(allRepos, w.GetFingerPrint())
ORDER:
for _, ass := range ordered {
if ass.Value {
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
if err != nil {
return errors.Wrap(err, "Error getting package "+ass.Package.GetFingerPrint())
}
if helpers.Exists(treePackage.Rel(tree.FinalizerFile)) {
Info("Executing finalizer for " + ass.Package.GetName())
finalizerRaw, err := ioutil.ReadFile(treePackage.Rel(tree.FinalizerFile))
if err != nil {
return errors.Wrap(err, "Error reading file "+treePackage.Rel(tree.FinalizerFile))
installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok {
// It was a dep already installed in the system, so we can skip it safely
continue ORDER
}
if _, exists := executedFinalizer[ass.Package.GetFingerPrint()]; !exists {
finalizer, err := NewLuetFinalizerFromYaml(finalizerRaw)
if err != nil {
return errors.Wrap(err, "Error reading finalizer "+treePackage.Rel(tree.FinalizerFile))
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
if err != nil {
return errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
}
if helpers.Exists(treePackage.Rel(tree.FinalizerFile)) {
Info("Executing finalizer for " + ass.Package.HumanReadableString())
finalizerRaw, err := ioutil.ReadFile(treePackage.Rel(tree.FinalizerFile))
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error reading file "+treePackage.Rel(tree.FinalizerFile))
}
err = finalizer.RunInstall()
if err != nil {
return errors.Wrap(err, "Error executing install finalizer "+treePackage.Rel(tree.FinalizerFile))
if _, exists := executedFinalizer[ass.Package.GetFingerPrint()]; !exists {
finalizer, err := NewLuetFinalizerFromYaml(finalizerRaw)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error reading finalizer "+treePackage.Rel(tree.FinalizerFile))
}
err = finalizer.RunInstall()
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error executing install finalizer "+treePackage.Rel(tree.FinalizerFile))
}
executedFinalizer[ass.Package.GetFingerPrint()] = true
}
executedFinalizer[ass.Package.GetFingerPrint()] = true
}
}
}
}
} else {
for _, c := range toInstall {
treePackage, err := c.Repository.GetTree().GetDatabase().FindPackage(c.Package)
if err != nil {
return errors.Wrap(err, "Error getting package "+c.Package.HumanReadableString())
}
if helpers.Exists(treePackage.Rel(tree.FinalizerFile)) {
Info("Executing finalizer for " + c.Package.HumanReadableString())
finalizerRaw, err := ioutil.ReadFile(treePackage.Rel(tree.FinalizerFile))
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error reading file "+treePackage.Rel(tree.FinalizerFile))
}
if _, exists := executedFinalizer[c.Package.GetFingerPrint()]; !exists {
finalizer, err := NewLuetFinalizerFromYaml(finalizerRaw)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error reading finalizer "+treePackage.Rel(tree.FinalizerFile))
}
err = finalizer.RunInstall()
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error executing install finalizer "+treePackage.Rel(tree.FinalizerFile))
}
executedFinalizer[c.Package.GetFingerPrint()] = true
}
}
}
}
return nil
}
func (l *LuetInstaller) installPackage(a ArtifactMatch, s *System) error {
func (l *LuetInstaller) installPackage(a ArtifactMatch, s *System, downloadOnly bool) error {
artifact, err := a.Repository.Client().DownloadArtifact(a.Artifact)
if err != nil {
@@ -298,17 +416,20 @@ func (l *LuetInstaller) installPackage(a ArtifactMatch, s *System) error {
}
err = artifact.Verify()
if err != nil {
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Artifact integrity check failure")
}
if downloadOnly {
return nil
}
files, err := artifact.FileList()
if err != nil {
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Could not open package archive")
}
err = artifact.Unpack(s.Target, true)
if err != nil {
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error met while unpacking rootfs")
}
@@ -317,17 +438,24 @@ func (l *LuetInstaller) installPackage(a ArtifactMatch, s *System) error {
return s.Database.SetPackageFiles(&pkg.PackageFile{PackageFingerprint: a.Package.GetFingerPrint(), Files: files})
}
func (l *LuetInstaller) installerWorker(i int, wg *sync.WaitGroup, c <-chan ArtifactMatch, s *System) error {
func (l *LuetInstaller) installerWorker(i int, wg *sync.WaitGroup, c <-chan ArtifactMatch, s *System, downloadOnly bool) error {
defer wg.Done()
for p := range c {
// TODO: Keep trace of what was added from the tar, and save it into system
err := l.installPackage(p, s)
if err != nil {
err := l.installPackage(p, s, downloadOnly)
if err != nil && !l.Options.Force {
//TODO: Uninstall, rollback.
Fatal("Failed installing package "+p.Package.GetName(), err.Error())
return errors.Wrap(err, "Failed installing package "+p.Package.GetName())
}
if err == nil && downloadOnly {
Info(":package: ", p.Package.HumanReadableString(), "downloaded")
} else if err == nil {
Info(":package: ", p.Package.HumanReadableString(), "installed")
} else if err != nil && l.Options.Force {
Info(":package: ", p.Package.HumanReadableString(), "installed with failures (force install)")
}
}
return nil
@@ -342,7 +470,15 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
// Remove from target
for _, f := range files {
target := filepath.Join(s.Target, f)
Info("Removing", target)
Debug("Removing", target)
if l.Options.PreserveSystemEssentialData &&
strings.HasPrefix(f, config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath()) ||
strings.HasPrefix(f, config.LuetCfg.GetSystem().GetSystemRepoDatabaseDirPath()) {
Warning("Preserve ", f, " which is required by luet ( you have to delete it manually if you really need to)")
continue
}
err := os.Remove(target)
if err != nil {
Warning("Failed removing file (not present in the system target ?)", target)
@@ -362,19 +498,34 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
}
func (l *LuetInstaller) Uninstall(p pkg.Package, s *System) error {
// compute uninstall from all world - remove packages in parallel - run uninstall finalizer (in order) - mark the uninstallation in db
// compute uninstall from all world - remove packages in parallel - run uninstall finalizer (in order) TODO - mark the uninstallation in db
// Get installed definition
solv := solver.NewResolver(s.Database, s.Database, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
solution, err := solv.Uninstall(p)
if err != nil {
return errors.Wrap(err, "Uninstall failed")
checkConflicts := true
if l.Options.Force == true {
checkConflicts = false
}
for _, p := range solution {
Info("Uninstalling", p.GetFingerPrint())
if !l.Options.NoDeps {
solv := solver.NewResolver(s.Database, s.Database, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
solution, err := solv.Uninstall(p, checkConflicts)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
}
for _, p := range solution {
Info("Uninstalling", p.HumanReadableString())
err := l.uninstall(p, s)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Uninstall failed")
}
}
} else {
Info("Uninstalling", p.HumanReadableString(), "without deps")
err := l.uninstall(p, s)
if err != nil {
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Uninstall failed")
}
Info(":package: ", p.HumanReadableString(), "uninstalled")
}
return nil

View File

@@ -113,7 +113,7 @@ urls:
Expect(repo.GetType()).To(Equal("disk"))
systemDB := pkg.NewInMemoryDatabase(false)
system := &System{Database: systemDB, Target: fakeroot}
err = inst.Install([]pkg.Package{&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}}, system)
err = inst.Install([]pkg.Package{&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}}, system, false)
Expect(err).ToNot(HaveOccurred())
Expect(helpers.Exists(filepath.Join(fakeroot, "test5"))).To(BeTrue())
@@ -231,7 +231,7 @@ urls:
systemDB := pkg.NewBoltDatabase(filepath.Join(bolt, "db.db"))
system := &System{Database: systemDB, Target: fakeroot}
err = inst.Install([]pkg.Package{&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}}, system)
err = inst.Install([]pkg.Package{&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}}, system, false)
Expect(err).ToNot(HaveOccurred())
Expect(helpers.Exists(filepath.Join(fakeroot, "test5"))).To(BeTrue())
@@ -339,7 +339,7 @@ urls:
systemDB := pkg.NewBoltDatabase(filepath.Join(bolt, "db.db"))
system := &System{Database: systemDB, Target: fakeroot}
err = inst.Install([]pkg.Package{&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}}, system)
err = inst.Install([]pkg.Package{&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}}, system, false)
Expect(err).ToNot(HaveOccurred())
Expect(helpers.Exists(filepath.Join(fakeroot, "test5"))).To(BeTrue())
@@ -454,7 +454,7 @@ urls:
systemDB := pkg.NewBoltDatabase(filepath.Join(bolt, "db.db"))
system := &System{Database: systemDB, Target: fakeroot}
err = inst.Install([]pkg.Package{&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}}, system)
err = inst.Install([]pkg.Package{&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}}, system, false)
Expect(err).ToNot(HaveOccurred())
Expect(helpers.Exists(filepath.Join(fakeroot, "test5"))).To(BeTrue())

View File

@@ -23,11 +23,12 @@ import (
)
type Installer interface {
Install([]pkg.Package, *System) error
Install([]pkg.Package, *System, bool) error
Uninstall(pkg.Package, *System) error
Upgrade(s *System) error
Repositories([]Repository)
SyncRepositories(bool) (Repositories, error)
Swap([]pkg.Package, []pkg.Package, *System) error
}
type Client interface {

View File

@@ -21,7 +21,6 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
@@ -69,6 +68,19 @@ type LuetSystemRepositorySerialized struct {
TreeChecksums compiler.Checksums `json:"treechecksums"`
}
type LuetSearchModeType string
const (
SLabel LuetSearchModeType = "label"
SRegexPkg LuetSearchModeType = "regexPkg"
SRegexLabel LuetSearchModeType = "regexLabel"
)
type LuetSearchOpts struct {
Pattern string
Mode LuetSearchModeType
}
func GenerateRepository(name, descr, t string, urls []string, priority int, src, treeDir string, db pkg.PackageDatabase) (Repository, error) {
art, err := buildPackageIndex(src)
@@ -530,14 +542,21 @@ func (re Repositories) ResolveSelectors(p []pkg.Package) []pkg.Package {
var matches []pkg.Package
PACKAGE:
for _, pack := range p {
REPOSITORY:
for _, r := range re {
if pack.IsSelector() {
c, err := r.GetTree().GetDatabase().FindPackageCandidate(pack)
// If FindPackageCandidate returns the same package, it means it couldn't find one.
// Skip this repository and keep looking.
if c.String() == pack.String() {
continue REPOSITORY
}
if err == nil {
matches = append(matches, c)
continue PACKAGE
}
} else {
// If it's not a selector, just append it
matches = append(matches, pack)
}
}
@@ -547,14 +566,24 @@ PACKAGE:
}
func (re Repositories) Search(s string) []PackageMatch {
func (re Repositories) SearchPackages(p string, o LuetSearchOpts) []PackageMatch {
sort.Sort(re)
var term = regexp.MustCompile(s)
var matches []PackageMatch
var err error
for _, r := range re {
for _, pack := range r.GetTree().GetDatabase().World() {
if term.MatchString(pack.GetName()) {
var repoMatches []pkg.Package
if o.Mode == SRegexPkg {
repoMatches, err = r.GetTree().GetDatabase().FindPackageMatch(p)
} else if o.Mode == SLabel {
repoMatches, err = r.GetTree().GetDatabase().FindPackageLabel(p)
} else if o.Mode == SRegexLabel {
repoMatches, err = r.GetTree().GetDatabase().FindPackageLabelMatch(p)
}
if err == nil && len(repoMatches) > 0 {
for _, pack := range repoMatches {
matches = append(matches, PackageMatch{Package: pack, Repo: r})
}
}
@@ -562,3 +591,15 @@ func (re Repositories) Search(s string) []PackageMatch {
return matches
}
func (re Repositories) SearchLabelMatch(s string) []PackageMatch {
return re.SearchPackages(s, LuetSearchOpts{Pattern: s, Mode: SRegexLabel})
}
func (re Repositories) SearchLabel(s string) []PackageMatch {
return re.SearchPackages(s, LuetSearchOpts{Pattern: s, Mode: SLabel})
}
func (re Repositories) Search(s string) []PackageMatch {
return re.SearchPackages(s, LuetSearchOpts{Pattern: s, Mode: SRegexPkg})
}

View File

@@ -45,6 +45,9 @@ type PackageSet interface {
World() []Package
FindPackageCandidate(p Package) (Package, error)
FindPackageLabel(labelKey string) ([]Package, error)
FindPackageLabelMatch(pattern string) ([]Package, error)
FindPackageMatch(pattern string) ([]Package, error)
}
type PackageFile struct {

View File

@@ -18,6 +18,7 @@ package pkg
import (
"encoding/base64"
"os"
"regexp"
"strconv"
"sync"
"time"
@@ -384,3 +385,61 @@ func (db *BoltDatabase) FindPackageVersions(p Package) ([]Package, error) {
}
return versionsInWorld, nil
}
func (db *BoltDatabase) FindPackageLabel(labelKey string) ([]Package, error) {
var ans []Package
for _, k := range db.GetPackages() {
pack, err := db.GetPackage(k)
if err != nil {
return ans, err
}
if pack.HasLabel(labelKey) {
ans = append(ans, pack)
}
}
return ans, nil
}
func (db *BoltDatabase) FindPackageLabelMatch(pattern string) ([]Package, error) {
var ans []Package
re := regexp.MustCompile(pattern)
if re == nil {
return nil, errors.New("Invalid regex " + pattern + "!")
}
for _, k := range db.GetPackages() {
pack, err := db.GetPackage(k)
if err != nil {
return ans, err
}
if pack.MatchLabel(re) {
ans = append(ans, pack)
}
}
return ans, nil
}
func (db *BoltDatabase) FindPackageMatch(pattern string) ([]Package, error) {
var ans []Package
re := regexp.MustCompile(pattern)
if re == nil {
return nil, errors.New("Invalid regex " + pattern + "!")
}
for _, k := range db.GetPackages() {
pack, err := db.GetPackage(k)
if err != nil {
return ans, err
}
if re.MatchString(pack.GetCategory() + pack.GetName()) {
ans = append(ans, pack)
}
}
return ans, nil
}

View File

@@ -18,6 +18,7 @@ package pkg
import (
"encoding/base64"
"encoding/json"
"regexp"
"sync"
"github.com/pkg/errors"
@@ -358,3 +359,62 @@ func (db *InMemoryDatabase) FindPackageCandidate(p Package) (Package, error) {
return required, err
}
func (db *InMemoryDatabase) FindPackageLabel(labelKey string) ([]Package, error) {
var ans []Package
for _, k := range db.GetPackages() {
pack, err := db.GetPackage(k)
if err != nil {
return ans, err
}
if pack.HasLabel(labelKey) {
ans = append(ans, pack)
}
}
return ans, nil
}
func (db *InMemoryDatabase) FindPackageLabelMatch(pattern string) ([]Package, error) {
var ans []Package
re := regexp.MustCompile(pattern)
if re == nil {
return nil, errors.New("Invalid regex " + pattern + "!")
}
for _, k := range db.GetPackages() {
pack, err := db.GetPackage(k)
if err != nil {
return ans, err
}
if pack.MatchLabel(re) {
ans = append(ans, pack)
}
}
return ans, nil
}
func (db *InMemoryDatabase) FindPackageMatch(pattern string) ([]Package, error) {
var ans []Package
re := regexp.MustCompile(pattern)
if re == nil {
return nil, errors.New("Invalid regex " + pattern + "!")
}
for _, k := range db.GetPackages() {
pack, err := db.GetPackage(k)
if err != nil {
return ans, err
}
if re.MatchString(pack.GetCategory() + pack.GetName()) {
ans = append(ans, pack)
}
}
return ans, nil
}

View File

@@ -17,19 +17,22 @@ package pkg
import (
"bytes"
"crypto/md5"
"encoding/json"
"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)
@@ -45,6 +48,7 @@ type Package interface {
Requires([]*DefaultPackage) Package
Conflicts([]*DefaultPackage) Package
Revdeps(PackageDatabase) []Package
LabelDeps(PackageDatabase, string) []Package
GetProvides() []*DefaultPackage
SetProvides([]*DefaultPackage) Package
@@ -60,7 +64,7 @@ type Package interface {
GetVersion() string
RequiresContains(PackageDatabase, Package) (bool, error)
Matches(m Package) bool
Bigger(m Package) bool
BumpBuildVersion() error
AddUse(use string)
RemoveUse(use string)
@@ -82,11 +86,18 @@ type Package interface {
SetLicense(string)
GetLicense() string
AddLabel(string, string)
GetLabels() map[string]string
HasLabel(string) bool
MatchLabel(*regexp.Regexp) bool
IsSelector() bool
VersionMatchSelector(string) (bool, error)
SelectorMatchVersion(string) (bool, error)
String() string
HumanReadableString() string
HashFingerprint() string
}
type Tree interface {
@@ -146,9 +157,11 @@ type DefaultPackage struct {
// Path is set only internally when tree is loaded from disk
Path string `json:"path,omitempty"`
Description string `json:"description"`
Uri []string `json:"uri"`
License string `json:"license"`
Description string `json:"description,omitempty"`
Uri []string `json:"uri,omitempty"`
License string `json:"license,omitempty"`
Labels map[string]string `json:labels,omitempty`
}
// State represent the package state
@@ -156,7 +169,13 @@ type State string
// NewPackage returns a new package
func NewPackage(name, version string, requires []*DefaultPackage, conflicts []*DefaultPackage) *DefaultPackage {
return &DefaultPackage{Name: name, Version: version, PackageRequires: requires, PackageConflicts: conflicts}
return &DefaultPackage{
Name: name,
Version: version,
PackageRequires: requires,
PackageConflicts: conflicts,
Labels: make(map[string]string, 0),
}
}
func (p *DefaultPackage) String() string {
@@ -173,6 +192,16 @@ func (p *DefaultPackage) GetFingerPrint() string {
return fmt.Sprintf("%s-%s-%s", p.Name, p.Category, p.Version)
}
func (p *DefaultPackage) HashFingerprint() string {
h := md5.New()
io.WriteString(h, p.GetFingerPrint())
return fmt.Sprintf("%x", h.Sum(nil))
}
func (p *DefaultPackage) HumanReadableString() string {
return fmt.Sprintf("%s/%s-%s", p.Category, p.Name, p.Version)
}
func FromString(s string) Package {
var unescaped DefaultPackage
@@ -204,6 +233,28 @@ func (p *DefaultPackage) IsSelector() bool {
return strings.ContainsAny(p.GetVersion(), "<>=")
}
func (p *DefaultPackage) HasLabel(label string) bool {
ans := false
for k, _ := range p.Labels {
if k == label {
ans = true
break
}
}
return ans
}
func (p *DefaultPackage) MatchLabel(r *regexp.Regexp) bool {
ans := false
for k, v := range p.Labels {
if r.MatchString(k + "=" + v) {
ans = true
break
}
}
return ans
}
// AddUse adds a use to a package
func (p *DefaultPackage) AddUse(use string) {
for _, v := range p.UseFlags {
@@ -288,6 +339,12 @@ func (p *DefaultPackage) SetCategory(s string) {
func (p *DefaultPackage) GetUses() []string {
return p.UseFlags
}
func (p *DefaultPackage) AddLabel(k, v string) {
p.Labels[k] = v
}
func (p *DefaultPackage) GetLabels() map[string]string {
return p.Labels
}
func (p *DefaultPackage) GetProvides() []*DefaultPackage {
return p.Provides
}
@@ -320,13 +377,6 @@ func (p *DefaultPackage) Matches(m Package) bool {
}
return false
}
func (p *DefaultPackage) Bigger(m Package) bool {
low := Lower([]Package{p, m})
if low.Matches(m) {
return true
}
return false
}
func (p *DefaultPackage) Expand(definitiondb PackageDatabase) ([]Package, error) {
var versionsInWorld []Package
@@ -365,6 +415,19 @@ func (p *DefaultPackage) Revdeps(definitiondb PackageDatabase) []Package {
return versionsInWorld
}
func (p *DefaultPackage) LabelDeps(definitiondb PackageDatabase, labelKey string) []Package {
var pkgsWithLabelInWorld []Package
// TODO: check if integrate some index to improve
// research instead of iterate all list.
for _, w := range definitiondb.World() {
if w.HasLabel(labelKey) {
pkgsWithLabelInWorld = append(pkgsWithLabelInWorld, w)
}
}
return pkgsWithLabelInWorld
}
func DecodePackage(ID string, db PackageDatabase) (Package, error) {
return db.GetPackage(ID)
}
@@ -394,29 +457,7 @@ func (pack *DefaultPackage) RequiresContains(definitiondb PackageDatabase, s Pac
return false, nil
}
func Lower(set []Package) Package {
var versionsMap map[string]Package = make(map[string]Package)
if len(set) == 0 {
panic("Best needs a list with elements")
}
versionsRaw := []string{}
for _, p := range set {
versionsRaw = append(versionsRaw, p.GetVersion())
versionsMap[p.GetVersion()] = p
}
versions := make([]*version.Version, len(versionsRaw))
for i, raw := range versionsRaw {
v, _ := version.NewVersion(raw)
versions[i] = v
}
// After this, the versions are properly sorted
sort.Sort(version.Collection(versions))
return versionsMap[versions[0].Original()]
}
func Best(set []Package) Package {
var versionsMap map[string]Package = make(map[string]Package)
if len(set) == 0 {
@@ -454,6 +495,22 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
A := bf.Var(encodedA)
var formulas []bf.Formula
// Do conflict with other packages versions (if A is selected, then conflict with other versions of A)
packages, _ := definitiondb.FindPackageVersions(p)
if len(packages) > 0 {
for _, cp := range packages {
encodedB, err := cp.Encode(db)
if err != nil {
return nil, err
}
B := bf.Var(encodedB)
if !p.Matches(cp) {
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.Not(A), bf.Not(B))))
}
}
}
for _, requiredDef := range p.GetRequires() {
required, err := definitiondb.FindPackage(requiredDef)
if err != nil || requiredDef.IsSelector() {
@@ -465,62 +522,60 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
if err != nil || len(packages) == 0 {
required = requiredDef
} else {
if len(packages) == 1 {
required = packages[0]
} else {
var ALO, priorityConstraints, priorityALO []bf.Formula
// Try to prio best match
// Force the solver to consider first our candidate (if does exists).
// Then builds ALO and AMO for the requires.
c, candidateErr := definitiondb.FindPackageCandidate(requiredDef)
var C bf.Formula
if candidateErr == nil {
// We have a desired candidate, try to look a solution with that included first
for _, o := range packages {
encodedB, err := o.Encode(db)
if err != nil {
return nil, err
}
B := bf.Var(encodedB)
if !o.Matches(c) {
priorityConstraints = append(priorityConstraints, bf.Not(B))
priorityALO = append(priorityALO, B)
}
}
encodedC, err := c.Encode(db)
if err != nil {
return nil, err
}
C = bf.Var(encodedC)
// Or the Candidate is true, or all the others might be not true
// This forces the CDCL sat implementation to look first at a solution with C=true
formulas = append(formulas, bf.Or(bf.Or(C, bf.Or(priorityConstraints...)), bf.Or(bf.Not(C), bf.Or(priorityALO...))))
}
var ALO, priorityConstraints, priorityALO []bf.Formula
// AMO - At most one
// Try to prio best match
// Force the solver to consider first our candidate (if does exists).
// Then builds ALO and AMO for the requires.
c, candidateErr := definitiondb.FindPackageCandidate(requiredDef)
var C bf.Formula
if candidateErr == nil {
// We have a desired candidate, try to look a solution with that included first
for _, o := range packages {
encodedB, err := o.Encode(db)
if err != nil {
return nil, err
}
B := bf.Var(encodedB)
ALO = append(ALO, B)
for _, i := range packages {
encodedI, err := i.Encode(db)
if err != nil {
return nil, err
}
I := bf.Var(encodedI)
if !o.Matches(i) {
formulas = append(formulas, bf.Or(bf.Not(I), bf.Not(B)))
}
if !o.Matches(c) {
priorityConstraints = append(priorityConstraints, bf.Not(B))
priorityALO = append(priorityALO, B)
}
}
formulas = append(formulas, bf.Or(ALO...)) // ALO - At least one
continue
encodedC, err := c.Encode(db)
if err != nil {
return nil, err
}
C = bf.Var(encodedC)
// Or the Candidate is true, or all the others might be not true
// This forces the CDCL sat implementation to look first at a solution with C=true
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.Or(C, bf.Or(priorityConstraints...)), bf.Or(bf.Not(C), bf.Or(priorityALO...)))))
}
// AMO - At most one
for _, o := range packages {
encodedB, err := o.Encode(db)
if err != nil {
return nil, err
}
B := bf.Var(encodedB)
ALO = append(ALO, B)
for _, i := range packages {
encodedI, err := i.Encode(db)
if err != nil {
return nil, err
}
I := bf.Var(encodedI)
if !o.Matches(i) {
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.Not(I), bf.Not(B))))
}
}
}
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(ALO...))) // ALO - At least one
continue
}
}
encodedB, err := required.Encode(db)
@@ -586,6 +641,7 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
formulas = append(formulas, f...)
}
return formulas, nil
}
@@ -608,3 +664,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
}

View File

@@ -16,6 +16,8 @@
package pkg_test
import (
"regexp"
. "github.com/mudler/luet/pkg/package"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -25,11 +27,18 @@ var _ = Describe("Package", func() {
Context("Encoding/Decoding", func() {
a := &DefaultPackage{Name: "test", Version: "1", Category: "t"}
a1 := NewPackage("A", "1.0", []*DefaultPackage{}, []*DefaultPackage{})
It("Encodes and decodes correctly", func() {
Expect(a.String()).ToNot(Equal(""))
p := FromString(a.String())
Expect(p).To(Equal(a))
})
It("Generates packages fingerprint's hashes", func() {
Expect(a.HashFingerprint()).ToNot(Equal(a1.HashFingerprint()))
Expect(a.HashFingerprint()).To(Equal("c64caa391b79adb598ad98e261aa79a0"))
})
})
Context("Simple package", func() {
@@ -54,6 +63,34 @@ var _ = Describe("Package", func() {
})
})
Context("Find label on packages", func() {
a := NewPackage("A", ">=1.0", []*DefaultPackage{}, []*DefaultPackage{})
a.AddLabel("project1", "test1")
a.AddLabel("label2", "value1")
b := NewPackage("B", "1.0", []*DefaultPackage{}, []*DefaultPackage{})
b.AddLabel("project2", "test2")
b.AddLabel("label2", "value1")
It("Expands correctly", func() {
var err error
definitions := NewInMemoryDatabase(false)
for _, p := range []Package{a, b} {
_, err = definitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
re := regexp.MustCompile("project[0-9][=].*")
Expect(err).ToNot(HaveOccurred())
Expect(re).ToNot(BeNil())
Expect(a.HasLabel("label2")).To(Equal(true))
Expect(a.HasLabel("label3")).To(Equal(false))
Expect(a.HasLabel("project1")).To(Equal(true))
Expect(b.HasLabel("project2")).To(Equal(true))
Expect(b.HasLabel("label2")).To(Equal(true))
Expect(b.MatchLabel(re)).To(Equal(true))
Expect(a.MatchLabel(re)).To(Equal(true))
})
})
Context("Check description", func() {
a := NewPackage("A", ">=1.0", []*DefaultPackage{}, []*DefaultPackage{})
a.SetDescription("Description A")
@@ -204,7 +241,7 @@ var _ = Describe("Package", func() {
f, err := a1.BuildFormula(definitions, db)
Expect(err).ToNot(HaveOccurred())
Expect(len(f)).To(Equal(2))
Expect(len(f)).To(Equal(8))
// Expect(f[0].String()).To(Equal("or(not(c31f5842), a4910f77)"))
// Expect(f[1].String()).To(Equal("or(not(c31f5842), not(a97670be))"))
})
@@ -245,4 +282,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"))
})
})
})

View File

@@ -161,6 +161,35 @@ func ParseVersion(v string) (PkgVersionSelector, error) {
ans.Condition = PkgCondNot
}
// Check if build number is present
buildIdx := strings.Index(v, "+")
buildVersion := ""
if buildIdx > 0 {
// <pre-release> ::= <dot-separated pre-release identifiers>
//
// <dot-separated pre-release identifiers> ::=
// <pre-release identifier> | <pre-release identifier> "."
// <dot-separated pre-release identifiers>
//
// <build> ::= <dot-separated build identifiers>
//
// <dot-separated build identifiers> ::= <build identifier>
// | <build identifier> "." <dot-separated build identifiers>
//
// <pre-release identifier> ::= <alphanumeric identifier>
// | <numeric identifier>
//
// <build identifier> ::= <alphanumeric identifier>
// | <digits>
//
// <alphanumeric identifier> ::= <non-digit>
// | <non-digit> <identifier characters>
// | <identifier characters> <non-digit>
// | <identifier characters> <non-digit> <identifier characters>
buildVersion = v[buildIdx:]
v = v[0:buildIdx]
}
regexPkg := regexp.MustCompile(
fmt.Sprintf("(%s|%s|%s|%s|%s|%s)((%s|%s|%s|%s|%s|%s|%s)+)*$",
// Version regex
@@ -216,6 +245,8 @@ func ParseVersion(v string) (PkgVersionSelector, error) {
ans.Condition = PkgCondEqual
}
ans.Version += buildVersion
// NOTE: Now suffix complex like _alpha_rc1 are not supported.
return ans, nil
}
@@ -225,15 +256,20 @@ func PackageAdmit(selector, i PkgVersionSelector) (bool, error) {
var v2 *version.Version = nil
var ans bool
var err error
var sanitizedSelectorVersion, sanitizedIVersion string
if selector.Version != "" {
v1, err = version.NewVersion(selector.Version)
// TODO: This is temporary!. I promise it.
sanitizedSelectorVersion = strings.ReplaceAll(selector.Version, "_", "-")
v1, err = version.NewVersion(sanitizedSelectorVersion)
if err != nil {
return false, err
}
}
if i.Version != "" {
v2, err = version.NewVersion(i.Version)
sanitizedIVersion = strings.ReplaceAll(i.Version, "_", "-")
v2, err = version.NewVersion(sanitizedIVersion)
if err != nil {
return false, err
}
@@ -259,7 +295,7 @@ func PackageAdmit(selector, i PkgVersionSelector) (bool, error) {
// TODO: case of 7.3* where 7.30 is accepted.
if v1 != nil && v2 != nil {
segments := v1.Segments()
n := strings.Count(selector.Version, ".")
n := strings.Count(sanitizedIVersion, ".")
switch n {
case 0:
segments[0]++
@@ -272,7 +308,7 @@ func PackageAdmit(selector, i PkgVersionSelector) (bool, error) {
}
nextVersion := strings.Trim(strings.Replace(fmt.Sprint(segments), " ", ".", -1), "[]")
constraints, err := version.NewConstraint(
fmt.Sprintf(">= %s, < %s", selector.Version, nextVersion),
fmt.Sprintf(">= %s, < %s", sanitizedSelectorVersion, nextVersion),
)
if err != nil {
return false, err

View File

@@ -136,6 +136,72 @@ var _ = Describe("Versions", func() {
})
})
Context("Versions Parser11 - semver", func() {
v, err := ParseVersion("0.1.0+0")
It("ParseVersion10", func() {
var c PkgSelectorCondition = PkgCondEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("0.1.0+0"))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser12 - semver", func() {
v, err := ParseVersion(">=0.1.0_alpha+AB")
It("ParseVersion10", func() {
var c PkgSelectorCondition = PkgCondGreaterEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("0.1.0+AB"))
Expect(v.VersionSuffix).Should(Equal("_alpha"))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser13 - semver", func() {
v, err := ParseVersion(">=0.1.0_alpha+0.1.22")
It("ParseVersion10", func() {
var c PkgSelectorCondition = PkgCondGreaterEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("0.1.0+0.1.22"))
Expect(v.VersionSuffix).Should(Equal("_alpha"))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser14 - semver", func() {
v, err := ParseVersion(">=0.1.0_alpha+0.1.22")
It("ParseVersion10", func() {
var c PkgSelectorCondition = PkgCondGreaterEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("0.1.0+0.1.22"))
Expect(v.VersionSuffix).Should(Equal("_alpha"))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser15 - semver", func() {
v, err := ParseVersion("<=0.3.222.4.5+AB")
It("ParseVersion10", func() {
var c PkgSelectorCondition = PkgCondLessEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("0.3.222.4.5+AB"))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser16 - semver", func() {
v, err := ParseVersion("<=1.0.29+pre2_p20191024")
It("ParseVersion10", func() {
var c PkgSelectorCondition = PkgCondLessEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("1.0.29+pre2_p20191024"))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Selector1", func() {
v1, err := ParseVersion(">=0.0.20190406.4.9.172-r1")
v2, err2 := ParseVersion("1.0.111")
@@ -184,6 +250,54 @@ var _ = Describe("Versions", func() {
})
})
Context("Selector5", func() {
v1, err := ParseVersion(">0.1.0+0.4")
v2, err2 := ParseVersion("0.1.0+0.3")
match, err3 := PackageAdmit(v1, v2)
It("Selector5", func() {
Expect(err).Should(BeNil())
Expect(err2).Should(BeNil())
Expect(err3).Should(BeNil())
Expect(match).Should(Equal(false))
})
})
Context("Selector6", func() {
v1, err := ParseVersion(">=0.1.0+0.4")
v2, err2 := ParseVersion("0.1.0+0.5")
match, err3 := PackageAdmit(v1, v2)
It("Selector6", func() {
Expect(err).Should(BeNil())
Expect(err2).Should(BeNil())
Expect(err3).Should(BeNil())
Expect(match).Should(Equal(true))
})
})
PContext("Selector7", func() {
v1, err := ParseVersion(">0.1.0+0.4")
v2, err2 := ParseVersion("0.1.0+0.5")
match, err3 := PackageAdmit(v1, v2)
It("Selector7", func() {
Expect(err).Should(BeNil())
Expect(err2).Should(BeNil())
Expect(err3).Should(BeNil())
Expect(match).Should(Equal(true))
})
})
Context("Selector8", func() {
v1, err := ParseVersion(">=0")
v2, err2 := ParseVersion("1.0.29+pre2_p20191024")
match, err3 := PackageAdmit(v1, v2)
It("Selector8", func() {
Expect(err).Should(BeNil())
Expect(err2).Should(BeNil())
Expect(err3).Should(BeNil())
Expect(match).Should(Equal(true))
})
})
Context("Condition Converter 1", func() {
gp, err := gentoo.ParsePackageStr("=layer/build-1.0")
var cond gentoo.PackageCond = gentoo.PkgCondEqual

View File

@@ -35,7 +35,7 @@ func LoadRepositories(c *LuetConfig) error {
files, err := ioutil.ReadDir(rdir)
if err != nil {
Warning("Skip dir", rdir, ":", err.Error())
Debug("Skip dir", rdir, ":", err.Error())
continue
}

View File

@@ -28,11 +28,11 @@ import (
type PackageSolver interface {
SetDefinitionDatabase(pkg.PackageDatabase)
Install(p []pkg.Package) (PackagesAssertions, error)
Uninstall(candidate pkg.Package) ([]pkg.Package, error)
Uninstall(candidate pkg.Package, checkconflicts bool) ([]pkg.Package, error)
ConflictsWithInstalled(p pkg.Package) (bool, error)
ConflictsWith(p pkg.Package, ls []pkg.Package) (bool, error)
World() []pkg.Package
Upgrade() ([]pkg.Package, PackagesAssertions, error)
Upgrade(checkconflicts bool) ([]pkg.Package, PackagesAssertions, error)
SetResolver(PackageResolver)
@@ -210,7 +210,7 @@ func (s *Solver) ConflictsWithInstalled(p pkg.Package) (bool, error) {
return s.ConflictsWith(p, s.Installed())
}
func (s *Solver) Upgrade() ([]pkg.Package, PackagesAssertions, error) {
func (s *Solver) Upgrade(checkconflicts bool) ([]pkg.Package, PackagesAssertions, error) {
// First get candidates that needs to be upgraded..
@@ -236,12 +236,11 @@ func (s *Solver) Upgrade() ([]pkg.Package, PackagesAssertions, error) {
}
}
}
s2 := NewSolver(installedcopy, s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
s2.SetResolver(s.Resolver)
// Then try to uninstall the versions in the system, and store that tree
for _, p := range toUninstall {
r, err := s.Uninstall(p)
r, err := s.Uninstall(p, checkconflicts)
if err != nil {
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall selected candidate "+p.GetFingerPrint())
}
@@ -262,7 +261,7 @@ func (s *Solver) Upgrade() ([]pkg.Package, PackagesAssertions, error) {
// Uninstall takes a candidate package and return a list of packages that would be removed
// in order to purge the candidate. Returns error if unsat.
func (s *Solver) Uninstall(c pkg.Package) ([]pkg.Package, error) {
func (s *Solver) Uninstall(c pkg.Package, checkconflicts bool) ([]pkg.Package, error) {
var res []pkg.Package
candidate, err := s.InstalledDatabase.FindPackage(c)
if err != nil {
@@ -294,17 +293,19 @@ func (s *Solver) Uninstall(c pkg.Package) ([]pkg.Package, error) {
}
}
s2 := NewSolver(pkg.NewInMemoryDatabase(false), s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
s2.SetResolver(s.Resolver)
// Get the requirements to install the candidate
saved := s.InstalledDatabase
s.InstalledDatabase = pkg.NewInMemoryDatabase(false)
asserts, err := s.Install([]pkg.Package{candidate})
asserts, err := s2.Install([]pkg.Package{candidate})
if err != nil {
return nil, err
}
s.InstalledDatabase = saved
for _, a := range asserts {
if a.Value {
if !checkconflicts {
res = append(res, a.Package.IsFlagged(false))
continue
}
c, err := s.ConflictsWithInstalled(a.Package)
if err != nil {

View File

@@ -363,12 +363,19 @@ var _ = Describe("Solver", func() {
It("Selects best version", func() {
E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E.SetCategory("test")
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
C.SetCategory("test")
D2 := pkg.NewPackage("D", "1.9", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
D2.SetCategory("test")
D := pkg.NewPackage("D", "1.8", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
D.SetCategory("test")
D1 := pkg.NewPackage("D", "1.4", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "1.1", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "D", Version: ">=1.0"}}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "D", Version: ">=1.0"}}, []*pkg.DefaultPackage{})
D1.SetCategory("test")
B := pkg.NewPackage("B", "1.1", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "D", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
B.SetCategory("test")
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "D", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
A.SetCategory("test")
for _, p := range []pkg.Package{A, B, C, D, D1, D2, E} {
_, err := dbDefinitions.CreatePackage(p)
@@ -397,16 +404,23 @@ var _ = Describe("Solver", func() {
It("Support provides", func() {
E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E.SetCategory("test")
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
C.SetCategory("test")
D2 := pkg.NewPackage("D", "1.9", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
D2.SetCategory("test")
D := pkg.NewPackage("D", "1.8", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
D.SetCategory("test")
D1 := pkg.NewPackage("D", "1.4", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "1.1", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "D", Version: ">=1.0"}}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "D", Version: ">=1.0"}}, []*pkg.DefaultPackage{})
D1.SetCategory("test")
B := pkg.NewPackage("B", "1.1", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "D", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
B.SetCategory("test")
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "D", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
A.SetCategory("test")
D2.SetProvides([]*pkg.DefaultPackage{{Name: "E"}})
A2 := pkg.NewPackage("A", "1.3", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "E", Version: ""}}, []*pkg.DefaultPackage{})
D2.SetProvides([]*pkg.DefaultPackage{{Name: "E", Category: "test"}})
A2 := pkg.NewPackage("A", "1.3", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "E", Version: "", Category: "test"}}, []*pkg.DefaultPackage{})
A2.SetCategory("test")
for _, p := range []pkg.Package{A, B, C, D, D1, D2, A2, E} {
_, err := dbDefinitions.CreatePackage(p)
@@ -429,7 +443,7 @@ var _ = Describe("Solver", func() {
Expect(solution).To(ContainElement(PackageAssert{Package: D1, Value: false}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: E, Value: true}))
Expect(len(solution)).To(Equal(5))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
})
@@ -470,7 +484,7 @@ var _ = Describe("Solver", func() {
Expect(solution).To(ContainElement(PackageAssert{Package: D1, Value: false}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: E, Value: true}))
Expect(len(solution)).To(Equal(4))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
})
@@ -511,7 +525,7 @@ var _ = Describe("Solver", func() {
Expect(solution).To(ContainElement(PackageAssert{Package: D1, Value: false}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: E, Value: true}))
Expect(len(solution)).To(Equal(4))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
})
@@ -533,7 +547,7 @@ var _ = Describe("Solver", func() {
}
s = NewSolver(dbInstalled, dbDefinitions, db)
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -559,7 +573,7 @@ var _ = Describe("Solver", func() {
}
s = NewSolver(dbInstalled, dbDefinitions, db)
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"})
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"}, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -674,7 +688,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -698,7 +712,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -721,7 +735,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -746,7 +760,7 @@ var _ = Describe("Solver", func() {
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -772,7 +786,7 @@ var _ = Describe("Solver", func() {
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -893,7 +907,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
uninstall, solution, err := s.Upgrade()
uninstall, solution, err := s.Upgrade(true)
Expect(err).ToNot(HaveOccurred())
Expect(len(uninstall)).To(Equal(1))

View File

@@ -26,5 +26,5 @@ type Builder interface {
GetDatabase() pkg.PackageDatabase
WithDatabase(d pkg.PackageDatabase)
GetSourcePath() string
GetSourcePath() []string
}

View File

@@ -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
@@ -45,7 +59,7 @@ type CompilerRecipe struct {
func (r *CompilerRecipe) Load(path string) error {
r.SourcePath = path
r.SourcePath = append(r.SourcePath, path)
//tmpfile, err := ioutil.TempFile("", "luet")
//if err != nil {
// return err
@@ -56,17 +70,17 @@ func (r *CompilerRecipe) Load(path string) error {
// the function that handles each file or dir
var ff = func(currentpath string, info os.FileInfo, err error) error {
if err != nil {
return errors.Wrap(err, "Error on walk path "+currentpath)
}
if info.Name() != DefinitionFile {
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))
@@ -74,13 +88,17 @@ 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 "+currentpath)
return errors.Wrap(err,
"Error reading file "+CompilerDefinitionFile+" from "+
filepath.Dir(currentpath))
}
packbuild, err := pkg.DefaultPackageFromYaml(dat)
if err != nil {
return errors.Wrap(err, "Error reading yaml "+currentpath)
return errors.Wrap(err,
"Error reading yaml "+CompilerDefinitionFile+" from "+
filepath.Dir(currentpath))
}
pack.Requires(packbuild.GetRequires())
pack.Conflicts(packbuild.GetConflicts())
@@ -103,4 +121,4 @@ func (r *CompilerRecipe) Load(path string) error {
func (r *CompilerRecipe) GetDatabase() pkg.PackageDatabase { return r.Database }
func (r *CompilerRecipe) WithDatabase(d pkg.PackageDatabase) { r.Database = d }
func (r *CompilerRecipe) GetSourcePath() string { return r.SourcePath }
func (r *CompilerRecipe) GetSourcePath() []string { return r.SourcePath }

View File

@@ -40,7 +40,7 @@ func NewInstallerRecipe(db pkg.PackageDatabase) Builder {
// InstallerRecipe is the "general" reciper for Trees
type InstallerRecipe struct {
SourcePath string
SourcePath []string
Database pkg.PackageDatabase
}
@@ -74,7 +74,7 @@ func (r *InstallerRecipe) Load(path string) error {
// if err != nil {
// return err
// }
r.SourcePath = path
r.SourcePath = append(r.SourcePath, path)
//r.Tree().SetPackageSet(pkg.NewBoltDatabase(tmpfile.Name()))
// TODO: Handle cleaning after? Cleanup implemented in GetPackageSet().Clean()
@@ -114,4 +114,4 @@ func (r *InstallerRecipe) Load(path string) error {
func (r *InstallerRecipe) GetDatabase() pkg.PackageDatabase { return r.Database }
func (r *InstallerRecipe) WithDatabase(d pkg.PackageDatabase) { r.Database = d }
func (r *InstallerRecipe) GetSourcePath() string { return r.SourcePath }
func (r *InstallerRecipe) GetSourcePath() []string { return r.SourcePath }

View File

@@ -37,24 +37,32 @@ func NewGeneralRecipe(db pkg.PackageDatabase) Builder { return &Recipe{Database:
// Recipe is the "general" reciper for Trees
type Recipe struct {
SourcePath string
SourcePath []string
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
}
@@ -65,7 +73,7 @@ func (r *Recipe) Load(path string) error {
// if err != nil {
// return err
// }
r.SourcePath = path
r.SourcePath = append(r.SourcePath, path)
if r.Database == nil {
r.Database = pkg.NewInMemoryDatabase(false)
@@ -109,4 +117,4 @@ func (r *Recipe) Load(path string) error {
func (r *Recipe) GetDatabase() pkg.PackageDatabase { return r.Database }
func (r *Recipe) WithDatabase(d pkg.PackageDatabase) { r.Database = d }
func (r *Recipe) GetSourcePath() string { return r.SourcePath }
func (r *Recipe) GetSourcePath() []string { return r.SourcePath }

View File

@@ -96,4 +96,77 @@ var _ = Describe("Tree", func() {
})
})
Context("Multiple trees", func() {
It("Merges", func() {
for index := 0; index < 300; index++ { // Just to make sure we don't have false positives
db := pkg.NewInMemoryDatabase(false)
generalRecipe := NewCompilerRecipe(db)
tmpdir, err := ioutil.TempDir("", "package")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpdir) // clean up
err = generalRecipe.Load("../../tests/fixtures/buildableseed")
Expect(err).ToNot(HaveOccurred())
Expect(len(generalRecipe.GetDatabase().World())).To(Equal(4))
err = generalRecipe.Load("../../tests/fixtures/layers")
Expect(err).ToNot(HaveOccurred())
Expect(len(generalRecipe.GetDatabase().World())).To(Equal(6))
extra, err := generalRecipe.GetDatabase().FindPackage(&pkg.DefaultPackage{Name: "extra", Category: "layer", Version: "1.0"})
Expect(err).ToNot(HaveOccurred())
Expect(extra).ToNot(BeNil())
D, err := generalRecipe.GetDatabase().FindPackage(&pkg.DefaultPackage{Name: "d", Category: "test", Version: "1.0"})
Expect(err).ToNot(HaveOccurred())
Expect(D.GetRequires()[0].GetName()).To(Equal("c"))
CfromD, err := generalRecipe.GetDatabase().FindPackage(D.GetRequires()[0])
Expect(err).ToNot(HaveOccurred())
Expect(len(CfromD.GetRequires()) != 0).To(BeTrue())
Expect(CfromD.GetRequires()[0].GetName()).To(Equal("b"))
s := solver.NewSolver(pkg.NewInMemoryDatabase(false), generalRecipe.GetDatabase(), db)
Dd, err := generalRecipe.GetDatabase().FindPackage(&pkg.DefaultPackage{Name: "d", Category: "test", Version: "1.0"})
Expect(err).ToNot(HaveOccurred())
solution, err := s.Install([]pkg.Package{Dd})
Expect(err).ToNot(HaveOccurred())
solution = solution.Order(generalRecipe.GetDatabase(), Dd.GetFingerPrint())
pack, err := generalRecipe.GetDatabase().FindPackage(&pkg.DefaultPackage{Name: "a", Category: "test", Version: "1.0"})
Expect(err).ToNot(HaveOccurred())
base, err := generalRecipe.GetDatabase().FindPackage(&pkg.DefaultPackage{Name: "base", Category: "layer", Version: "0.2"})
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(solver.PackageAssert{Package: pack.(*pkg.DefaultPackage), Value: false}))
Expect(solution).To(ContainElement(solver.PackageAssert{Package: D.(*pkg.DefaultPackage), Value: true}))
Expect(solution).To(ContainElement(solver.PackageAssert{Package: extra.(*pkg.DefaultPackage), Value: false}))
Expect(solution).To(ContainElement(solver.PackageAssert{Package: base.(*pkg.DefaultPackage), Value: false}))
Expect(len(solution)).To(Equal(6))
}
})
})
Context("Simple tree with labels", func() {
It("Read tree with labels", func() {
db := pkg.NewInMemoryDatabase(false)
generalRecipe := NewCompilerRecipe(db)
err := generalRecipe.Load("../../tests/fixtures/labels")
Expect(err).ToNot(HaveOccurred())
Expect(len(generalRecipe.GetDatabase().World())).To(Equal(1))
pack, err := generalRecipe.GetDatabase().FindPackage(&pkg.DefaultPackage{Name: "pkgA", Category: "test", Version: "0.1"})
Expect(err).ToNot(HaveOccurred())
Expect(pack.HasLabel("label1")).To(Equal(true))
Expect(pack.HasLabel("label3")).To(Equal(false))
})
})
})

View File

@@ -0,0 +1,3 @@
image: "alpine"
steps:
- echo "test" > /file1

View File

@@ -0,0 +1,6 @@
category: "test"
name: "pkgA"
version: "0.1"
labels:
label1: "value1"
label2: "value2"

View File

@@ -1,2 +1,2 @@
image: "golang:alpine"
image: "golang"
unpack: true

View File

@@ -3,8 +3,8 @@ requires:
name: "base"
version: "0.1"
prelude:
- apk update
- apk add git
- apt-get update
- apt-get install git
- rm -rf /go/pkg/mod
steps:
- echo test > /extra-layer

View File

@@ -3,7 +3,7 @@ requires:
name: "extra"
version: "0.1"
prelude:
- apk add git
- apt-get install git
- mkdir -p /go/src/github.com/Sabayon/
- git clone https://github.com/Sabayon/pkgs-checker /go/src/github.com/Sabayon/pkgs-checker
steps:

View File

@@ -0,0 +1,10 @@
prelude:
- echo foo > /test
- echo bar > /test2
steps:
- echo c > /c
- echo c > /cd
requires:
- category: "test"
name: "a"
version: ">=1.0"

View File

@@ -0,0 +1,9 @@
category: "test"
name: "c"
version: "1.0"
# Boom?
requires:
- category: "test"
name: "a"
version: ">=0.1"

View File

@@ -0,0 +1,11 @@
image: "alpine"
prelude:
- echo foo > /test
- echo bar > /test2
steps:
- echo artifact3 > /test3
- echo artifact4 > /test4
requires:
- category: "test"
name: "b"
version: "1.0"

View File

@@ -0,0 +1,8 @@
category: "test"
name: "a"
version: "1.1"
requires:
- category: "test"
name: "b"
version: ">=0.1"

View File

@@ -0,0 +1,11 @@
image: "alpine"
prelude:
- echo foo > /test
- echo bar > /test2
steps:
- echo artifact3 > /testaa
- echo artifact4 > /testaa2
requires:
- category: "test"
name: "b"
version: "1.0"

View File

@@ -0,0 +1,3 @@
category: "test"
name: "a"
version: "1.0"

View File

@@ -0,0 +1,11 @@
image: "alpine"
prelude:
- echo foo > /test
- echo bar > /test2
steps:
- echo artifact3 > /testlatest
- echo artifact4 > /testlatest2
requires:
- category: "test"
name: "b"
version: "1.0"

View File

@@ -0,0 +1,3 @@
category: "test"
name: "a"
version: "1.2"

View File

@@ -0,0 +1,9 @@
image: "alpine"
prelude:
- echo foo > /test
- echo bar > /test2
- chmod +x generate.sh
steps:
- echo artifact5 > /newc
- echo artifact6 > /newnewc
- ./generate.sh

View File

@@ -0,0 +1,3 @@
category: "test"
name: "b"
version: "1.1"

View File

@@ -0,0 +1 @@
echo generated > /sonewc

View File

@@ -0,0 +1,9 @@
image: "alpine"
prelude:
- echo foo > /test
- echo bar > /test2
- chmod +x generate.sh
steps:
- echo artifact5 > /test5
- echo artifact6 > /test6
- ./generate.sh

View File

@@ -0,0 +1,3 @@
category: "test"
name: "b"
version: "1.0"

View File

@@ -0,0 +1 @@
echo generated > /artifact42

View File

@@ -12,7 +12,7 @@ oneTimeTearDown() {
testBuild() {
mkdir $tmpdir/testbuild
luet build --all --tree "$ROOT_DIR/tests/fixtures/qlearning" --destination $tmpdir/testbuild --compression gzip
luet build --all --concurrency 1 --tree "$ROOT_DIR/tests/fixtures/qlearning" --destination $tmpdir/testbuild --compression gzip
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package dep B' "[ -e '$tmpdir/testbuild/b-test-1.0.package.tar.gz' ]"

122
tests/integration/05_upgrade.sh Executable file
View File

@@ -0,0 +1,122 @@
#!/bin/bash
export LUET_NOLOCK=true
oneTimeSetUp() {
export tmpdir="$(mktemp -d)"
}
oneTimeTearDown() {
rm -rf "$tmpdir"
}
testBuild() {
mkdir $tmpdir/testbuild
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/b-1.0
buildst=$?
assertTrue 'create package B 1.0' "[ -e '$tmpdir/testbuild/b-test-1.0.package.tar.gz' ]"
assertEquals 'builds successfully' "$buildst" "0"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/b-1.1
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package B 1.1' "[ -e '$tmpdir/testbuild/b-test-1.1.package.tar.gz' ]"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/a-1.0
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package A 1.0' "[ -e '$tmpdir/testbuild/a-test-1.0.package.tar.gz' ]"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/a-1.1
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package A 1.1' "[ -e '$tmpdir/testbuild/a-test-1.1.package.tar.gz' ]"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/a-1.2
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package A 1.2' "[ -e '$tmpdir/testbuild/a-test-1.2.package.tar.gz' ]"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/c-1.0
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package C 1.0' "[ -e '$tmpdir/testbuild/c-test-1.0.package.tar.gz' ]"
}
testRepo() {
assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]"
luet create-repo --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" \
--output $tmpdir/testbuild \
--packages $tmpdir/testbuild \
--name "test" \
--descr "Test Repo" \
--urls $tmpdir/testrootfs \
--type disk
createst=$?
assertEquals 'create repo successfully' "$createst" "0"
assertTrue 'create repository' "[ -e '$tmpdir/testbuild/repository.yaml' ]"
}
testConfig() {
mkdir $tmpdir/testrootfs
cat <<EOF > $tmpdir/luet.yaml
general:
debug: true
system:
rootfs: $tmpdir/testrootfs
database_path: "/"
database_engine: "boltdb"
repositories:
- name: "main"
type: "disk"
enable: true
urls:
- "$tmpdir/testbuild"
EOF
luet config --config $tmpdir/luet.yaml
res=$?
assertEquals 'config test successfully' "$res" "0"
}
testInstall() {
luet install --config $tmpdir/luet.yaml test/b-1.0
installst=$?
assertEquals 'install test successfully' "$installst" "0"
assertTrue 'package installed B' "[ -e '$tmpdir/testrootfs/test5' ]"
luet install --config $tmpdir/luet.yaml test/a-1.0
assertTrue 'package installed A' "[ -e '$tmpdir/testrootfs/testaa' ]"
installst=$?
assertEquals 'install test successfully' "$installst" "0"
luet install --config $tmpdir/luet.yaml test/a-1.1
assertTrue 'package installed A' "[ -e '$tmpdir/testrootfs/testaa' ]"
installst=$?
assertEquals 'install test successfully' "$installst" "0"
assertTrue 'package keeps old A' "[ -e '$tmpdir/testrootfs/testaa' ]"
assertTrue 'package new A was not installed' "[ ! -e '$tmpdir/testrootfs/testlatest' ]"
luet install --config $tmpdir/luet.yaml test/c-1.0
installst=$?
assertEquals 'install test successfully' "$installst" "0"
assertTrue 'package installed C' "[ -e '$tmpdir/testrootfs/c' ]"
}
testUpgrade() {
luet --config $tmpdir/luet.yaml upgrade
installst=$?
assertEquals 'install test successfully' "$installst" "0"
assertTrue 'package uninstalled B' "[ ! -e '$tmpdir/testrootfs/test5' ]"
assertTrue 'package installed B' "[ -e '$tmpdir/testrootfs/newc' ]"
assertTrue 'package uninstalled A' "[ ! -e '$tmpdir/testrootfs/testaa' ]"
assertTrue 'package installed new A' "[ -e '$tmpdir/testrootfs/testlatest' ]"
}
# Load shUnit2.
. "$ROOT_DIR/tests/integration/shunit2"/shunit2

View File

@@ -54,15 +54,22 @@ const (
PkgCondMatchVersion = 8
)
const (
RegexCatString = `(^[a-z]+[0-9]*[a-z]*[-]*[a-z]+[0-9]*[a-z]*|^virtual)`
RegexPkgNameString = `([a-zA-Z]*[0-9a-zA-Z\.\-_]*[a-zA-Z0-9]+|[a-zA-Z]+[+]+)`
)
type GentooPackage struct {
Name string `json:"name",omitempty"`
Category string `json:"category",omitempty"`
Version string `json:"version",omitempty"`
VersionSuffix string `json:"version_suffix",omitempty"`
VersionBuild string `json:"version_build",omitempty"`
Slot string `json:"slot",omitempty"`
Condition PackageCond
Repository string `json:"repository",omitempty"`
UseFlags []string `json:"use_flags",omitempty"`
License string `json:"license",omitempty"`
}
func (p *GentooPackage) String() string {
@@ -191,14 +198,23 @@ func (p *GentooPackage) Admit(i *GentooPackage) (bool, error) {
fmt.Sprintf("Wrong name for package %s", i.Name))
}
if p.Version != "" {
v1, err = version.NewVersion(p.Version)
v1s := p.Version
v2s := i.Version
if v1s != "" {
if p.VersionBuild != "" {
v1s = p.Version + "+" + p.VersionBuild
}
v1, err = version.NewVersion(v1s)
if err != nil {
return false, err
}
}
if i.Version != "" {
v2, err = version.NewVersion(i.Version)
if v2s != "" {
if i.VersionBuild != "" {
v2s = i.Version + "+" + i.VersionBuild
}
v2, err = version.NewVersion(v2s)
if err != nil {
return false, err
}
@@ -210,7 +226,8 @@ func (p *GentooPackage) Admit(i *GentooPackage) (bool, error) {
} else {
if p.Condition == PkgCondInvalid || p.Condition == PkgCondEqual {
// case 1: source-pkg-1.0 and dest-pkg-1.0 or dest-pkg without version
if i.Version != "" && i.Version == p.Version && p.VersionSuffix == i.VersionSuffix {
if i.Version != "" && i.Version == p.Version && p.VersionSuffix == i.VersionSuffix &&
p.VersionBuild == i.VersionBuild {
ans = true
}
} else if p.Condition == PkgCondAnyRevision {
@@ -270,8 +287,9 @@ func ParsePackageStr(pkg string) (*GentooPackage, error) {
}
ans := GentooPackage{
Slot: "0",
Condition: PkgCondInvalid,
Slot: "0",
Condition: PkgCondInvalid,
VersionBuild: "",
}
// Check if pkg string contains inline use flags
@@ -315,6 +333,67 @@ func ParsePackageStr(pkg string) (*GentooPackage, error) {
ans.Condition = PkgCondNot
}
regexVerString := fmt.Sprintf("[-](%s|%s|%s|%s|%s|%s)((%s|%s|%s|%s|%s|%s|%s)+)*",
// Version regex
// 1.1
"[0-9]+[.][0-9]+[a-z]*",
// 1
"[0-9]+[a-z]*",
// 1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// suffix
"-r[0-9]+",
"_p[0-9]+",
"_pre[0-9]*",
"_rc[0-9]+",
// handle also rc without number
"_rc",
"_alpha",
"_beta",
)
hasBuild, _ := regexp.MatchString(
fmt.Sprintf("(%s[/]%s%s([[:]{1,2}[0-9a-zA-Z]*]*)*[+])",
RegexCatString, RegexPkgNameString, regexVerString),
pkg,
)
if hasBuild {
// Check if build number is present
buildIdx := strings.LastIndex(pkg, "+")
if buildIdx > 0 {
// <pre-release> ::= <dot-separated pre-release identifiers>
//
// <dot-separated pre-release identifiers> ::=
// <pre-release identifier> | <pre-release identifier> "."
// <dot-separated pre-release identifiers>
//
// <build> ::= <dot-separated build identifiers>
//
// <dot-separated build identifiers> ::= <build identifier>
// | <build identifier> "." <dot-separated build identifiers>
//
// <pre-release identifier> ::= <alphanumeric identifier>
// | <numeric identifier>
//
// <build identifier> ::= <alphanumeric identifier>
// | <digits>
//
// <alphanumeric identifier> ::= <non-digit>
// | <non-digit> <identifier characters>
// | <identifier characters> <non-digit>
// | <identifier characters> <non-digit> <identifier characters>
ans.VersionBuild = pkg[buildIdx+1:]
pkg = pkg[0:buildIdx]
}
}
words := strings.Split(pkg, "/")
if len(words) != 2 {
return nil, errors.New(fmt.Sprintf("Invalid package string %s", pkg))
@@ -337,31 +416,9 @@ func ParsePackageStr(pkg string) (*GentooPackage, error) {
}
regexPkg := regexp.MustCompile(
fmt.Sprintf("[-](%s|%s|%s|%s|%s|%s)((%s|%s|%s|%s|%s|%s|%s)+)*$",
// Version regex
// 1.1
"[0-9]+[.][0-9]+[a-z]*",
// 1
"[0-9]+[a-z]*",
// 1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// suffix
"-r[0-9]+",
"_p[0-9]+",
"_pre[0-9]*",
"_rc[0-9]+",
// handle also rc without number
"_rc",
"_alpha",
"_beta",
),
fmt.Sprintf("%s$", regexVerString),
)
matches := regexPkg.FindAllString(pkgname, -1)
// NOTE: Now suffix comples like _alpha_rc1 are not supported.

2
vendor/modules.txt vendored
View File

@@ -23,7 +23,7 @@ github.com/Microsoft/hcsshim/internal/longpath
github.com/Microsoft/hcsshim/internal/safefile
# github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5
github.com/Nvveen/Gotty
# github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7
# github.com/Sabayon/pkgs-checker v0.6.2-0.20200315232328-b6efed54b4b1
github.com/Sabayon/pkgs-checker/pkg/gentoo
# github.com/apex/log v1.1.1
github.com/apex/log