diff --git a/core/resolver.go b/api/download.go similarity index 66% rename from core/resolver.go rename to api/download.go index 52e0d76..19792b6 100644 --- a/core/resolver.go +++ b/api/download.go @@ -1,4 +1,4 @@ -package core +package api import ( "crypto/sha256" @@ -11,59 +11,19 @@ import ( "strings" ) -// ResolveSources resolves the sources of a recipe and downloads them -// to the downloads directory. Note that modules in this function are -// returned in the order they should be built. -func ResolveSources(recipe *Recipe) ([]Module, []Source, error) { - fmt.Println("Resolving sources") - - modules := GetAllModules(recipe.Modules) - var sources []Source - - for _, module := range modules { - fmt.Printf("Resolving source for: %s\n", module.Name) - - if module.Source.URL == "" { - continue - } - - module.Source.Module = module.Name - err := DownloadSource(recipe, module.Source) - if err != nil { - return nil, nil, err - } - - sources = append(sources, module.Source) - } - - return modules, sources, nil -} - -// GetAllModules returns a list of all modules in a ordered list -func GetAllModules(modules []Module) []Module { - var orderedList []Module - - for _, module := range modules { - orderedList = append(orderedList, GetAllModules(module.Modules)...) - orderedList = append(orderedList, module) - } - - return orderedList -} - // DownloadSource downloads a source to the downloads directory // according to its type (git, tar, ...) -func DownloadSource(recipe *Recipe, source Source) error { +func DownloadSource(downloadPath string, source Source, moduleName string) error { fmt.Printf("Downloading source: %s\n", source.URL) if source.Type == "git" { - return DownloadGitSource(recipe, source) + return DownloadGitSource(downloadPath, source, moduleName) } else if source.Type == "tar" { - err := DownloadTarSource(recipe, source) + err := DownloadTarSource(downloadPath, source, moduleName) if err != nil { return err } - return checksumValidation(source, filepath.Join(recipe.DownloadsPath, source.Module)) + return checksumValidation(source, filepath.Join(downloadPath, moduleName)) } else { return fmt.Errorf("unsupported source type %s", source.Type) } @@ -71,10 +31,10 @@ func DownloadSource(recipe *Recipe, source Source) error { // DownloadGitSource downloads a git source to the downloads directory // and checks out the commit or tag -func DownloadGitSource(recipe *Recipe, source Source) error { +func DownloadGitSource(downloadPath string, source Source, moduleName string) error { fmt.Printf("Source is git: %s\n", source.URL) - dest := filepath.Join(recipe.DownloadsPath, source.Module) + dest := filepath.Join(downloadPath, moduleName) if source.Commit == "" && source.Tag == "" && source.Branch == "" { return fmt.Errorf("missing source commit, tag or branch") @@ -156,10 +116,10 @@ func DownloadGitSource(recipe *Recipe, source Source) error { } // DownloadTarSource downloads a tar archive to the downloads directory -func DownloadTarSource(recipe *Recipe, source Source) error { +func DownloadTarSource(downloadPath string, source Source, moduleName string) error { fmt.Printf("Source is tar: %s\n", source.URL) //Create the destination path - dest := filepath.Join(recipe.DownloadsPath, source.Module) + dest := filepath.Join(downloadPath, moduleName) //Download the resource res, err := http.Get(source.URL) if err != nil { @@ -185,11 +145,11 @@ func DownloadTarSource(recipe *Recipe, source Source) error { // MoveSources moves all sources from the downloads directory to the // sources directory -func MoveSources(recipe *Recipe, sources []Source) error { +func MoveSources(downloadPath string, sourcesPath string, sources []Source, moduleName string) error { fmt.Println("Moving sources") for _, source := range sources { - err := MoveSource(recipe, source) + err := MoveSource(downloadPath, sourcesPath, source, moduleName) if err != nil { return err } @@ -201,26 +161,26 @@ func MoveSources(recipe *Recipe, sources []Source) error { // MoveSource moves a source from the downloads directory to the // sources directory, by extracting if a tar archive or moving if a // git repository -func MoveSource(recipe *Recipe, source Source) error { - fmt.Printf("Moving source: %s\n", source.Module) +func MoveSource(downloadPath string, sourcesPath string, source Source, moduleName string) error { + fmt.Printf("Moving source: %s\n", moduleName) if source.Type == "git" { return os.Rename( - filepath.Join(recipe.DownloadsPath, source.Module), - filepath.Join(recipe.SourcesPath, source.Module), + filepath.Join(downloadPath, moduleName), + filepath.Join(sourcesPath, moduleName), ) } else if source.Type == "tar" { cmd := exec.Command( "tar", - "-xf", filepath.Join(recipe.DownloadsPath, source.Module), - "-C", recipe.SourcesPath, + "-xf", filepath.Join(downloadPath, moduleName), + "-C", sourcesPath, ) err := cmd.Run() if err != nil { return err } - return os.Remove(filepath.Join(recipe.DownloadsPath, source.Module)) + return os.Remove(filepath.Join(downloadPath, moduleName)) } else { return fmt.Errorf("unsupported source type %s", source.Type) } diff --git a/api/go.mod b/api/go.mod new file mode 100644 index 0000000..ea4497b --- /dev/null +++ b/api/go.mod @@ -0,0 +1,3 @@ +module github.com/vanilla-os/vib/api + +go 1.21 diff --git a/api/structs.go b/api/structs.go new file mode 100644 index 0000000..7a6bb62 --- /dev/null +++ b/api/structs.go @@ -0,0 +1,31 @@ +package api + +type Source struct { + URL string `json:"url"` + Checksum string `json:"checksum"` + Type string `json:"type"` + Commit string `json:"commit"` + Tag string `json:"tag"` + Branch string `json:"branch"` + Packages []string `json:"packages"` + Paths []string `json:"paths"` +} + +type Recipe struct { + Base string `json:"base"` + Name string + Id string + SingleLayer bool `json:"singlelayer"` + Labels map[string]string `json:"labels"` + Adds map[string]string `json:"adds"` + Args map[string]string `json:"args"` + Runs []string `json:"runs"` + Cmd string `json:"cmd"` + Modules []interface{} `json:"modules"` + Path string + ParentPath string + DownloadsPath string + SourcesPath string + PluginPath string + Containerfile string +} diff --git a/core/apt.go b/core/apt.go index 72c374a..96692ef 100644 --- a/core/apt.go +++ b/core/apt.go @@ -4,13 +4,26 @@ import ( "bufio" "errors" "fmt" + "github.com/mitchellh/mapstructure" + "github.com/vanilla-os/vib/api" "os" "path/filepath" ) +type AptModule struct { + Name string `json:"name"` + Type string `json:"type"` + Source api.Source `json:"source"` +} + // BuildAptModule builds a module that installs packages // using the apt package manager -func BuildAptModule(recipe *Recipe, module Module) (string, error) { +func BuildAptModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { + var module AptModule + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return "", err + } if len(module.Source.Packages) > 0 { packages := "" for _, pkg := range module.Source.Packages { diff --git a/core/build.go b/core/build.go index ae68c79..888ab5e 100644 --- a/core/build.go +++ b/core/build.go @@ -2,44 +2,34 @@ package core import ( "fmt" + "github.com/mitchellh/mapstructure" + "github.com/vanilla-os/vib/api" "os" + "strings" ) // BuildRecipe builds a Containerfile from a recipe path -func BuildRecipe(recipePath string) (Recipe, error) { +func BuildRecipe(recipePath string) (api.Recipe, error) { // load the recipe recipe, err := LoadRecipe(recipePath) if err != nil { - return Recipe{}, err + return api.Recipe{}, err } fmt.Printf("Building recipe %s\n", recipe.Name) - // resolve (and download) the sources - modules, sources, err := ResolveSources(recipe) - if err != nil { - return Recipe{}, err - } - - // move them to the sources directory so they can be - // used by the modules during the build - err = MoveSources(recipe, sources) - if err != nil { - return Recipe{}, err - } - // build the modules* // * actually just build the commands that will be used // in the Containerfile to build the modules - cmds, err := BuildModules(recipe, modules) + cmds, err := BuildModules(recipe, recipe.Modules) if err != nil { - return Recipe{}, err + return api.Recipe{}, err } // build the Containerfile err = BuildContainerfile(recipe, cmds) if err != nil { - return Recipe{}, err + return api.Recipe{}, err } return *recipe, nil @@ -47,7 +37,7 @@ func BuildRecipe(recipePath string) (Recipe, error) { // BuildContainerfile builds a Containerfile from a recipe // and a list of modules commands -func BuildContainerfile(recipe *Recipe, cmds []ModuleCommand) error { +func BuildContainerfile(recipe *api.Recipe, cmds []ModuleCommand) error { containerfile, err := os.Create(recipe.Containerfile) if err != nil { return err @@ -172,13 +162,16 @@ func BuildContainerfile(recipe *Recipe, cmds []ModuleCommand) error { } // BuildModules builds a list of modules commands from a list of modules -func BuildModules(recipe *Recipe, modules []Module) ([]ModuleCommand, error) { +func BuildModules(recipe *api.Recipe, modules []interface{}) ([]ModuleCommand, error) { cmds := []ModuleCommand{} + for _, moduleInterface := range modules { + var module Module + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return nil, err + } - for _, module := range modules { - fmt.Printf("Creating build command for %s\n", module.Name) - - cmd, err := BuildModule(recipe, module) + cmd, err := BuildModule(recipe, moduleInterface) if err != nil { return nil, err } @@ -195,25 +188,81 @@ func BuildModules(recipe *Recipe, modules []Module) ([]ModuleCommand, error) { // BuildModule builds a module command from a module // this is done by calling the appropriate module builder // function based on the module type -func BuildModule(recipe *Recipe, module Module) (string, error) { +func BuildModule(recipe *api.Recipe, moduleInterface interface{}) (string, error) { + var module Module + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return "", err + } + var commands string + if len(module.Modules) > 0 { + for _, nestedModule := range module.Modules { + buildModule, err := BuildModule(recipe, nestedModule) + if err != nil { + return "", err + } + commands = commands + " && " + buildModule + } + } + switch module.Type { case "apt": - return BuildAptModule(recipe, module) + command, err := BuildAptModule(moduleInterface, recipe) + if err != nil { + return "", err + } + commands = commands + " && " + command case "cmake": - return BuildCMakeModule(module) + command, err := BuildCMakeModule(moduleInterface, recipe) + if err != nil { + return "", err + } + commands = commands + " && " + command case "dpkg": - return BuildDpkgModule(module) + command, err := BuildDpkgModule(moduleInterface, recipe) + if err != nil { + return "", err + } + commands = commands + " && " + command case "dpkg-buildpackage": - return BuildDpkgBuildPkgModule(module) + command, err := BuildDpkgBuildPkgModule(moduleInterface, recipe) + if err != nil { + return "", err + } + commands = commands + " && " + command case "go": - return BuildGoModule(module) + command, err := BuildGoModule(moduleInterface, recipe) + if err != nil { + return "", err + } + commands = commands + " && " + command case "make": - return BuildMakeModule(module) + command, err := BuildMakeModule(moduleInterface, recipe) + if err != nil { + return "", err + } + commands = commands + " && " + command case "meson": - return BuildMesonModule(module) + command, err := BuildMesonModule(moduleInterface, recipe) + if err != nil { + return "", err + } + commands = commands + " && " + command case "shell": - return BuildShellModule(module) + command, err := BuildShellModule(moduleInterface, recipe) + if err != nil { + return "", err + } + commands = commands + " && " + command + case "includes": + return "", nil default: - return "", fmt.Errorf("unknown module type %s", module.Type) + command, err := LoadPlugin(module.Type, moduleInterface, recipe) + if err != nil { + return "", err + } + commands = commands + " && " + command } + + return strings.TrimPrefix(commands, " && "), err } diff --git a/core/cmake.go b/core/cmake.go index 384352e..8755e1a 100644 --- a/core/cmake.go +++ b/core/cmake.go @@ -1,9 +1,35 @@ package core -import "fmt" +import ( + "fmt" + "github.com/mitchellh/mapstructure" + + "github.com/vanilla-os/vib/api" +) + +type CMakeModule struct { + Name string `json:"name"` + Type string `json:"type"` + BuildVars map[string]string `json:"buildvars"` + BuildFlags string `json:"buildflags"` + Source api.Source +} // BuildCMakeModule builds a module that builds a CMake project -func BuildCMakeModule(module Module) (string, error) { +func BuildCMakeModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { + var module CMakeModule + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return "", err + } + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) + if err != nil { + return "", err + } + err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) + if err != nil { + return "", err + } buildVars := map[string]string{} for k, v := range module.BuildVars { buildVars[k] = v diff --git a/core/compile.go b/core/compile.go index ec17965..e0a2c9e 100644 --- a/core/compile.go +++ b/core/compile.go @@ -2,6 +2,7 @@ package core import ( "fmt" + "github.com/vanilla-os/vib/api" "os" "os/exec" ) @@ -44,7 +45,7 @@ func CompileRecipe(recipePath string, runtime string) error { return nil } -func compileDocker(recipe Recipe, storePath string) error { +func compileDocker(recipe api.Recipe, storePath string) error { docker, err := exec.LookPath("docker") if err != nil { return err @@ -64,7 +65,7 @@ func compileDocker(recipe Recipe, storePath string) error { return cmd.Run() } -func compilePodman(recipe Recipe, storePath string) error { +func compilePodman(recipe api.Recipe, storePath string) error { podman, err := exec.LookPath("podman") if err != nil { return err diff --git a/core/dpkg-buildpackage.go b/core/dpkg-buildpackage.go index dbe2dae..7d6d229 100644 --- a/core/dpkg-buildpackage.go +++ b/core/dpkg-buildpackage.go @@ -1,10 +1,35 @@ package core -import "fmt" +import ( + "fmt" + "github.com/mitchellh/mapstructure" + "github.com/vanilla-os/vib/api" +) + +type DpkgBuildModule struct { + Name string `json:"name"` + Type string `json:"type"` + Source api.Source +} // BuildDpkgModule builds a module that builds a dpkg project // and installs the resulting .deb package -func BuildDpkgBuildPkgModule(module Module) (string, error) { +func BuildDpkgBuildPkgModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { + var module DpkgBuildModule + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return "", err + } + + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) + if err != nil { + return "", err + } + err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) + if err != nil { + return "", err + } + cmd := fmt.Sprintf( "cd /sources/%s && dpkg-buildpackage -d -us -uc -b", module.Name, diff --git a/core/dpkg.go b/core/dpkg.go index 97a5f13..52969d0 100644 --- a/core/dpkg.go +++ b/core/dpkg.go @@ -1,9 +1,32 @@ package core -import "fmt" +import ( + "fmt" + "github.com/mitchellh/mapstructure" + "github.com/vanilla-os/vib/api" +) + +type DpkgModule struct { + Name string `json:"name"` + Type string `json:"string"` + Source api.Source +} // BuildDpkgModule builds a module that installs a .deb package -func BuildDpkgModule(module Module) (string, error) { +func BuildDpkgModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { + var module CMakeModule + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return "", err + } + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) + if err != nil { + return "", err + } + err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) + if err != nil { + return "", err + } cmd := "" for _, path := range module.Source.Paths { cmd += fmt.Sprintf(" dpkg -i /sources/%s && apt install -f && ", path) diff --git a/core/go.go b/core/go.go index e674d12..a9437d1 100644 --- a/core/go.go +++ b/core/go.go @@ -1,11 +1,37 @@ package core -import "fmt" +import ( + "fmt" + "github.com/mitchellh/mapstructure" + "github.com/vanilla-os/vib/api" +) + +type GoModule struct { + Name string `json:"name"` + Type string `json:"type"` + Source api.Source + BuildVars map[string]string + BuildFlags string +} // BuildGoModule builds a module that builds a Go project // buildVars are used to customize the build command // like setting the output binary name and location -func BuildGoModule(module Module) (string, error) { +func BuildGoModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { + var module GoModule + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return "", err + } + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) + if err != nil { + return "", err + } + err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) + if err != nil { + return "", err + } + buildVars := map[string]string{} for k, v := range module.BuildVars { buildVars[k] = v diff --git a/core/loader.go b/core/loader.go index 8231aa7..4a6330e 100644 --- a/core/loader.go +++ b/core/loader.go @@ -2,6 +2,8 @@ package core import ( "errors" + "github.com/mitchellh/mapstructure" + "github.com/vanilla-os/vib/api" "io" "os" "path/filepath" @@ -12,8 +14,8 @@ import ( // LoadRecipe loads a recipe from a file and returns a Recipe // Does not validate the recipe but it will catch some errors // a proper validation will be done in the future -func LoadRecipe(path string) (*Recipe, error) { - recipe := &Recipe{} +func LoadRecipe(path string) (*api.Recipe, error) { + recipe := &api.Recipe{} // we use the absolute path to the recipe file as the // root path for the recipe and all its files @@ -81,6 +83,10 @@ func LoadRecipe(path string) (*Recipe, error) { return nil, err } + // the plugins directory contains all plugins that vib can load + // and use for unknown modules in the recipe + recipe.PluginPath = filepath.Join(filepath.Dir(recipePath), "plugins") + // the includes directory is the place where we store all the // files to be included in the container, this is useful for // example to include configuration files. Each file must follow @@ -106,36 +112,29 @@ func LoadRecipe(path string) (*Recipe, error) { } } - // here we expand modules of type "gen-modules" - newRecipeModules := []Module{} - - for _, module := range recipe.Modules { - if module.Type == "gen-modules" { // DEPRECATED: use the "includes" module instead - genModulePaths, err := filepath.Glob(filepath.Join(recipe.ParentPath, module.Path, "*.yml")) - if err != nil { - return nil, err - } - - genModules := []Module{} + // here we expand modules of type "includes" + var newRecipeModules []interface{} - for _, genModulePath := range genModulePaths { - genModule, err := GenModule(genModulePath) + for _, moduleInterface := range recipe.Modules { - if err != nil { - return nil, err - } + var module Module + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return nil, err + } - genModules = append(genModules, genModule) + if module.Type == "includes" { + var include IncludesModule + err := mapstructure.Decode(moduleInterface, &include) + if err != nil { + return nil, err } - newRecipeModules = append(newRecipeModules, genModules...) - continue - } else if module.Type == "includes" { - if len(module.Includes) == 0 { + if len(include.Includes) == 0 { return nil, errors.New("includes module must have at least one module to include") } - for _, include := range module.Includes { + for _, include := range include.Includes { includeModule, err := GenModule(filepath.Join(recipe.ParentPath, include+".yml")) if err != nil { return nil, err @@ -147,7 +146,7 @@ func LoadRecipe(path string) (*Recipe, error) { continue } - newRecipeModules = append(newRecipeModules, module) + newRecipeModules = append(newRecipeModules, moduleInterface) } recipe.Modules = newRecipeModules @@ -156,24 +155,24 @@ func LoadRecipe(path string) (*Recipe, error) { } // GenModule generate a Module struct from a module path -func GenModule(modulePath string) (Module, error) { - module := &Module{} +func GenModule(modulePath string) (map[string]interface{}, error) { + var module map[string]interface{} moduleFile, err := os.Open(modulePath) if err != nil { - return *module, err + return module, err } defer moduleFile.Close() moduleYAML, err := io.ReadAll(moduleFile) if err != nil { - return *module, err + return module, err } - err = yaml.Unmarshal(moduleYAML, module) + err = yaml.Unmarshal(moduleYAML, &module) if err != nil { - return *module, err + return module, err } - return *module, nil + return module, nil } diff --git a/core/make.go b/core/make.go index c0adb11..15587d3 100644 --- a/core/make.go +++ b/core/make.go @@ -1,6 +1,31 @@ package core +import ( + "github.com/mitchellh/mapstructure" + "github.com/vanilla-os/vib/api" +) + +type MakeModule struct { + Name string `json:"name"` + Type string `json:"type"` + Source api.Source +} + // BuildMakeModule builds a module that builds a Make project -func BuildMakeModule(module Module) (string, error) { +func BuildMakeModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { + var module MakeModule + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return "", err + } + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) + if err != nil { + return "", err + } + err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) + if err != nil { + return "", err + } + return "cd /sources/" + module.Name + " && make && make install", nil } diff --git a/core/meson.go b/core/meson.go index d46951e..0d5486e 100644 --- a/core/meson.go +++ b/core/meson.go @@ -2,13 +2,34 @@ package core import ( "fmt" + "github.com/mitchellh/mapstructure" + "github.com/vanilla-os/vib/api" "github.com/google/uuid" ) +type MesonModule struct { + Name string + Type string + Source api.Source +} + // BuildMesonModule builds a module that builds a Meson project -func BuildMesonModule(module Module) (string, error) { +func BuildMesonModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { tmpDir := "/tmp/" + uuid.New().String() + var module MesonModule + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return "", err + } + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) + if err != nil { + return "", err + } + err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) + if err != nil { + return "", err + } cmd := fmt.Sprintf( "cd /sources/%s && meson %s && ninja -C %s && ninja -C %s install", diff --git a/core/plugins.go b/core/plugins.go new file mode 100644 index 0000000..dafe7b8 --- /dev/null +++ b/core/plugins.go @@ -0,0 +1,38 @@ +package core + +import ( + "fmt" + "github.com/vanilla-os/vib/api" + "plugin" +) + +var openedPlugins map[string]Plugin + +func LoadPlugin(name string, module interface{}, recipe *api.Recipe) (string, error) { + if openedPlugins == nil { + openedPlugins = make(map[string]Plugin) + } + pluginOpened := false + var buildModule Plugin + buildModule, pluginOpened = openedPlugins[name] + if !pluginOpened { + fmt.Println("Loading new plugin") + buildModule = Plugin{Name: name} + var err error + loadedPlugin, err := plugin.Open(fmt.Sprintf("%s/%s.so", recipe.PluginPath, name)) + if err != nil { + panic(err) + } + buildFunction, err := loadedPlugin.Lookup("BuildModule") + if err != nil { + panic(err) + } + buildModule.BuildFunc = buildFunction.(func(interface{}, *api.Recipe) (string, error)) + buildModule.LoadedPlugin = loadedPlugin + + openedPlugins[name] = buildModule + } + fmt.Printf("Using plugin: %s\n", buildModule.Name) + fmt.Println(buildModule.BuildFunc(module, recipe)) + return buildModule.BuildFunc(module, recipe) +} diff --git a/core/shell.go b/core/shell.go index 4d06508..b292072 100644 --- a/core/shell.go +++ b/core/shell.go @@ -1,8 +1,37 @@ package core -import "errors" +import ( + "errors" + "github.com/mitchellh/mapstructure" + "github.com/vanilla-os/vib/api" + "strings" +) + +type ShellModule struct { + Name string `json:"name"` + Type string `json:"type"` + Source api.Source + Commands []string +} + +func BuildShellModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { + var module ShellModule + err := mapstructure.Decode(moduleInterface, &module) + if err != nil { + return "", err + } + + if strings.TrimSpace(module.Source.Type) != "" { + err := api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) + if err != nil { + return "", err + } + err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) + if err != nil { + return "", err + } + } -func BuildShellModule(module Module) (string, error) { if len(module.Commands) == 0 { return "", errors.New("no commands specified") } diff --git a/core/structs.go b/core/structs.go index 23febcb..78d0e46 100644 --- a/core/structs.go +++ b/core/structs.go @@ -1,48 +1,30 @@ package core -type Recipe struct { - Base string `json:"base"` - Name string - Id string - SingleLayer bool `json:"singlelayer"` - Labels map[string]string `json:"labels"` - Adds map[string]string `json:"adds"` - Args map[string]string `json:"args"` - Runs []string `json:"runs"` - Cmd string `json:"cmd"` - Modules []Module `json:"modules"` - Path string - ParentPath string - DownloadsPath string - SourcesPath string - Containerfile string -} +import ( + "github.com/vanilla-os/vib/api" + "plugin" +) type Module struct { - Name string `json:"name"` - Type string `json:"type"` - Path string `json:"path"` - Source Source `json:"source"` - Modules []Module `json:"modules"` - BuildFlags string `json:"buildflags"` - BuildVars map[string]string `json:"buildvars"` - Commands []string `json:"commands"` - Includes []string `json:"includes"` + Name string `json:"name"` + Type string `json:"type"` + Modules []map[string]interface{} + Content []byte // The entire module unparsed as a []byte, used by plugins } -type Source struct { - URL string `json:"url"` - Checksum string `json:"checksum"` +type IncludesModule struct { + Name string `json:"name"` Type string `json:"type"` - Commit string `json:"commit"` - Tag string `json:"tag"` - Branch string `json:"branch"` - Packages []string `json:"packages"` - Paths []string `json:"paths"` - Module string + Includes []string `json:"includes"` } type ModuleCommand struct { Name string Command string } + +type Plugin struct { + Name string + BuildFunc func(interface{}, *api.Recipe) (string, error) + LoadedPlugin *plugin.Plugin +} diff --git a/go.mod b/go.mod index 21d7a62..c84e767 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,17 @@ module github.com/vanilla-os/vib -go 1.20 +go 1.21 require github.com/spf13/cobra v1.7.0 +require github.com/mitchellh/mapstructure v1.5.0 + require ( github.com/google/uuid v1.3.0 github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/vanilla-os/vib/api v0.0.0 gopkg.in/yaml.v3 v3.0.1 ) + +replace github.com/vanilla-os/vib/api v0.0.0 => ./api/ diff --git a/go.sum b/go.sum index 838df8c..f5b2fd5 100644 --- a/go.sum +++ b/go.sum @@ -3,11 +3,14 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=