Skip to content

Commit

Permalink
#44: Commands for package management
Browse files Browse the repository at this point in the history
  • Loading branch information
iignatevich committed Jun 20, 2024
1 parent 60f7dd0 commit 76c0d34
Show file tree
Hide file tree
Showing 9 changed files with 761 additions and 30 deletions.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ The file format includes the following elements:
- source: The source for the package, including the type of source (Git, HTTP), URL or file path, merge strategy and other metadata.
- dependencies: A list of required dependencies.

List of strategies:
- overwrite-local-file
- remove-extra-local-files
- ignore-extra-package-files
- filter-package-files

Example:

```yaml
Expand All @@ -43,7 +49,13 @@ dependencies:
url: https://github.com/example/compose-example.git
strategy:
- name: remove-extra-local-files
path: path/to/remove-extra-local-files
path:
- path/to/remove-extra-local-files
- name: ignore-extra-package-files
path:
- library/inventories/platform_nodes/configuration/dev.yaml
- library/inventories/platform_nodes/configuration/prod.yaml
- library/inventories/platform_nodes/configuration/whatever.yaml
```
Expand Down
133 changes: 133 additions & 0 deletions compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
package compose

import (
"errors"
"fmt"
"os"
"path/filepath"

"github.com/launchrctl/launchr/pkg/cli"

"github.com/launchrctl/keyring"
"github.com/launchrctl/launchr"
"github.com/launchrctl/launchr/pkg/action"
Expand Down Expand Up @@ -76,6 +80,135 @@ func (p *Plugin) CobraAddCommands(rootCmd *cobra.Command) error {
composeCmd.Flags().BoolVar(&conflictsVerbosity, "conflicts-verbosity", false, "Log files conflicts")
composeCmd.Flags().BoolVar(&clean, "clean", false, "Remove .compose dir on start")
composeCmd.Flags().BoolVar(&interactive, "interactive", true, "Interactive mode allows to submit user credentials during action")

composeDependency := &compose.Dependency{}
strategies := &compose.RawStrategies{}
var createNew bool
var addCmd = &cobra.Command{
Use: "compose:add",
Short: "Add a new package to plasma-compose",
PreRunE: func(cmd *cobra.Command, args []string) error {
return packagePreRunValidate(cmd, args)
},
RunE: func(cmd *cobra.Command, args []string) error {
// Don't show usage help on a runtime error.
cmd.SilenceUsage = true

return compose.AddPackage(createNew, composeDependency, strategies, p.wd)
},
}

var updateCmd = &cobra.Command{
Use: "compose:update",
Short: "Update a plasma-compose package",
PreRunE: func(cmd *cobra.Command, args []string) error {
return packagePreRunValidate(cmd, args)
},
RunE: func(cmd *cobra.Command, args []string) error {
// Don't show usage help on a runtime error.
cmd.SilenceUsage = true

if composeDependency.Name != "" && composeDependency.Source.URL != "" {
return compose.UpdatePackage(composeDependency, strategies, p.wd)
}

return compose.UpdatePackages(p.wd)
},
}

var toDeletePackages []string
var deleteCmd = &cobra.Command{
Use: "compose:delete",
Short: "Remove a package from plasma-compose",
RunE: func(cmd *cobra.Command, args []string) error {
// Don't show usage help on a runtime error.
cmd.SilenceUsage = true

return compose.DeletePackages(toDeletePackages, p.wd)
},
}

addCmd.Flags().BoolVarP(&createNew, "allow-create", "", false, "Create plasma-compose if not exist")
addPackageFlags(addCmd, composeDependency, strategies)
addPackageFlags(updateCmd, composeDependency, strategies)

deleteCmd.Flags().StringSliceVarP(&toDeletePackages, "packages", "", []string{}, "List of packages to remove. Comma separated.")

rootCmd.AddCommand(composeCmd)
rootCmd.AddCommand(addCmd)
rootCmd.AddCommand(updateCmd)
rootCmd.AddCommand(deleteCmd)

return nil
}

func addPackageFlags(cmd *cobra.Command, dependency *compose.Dependency, strategies *compose.RawStrategies) {
cmd.Flags().StringVarP(&dependency.Name, "name", "", "", "Name of the package")
compose.EnumVarP(cmd, &dependency.Source.Type, "type", "", compose.GitType, []string{compose.GitType, compose.HTTPType}, "Type of the package source: git, http (default 'git')")
cmd.Flags().StringVarP(&dependency.Source.Ref, "ref", "", "", "Reference of the package source")
cmd.Flags().StringVarP(&dependency.Source.Tag, "tag", "", "", "Tag of the package source")
cmd.Flags().StringVarP(&dependency.Source.URL, "url", "", "", "URL of the package source")

cmd.Flags().StringSliceVarP(&strategies.Names, "strategy", "", []string{}, "Strategy name")
cmd.Flags().StringSliceVarP(&strategies.Paths, "strategy-path", "", []string{}, "Strategy paths. paths separated by |, strategies are comma separated (path/1|path/2,path/1|path/2")
}

func packagePreRunValidate(cmd *cobra.Command, _ []string) error {
tagChanged := cmd.Flag("tag").Changed
refChanged := cmd.Flag("ref").Changed
if tagChanged && refChanged {
return errors.New("tag and ref cannot be used at the same time")
}

typeFlag, err := cmd.Flags().GetString("type")
if err != nil {
return err
}

if typeFlag == compose.HTTPType {
if tagChanged {
cli.Println("Tag can't be used with HTTP source")
err = cmd.Flags().Set("tag", "")
}
if refChanged {
cli.Println("Ref can't be used with HTTP source")
err = cmd.Flags().Set("ref", "")
}
}

strategyChanged := cmd.Flag("strategy").Changed
pathsChanged := cmd.Flag("strategy-path").Changed
if strategyChanged || pathsChanged {
var strategies []string
var paths []string

strategies, err = cmd.Flags().GetStringSlice("strategy")
if err != nil {
return err
}

paths, err = cmd.Flags().GetStringSlice("strategy-path")
if err != nil {
return err
}

if len(strategies) != len(paths) {
return errors.New("number of strategies and paths must be equal")
}

list := map[string]bool{
compose.StrategyOverwriteLocal: true,
compose.StrategyRemoveExtraLocal: true,
compose.StrategyIgnoreExtraPackage: true,
compose.StrategyFilterPackage: true,
}

for _, strategy := range strategies {
if _, ok := list[strategy]; !ok {
return fmt.Errorf("submitted strategy %s doesn't exist", strategy)
}
}
}

return err
}
26 changes: 19 additions & 7 deletions compose/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ const (
packageStrategy mergeStrategyTarget = 2
)

var (

// StrategyOverwriteLocal string const
StrategyOverwriteLocal = "overwrite-local-file"
// StrategyRemoveExtraLocal string const
StrategyRemoveExtraLocal = "remove-extra-local-files"
// StrategyIgnoreExtraPackage string const
StrategyIgnoreExtraPackage = "ignore-extra-package-files"
// StrategyFilterPackage string const
StrategyFilterPackage = "filter-package-files"
)

// return conflict const (0 - no warning, 1 - conflict with local, 2 conflict with pacakge)

func retrieveStrategies(packages []*Package) ([]*mergeStrategy, map[string][]*mergeStrategy) {
Expand Down Expand Up @@ -75,14 +87,14 @@ func identifyStrategy(name string) (mergeStrategyType, mergeStrategyTarget) {
t := packageStrategy

switch name {
case "overwrite-local-file":
case StrategyOverwriteLocal:
s = overwriteLocalFile
case "remove-extra-local-files":
case StrategyRemoveExtraLocal:
s = removeExtraLocalFiles
t = localStrategy
case "ignore-extra-package-files":
case StrategyIgnoreExtraPackage:
s = ignoreExtraPackageFiles
case "filter-package-files":
case StrategyFilterPackage:
s = filterPackageFiles
}

Expand Down Expand Up @@ -323,7 +335,7 @@ func addStrategyEntries(strategies []*mergeStrategy, entriesTree []*fsEntry, ent
for _, ms := range strategies {
switch ms.s {
case overwriteLocalFile:
// Skip strategy if filepath does not match strategy paths
// Skip strategy if filepath does not match strategy Paths
if !ensureStrategyPrefixPath(path, ms.paths) {
continue
}
Expand All @@ -336,7 +348,7 @@ func addStrategyEntries(strategies []*mergeStrategy, entriesTree []*fsEntry, ent
localMapEntry.Entry = entry.Entry
localMapEntry.From = entry.From

// Strategy replaces local paths by package one.
// Strategy replaces local Paths by package one.
conflictResolve = resolveToPackage
}
case filterPackageFiles:
Expand All @@ -346,7 +358,7 @@ func addStrategyEntries(strategies []*mergeStrategy, entriesTree []*fsEntry, ent
}

case ignoreExtraPackageFiles:
// Skip strategy if filepath does not match strategy paths
// Skip strategy if filepath does not match strategy Paths
if !ensureStrategyPrefixPath(path, ms.paths) {
continue
}
Expand Down
46 changes: 46 additions & 0 deletions compose/cobra.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package compose

import (
"fmt"

"github.com/spf13/cobra"
)

// EnumValue struct to hold enumeration.
type EnumValue struct {
Enum []string
defaultValue string
value *string
}

// Set sets EnumValue.
func (e *EnumValue) Set(value string) error {
for _, allowed := range e.Enum {
if value == allowed {
*e.value = value
return nil
}
}
return fmt.Errorf("expected one of %v, got '%s'", e.Enum, value)
}

// Type returns a string name for this EnumValue type
func (e *EnumValue) Type() string {
return "string"
}

func (e *EnumValue) String() string {
return *e.value
}

// EnumVarP adds custom enum flag to cobra command.
func EnumVarP(cmd *cobra.Command, value *string, name, shorthand string, defaultValue string, enumValues []string, usage string) {
ev := &EnumValue{
Enum: enumValues,
defaultValue: defaultValue,
value: value,
}

*ev.value = ev.defaultValue
cmd.Flags().VarP(ev, name, shorthand, usage)
}
16 changes: 11 additions & 5 deletions compose/downloadManager.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import (
)

const (
gitType = "git"
httpType = "http"
// GitType is const for GIT source type download.
GitType = "git"
// SourceReferenceTag represents git tag source.
SourceReferenceTag = "tag"
// SourceReferenceBranch represents git branch source.
SourceReferenceBranch = "ref"
// HTTPType is const for http source type download.
HTTPType = "http"
)

// Downloader interface
Expand All @@ -31,9 +37,9 @@ func CreateDownloadManager(keyring *keyringWrapper) DownloadManager {

func getDownloaderForPackage(downloadType string) Downloader {
switch {
case downloadType == gitType:
case downloadType == GitType:
return newGit()
case downloadType == httpType:
case downloadType == HTTPType:
return newHTTP()
default:
return newGit()
Expand Down Expand Up @@ -115,7 +121,7 @@ func downloadPackage(pkg *Package, targetDir string, kw *keyringWrapper) error {
}

// temporary
if dtype := pkg.GetType(); dtype == httpType {
if dtype := pkg.GetType(); dtype == HTTPType {
downloadPath = packagePath
}

Expand Down
Loading

0 comments on commit 76c0d34

Please sign in to comment.