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-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"
2021-08-03 14:48:29 +00:00
"github.com/mudler/luet/cmd/util"
2022-01-06 22:57:56 +00:00
"github.com/mudler/luet/pkg/api/core/types"
2021-10-19 15:06:48 +00:00
"github.com/mudler/luet/pkg/api/core/types/artifact"
2019-11-10 21:40:31 +00:00
"github.com/mudler/luet/pkg/compiler"
2021-04-12 17:00:36 +00:00
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
2021-04-14 14:49:43 +00:00
"github.com/mudler/luet/pkg/installer"
2021-04-12 17:00:36 +00:00
"github.com/mudler/luet/pkg/compiler/types/compression"
"github.com/mudler/luet/pkg/compiler/types/options"
2022-01-06 22:57:56 +00:00
pkg "github.com/mudler/luet/pkg/database"
2021-10-24 16:00:29 +00:00
fileHelpers "github.com/mudler/luet/pkg/helpers/file"
2019-11-10 21:40:31 +00:00
tree "github.com/mudler/luet/pkg/tree"
"github.com/spf13/cobra"
2021-10-19 20:26:23 +00:00
"github.com/spf13/viper"
2019-11-10 21:40:31 +00:00
)
var buildCmd = & cobra . Command {
2021-12-17 14:21:03 +00:00
// Skip processing output
Annotations : map [ string ] string {
util . CommandProcessOutput : "" ,
} ,
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 ...
2021-04-14 14:49:43 +00:00
2020-12-02 17:24:32 +00:00
Builds all packages
2021-04-14 14:49:43 +00:00
2020-12-02 17:24:32 +00:00
$ luet build -- all
2021-04-14 14:49:43 +00:00
2020-12-02 17:24:32 +00:00
Builds only the leaf packages :
2021-04-14 14:49:43 +00:00
2020-12-02 17:24:32 +00:00
$ luet build -- full
Build package revdeps :
2021-04-14 14:49:43 +00:00
2020-12-02 17:24:32 +00:00
$ 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 ) {
2021-10-19 20:26:23 +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 ( "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" ) )
2021-08-09 10:57:36 +00:00
util . BindValuesFlags ( cmd )
2021-10-19 20:26:23 +00:00
viper . BindPFlag ( "backend-args" , cmd . Flags ( ) . Lookup ( "backend-args" ) )
2020-02-12 10:23:38 +00:00
2021-10-19 20:26:23 +00:00
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 ( "wait" , cmd . Flags ( ) . Lookup ( "wait" ) )
viper . BindPFlag ( "keep-images" , cmd . Flags ( ) . Lookup ( "keep-images" ) )
2020-02-15 13:31:23 +00:00
2021-10-19 20:26:23 +00:00
viper . BindPFlag ( "backend-args" , cmd . Flags ( ) . Lookup ( "backend-args" ) )
2021-02-01 18:10:16 +00:00
2019-11-25 18:55:30 +00:00
} ,
2019-11-10 21:40:31 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2021-10-19 20:26:23 +00:00
treePaths := viper . GetStringSlice ( "tree" )
dst := viper . GetString ( "destination" )
2021-12-17 14:21:03 +00:00
concurrency := util . DefaultContext . Config . General . Concurrency
2021-10-19 20:26:23 +00:00
backendType := viper . GetString ( "backend" )
privileged := viper . GetBool ( "privileged" )
revdeps := viper . GetBool ( "revdeps" )
all := viper . GetBool ( "all" )
compressionType := viper . GetString ( "compression" )
imageRepository := viper . GetString ( "image-repository" )
2021-08-09 10:57:36 +00:00
values := util . ValuesFlags ( )
2021-10-19 20:26:23 +00:00
wait := viper . GetBool ( "wait" )
push := viper . GetBool ( "push" )
pull := viper . GetBool ( "pull" )
keepImages := viper . GetBool ( "keep-images" )
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" )
2021-04-22 22:53:40 +00:00
rebuild , _ := cmd . Flags ( ) . GetBool ( "rebuild" )
2021-11-04 21:31:03 +00:00
pushFinalImages , _ := cmd . Flags ( ) . GetBool ( "push-final-images" )
pushFinalImagesRepository , _ := cmd . Flags ( ) . GetString ( "push-final-images-repository" )
pushFinalImagesForce , _ := cmd . Flags ( ) . GetBool ( "push-final-images-force" )
2021-04-22 22:53:40 +00:00
2020-11-03 17:06:56 +00:00
var results Results
2021-10-19 20:26:23 +00:00
backendArgs := viper . GetStringSlice ( "backend-args" )
2020-11-03 17:06:56 +00:00
out , _ := cmd . Flags ( ) . GetString ( "output" )
pretend , _ := cmd . Flags ( ) . GetBool ( "pretend" )
2021-04-14 14:49:43 +00:00
fromRepo , _ := cmd . Flags ( ) . GetBool ( "from-repositories" )
2021-04-12 17:00:36 +00:00
compilerSpecs := compilerspec . NewLuetCompilationspecs ( )
2022-01-06 22:57:56 +00:00
var db types . PackageDatabase
2021-01-18 16:58:32 +00:00
2021-10-20 22:13:02 +00:00
compilerBackend , err := compiler . NewBackend ( util . DefaultContext , backendType )
2021-04-12 17:00:36 +00:00
helpers . CheckErr ( err )
2019-11-10 21:40:31 +00:00
2021-04-14 14:49:43 +00:00
db = pkg . NewInMemoryDatabase ( false )
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 {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Info ( "Loading tree" , src )
2021-04-12 17:00:36 +00:00
helpers . CheckErr ( generalRecipe . Load ( src ) )
2019-11-10 21:40:31 +00:00
}
2022-01-09 10:58:13 +00:00
templateFolders := [ ] string { }
if fromRepo {
bt , err := installer . LoadBuildTree ( generalRecipe , db , util . DefaultContext )
if err != nil {
util . DefaultContext . Warning ( "errors while loading trees from repositories" , err . Error ( ) )
}
templateFolders = util . TemplateFolders ( util . DefaultContext , bt , treePaths )
} else {
templateFolders = util . TemplateFolders ( util . DefaultContext , installer . BuildTreeResult { } , treePaths )
}
2020-02-12 11:24:07 +00:00
2021-10-20 22:13:02 +00:00
util . DefaultContext . Info ( "Building in" , dst )
2020-05-10 18:01:27 +00:00
2021-10-24 16:00:29 +00:00
if ! fileHelpers . Exists ( dst ) {
os . MkdirAll ( dst , 0600 )
util . DefaultContext . Debug ( "Creating destination folder" , dst )
}
2021-12-17 14:21:03 +00:00
opts := util . DefaultContext . GetConfig ( ) . Solver
2021-04-07 13:54:38 +00:00
pullRepo , _ := cmd . Flags ( ) . GetStringArray ( "pull-repository" )
2020-02-12 11:24:07 +00:00
2021-10-20 22:13:02 +00:00
util . DefaultContext . Debug ( "Solver" , opts . CompactString ( ) )
2021-04-12 17:00:36 +00:00
2021-11-04 21:31:03 +00:00
compileropts := [ ] options . Option { options . NoDeps ( nodeps ) ,
2021-04-12 17:00:36 +00:00
options . WithBackendType ( backendType ) ,
options . PushImages ( push ) ,
options . WithBuildValues ( values ) ,
options . WithPullRepositories ( pullRepo ) ,
options . WithPushRepository ( imageRepository ) ,
2021-04-22 22:53:40 +00:00
options . Rebuild ( rebuild ) ,
2022-01-09 10:58:13 +00:00
options . WithTemplateFolder ( templateFolders ) ,
2021-12-17 14:21:03 +00:00
options . WithSolverOptions ( opts ) ,
2021-04-12 17:00:36 +00:00
options . Wait ( wait ) ,
options . OnlyTarget ( onlyTarget ) ,
options . PullFirst ( pull ) ,
options . KeepImg ( keepImages ) ,
options . OnlyDeps ( onlydeps ) ,
2021-10-20 22:13:02 +00:00
options . WithContext ( util . DefaultContext ) ,
2021-04-12 17:00:36 +00:00
options . BackendArgs ( backendArgs ) ,
options . Concurrency ( concurrency ) ,
2021-11-04 21:31:03 +00:00
options . WithCompressionType ( compression . Implementation ( compressionType ) ) }
if pushFinalImages {
compileropts = append ( compileropts , options . EnablePushFinalImages )
if pushFinalImagesForce {
compileropts = append ( compileropts , options . ForcePushFinalImages )
}
if pushFinalImagesRepository != "" {
compileropts = append ( compileropts , options . WithFinalRepository ( pushFinalImagesRepository ) )
} else if imageRepository != "" {
compileropts = append ( compileropts , options . WithFinalRepository ( imageRepository ) )
}
}
luetCompiler := compiler . NewLuetCompiler ( compilerBackend , generalRecipe . GetDatabase ( ) , compileropts ... )
2021-04-12 17:00:36 +00:00
2020-06-06 06:58:18 +00:00
if full {
specs , err := luetCompiler . FromDatabase ( generalRecipe . GetDatabase ( ) , true , dst )
if err != nil {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Fatal ( err . Error ( ) )
2020-06-06 06:58:18 +00:00
}
for _ , spec := range specs {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Info ( ":package: Selecting " , spec . GetPackage ( ) . GetName ( ) , spec . GetPackage ( ) . GetVersion ( ) )
2020-06-06 09:18:54 +00:00
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 {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Fatal ( "Invalid package string " , a , ": " , err . Error ( ) )
2019-12-24 14:34:02 +00:00
}
spec , err := luetCompiler . FromPackage ( pack )
2019-11-15 17:12:50 +00:00
if err != nil {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Fatal ( "Error: " + err . Error ( ) )
2019-11-15 17:12:50 +00:00
}
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 {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Fatal ( "Error: " + err . Error ( ) )
2019-11-15 17:12:50 +00:00
}
2021-10-20 22:13:02 +00:00
util . DefaultContext . 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
}
2021-04-12 17:00:36 +00:00
var artifact [ ] * artifact . PackageArtifact
2019-11-15 17:12:50 +00:00
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 {
2021-04-12 17:00:36 +00:00
toCalculate := [ ] * compilerspec . LuetCompilationSpec { }
2020-11-03 17:06:56 +00:00
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 {
2021-05-11 07:46:54 +00:00
ht := compiler . NewHashTree ( generalRecipe . GetDatabase ( ) )
hashTree , err := ht . Query ( luetCompiler , sp )
2020-11-03 17:06:56 +00:00
if err != nil {
errs = append ( errs , err )
}
2021-05-11 07:46:54 +00:00
for _ , p := range hashTree . Dependencies {
2020-11-03 17:06:56 +00:00
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 {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Info ( p . String ( ) )
2020-11-03 17:06:56 +00:00
}
}
2019-11-15 17:12:50 +00:00
} else {
2021-04-12 17:00:36 +00:00
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 {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Error ( "Error: " + e . Error ( ) )
2019-11-12 21:17:23 +00:00
}
2021-10-20 22:13:02 +00:00
util . DefaultContext . Fatal ( "Bailing out" )
2019-11-12 21:17:23 +00:00
}
for _ , a := range artifact {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Info ( "Artifact generated:" , a . Path )
2019-11-12 21:17:23 +00:00
}
2019-11-10 21:40:31 +00:00
} ,
}
func init ( ) {
path , err := os . Getwd ( )
if err != nil {
2021-10-20 22:13:02 +00:00
util . DefaultContext . Fatal ( err )
2019-11-10 21:40:31 +00:00
}
2021-02-22 12:48:26 +00:00
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)" )
2021-03-18 09:47:39 +00:00
buildCmd . Flags ( ) . Bool ( "privileged" , true , "Privileged (Keep permissions)" )
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" )
2021-11-04 21:31:03 +00:00
buildCmd . Flags ( ) . Bool ( "push-final-images" , false , "Push final images while building" )
buildCmd . Flags ( ) . Bool ( "push-final-images-force" , false , "Override existing images" )
buildCmd . Flags ( ) . String ( "push-final-images-repository" , "" , "Repository where to push final images to" )
2020-06-06 06:58:18 +00:00
buildCmd . Flags ( ) . Bool ( "full" , false , "Build all packages (optimized)" )
2021-04-12 10:04:04 +00:00
buildCmd . Flags ( ) . StringSlice ( "values" , [ ] string { } , "Build values file to interpolate with each package" )
2021-02-22 12:48:26 +00:00
buildCmd . Flags ( ) . StringSliceP ( "backend-args" , "a" , [ ] string { } , "Backend args" )
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)" )
2021-04-14 14:49:43 +00:00
buildCmd . Flags ( ) . Bool ( "from-repositories" , false , "Consume the user-defined repositories to pull specfiles from" )
2021-04-22 22:53:40 +00:00
buildCmd . Flags ( ) . Bool ( "rebuild" , false , "To combine with --pull. Allows to rebuild the target package even if an image is available, against a local values file" )
2020-11-03 17:06:56 +00:00
buildCmd . Flags ( ) . Bool ( "pretend" , false , "Just print what packages will be compiled" )
2021-04-07 13:54:38 +00:00
buildCmd . Flags ( ) . StringArrayP ( "pull-repository" , "p" , [ ] string { } , "A list of repositories to pull the cache from" )
2020-11-03 17:06:56 +00:00
buildCmd . Flags ( ) . StringP ( "output" , "o" , "terminal" , "Output format ( Defaults: terminal, available: json,yaml )" )
2019-11-10 21:40:31 +00:00
RootCmd . AddCommand ( buildCmd )
}