2019-11-10 21:40:31 +00:00
// Copyright © 2019 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 cmd
import (
2020-11-03 17:06:56 +00:00
"fmt"
2019-11-16 10:51:30 +00:00
"io/ioutil"
2019-11-15 17:12:50 +00:00
"os"
2021-01-18 16:58:32 +00:00
"path/filepath"
2019-11-15 17:12:50 +00:00
2020-11-03 17:06:56 +00:00
"github.com/ghodss/yaml"
2020-05-23 06:51:33 +00:00
helpers "github.com/mudler/luet/cmd/helpers"
2019-11-10 21:40:31 +00:00
"github.com/mudler/luet/pkg/compiler"
"github.com/mudler/luet/pkg/compiler/backend"
2019-12-29 15:33:10 +00:00
. "github.com/mudler/luet/pkg/config"
2019-11-10 21:40:31 +00:00
. "github.com/mudler/luet/pkg/logger"
pkg "github.com/mudler/luet/pkg/package"
2020-10-25 17:43:35 +00:00
"github.com/mudler/luet/pkg/solver"
2019-11-10 21:40:31 +00:00
tree "github.com/mudler/luet/pkg/tree"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var buildCmd = & cobra . Command {
2019-11-12 21:17:23 +00:00
Use : "build <package name> <package name> <package name> ..." ,
2019-11-10 21:40:31 +00:00
Short : "build a package or a tree" ,
2020-12-02 17:24:32 +00:00
Long : ` Builds one or more packages from a tree ( current directory is implied ) :
$ luet build utils / busybox utils / yq ...
Builds all packages
$ luet build -- all
Builds only the leaf packages :
$ luet build -- full
Build package revdeps :
$ luet build -- revdeps utils / yq
Build package without dependencies ( needs the images already in the host , or either need to be available online ) :
$ luet build -- nodeps utils / yq ...
Build packages specifying multiple definition trees :
$ luet build -- tree overlay / path -- tree overlay / path2 utils / yq ...
` , PreRun : func ( cmd * cobra . Command , args [ ] string ) {
2019-11-25 18:55:30 +00:00
viper . BindPFlag ( "tree" , cmd . Flags ( ) . Lookup ( "tree" ) )
viper . BindPFlag ( "destination" , cmd . Flags ( ) . Lookup ( "destination" ) )
viper . BindPFlag ( "backend" , cmd . Flags ( ) . Lookup ( "backend" ) )
viper . BindPFlag ( "privileged" , cmd . Flags ( ) . Lookup ( "privileged" ) )
viper . BindPFlag ( "database" , cmd . Flags ( ) . Lookup ( "database" ) )
viper . BindPFlag ( "revdeps" , cmd . Flags ( ) . Lookup ( "revdeps" ) )
viper . BindPFlag ( "all" , cmd . Flags ( ) . Lookup ( "all" ) )
2019-12-30 13:52:34 +00:00
viper . BindPFlag ( "compression" , cmd . Flags ( ) . Lookup ( "compression" ) )
2020-02-18 17:22:18 +00:00
viper . BindPFlag ( "nodeps" , cmd . Flags ( ) . Lookup ( "nodeps" ) )
viper . BindPFlag ( "onlydeps" , cmd . Flags ( ) . Lookup ( "onlydeps" ) )
2020-11-27 21:22:20 +00:00
viper . BindPFlag ( "values" , cmd . Flags ( ) . Lookup ( "values" ) )
2020-02-12 10:23:38 +00:00
2020-02-15 13:31:23 +00:00
viper . BindPFlag ( "image-repository" , cmd . Flags ( ) . Lookup ( "image-repository" ) )
2020-02-15 13:45:05 +00:00
viper . BindPFlag ( "push" , cmd . Flags ( ) . Lookup ( "push" ) )
viper . BindPFlag ( "pull" , cmd . Flags ( ) . Lookup ( "pull" ) )
2020-12-18 22:19:18 +00:00
viper . BindPFlag ( "wait" , cmd . Flags ( ) . Lookup ( "wait" ) )
2020-02-15 13:45:05 +00:00
viper . BindPFlag ( "keep-images" , cmd . Flags ( ) . Lookup ( "keep-images" ) )
2020-02-15 13:31:23 +00:00
2020-02-12 10:23:38 +00:00
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" ) )
LuetCfg . Viper . BindPFlag ( "solver.max_attempts" , cmd . Flags ( ) . Lookup ( "solver-attempts" ) )
2019-11-25 18:55:30 +00:00
} ,
2019-11-10 21:40:31 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2020-05-10 18:01:27 +00:00
treePaths := viper . GetStringSlice ( "tree" )
2019-11-25 18:55:30 +00:00
dst := viper . GetString ( "destination" )
2019-12-29 15:33:10 +00:00
concurrency := LuetCfg . GetGeneral ( ) . Concurrency
2019-11-10 21:40:31 +00:00
backendType := viper . GetString ( "backend" )
privileged := viper . GetBool ( "privileged" )
2019-11-15 17:12:50 +00:00
revdeps := viper . GetBool ( "revdeps" )
all := viper . GetBool ( "all" )
2019-11-16 10:51:30 +00:00
databaseType := viper . GetString ( "database" )
2019-12-30 13:52:34 +00:00
compressionType := viper . GetString ( "compression" )
2020-02-15 13:31:23 +00:00
imageRepository := viper . GetString ( "image-repository" )
2020-11-27 21:22:20 +00:00
values := viper . GetString ( "values" )
2020-12-18 22:19:18 +00:00
wait := viper . GetBool ( "wait" )
2020-02-15 13:45:05 +00:00
push := viper . GetBool ( "push" )
pull := viper . GetBool ( "pull" )
keepImages := viper . GetBool ( "keep-images" )
2020-02-18 17:22:18 +00:00
nodeps := viper . GetBool ( "nodeps" )
onlydeps := viper . GetBool ( "onlydeps" )
2020-10-06 15:57:57 +00:00
onlyTarget , _ := cmd . Flags ( ) . GetBool ( "only-target-package" )
2020-06-06 09:20:48 +00:00
full , _ := cmd . Flags ( ) . GetBool ( "full" )
2020-10-25 17:43:35 +00:00
concurrent , _ := cmd . Flags ( ) . GetBool ( "solver-concurrent" )
2020-11-03 17:06:56 +00:00
var results Results
2019-11-10 21:40:31 +00:00
2020-11-03 17:06:56 +00:00
out , _ := cmd . Flags ( ) . GetString ( "output" )
if out != "terminal" {
LuetCfg . GetLogging ( ) . SetLogLevel ( "error" )
}
pretend , _ := cmd . Flags ( ) . GetBool ( "pretend" )
2019-11-15 18:31:56 +00:00
compilerSpecs := compiler . NewLuetCompilationspecs ( )
2019-11-16 10:51:30 +00:00
var db pkg . PackageDatabase
2021-01-18 16:58:32 +00:00
compilerBackend := backend . NewBackend ( backendType )
2019-11-10 21:40:31 +00:00
2019-11-16 10:51:30 +00:00
switch databaseType {
case "memory" :
db = pkg . NewInMemoryDatabase ( false )
2019-11-29 18:01:53 +00:00
2019-11-16 10:51:30 +00:00
case "boltdb" :
tmpdir , err := ioutil . TempDir ( "" , "package" )
if err != nil {
Fatal ( err )
}
db = pkg . NewBoltDatabase ( tmpdir )
2019-11-29 18:01:53 +00:00
2019-11-16 10:51:30 +00:00
}
defer db . Clean ( )
generalRecipe := tree . NewCompilerRecipe ( db )
2019-11-10 21:40:31 +00:00
2020-05-10 18:01:27 +00:00
for _ , src := range treePaths {
Info ( "Loading tree" , src )
err := generalRecipe . Load ( src )
if err != nil {
Fatal ( "Error: " + err . Error ( ) )
}
2019-11-10 21:40:31 +00:00
}
2020-02-12 11:24:07 +00:00
2020-05-10 18:01:27 +00:00
Info ( "Building in" , dst )
2020-02-12 11:24:07 +00:00
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" )
LuetCfg . GetSolverOptions ( ) . Type = stype
LuetCfg . GetSolverOptions ( ) . LearnRate = float32 ( rate )
LuetCfg . GetSolverOptions ( ) . Discount = float32 ( discount )
LuetCfg . GetSolverOptions ( ) . MaxAttempts = attempts
Debug ( "Solver" , LuetCfg . GetSolverOptions ( ) . CompactString ( ) )
2020-01-05 14:39:34 +00:00
opts := compiler . NewDefaultCompilerOptions ( )
2020-02-12 10:23:38 +00:00
opts . SolverOptions = * LuetCfg . GetSolverOptions ( )
2020-02-15 13:31:23 +00:00
opts . ImageRepository = imageRepository
2020-02-15 13:45:05 +00:00
opts . PullFirst = pull
opts . KeepImg = keepImages
opts . Push = push
2020-02-18 17:22:18 +00:00
opts . OnlyDeps = onlydeps
opts . NoDeps = nodeps
2020-12-18 22:19:18 +00:00
opts . Wait = wait
2020-10-06 15:57:57 +00:00
opts . PackageTargetOnly = onlyTarget
2020-11-27 21:22:20 +00:00
opts . BuildValuesFile = values
2020-10-25 17:43:35 +00:00
var solverOpts solver . Options
if concurrent {
2020-10-25 19:12:36 +00:00
solverOpts = solver . Options { Type : solver . ParallelSimple , Concurrency : concurrency }
2020-10-25 19:09:22 +00:00
} else {
2020-10-25 19:12:36 +00:00
solverOpts = solver . Options { Type : solver . SingleCoreSimple , Concurrency : concurrency }
2020-10-25 17:43:35 +00:00
}
luetCompiler := compiler . NewLuetCompiler ( compilerBackend , generalRecipe . GetDatabase ( ) , opts , solverOpts )
2019-12-30 13:52:34 +00:00
luetCompiler . SetConcurrency ( concurrency )
luetCompiler . SetCompressionType ( compiler . CompressionImplementation ( compressionType ) )
2020-06-06 06:58:18 +00:00
if full {
specs , err := luetCompiler . FromDatabase ( generalRecipe . GetDatabase ( ) , true , dst )
if err != nil {
Fatal ( err . Error ( ) )
}
for _ , spec := range specs {
2020-06-06 09:18:54 +00:00
Info ( ":package: Selecting " , spec . GetPackage ( ) . GetName ( ) , spec . GetPackage ( ) . GetVersion ( ) )
2020-06-06 06:58:18 +00:00
compilerSpecs . Add ( spec )
}
} else if ! all {
2019-11-15 17:12:50 +00:00
for _ , a := range args {
2020-02-21 20:56:32 +00:00
pack , err := helpers . ParsePackageStr ( a )
2019-11-15 17:12:50 +00:00
if err != nil {
2019-12-24 14:34:02 +00:00
Fatal ( "Invalid package string " , a , ": " , err . Error ( ) )
}
spec , err := luetCompiler . FromPackage ( pack )
2019-11-15 17:12:50 +00:00
if err != nil {
Fatal ( "Error: " + err . Error ( ) )
}
2019-11-12 21:17:23 +00:00
2019-11-15 17:12:50 +00:00
spec . SetOutputPath ( dst )
compilerSpecs . Add ( spec )
}
} else {
2019-11-29 18:01:53 +00:00
w := generalRecipe . GetDatabase ( ) . World ( )
2019-11-15 17:12:50 +00:00
for _ , p := range w {
spec , err := luetCompiler . FromPackage ( p )
if err != nil {
Fatal ( "Error: " + err . Error ( ) )
}
2019-11-16 13:16:44 +00:00
Info ( ":package: Selecting " , p . GetName ( ) , p . GetVersion ( ) )
2019-11-25 18:55:30 +00:00
spec . SetOutputPath ( dst )
2019-11-15 17:12:50 +00:00
compilerSpecs . Add ( spec )
}
2019-11-10 21:40:31 +00:00
}
2019-11-15 17:12:50 +00:00
var artifact [ ] compiler . Artifact
var errs [ ] error
if revdeps {
2019-12-30 13:52:34 +00:00
artifact , errs = luetCompiler . CompileWithReverseDeps ( privileged , compilerSpecs )
2019-11-15 17:12:50 +00:00
2020-11-03 17:06:56 +00:00
} else if pretend {
toCalculate := [ ] compiler . CompilationSpec { }
if full {
var err error
toCalculate , err = luetCompiler . ComputeMinimumCompilableSet ( compilerSpecs . All ( ) ... )
if err != nil {
errs = append ( errs , err )
}
} else {
toCalculate = compilerSpecs . All ( )
}
for _ , sp := range toCalculate {
packs , err := luetCompiler . ComputeDepTree ( sp )
if err != nil {
errs = append ( errs , err )
}
for _ , p := range packs {
results . Packages = append ( results . Packages ,
PackageResult {
Name : p . Package . GetName ( ) ,
Version : p . Package . GetVersion ( ) ,
Category : p . Package . GetCategory ( ) ,
Repository : "" ,
Hidden : p . Package . IsHidden ( ) ,
Target : sp . GetPackage ( ) . HumanReadableString ( ) ,
} )
}
}
y , err := yaml . Marshal ( results )
if err != nil {
fmt . Printf ( "err: %v\n" , err )
return
}
switch out {
case "yaml" :
fmt . Println ( string ( y ) )
case "json" :
j2 , err := yaml . YAMLToJSON ( y )
if err != nil {
fmt . Printf ( "err: %v\n" , err )
return
}
fmt . Println ( string ( j2 ) )
case "terminal" :
for _ , p := range results . Packages {
Info ( p . String ( ) )
}
}
2019-11-15 17:12:50 +00:00
} else {
2019-12-30 13:52:34 +00:00
artifact , errs = luetCompiler . CompileParallel ( privileged , compilerSpecs )
2019-11-15 17:12:50 +00:00
}
2019-11-12 21:17:23 +00:00
if len ( errs ) != 0 {
for _ , e := range errs {
Error ( "Error: " + e . Error ( ) )
}
Fatal ( "Bailing out" )
}
for _ , a := range artifact {
Info ( "Artifact generated:" , a . GetPath ( ) )
}
2019-11-10 21:40:31 +00:00
} ,
}
func init ( ) {
path , err := os . Getwd ( )
if err != nil {
Fatal ( err )
}
2021-01-18 09:40:41 +00:00
buildCmd . Flags ( ) . StringSliceP ( "tree" , "t" , [ ] string { path } , "Path of the tree to use." )
2019-11-10 21:40:31 +00:00
buildCmd . Flags ( ) . String ( "backend" , "docker" , "backend used (docker,img)" )
buildCmd . Flags ( ) . Bool ( "privileged" , false , "Privileged (Keep permissions)" )
2019-11-16 10:51:30 +00:00
buildCmd . Flags ( ) . String ( "database" , "memory" , "database used for solving (memory,boltdb)" )
2019-11-15 17:12:50 +00:00
buildCmd . Flags ( ) . Bool ( "revdeps" , false , "Build with revdeps" )
2020-06-06 06:58:18 +00:00
buildCmd . Flags ( ) . Bool ( "all" , false , "Build all specfiles in the tree" )
buildCmd . Flags ( ) . Bool ( "full" , false , "Build all packages (optimized)" )
2020-11-27 21:22:20 +00:00
buildCmd . Flags ( ) . String ( "values" , "" , "Build values file to interpolate with each package" )
2020-06-06 06:58:18 +00:00
2021-01-18 16:58:32 +00:00
buildCmd . Flags ( ) . String ( "destination" , filepath . Join ( path , "build" ) , "Destination folder" )
2020-12-18 20:23:04 +00:00
buildCmd . Flags ( ) . String ( "compression" , "none" , "Compression alg: none, gzip, zstd" )
2020-02-15 13:31:23 +00:00
buildCmd . Flags ( ) . String ( "image-repository" , "luet/cache" , "Default base image string for generated image" )
2020-02-15 13:45:05 +00:00
buildCmd . Flags ( ) . Bool ( "push" , false , "Push images to a hub" )
buildCmd . Flags ( ) . Bool ( "pull" , false , "Pull images from a hub" )
2020-12-18 22:19:18 +00:00
buildCmd . Flags ( ) . Bool ( "wait" , false , "Don't build all intermediate images, but wait for them until they are available" )
2020-02-15 13:45:05 +00:00
buildCmd . Flags ( ) . Bool ( "keep-images" , true , "Keep built docker images in the host" )
2020-02-18 17:22:18 +00:00
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" )
2020-10-06 15:57:57 +00:00
buildCmd . Flags ( ) . Bool ( "only-target-package" , false , "Build packages of only the required target. Otherwise builds all the necessary ones not present in the destination" )
2020-02-12 10:23:38 +00:00
buildCmd . Flags ( ) . String ( "solver-type" , "" , "Solver strategy" )
buildCmd . Flags ( ) . Float32 ( "solver-rate" , 0.7 , "Solver learning rate" )
buildCmd . Flags ( ) . Float32 ( "solver-discount" , 1.0 , "Solver discount rate" )
buildCmd . Flags ( ) . Int ( "solver-attempts" , 9000 , "Solver maximum attempts" )
2020-10-25 17:43:35 +00:00
buildCmd . Flags ( ) . Bool ( "solver-concurrent" , false , "Use concurrent solver (experimental)" )
2020-02-12 10:23:38 +00:00
2020-11-03 17:06:56 +00:00
buildCmd . Flags ( ) . Bool ( "pretend" , false , "Just print what packages will be compiled" )
buildCmd . Flags ( ) . StringP ( "output" , "o" , "terminal" , "Output format ( Defaults: terminal, available: json,yaml )" )
2019-11-10 21:40:31 +00:00
RootCmd . AddCommand ( buildCmd )
}