Compare commits

...

25 Commits

Author SHA1 Message Date
Ettore Di Giacinto
9ca5d24856 Tag 0.9.12 2020-12-07 20:18:49 +01:00
Ettore Di Giacinto
9a34296be0 Build step is always required for tagging images 2020-12-07 19:39:56 +01:00
Ettore Di Giacinto
ebd18ae22c Set builderTagged image afterwards 2020-12-07 18:58:14 +01:00
Ettore Di Giacinto
7f10a19be5 Don't hide build output 2020-12-07 18:56:39 +01:00
Ettore Di Giacinto
6bf7368993 Don't replace buildertaggedImage if there aren't build steps 2020-12-07 18:39:15 +01:00
Ettore Di Giacinto
338f310d67 Tag and push an image when virtual is supplied, to have a track of it in the image graph tree 2020-12-07 17:59:30 +01:00
Ettore Di Giacinto
3fd1bdbfc8 ADD automatically extracts as well 2020-12-07 17:21:06 +01:00
Ettore Di Giacinto
59d78c3f5c While upgrading always use nodeps while computing uninstall 2020-12-07 17:20:55 +01:00
Ettore Di Giacinto
86c256a062 Generate empty tar 2020-12-07 17:20:32 +01:00
Ettore Di Giacinto
876e3659fb Turn full off by default on upgrade 2020-12-07 00:48:28 +01:00
Ettore Di Giacinto
3c0dd2b71d Adapt test 2020-12-07 00:07:57 +01:00
Ettore Di Giacinto
e9b4d66a3e Retrieve should be rendered also for step images 2020-12-07 00:00:32 +01:00
Ettore Di Giacinto
5047316b70 Try to build only when strictly necessary 2020-12-06 23:50:51 +01:00
Ettore Di Giacinto
02edc10c58 Tag 0.9.11 2020-12-06 22:52:15 +01:00
Ettore Di Giacinto
d479ada402 Don't consider deps while uninstalling during package Swap
Beside being forced, it also doesn't need to look deep into the deps, as
we already have precalculated those
2020-12-06 22:48:48 +01:00
Ettore Di Giacinto
7b800c9a20 Pre-compute swap step
Otherwise, while upgrading, it could happen that package dependencies
aren't downloaded before, and they would just be installed in the middle
of installation, after removal already happened.
2020-12-06 22:11:17 +01:00
Ettore Di Giacinto
18e6e085d5 Sort correctly also subfolders 2020-12-05 23:17:05 +01:00
Ettore Di Giacinto
6d19f8d2cc Tag 0.9.10 2020-12-03 21:02:57 +01:00
Ettore Di Giacinto
67c43eb936 Don't bail out if package is installed and we have a list 2020-12-03 20:03:37 +01:00
Ettore Di Giacinto
cf80e5fc09 Resolvers might omit packages 2020-12-03 18:53:57 +01:00
Ettore Di Giacinto
d668d8344b Accept selectors on uninstall and fixup failure logic 2020-12-03 18:32:24 +01:00
Ettore Di Giacinto
b17ac447f1 Display matched packages only, and check if they are available 2020-12-03 17:25:29 +01:00
Ettore Di Giacinto
c8bcd88f1f Add command usage in CLI
Add Long description for missing commands along with practical examples
2020-12-02 23:15:23 +01:00
Ettore Di Giacinto
034fb54c25 Update README 2020-12-02 21:18:21 +01:00
Ettore Di Giacinto
6dbf19f085 Use single image to build packages 2020-12-02 21:18:12 +01:00
19 changed files with 302 additions and 91 deletions

View File

@@ -1,13 +1,14 @@
# luet - Container-based Package manager
[![Docker Repository on Quay](https://quay.io/repository/luet/base/status "Docker Repository on Quay")](https://quay.io/repository/luet/base)
[![Go Report Card](https://goreportcard.com/badge/github.com/mudler/luet)](https://goreportcard.com/report/github.com/mudler/luet)
[![Build Status](https://travis-ci.org/mudler/luet.svg?branch=master)](https://travis-ci.org/mudler/luet)
[![GoDoc](https://godoc.org/github.com/mudler/luet?status.svg)](https://godoc.org/github.com/mudler/luet)
[![codecov](https://codecov.io/gh/mudler/luet/branch/master/graph/badge.svg)](https://codecov.io/gh/mudler/luet)
Luet is a multi-platform Package Manager based off from containers - it uses Docker (and other tech) to sandbox your builds and generate packages from them. It has zero dependencies and it is well suitable for "from scratch" environments. It can also version entire rootfs and enables delivery of OTA-alike updates, making it a perfect fit for the Edge computing era and IoT embedded devices.
Luet is a multi-platform Package Manager based off from containers - it uses Docker (and others) to build packages. It has zero dependencies and it is well suitable for "from scratch" environments. It can also version entire rootfs and enables delivery of OTA-alike updates, making it a perfect fit for the Edge computing era and IoT embedded devices.
It offers a simple [specfile format](https://luet-lab.github.io/docs/docs/concepts/specfile/) in YAML notation to define both packages and rootfs. As it is based on containers, it can be used to build seed stages for Linux From Scratch installations and it can build and track updates for those systems.
It offers a simple [specfile format](https://luet-lab.github.io/docs/docs/concepts/specfile/) in YAML notation to define both packages and rootfs. As it is based on containers, it can be also used to build stages for Linux From Scratch installations and it can build and track updates for those systems.
It is written entirely in Golang and where used as package manager, it can run in from scratch environment, with zero dependencies.
@@ -16,20 +17,35 @@ It is written entirely in Golang and where used as package manager, it can run i
- Luet can reuse Gentoo's portage tree hierarchy, and it is heavily inspired from it.
- It builds, installs, uninstalls and perform upgrades on machines
- Installer doesn't depend on anything ( 0 dep installer !), statically built
- You can install it aside also with your current distro package manager, and start building and distributing your packages
- Support for packages as "layers"
- It uses SAT solving techniques to solve the deptree ( Inspired by [OPIUM](https://ranjitjhala.github.io/static/opium.pdf) )
- [It uses SAT solving techniques to solve the deptree](https://luet-lab.github.io/docs/docs/concepts/constraints/) ( Inspired by [OPIUM](https://ranjitjhala.github.io/static/opium.pdf) )
- Support for collections and templated package definitions
- [Can be extended with Plugins and Extensions](https://luet-lab.github.io/docs/docs/plugins-and-extensions/)
## Install
To install luet, you can grab a release on the [Release page](https://github.com/mudler/luet/releases) or compile it in your machine (requires Golang installed):
To install luet, you can grab a release on the [Release page](https://github.com/mudler/luet/releases) or to install it in your system:
$ git clone https://github.com/mudler/luet.git
$ cd luet
$ make build
```bash
$ curl https://get.mocaccino.org/luet/get_luet_root.sh | sudo sh
$ luet search ...
$ luet install ..
$ luet --help
```
## Status
## Build from source
Luet is not feature-complete yet, it can build, install/uninstall/upgrade packages - but it doesn't support yet all the features you would normally expect from a Package Manager nowadays.
```bash
$ git clone https://github.com/mudler/luet.git
$ cd luet
$ make build
```
## Documentation
[Documentation](https://luet-lab.github.io/docs) is available, or
run `luet --help`, any subcommand is documented as well, try e.g.: `luet build --help`.
# Dependency solving
@@ -48,10 +64,6 @@ when they arises while trying to validate your queries against the system model.
To leverage it, simply pass ```--solver-type qlearning``` to the subcommands that supports it ( you can check out by invoking ```--help``` ).
## Documentation
[Documentation](https://luet-lab.github.io/docs) is available, or
run `luet --help`, any subcommand is documented as well, try e.g.: `luet build --help`.
## Authors

View File

@@ -30,7 +30,26 @@ import (
var createrepoCmd = &cobra.Command{
Use: "create-repo",
Short: "Create a luet repository from a build",
Long: `Generate and renew repository metadata`,
Long: `Builds tree metadata from a set of packages and a tree definition:
$ luet create-repo
Provide specific paths for packages, tree, and metadata output which is generated:
$ luet create-repo --packages my/packages/path --tree my/tree/path --output my/packages/path ...
Provide name and description of the repository:
$ luet create-repo --name "foo" --description "bar" ...
Change compression method:
$ luet create-repo --tree-compression gzip --meta-compression gzip
Create a repository from the metadata description defined in the luet.yaml config file:
$ luet create-repo --repo repository1
`,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("packages", cmd.Flags().Lookup("packages"))
viper.BindPFlag("tree", cmd.Flags().Lookup("tree"))

View File

@@ -25,6 +25,10 @@ import (
var databaseGroupCmd = &cobra.Command{
Use: "database [command] [OPTIONS]",
Short: "Manage system database (dangerous commands ahead!)",
Long: `Allows to manipulate Luet internal database of installed packages. Use with caution!
Removing packages by hand from the database can result in a broken system, and thus it's not reccomended.
`,
}
func init() {

View File

@@ -32,7 +32,18 @@ func NewDatabaseCreateCommand() *cobra.Command {
var ans = &cobra.Command{
Use: "create <artifact_metadata1.yaml> <artifact_metadata1.yaml>",
Short: "Insert a package in the system DB",
Args: cobra.OnlyValidArgs,
Long: `Inserts a package in the system database:
$ luet database create foo.yaml
"luet database create" injects a package in the system database without actually installing it, use it with caution.
This commands takes multiple yaml input file representing package artifacts, that are usually generated while building packages.
The yaml must contain the package definition, and the file list at least.
For reference, inspect a "metadata.yaml" file generated while running "luet build"`,
Args: cobra.OnlyValidArgs,
PreRun: func(cmd *cobra.Command, args []string) {
LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath"))
LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target"))

View File

@@ -31,7 +31,13 @@ func NewDatabaseRemoveCommand() *cobra.Command {
var ans = &cobra.Command{
Use: "remove [package1] [package2] ...",
Short: "Remove a package from the system DB (forcefully - you normally don't want to do that)",
Args: cobra.OnlyValidArgs,
Long: `Removes a package in the system database without actually uninstalling it:
$ luet database remove foo/bar
This commands takes multiple packages as arguments and prunes their entries from the system database.
`,
Args: cobra.OnlyValidArgs,
PreRun: func(cmd *cobra.Command, args []string) {
LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath"))
LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target"))

View File

@@ -31,7 +31,16 @@ import (
var packCmd = &cobra.Command{
Use: "pack <package name>",
Short: "pack a custom package",
Long: `pack and creates metadata directly from a source path`,
Long: `Pack creates a package from a directory, generating the metadata required from a tree to generate a repository.
Pack can be used to manually replace what "luet build" does automatically by reading the packages build.yaml files.
$ mkdir -p output/etc/foo
$ echo "my config" > output/etc/foo
$ luet pack foo/bar@1.1 --source output
Afterwards, you can use the content generated and associate it with a tree and a corresponding definition.yaml file with "luet create-repo".
`,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("destination", cmd.Flags().Lookup("destination"))
viper.BindPFlag("compression", cmd.Flags().Lookup("compression"))

View File

@@ -35,8 +35,12 @@ var reclaimCmd = &cobra.Command{
LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target"))
LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force"))
},
Long: `Add packages to the systemdb if files belonging to packages
in available repositories exists in the target root.`,
Long: `Reclaim tries to find association between packages in the online repositories and the system one.
$ luet reclaim
It scans the target file system, and if finds a match with a package available in the repositories, it marks as installed in the system database.
`,
Run: func(cmd *cobra.Command, args []string) {
var systemDB pkg.PackageDatabase

View File

@@ -40,7 +40,7 @@ var Verbose bool
var LockedCommands = []string{"install", "uninstall", "upgrade"}
const (
LuetCLIVersion = "0.9.9"
LuetCLIVersion = "0.9.12"
LuetEnvPrefix = "LUET"
)

View File

@@ -66,9 +66,41 @@ func packageToList(l list.Writer, repo string, p pkg.Package) {
}
var searchCmd = &cobra.Command{
Use: "search <term>",
Short: "Search packages",
Long: `Search for installed and available packages`,
Use: "search <term>",
Short: "Search packages",
Long: `Search for installed and available packages
To search a package in the repositories:
$ luet search <regex>
To search a package and display results in a table (wide screens):
$ luet search --table <regex>
To look into the installed packages:
$ luet search --installed <regex>
Note: the regex argument is optional, if omitted implies "all"
To search a package by label:
$ luet search --by-label <label>
or by regex against the label:
$ luet search --by-label-regex <label>
It can also show a package revdeps by:
$ luet search --revdeps <regex>
Search can also return results in the terminal in different ways: as terminal output, as json or as yaml.
$ luet search --json <regex> # JSON output
$ luet search --yaml <regex> # YAML output
`,
Aliases: []string{"s"},
PreRun: func(cmd *cobra.Command, args []string) {
LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath"))

View File

@@ -123,7 +123,7 @@ func init() {
upgradeCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
upgradeCmd.Flags().Bool("force", false, "Force upgrade by ignoring errors")
upgradeCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful! overrides checkconflicts and full!)")
upgradeCmd.Flags().Bool("full", true, "Attempts to remove as much packages as possible which aren't required (slow)")
upgradeCmd.Flags().Bool("full", false, "Attempts to remove as much packages as possible which aren't required (slow)")
upgradeCmd.Flags().Bool("universe", false, "Use ONLY the SAT solver to compute upgrades (experimental)")
upgradeCmd.Flags().Bool("clean", false, "Try to drop removed packages (experimental, only when --universe is enabled)")
upgradeCmd.Flags().Bool("sync", false, "Upgrade packages with new revisions (experimental)")

View File

@@ -16,6 +16,7 @@
package compiler
import (
"archive/tar"
"fmt"
"io/ioutil"
"os"
@@ -304,13 +305,13 @@ func (cs *LuetCompiler) buildPackageImage(image, buildertaggedImage, packageImag
fp := p.GetPackage().HashFingerprint(packageImage)
if buildertaggedImage == "" {
buildertaggedImage = cs.ImageRepository + "-" + fp + "-builder"
buildertaggedImage = cs.ImageRepository + ":builder-" + fp
Debug(pkgTag, "Creating intermediary image", buildertaggedImage, "from", image)
}
// TODO: Cleanup, not actually hit
if packageImage == "" {
packageImage = cs.ImageRepository + "-" + fp
packageImage = cs.ImageRepository + ":builder-invalid" + fp
}
p.SetSeedImage(image) // In this case, we ignore the build deps as we suppose that the image has them - otherwise we recompose the tree with a solver,
@@ -345,6 +346,10 @@ func (cs *LuetCompiler) buildPackageImage(image, buildertaggedImage, packageImag
return builderOpts, runnerOpts, errors.Wrap(err, "Could not generate image definition")
}
if len(p.GetPreBuildSteps()) == 0 {
buildertaggedImage = image
}
// Then we write the step image, which uses the builder one
if err := p.WriteStepImageDefinition(buildertaggedImage, filepath.Join(buildDir, p.GetPackage().GetFingerPrint()+".dockerfile")); err != nil {
return builderOpts, runnerOpts, errors.Wrap(err, "Could not generate image definition")
@@ -392,11 +397,15 @@ func (cs *LuetCompiler) buildPackageImage(image, buildertaggedImage, packageImag
}
return nil
}
Info(pkgTag, ":whale: Generating 'builder' image from", image, "as", buildertaggedImage, "with prelude steps")
if err := buildAndPush(builderOpts); err != nil {
return builderOpts, runnerOpts, errors.Wrap(err, "Could not push image: "+image+" "+builderOpts.DockerFileName)
if len(p.GetPreBuildSteps()) != 0 {
Info(pkgTag, ":whale: Generating 'builder' image from", image, "as", buildertaggedImage, "with prelude steps")
if err := buildAndPush(builderOpts); err != nil {
return builderOpts, runnerOpts, errors.Wrap(err, "Could not push image: "+image+" "+builderOpts.DockerFileName)
}
}
// Even if we might not have any steps to build, we do that so we can tag the image used in this moment and use that to cache it in a registry, or in the system.
// acting as a docker tag.
Info(pkgTag, ":whale: Generating 'package' image from", buildertaggedImage, "as", packageImage, "with build steps")
if err := buildAndPush(runnerOpts); err != nil {
return builderOpts, runnerOpts, errors.Wrap(err, "Could not push image: "+image+" "+builderOpts.DockerFileName)
@@ -420,6 +429,23 @@ func (cs *LuetCompiler) genArtifact(p CompilationSpec, builderOpts, runnerOpts C
unpack = true
}
if len(p.BuildSteps()) == 0 && len(p.GetPreBuildSteps()) == 0 && !unpack {
fakePackage := p.Rel(p.GetPackage().GetFingerPrint() + ".package.tar")
// We can't generate delta in this case. It implies the package is a virtual, and nothing has to be done really
file, err := os.Create(fakePackage)
if err != nil {
return nil, errors.Wrap(err, "Failed creating virtual package")
}
defer file.Close()
tw := tar.NewWriter(file)
defer tw.Close()
artifact := NewPackageArtifact(fakePackage)
artifact.SetCompressionType(cs.CompressionType)
return artifact, nil
}
// prepare folder content of the image with the package compiled inside
if err := cs.Backend.ExportImage(runnerOpts); err != nil {
return nil, errors.Wrap(err, "Failed exporting image")

View File

@@ -247,6 +247,19 @@ ENV PACKAGE_NAME=` + cs.Package.GetName() + `
ENV PACKAGE_VERSION=` + cs.Package.GetVersion() + `
ENV PACKAGE_CATEGORY=` + cs.Package.GetCategory()
if len(cs.Retrieve) > 0 {
for _, s := range cs.Retrieve {
//var file string
// if helpers.IsValidUrl(s) {
// file = s
// } else {
// file = cs.Rel(s)
// }
spec = spec + `
ADD ` + s + ` /luetbuild/`
}
}
for _, s := range cs.Env {
spec = spec + `
ENV ` + s

View File

@@ -173,6 +173,8 @@ WORKDIR /luetbuild
ENV PACKAGE_NAME=a
ENV PACKAGE_VERSION=1.0
ENV PACKAGE_CATEGORY=test
ADD test /luetbuild/
ADD http://www.google.com /luetbuild/
ENV test=1
RUN echo foo > /test
RUN echo bar > /test2`))

View File

@@ -71,6 +71,15 @@ type LuetSolverOptions struct {
Implementation solver.SolverType `mapstructure:"implementation"`
}
func (opts LuetSolverOptions) ResolverIsSet() bool {
switch opts.Type {
case solver.QLearningResolverType:
return true
default:
return false
}
}
func (opts LuetSolverOptions) Resolver() solver.PackageResolver {
switch opts.Type {
case solver.QLearningResolverType:

View File

@@ -19,6 +19,8 @@ import (
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"time"
copy "github.com/otiai10/copy"
@@ -41,6 +43,8 @@ func OrderFiles(target string, files []string) ([]string, []string) {
}
}
dirs := []string{}
for _, f := range files {
target := filepath.Join(target, f)
fi, err := os.Lstat(target)
@@ -48,11 +52,16 @@ func OrderFiles(target string, files []string) ([]string, []string) {
continue
}
if m := fi.Mode(); m.IsDir() {
newFiles = append(newFiles, f)
dirs = append(dirs, f)
}
}
return newFiles, notPresent
// Compare how many sub paths there are, and push at the end the ones that have less subpaths
sort.Slice(dirs, func(i, j int) bool {
return len(strings.Split(dirs[i], string(os.PathSeparator))) > len(strings.Split(dirs[j], string(os.PathSeparator)))
})
return append(newFiles, dirs...), notPresent
}
func ListDir(dir string) ([]string, error) {

View File

@@ -60,5 +60,27 @@ var _ = Describe("Helpers", func() {
Expect(ordered).To(Equal([]string{"baz", "bar/foo", "foo", "baz2/foo", "bar", "baz2"}))
Expect(notExisting).To(Equal([]string{"notexisting"}))
})
It("orders correctly when there are folders with folders", func() {
testDir, err := ioutil.TempDir(os.TempDir(), "test")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(testDir)
err = os.MkdirAll(filepath.Join(testDir, "bar"), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
err = os.MkdirAll(filepath.Join(testDir, "foo"), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
err = os.MkdirAll(filepath.Join(testDir, "foo", "bar"), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
err = os.MkdirAll(filepath.Join(testDir, "foo", "baz"), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
err = os.MkdirAll(filepath.Join(testDir, "foo", "baz", "fa"), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
ordered, _ := OrderFiles(testDir, []string{"foo", "foo/bar", "bar", "foo/baz/fa", "foo/baz"})
Expect(ordered).To(Equal([]string{"foo/baz/fa", "foo/bar", "foo/baz", "foo", "bar"}))
})
})
})

View File

@@ -16,6 +16,7 @@
package installer
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
@@ -83,7 +84,7 @@ func (l *LuetInstaller) computeUpgrade(syncedRepos Repositories, s *System) (pkg
return uninstall, toInstall, errors.Wrap(err, "Failed solving solution for upgrade")
}
} else {
uninstall, solution, err = solv.Upgrade(!l.Options.FullUninstall, l.Options.NoDeps)
uninstall, solution, err = solv.Upgrade(l.Options.FullUninstall, true)
if err != nil {
return uninstall, toInstall, errors.Wrap(err, "Failed solving solution for upgrade")
}
@@ -128,6 +129,15 @@ func packsToList(p pkg.Packages) string {
return strings.Join(packs, " ")
}
func matchesToList(artefacts map[string]ArtifactMatch) string {
var packs []string
for fingerprint, match := range artefacts {
packs = append(packs, fmt.Sprintf("%s (%s)", fingerprint, match.Repository.GetName()))
}
return strings.Join(packs, " ")
}
// Upgrade upgrades a System based on the Installer options. Returns error in case of failure
func (l *LuetInstaller) Upgrade(s *System) error {
@@ -231,10 +241,6 @@ func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, to
syncedRepos.SyncDatabase(allRepos)
toInstall = syncedRepos.ResolveSelectors(toInstall)
if err := l.download(syncedRepos, toInstall); 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
@@ -243,8 +249,43 @@ func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, to
// now the solver enforces the constraints and explictly denies two packages
// of the same version installed.
forced := l.Options.Force
nodeps := l.Options.NoDeps
l.Options.Force = true
l.Options.NoDeps = true
// First check what would have been done
installedtmp := pkg.NewInMemoryDatabase(false)
for _, i := range s.Database.World() {
_, err := installedtmp.CreatePackage(i)
if err != nil {
return errors.Wrap(err, "Failed create temporary in-memory db")
}
}
systemAfterChanges := &System{Database: installedtmp}
for _, u := range toRemove {
packs, err := l.computeUninstall(u, systemAfterChanges)
if err != nil && !l.Options.Force {
Error("Failed computing uninstall for ", u.HumanReadableString())
return errors.Wrap(err, "computing uninstall "+u.HumanReadableString())
}
for _, p := range packs {
err = systemAfterChanges.Database.RemovePackage(p)
if err != nil {
return errors.Wrap(err, "Failed removing package from database")
}
}
}
match, packages, assertions, allRepos, err := l.computeInstall(syncedRepos, toInstall, systemAfterChanges)
if err != nil {
return errors.Wrap(err, "computing installation")
}
if err := l.download(syncedRepos, match); err != nil {
return errors.Wrap(err, "Pre-downloading packages")
}
for _, u := range toRemove {
err := l.Uninstall(u, s)
@@ -253,13 +294,9 @@ func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, to
return errors.Wrap(err, "uninstalling "+u.HumanReadableString())
}
}
l.Options.Force = forced
match, packages, assertions, allRepos, err := l.computeInstall(syncedRepos, toInstall, s)
if err != nil {
return errors.Wrap(err, "computing installation")
}
l.Options.NoDeps = nodeps
return l.install(syncedRepos, match, packages, assertions, allRepos, s)
}
@@ -274,12 +311,34 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
return err
}
if len(packages) > 0 {
Info("Packages that are going to be installed in the system: \n ", Green(packsToList(packages)).BgBlack().String())
} else {
// Check if we have to process something, or return to the user an error
if len(match) == 0 {
Info("No packages to install")
return nil
}
// Resolvers might decide to remove some packages from being installed
if !l.Options.SolverOptions.ResolverIsSet() {
for _, p := range cp {
found := false
vers, _ := s.Database.FindPackageVersions(p) // If was installed, it is found, as it was filtered
if len(vers) >= 1 {
found = true
continue
}
for _, m := range match {
if m.Package.GetName() == p.GetName() {
found = true
}
}
if !found {
return fmt.Errorf("Package '%s' not found", p.HumanReadableString())
}
}
}
Info("Packages that are going to be installed in the system: \n ", Green(matchesToList(match)).BgBlack().String())
if l.Options.Ask {
Info("By going forward, you are also accepting the licenses of the packages that you are going to install in your system.")
@@ -293,32 +352,8 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
return l.install(syncedRepos, match, packages, assertions, allRepos, s)
}
func (l *LuetInstaller) download(syncedRepos Repositories, cp pkg.Packages) error {
toDownload := map[string]ArtifactMatch{}
func (l *LuetInstaller) download(syncedRepos Repositories, toDownload map[string]ArtifactMatch) error {
// FIXME: This can be optimized. We don't need to re-match this to the repository
// But we could just do it once
// Gathers things to download
for _, currentPack := range cp {
matches := syncedRepos.PackageMatches(pkg.Packages{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()) {
toDownload[currentPack.GetFingerPrint()] = ArtifactMatch{Package: currentPack, Artifact: artefact, Repository: matches[0].Repo}
break A
}
}
}
// Download packages into cache in parallel.
all := make(chan ArtifactMatch)
@@ -476,25 +511,13 @@ func (l *LuetInstaller) computeInstall(syncedRepos Repositories, cp pkg.Packages
func (l *LuetInstaller) install(syncedRepos Repositories, toInstall map[string]ArtifactMatch, p pkg.Packages, solution solver.PackagesAssertions, allRepos pkg.PackageDatabase, s *System) error {
// Install packages into rootfs in parallel.
if err := l.download(syncedRepos, toInstall); err != nil {
return errors.Wrap(err, "Downloading packages")
}
all := make(chan ArtifactMatch)
var wg = new(sync.WaitGroup)
// Download first
for i := 0; i < l.Options.Concurrency; i++ {
wg.Add(1)
go l.downloadWorker(i, wg, all)
}
for _, c := range toInstall {
all <- c
}
close(all)
wg.Wait()
all = make(chan ArtifactMatch)
wg = new(sync.WaitGroup)
wg := new(sync.WaitGroup)
// Do the real install
for i := 0; i < l.Options.Concurrency; i++ {
@@ -775,6 +798,16 @@ func (l *LuetInstaller) computeUninstall(p pkg.Package, s *System) (pkg.Packages
return toUninstall, nil
}
func (l *LuetInstaller) Uninstall(p pkg.Package, s *System) error {
if p.IsSelector() {
if packs, _ := s.Database.FindPackages(p); len(packs) == 0 {
return errors.New("Package not found in the system")
}
} else {
if _, err := s.Database.FindPackage(p); err != nil {
return errors.Wrap(err, "package not found in the system")
}
}
Spinner(32)
toUninstall, err := l.computeUninstall(p, s)
if err != nil {

View File

@@ -1,6 +1,6 @@
steps:
- tar xvf a-test-1.0.package.* -C ./
- ls -liah /a
- mv a /b
requires:
- name: "a"

View File

@@ -12,7 +12,7 @@ oneTimeTearDown() {
testBuild() {
mkdir $tmpdir/testbuild
luet build --tree "$ROOT_DIR/tests/fixtures/finalizers" --destination $tmpdir/testbuild --compression gzip --all > /dev/null
luet build --tree "$ROOT_DIR/tests/fixtures/finalizers" --destination $tmpdir/testbuild --compression gzip --all
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package' "[ -e '$tmpdir/testbuild/alpine-seed-1.0.package.tar.gz' ]"