mirror of
https://github.com/chenasraf/sofmani.git
synced 2026-05-17 17:28:04 +00:00
refactor: use samber/lo wherever possible
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/chenasraf/sofmani/platform"
|
||||
"github.com/chenasraf/sofmani/utils"
|
||||
"github.com/eschao/config"
|
||||
"github.com/samber/lo"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -76,10 +77,7 @@ type AppConfigDefaults struct {
|
||||
|
||||
// GetCategoryDisplay returns the effective category display mode, defaulting to "border".
|
||||
func (c *AppConfig) GetCategoryDisplay() CategoryDisplayMode {
|
||||
if c.CategoryDisplay != nil {
|
||||
return *c.CategoryDisplay
|
||||
}
|
||||
return CategoryDisplayBorder
|
||||
return lo.FromPtrOr(c.CategoryDisplay, CategoryDisplayBorder)
|
||||
}
|
||||
|
||||
// Environ returns the combined environment variables as a slice of strings.
|
||||
@@ -172,21 +170,9 @@ func tryConfigDir(dir string) string {
|
||||
// GetConfigDesc returns a string slice describing the current configuration.
|
||||
func (c *AppConfig) GetConfigDesc() []string {
|
||||
desc := []string{}
|
||||
isDebug := false
|
||||
if c.Debug != nil {
|
||||
isDebug = *c.Debug
|
||||
}
|
||||
checkUpdates := false
|
||||
if c.CheckUpdates != nil {
|
||||
checkUpdates = *c.CheckUpdates
|
||||
}
|
||||
showSummary := true // default is enabled
|
||||
if c.Summary != nil {
|
||||
showSummary = *c.Summary
|
||||
}
|
||||
desc = append(desc, fmt.Sprintf("Debug: %t", isDebug))
|
||||
desc = append(desc, fmt.Sprintf("CheckUpdates: %t", checkUpdates))
|
||||
desc = append(desc, fmt.Sprintf("Summary: %t", showSummary))
|
||||
desc = append(desc, fmt.Sprintf("Debug: %t", lo.FromPtrOr(c.Debug, false)))
|
||||
desc = append(desc, fmt.Sprintf("CheckUpdates: %t", lo.FromPtrOr(c.CheckUpdates, false)))
|
||||
desc = append(desc, fmt.Sprintf("Summary: %t", lo.FromPtrOr(c.Summary, true)))
|
||||
|
||||
if c.Env != nil {
|
||||
desc = append(desc, "Environment Variables:")
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/chenasraf/sofmani/platform"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -16,18 +17,18 @@ func TestPlatformMapResolve(t *testing.T) {
|
||||
platform string
|
||||
expected *string
|
||||
}{
|
||||
{"MacOS", "darwin", strPtr("macos")},
|
||||
{"Linux", "linux", strPtr("linux")},
|
||||
{"Windows", "windows", strPtr("windows")},
|
||||
{"MacOS", "darwin", lo.ToPtr("macos")},
|
||||
{"Linux", "linux", lo.ToPtr("linux")},
|
||||
{"Windows", "windows", lo.ToPtr("windows")},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
platform.SetOS(tt.platform)
|
||||
pm := platform.PlatformMap[string]{
|
||||
MacOS: strPtr("macos"),
|
||||
Linux: strPtr("linux"),
|
||||
Windows: strPtr("windows"),
|
||||
MacOS: lo.ToPtr("macos"),
|
||||
Linux: lo.ToPtr("linux"),
|
||||
Windows: lo.ToPtr("windows"),
|
||||
}
|
||||
assert.Equal(t, tt.expected, pm.Resolve())
|
||||
})
|
||||
@@ -135,7 +136,3 @@ func TestFindConfigFile(t *testing.T) {
|
||||
assert.NoError(t, os.Chdir(dir))
|
||||
assert.True(t, strings.HasSuffix(FindConfigFile(), file))
|
||||
}
|
||||
|
||||
func strPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
18
cmd/root.go
18
cmd/root.go
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/chenasraf/sofmani/machine"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -153,26 +154,26 @@ func buildCliConfig(cmd *cobra.Command, args []string) *appconfig.AppCliConfig {
|
||||
|
||||
// Handle debug flag
|
||||
if cmd.Flags().Changed("debug") {
|
||||
config.Debug = boolPtr(true)
|
||||
config.Debug = lo.ToPtr(true)
|
||||
}
|
||||
if cmd.Flags().Changed("no-debug") {
|
||||
config.Debug = boolPtr(false)
|
||||
config.Debug = lo.ToPtr(false)
|
||||
}
|
||||
|
||||
// Handle update flag
|
||||
if cmd.Flags().Changed("update") {
|
||||
config.CheckUpdates = boolPtr(true)
|
||||
config.CheckUpdates = lo.ToPtr(true)
|
||||
}
|
||||
if cmd.Flags().Changed("no-update") {
|
||||
config.CheckUpdates = boolPtr(false)
|
||||
config.CheckUpdates = lo.ToPtr(false)
|
||||
}
|
||||
|
||||
// Handle summary flag
|
||||
if cmd.Flags().Changed("summary") {
|
||||
config.Summary = boolPtr(true)
|
||||
config.Summary = lo.ToPtr(true)
|
||||
}
|
||||
if cmd.Flags().Changed("no-summary") {
|
||||
config.Summary = boolPtr(false)
|
||||
config.Summary = lo.ToPtr(false)
|
||||
}
|
||||
|
||||
// Handle log file flag
|
||||
@@ -201,10 +202,5 @@ func buildCliConfig(cmd *cobra.Command, args []string) *appconfig.AppCliConfig {
|
||||
return config
|
||||
}
|
||||
|
||||
// boolPtr returns a pointer to a boolean value.
|
||||
func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
// RunMain is set by main.go to run the main application logic.
|
||||
var RunMain func(cliConfig *appconfig.AppCliConfig)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func newAptInstaller(data *appconfig.InstallerData) *AptInstaller {
|
||||
@@ -21,7 +22,7 @@ func TestAptValidation(t *testing.T) {
|
||||
logger.InitLogger(false)
|
||||
aptInstaller := newAptInstaller(
|
||||
&appconfig.InstallerData{
|
||||
Name: strPtr("test-apt"),
|
||||
Name: lo.ToPtr("test-apt"),
|
||||
Type: appconfig.InstallerTypeApt,
|
||||
},
|
||||
)
|
||||
@@ -33,7 +34,7 @@ func TestAptGetOpts(t *testing.T) {
|
||||
|
||||
// Test default opts (no options set)
|
||||
defaultData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeApt,
|
||||
}
|
||||
installer := newAptInstaller(defaultData)
|
||||
@@ -50,7 +51,7 @@ func TestAptGetOpts(t *testing.T) {
|
||||
|
||||
// Test with flags option
|
||||
flagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeApt,
|
||||
Opts: &map[string]any{
|
||||
"flags": "-y --no-install-recommends",
|
||||
@@ -64,7 +65,7 @@ func TestAptGetOpts(t *testing.T) {
|
||||
|
||||
// Test with install_flags option
|
||||
installFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeApt,
|
||||
Opts: &map[string]any{
|
||||
"install_flags": "--no-install-recommends",
|
||||
@@ -78,7 +79,7 @@ func TestAptGetOpts(t *testing.T) {
|
||||
|
||||
// Test with update_flags option
|
||||
updateFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeApt,
|
||||
Opts: &map[string]any{
|
||||
"update_flags": "--only-upgrade",
|
||||
@@ -92,7 +93,7 @@ func TestAptGetOpts(t *testing.T) {
|
||||
|
||||
// Test with all flags options combined
|
||||
allFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeApt,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--common",
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -25,14 +26,14 @@ func TestBrewValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid: No tap specified (tap is optional)
|
||||
emptyData := &appconfig.InstallerData{
|
||||
Name: strPtr("test-brew-valid"),
|
||||
Name: lo.ToPtr("test-brew-valid"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
}
|
||||
assertNoValidationErrors(t, newTestBrewInstaller(emptyData).Validate())
|
||||
|
||||
// 🟢 Valid: Well-formed tap (contains slash, sufficient length)
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("test-brew-valid-tap"),
|
||||
Name: lo.ToPtr("test-brew-valid-tap"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{"tap": "valid/tap"},
|
||||
}
|
||||
@@ -40,7 +41,7 @@ func TestBrewValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid: Tap and cask used together
|
||||
tapCaskData := &appconfig.InstallerData{
|
||||
Name: strPtr("test-brew-tap-cask"),
|
||||
Name: lo.ToPtr("test-brew-tap-cask"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{
|
||||
"tap": "homebrew/cask-versions",
|
||||
@@ -51,7 +52,7 @@ func TestBrewValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Invalid: Tap is present but malformed (missing slash or too short)
|
||||
invalidData := &appconfig.InstallerData{
|
||||
Name: strPtr("test-brew-invalid-tap"),
|
||||
Name: lo.ToPtr("test-brew-invalid-tap"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{"tap": "invalid-tap"},
|
||||
}
|
||||
@@ -85,7 +86,7 @@ func TestBrewGetOpts(t *testing.T) {
|
||||
|
||||
// Test default opts (no options set)
|
||||
defaultData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
}
|
||||
installer := newTestBrewInstaller(defaultData)
|
||||
@@ -108,7 +109,7 @@ func TestBrewGetOpts(t *testing.T) {
|
||||
|
||||
// Test with flags option
|
||||
flagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--verbose --debug",
|
||||
@@ -122,7 +123,7 @@ func TestBrewGetOpts(t *testing.T) {
|
||||
|
||||
// Test with install_flags option
|
||||
installFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{
|
||||
"install_flags": "--force",
|
||||
@@ -136,7 +137,7 @@ func TestBrewGetOpts(t *testing.T) {
|
||||
|
||||
// Test with update_flags option
|
||||
updateFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{
|
||||
"update_flags": "--dry-run",
|
||||
@@ -150,7 +151,7 @@ func TestBrewGetOpts(t *testing.T) {
|
||||
|
||||
// Test with all flags options combined
|
||||
allFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{
|
||||
"tap": "homebrew/core",
|
||||
@@ -250,7 +251,7 @@ func TestBrewGetFullName(t *testing.T) {
|
||||
|
||||
t.Run("returns name without tap", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
}
|
||||
installer := newTestBrewInstaller(data)
|
||||
@@ -259,7 +260,7 @@ func TestBrewGetFullName(t *testing.T) {
|
||||
|
||||
t.Run("returns tap/name with tap", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("sofmani"),
|
||||
Name: lo.ToPtr("sofmani"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{
|
||||
"tap": "chenasraf/tap",
|
||||
@@ -275,7 +276,7 @@ func TestBrewIsCask(t *testing.T) {
|
||||
|
||||
t.Run("returns false when cask is not set", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
}
|
||||
installer := newTestBrewInstaller(data)
|
||||
@@ -284,7 +285,7 @@ func TestBrewIsCask(t *testing.T) {
|
||||
|
||||
t.Run("returns false when cask is false", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("firefox"),
|
||||
Name: lo.ToPtr("firefox"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{
|
||||
"cask": false,
|
||||
@@ -296,7 +297,7 @@ func TestBrewIsCask(t *testing.T) {
|
||||
|
||||
t.Run("returns true when cask is true", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("firefox"),
|
||||
Name: lo.ToPtr("firefox"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{
|
||||
"cask": true,
|
||||
@@ -312,7 +313,7 @@ func TestBrewGetBinName(t *testing.T) {
|
||||
|
||||
t.Run("returns name when bin_name is not set", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
}
|
||||
installer := newTestBrewInstaller(data)
|
||||
@@ -322,7 +323,7 @@ func TestBrewGetBinName(t *testing.T) {
|
||||
t.Run("returns bin_name when set", func(t *testing.T) {
|
||||
binName := "nvim"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("neovim"),
|
||||
Name: lo.ToPtr("neovim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
BinName: &binName,
|
||||
}
|
||||
@@ -333,7 +334,7 @@ func TestBrewGetBinName(t *testing.T) {
|
||||
t.Run("returns name when bin_name is empty", func(t *testing.T) {
|
||||
binName := ""
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
BinName: &binName,
|
||||
}
|
||||
@@ -347,7 +348,7 @@ func TestBrewGetData(t *testing.T) {
|
||||
|
||||
t.Run("returns the installer data", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
}
|
||||
installer := newTestBrewInstaller(data)
|
||||
@@ -364,7 +365,7 @@ func TestNewBrewInstaller(t *testing.T) {
|
||||
t.Run("creates installer with config and data", func(t *testing.T) {
|
||||
cfg := &appconfig.AppConfig{}
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
}
|
||||
installer := NewBrewInstaller(cfg, data)
|
||||
@@ -382,7 +383,7 @@ func TestBrewCheckIsInstalled(t *testing.T) {
|
||||
t.Run("runs custom check when provided", func(t *testing.T) {
|
||||
checkCmd := "true"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-brew"),
|
||||
Name: lo.ToPtr("test-brew"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
CheckInstalled: &checkCmd,
|
||||
}
|
||||
@@ -396,7 +397,7 @@ func TestBrewCheckIsInstalled(t *testing.T) {
|
||||
t.Run("runs custom check that fails", func(t *testing.T) {
|
||||
checkCmd := "false"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-brew"),
|
||||
Name: lo.ToPtr("test-brew"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
CheckInstalled: &checkCmd,
|
||||
}
|
||||
@@ -414,7 +415,7 @@ func TestBrewCheckNeedsUpdate(t *testing.T) {
|
||||
t.Run("runs custom check when provided", func(t *testing.T) {
|
||||
checkCmd := "true" // Returns exit code 0, meaning update available
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-brew"),
|
||||
Name: lo.ToPtr("test-brew"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
CheckHasUpdate: &checkCmd,
|
||||
}
|
||||
@@ -428,7 +429,7 @@ func TestBrewCheckNeedsUpdate(t *testing.T) {
|
||||
t.Run("custom check returns false when no update", func(t *testing.T) {
|
||||
checkCmd := "false" // Returns exit code 1, meaning no update
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-brew"),
|
||||
Name: lo.ToPtr("test-brew"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
CheckHasUpdate: &checkCmd,
|
||||
}
|
||||
@@ -445,7 +446,7 @@ func TestBrewGetOptsWrongTypes(t *testing.T) {
|
||||
|
||||
t.Run("handles wrong type values gracefully", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
Opts: &map[string]any{
|
||||
"tap": 123, // Wrong type
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func newTestCargoInstaller(data *appconfig.InstallerData) *CargoInstaller {
|
||||
@@ -22,7 +23,7 @@ func TestCargoValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid cargo installer
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("ripgrep"),
|
||||
Name: lo.ToPtr("ripgrep"),
|
||||
Type: appconfig.InstallerTypeCargo,
|
||||
}
|
||||
assertNoValidationErrors(t, newTestCargoInstaller(validData).Validate())
|
||||
@@ -40,7 +41,7 @@ func TestCargoGetOpts(t *testing.T) {
|
||||
|
||||
// Test default opts (no options set)
|
||||
defaultData := &appconfig.InstallerData{
|
||||
Name: strPtr("ripgrep"),
|
||||
Name: lo.ToPtr("ripgrep"),
|
||||
Type: appconfig.InstallerTypeCargo,
|
||||
}
|
||||
installer := newTestCargoInstaller(defaultData)
|
||||
@@ -57,7 +58,7 @@ func TestCargoGetOpts(t *testing.T) {
|
||||
|
||||
// Test with flags option
|
||||
flagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("ripgrep"),
|
||||
Name: lo.ToPtr("ripgrep"),
|
||||
Type: appconfig.InstallerTypeCargo,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--locked",
|
||||
@@ -71,7 +72,7 @@ func TestCargoGetOpts(t *testing.T) {
|
||||
|
||||
// Test with install_flags option
|
||||
installFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("ripgrep"),
|
||||
Name: lo.ToPtr("ripgrep"),
|
||||
Type: appconfig.InstallerTypeCargo,
|
||||
Opts: &map[string]any{
|
||||
"install_flags": "--features pcre2",
|
||||
@@ -85,7 +86,7 @@ func TestCargoGetOpts(t *testing.T) {
|
||||
|
||||
// Test with update_flags option
|
||||
updateFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("ripgrep"),
|
||||
Name: lo.ToPtr("ripgrep"),
|
||||
Type: appconfig.InstallerTypeCargo,
|
||||
Opts: &map[string]any{
|
||||
"update_flags": "--force",
|
||||
@@ -99,7 +100,7 @@ func TestCargoGetOpts(t *testing.T) {
|
||||
|
||||
// Test with all flags options combined
|
||||
allFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("ripgrep"),
|
||||
Name: lo.ToPtr("ripgrep"),
|
||||
Type: appconfig.InstallerTypeCargo,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--common",
|
||||
@@ -125,7 +126,7 @@ func TestCargoGetBinName(t *testing.T) {
|
||||
|
||||
// Default: uses installer name
|
||||
defaultData := &appconfig.InstallerData{
|
||||
Name: strPtr("ripgrep"),
|
||||
Name: lo.ToPtr("ripgrep"),
|
||||
Type: appconfig.InstallerTypeCargo,
|
||||
}
|
||||
installer := newTestCargoInstaller(defaultData)
|
||||
@@ -135,9 +136,9 @@ func TestCargoGetBinName(t *testing.T) {
|
||||
|
||||
// Override: uses bin_name
|
||||
binNameData := &appconfig.InstallerData{
|
||||
Name: strPtr("ripgrep"),
|
||||
Name: lo.ToPtr("ripgrep"),
|
||||
Type: appconfig.InstallerTypeCargo,
|
||||
BinName: strPtr("rg"),
|
||||
BinName: lo.ToPtr("rg"),
|
||||
}
|
||||
installerWithBinName := newTestCargoInstaller(binNameData)
|
||||
if installerWithBinName.GetBinName() != "rg" {
|
||||
@@ -149,7 +150,7 @@ func TestCargoGetData(t *testing.T) {
|
||||
logger.InitLogger(false)
|
||||
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("ripgrep"),
|
||||
Name: lo.ToPtr("ripgrep"),
|
||||
Type: appconfig.InstallerTypeCargo,
|
||||
}
|
||||
installer := newTestCargoInstaller(data)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -23,17 +24,17 @@ func TestDockerValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid: just name and type
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("ghcr.io/open-webui/open-webui:main"),
|
||||
Name: lo.ToPtr("ghcr.io/open-webui/open-webui:main"),
|
||||
Type: appconfig.InstallerTypeDocker,
|
||||
BinName: strPtr("open-webui"),
|
||||
BinName: lo.ToPtr("open-webui"),
|
||||
}
|
||||
assertNoValidationErrors(t, newTestDockerInstaller(validData).Validate())
|
||||
|
||||
// 🟢 Valid: with flags
|
||||
withFlags := &appconfig.InstallerData{
|
||||
Name: strPtr("ghcr.io/open-webui/open-webui:main"),
|
||||
Name: lo.ToPtr("ghcr.io/open-webui/open-webui:main"),
|
||||
Type: appconfig.InstallerTypeDocker,
|
||||
BinName: strPtr("open-webui"),
|
||||
BinName: lo.ToPtr("open-webui"),
|
||||
Opts: &map[string]any{
|
||||
"flags": "-p 3300:8080 -v open-webui:/data",
|
||||
},
|
||||
|
||||
@@ -22,19 +22,14 @@ func FilterInstaller(installer IInstaller, filters []string) bool {
|
||||
return filter[1:], filter[0] == '!'
|
||||
})
|
||||
|
||||
keep := len(positives) == 0
|
||||
keep := len(positives) == 0 || lo.SomeBy(positives, func(f string) bool {
|
||||
return isFilteredIn(installer, f)
|
||||
})
|
||||
|
||||
for _, f := range positives {
|
||||
if isFilteredIn(installer, f) {
|
||||
keep = true
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, f := range negatives {
|
||||
if isFilteredIn(installer, f) {
|
||||
keep = false
|
||||
break
|
||||
}
|
||||
if keep && lo.SomeBy(negatives, func(f string) bool {
|
||||
return isFilteredIn(installer, f)
|
||||
}) {
|
||||
return false
|
||||
}
|
||||
|
||||
return keep
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -18,7 +19,7 @@ func TestFilterInstaller(t *testing.T) {
|
||||
{
|
||||
name: "No filters",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("tag1")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("tag1")},
|
||||
},
|
||||
filters: []string{},
|
||||
expected: true,
|
||||
@@ -26,7 +27,7 @@ func TestFilterInstaller(t *testing.T) {
|
||||
{
|
||||
name: "Positive filter match",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("tag1")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("tag1")},
|
||||
},
|
||||
filters: []string{"test"},
|
||||
expected: true,
|
||||
@@ -34,7 +35,7 @@ func TestFilterInstaller(t *testing.T) {
|
||||
{
|
||||
name: "Positive filter no match",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("tag1")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("tag1")},
|
||||
},
|
||||
filters: []string{"example"},
|
||||
expected: false,
|
||||
@@ -42,7 +43,7 @@ func TestFilterInstaller(t *testing.T) {
|
||||
{
|
||||
name: "Negative filter match",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("tag1")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("tag1")},
|
||||
},
|
||||
filters: []string{"!test"},
|
||||
expected: false,
|
||||
@@ -50,7 +51,7 @@ func TestFilterInstaller(t *testing.T) {
|
||||
{
|
||||
name: "Negative filter no match",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("tag1")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("tag1")},
|
||||
},
|
||||
filters: []string{"!example"},
|
||||
expected: true,
|
||||
@@ -58,7 +59,7 @@ func TestFilterInstaller(t *testing.T) {
|
||||
{
|
||||
name: "Tag filter match",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("tag1")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("tag1")},
|
||||
},
|
||||
filters: []string{"tag:tag1"},
|
||||
expected: true,
|
||||
@@ -66,7 +67,7 @@ func TestFilterInstaller(t *testing.T) {
|
||||
{
|
||||
name: "Tag filter no match",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("tag1")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("tag1")},
|
||||
},
|
||||
filters: []string{"tag:tag2"},
|
||||
expected: false,
|
||||
@@ -74,7 +75,7 @@ func TestFilterInstaller(t *testing.T) {
|
||||
{
|
||||
name: "Type filter match",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
},
|
||||
filters: []string{"type:brew"},
|
||||
expected: true,
|
||||
@@ -82,7 +83,7 @@ func TestFilterInstaller(t *testing.T) {
|
||||
{
|
||||
name: "Type filter no match",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
},
|
||||
filters: []string{"type:npm"},
|
||||
expected: false,
|
||||
@@ -115,28 +116,28 @@ func TestInstallerIsEnabled(t *testing.T) {
|
||||
{
|
||||
name: "Enabled is true",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("true")},
|
||||
data: &appconfig.InstallerData{Enabled: lo.ToPtr("true")},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Enabled is false",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("false")},
|
||||
data: &appconfig.InstallerData{Enabled: lo.ToPtr("false")},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Enabled is a command that succeeds",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("exit 0")},
|
||||
data: &appconfig.InstallerData{Enabled: lo.ToPtr("exit 0")},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Enabled is a command that fails",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("exit 1")},
|
||||
data: &appconfig.InstallerData{Enabled: lo.ToPtr("exit 1")},
|
||||
},
|
||||
expected: false,
|
||||
expectErr: false,
|
||||
@@ -161,7 +162,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Multiple tags - one matches", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("tag1 tag2 tag3")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("tag1 tag2 tag3")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"tag:tag2"})
|
||||
assert.True(t, result)
|
||||
@@ -169,7 +170,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Multiple tags - none match", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("tag1 tag2 tag3")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("tag1 tag2 tag3")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"tag:tag4"})
|
||||
assert.False(t, result)
|
||||
@@ -177,7 +178,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Mixed positive and negative filters - positive matches, negative matches too", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("dev prod")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("dev prod")},
|
||||
}
|
||||
// Should be excluded because negative filter matches
|
||||
result := FilterInstaller(installer, []string{"tag:dev", "!tag:prod"})
|
||||
@@ -186,7 +187,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Mixed positive and negative filters - only positive matches", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("dev")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("dev")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"tag:dev", "!tag:prod"})
|
||||
assert.True(t, result)
|
||||
@@ -194,7 +195,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Name substring match", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("my-awesome-tool"), Tags: strPtr("")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("my-awesome-tool"), Tags: lo.ToPtr("")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"awesome"})
|
||||
assert.True(t, result)
|
||||
@@ -202,7 +203,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Name exact match", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("vim"), Tags: strPtr("")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("vim"), Tags: lo.ToPtr("")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"vim"})
|
||||
assert.True(t, result)
|
||||
@@ -210,7 +211,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Type filter case insensitive", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"type:BREW"})
|
||||
assert.True(t, result)
|
||||
@@ -218,7 +219,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Multiple positive filters - one matches", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("vim"), Tags: strPtr("editor")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("vim"), Tags: lo.ToPtr("editor")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"neovim", "vim", "emacs"})
|
||||
assert.True(t, result)
|
||||
@@ -226,7 +227,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Multiple positive filters - none match", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("vim"), Tags: strPtr("editor")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("vim"), Tags: lo.ToPtr("editor")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"neovim", "emacs", "nano"})
|
||||
assert.False(t, result)
|
||||
@@ -234,7 +235,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Multiple negative filters - all don't match", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("vim"), Tags: strPtr("editor")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("vim"), Tags: lo.ToPtr("editor")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"!neovim", "!emacs"})
|
||||
assert.True(t, result)
|
||||
@@ -242,7 +243,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Multiple negative filters - one matches", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("vim"), Tags: strPtr("editor")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("vim"), Tags: lo.ToPtr("editor")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"!neovim", "!vim"})
|
||||
assert.False(t, result)
|
||||
@@ -250,7 +251,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Negative type filter", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"!type:npm"})
|
||||
assert.True(t, result)
|
||||
@@ -258,7 +259,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Negative type filter matches", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"!type:brew"})
|
||||
assert.False(t, result)
|
||||
@@ -266,7 +267,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Negative tag filter", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("dev")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("dev")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"!tag:prod"})
|
||||
assert.True(t, result)
|
||||
@@ -274,7 +275,7 @@ func TestFilterInstallerEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Negative tag filter matches", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Tags: strPtr("dev")},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Tags: lo.ToPtr("dev")},
|
||||
}
|
||||
result := FilterInstaller(installer, []string{"!tag:dev"})
|
||||
assert.False(t, result)
|
||||
@@ -286,7 +287,7 @@ func TestInstallerIsEnabledEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Enabled is TRUE (uppercase)", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("TRUE")},
|
||||
data: &appconfig.InstallerData{Enabled: lo.ToPtr("TRUE")},
|
||||
}
|
||||
result, err := InstallerIsEnabled(installer)
|
||||
assert.NoError(t, err)
|
||||
@@ -295,7 +296,7 @@ func TestInstallerIsEnabledEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Enabled is FALSE (uppercase)", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("FALSE")},
|
||||
data: &appconfig.InstallerData{Enabled: lo.ToPtr("FALSE")},
|
||||
}
|
||||
result, err := InstallerIsEnabled(installer)
|
||||
assert.NoError(t, err)
|
||||
@@ -304,7 +305,7 @@ func TestInstallerIsEnabledEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Enabled is True (mixed case)", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("True")},
|
||||
data: &appconfig.InstallerData{Enabled: lo.ToPtr("True")},
|
||||
}
|
||||
result, err := InstallerIsEnabled(installer)
|
||||
assert.NoError(t, err)
|
||||
@@ -313,7 +314,7 @@ func TestInstallerIsEnabledEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Enabled is a command that checks for which", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("which which")},
|
||||
data: &appconfig.InstallerData{Enabled: lo.ToPtr("which which")},
|
||||
}
|
||||
result, err := InstallerIsEnabled(installer)
|
||||
assert.NoError(t, err)
|
||||
@@ -322,7 +323,7 @@ func TestInstallerIsEnabledEdgeCases(t *testing.T) {
|
||||
|
||||
t.Run("Enabled is a command that checks for nonexistent binary", func(t *testing.T) {
|
||||
installer := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("which nonexistent-binary-12345")},
|
||||
data: &appconfig.InstallerData{Enabled: lo.ToPtr("which nonexistent-binary-12345")},
|
||||
}
|
||||
result, err := InstallerIsEnabled(installer)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func newTestGitInstaller(data *appconfig.InstallerData) *GitInstaller {
|
||||
@@ -21,7 +22,7 @@ func TestGitValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid: Both destination and ref are present
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("test-git-valid"),
|
||||
Name: lo.ToPtr("test-git-valid"),
|
||||
Type: appconfig.InstallerTypeGit,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/some/path",
|
||||
@@ -32,7 +33,7 @@ func TestGitValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid: Missing ref
|
||||
missingRefData := &appconfig.InstallerData{
|
||||
Name: strPtr("test-git-missing-ref"),
|
||||
Name: lo.ToPtr("test-git-missing-ref"),
|
||||
Type: appconfig.InstallerTypeGit,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/some/path",
|
||||
@@ -42,7 +43,7 @@ func TestGitValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Invalid: Missing destination
|
||||
missingDestData := &appconfig.InstallerData{
|
||||
Name: strPtr("test-git-missing-destination"),
|
||||
Name: lo.ToPtr("test-git-missing-destination"),
|
||||
Type: appconfig.InstallerTypeGit,
|
||||
Opts: &map[string]any{
|
||||
"ref": "main",
|
||||
@@ -56,7 +57,7 @@ func TestGitGetOpts(t *testing.T) {
|
||||
|
||||
// Test default opts (only destination set)
|
||||
defaultData := &appconfig.InstallerData{
|
||||
Name: strPtr("owner/repo"),
|
||||
Name: lo.ToPtr("owner/repo"),
|
||||
Type: appconfig.InstallerTypeGit,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/some/path",
|
||||
@@ -82,7 +83,7 @@ func TestGitGetOpts(t *testing.T) {
|
||||
|
||||
// Test with flags option
|
||||
flagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("owner/repo"),
|
||||
Name: lo.ToPtr("owner/repo"),
|
||||
Type: appconfig.InstallerTypeGit,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/some/path",
|
||||
@@ -97,7 +98,7 @@ func TestGitGetOpts(t *testing.T) {
|
||||
|
||||
// Test with install_flags option (for git clone)
|
||||
installFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("owner/repo"),
|
||||
Name: lo.ToPtr("owner/repo"),
|
||||
Type: appconfig.InstallerTypeGit,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/some/path",
|
||||
@@ -112,7 +113,7 @@ func TestGitGetOpts(t *testing.T) {
|
||||
|
||||
// Test with update_flags option (for git pull)
|
||||
updateFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("owner/repo"),
|
||||
Name: lo.ToPtr("owner/repo"),
|
||||
Type: appconfig.InstallerTypeGit,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/some/path",
|
||||
@@ -127,7 +128,7 @@ func TestGitGetOpts(t *testing.T) {
|
||||
|
||||
// Test with all options combined
|
||||
allOptsData := &appconfig.InstallerData{
|
||||
Name: strPtr("owner/repo"),
|
||||
Name: lo.ToPtr("owner/repo"),
|
||||
Type: appconfig.InstallerTypeGit,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/some/path",
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/chenasraf/sofmani/platform"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -25,7 +26,7 @@ func TestGitHubReleaseValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("ghr-valid"),
|
||||
Name: lo.ToPtr("ghr-valid"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"repository": "owner/repo",
|
||||
@@ -38,7 +39,7 @@ func TestGitHubReleaseValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Missing repository
|
||||
missingRepo := &appconfig.InstallerData{
|
||||
Name: strPtr("ghr-missing-repo"),
|
||||
Name: lo.ToPtr("ghr-missing-repo"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/some/path",
|
||||
@@ -49,7 +50,7 @@ func TestGitHubReleaseValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Missing download_filename
|
||||
missingDownloadFilename := &appconfig.InstallerData{
|
||||
Name: strPtr("ghr-missing-download"),
|
||||
Name: lo.ToPtr("ghr-missing-download"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"repository": "owner/repo",
|
||||
@@ -60,13 +61,13 @@ func TestGitHubReleaseValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Empty per-platform download_filename
|
||||
emptyPlatformFilename := &appconfig.InstallerData{
|
||||
Name: strPtr("ghr-empty-platform-filename"),
|
||||
Name: lo.ToPtr("ghr-empty-platform-filename"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"repository": "owner/repo",
|
||||
"destination": "/some/path",
|
||||
"download_filename": map[string]*string{
|
||||
string(platform.GetPlatform()): strPtr(""),
|
||||
string(platform.GetPlatform()): lo.ToPtr(""),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -74,7 +75,7 @@ func TestGitHubReleaseValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Invalid strategy
|
||||
invalidStrategy := &appconfig.InstallerData{
|
||||
Name: strPtr("ghr-invalid-strategy"),
|
||||
Name: lo.ToPtr("ghr-invalid-strategy"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"repository": "owner/repo",
|
||||
@@ -91,7 +92,7 @@ func TestGitHubReleaseGetOpts(t *testing.T) {
|
||||
|
||||
t.Run("parses all options correctly", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"repository": "owner/repo",
|
||||
@@ -110,7 +111,7 @@ func TestGitHubReleaseGetOpts(t *testing.T) {
|
||||
|
||||
t.Run("handles nil opts", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: nil,
|
||||
}
|
||||
@@ -124,7 +125,7 @@ func TestGitHubReleaseGetOpts(t *testing.T) {
|
||||
|
||||
t.Run("handles zip strategy", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"strategy": "zip",
|
||||
@@ -138,7 +139,7 @@ func TestGitHubReleaseGetOpts(t *testing.T) {
|
||||
|
||||
t.Run("handles none strategy", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"strategy": "none",
|
||||
@@ -157,7 +158,7 @@ func TestGitHubReleaseGetBinName(t *testing.T) {
|
||||
t.Run("returns bin_name when set", func(t *testing.T) {
|
||||
binName := "custom-bin"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("my-app"),
|
||||
Name: lo.ToPtr("my-app"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
BinName: &binName,
|
||||
}
|
||||
@@ -167,7 +168,7 @@ func TestGitHubReleaseGetBinName(t *testing.T) {
|
||||
|
||||
t.Run("returns base name when bin_name not set", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("owner/my-app"),
|
||||
Name: lo.ToPtr("owner/my-app"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
}
|
||||
installer := newTestGitHubReleaseInstaller(data)
|
||||
@@ -181,7 +182,7 @@ func TestGitHubReleaseGetArchiveBinName(t *testing.T) {
|
||||
t.Run("returns archive_bin_name when set", func(t *testing.T) {
|
||||
binName := "cospend"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("cospend-cli"),
|
||||
Name: lo.ToPtr("cospend-cli"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
BinName: &binName,
|
||||
Opts: &map[string]any{
|
||||
@@ -196,7 +197,7 @@ func TestGitHubReleaseGetArchiveBinName(t *testing.T) {
|
||||
t.Run("falls back to bin_name when archive_bin_name not set", func(t *testing.T) {
|
||||
binName := "custom-bin"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("my-app"),
|
||||
Name: lo.ToPtr("my-app"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
BinName: &binName,
|
||||
}
|
||||
@@ -206,7 +207,7 @@ func TestGitHubReleaseGetArchiveBinName(t *testing.T) {
|
||||
|
||||
t.Run("falls back to name when neither set", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("my-app"),
|
||||
Name: lo.ToPtr("my-app"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
}
|
||||
installer := newTestGitHubReleaseInstaller(data)
|
||||
@@ -219,7 +220,7 @@ func TestGitHubReleaseGetFilename(t *testing.T) {
|
||||
|
||||
t.Run("returns filename for current platform", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"download_filename": "app.tar.gz",
|
||||
@@ -231,7 +232,7 @@ func TestGitHubReleaseGetFilename(t *testing.T) {
|
||||
|
||||
t.Run("returns empty string when not set", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{},
|
||||
}
|
||||
@@ -254,7 +255,7 @@ func TestGitHubReleaseCacheOperations(t *testing.T) {
|
||||
|
||||
t.Run("UpdateCache writes tag to file", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-cache-app"),
|
||||
Name: lo.ToPtr("test-cache-app"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"repository": "owner/repo",
|
||||
@@ -276,7 +277,7 @@ func TestGitHubReleaseCacheOperations(t *testing.T) {
|
||||
|
||||
t.Run("GetCachedTag returns empty for non-existent cache", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("non-existent-app-12345"),
|
||||
Name: lo.ToPtr("non-existent-app-12345"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"repository": "owner/repo",
|
||||
@@ -293,7 +294,7 @@ func TestGitHubReleaseCacheOperations(t *testing.T) {
|
||||
|
||||
t.Run("UpdateCache overwrites existing cache", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-overwrite-app"),
|
||||
Name: lo.ToPtr("test-overwrite-app"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"repository": "owner/repo",
|
||||
@@ -323,7 +324,7 @@ func TestGitHubReleaseGetDestination(t *testing.T) {
|
||||
|
||||
t.Run("returns destination from opts", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/usr/local/bin",
|
||||
@@ -335,7 +336,7 @@ func TestGitHubReleaseGetDestination(t *testing.T) {
|
||||
|
||||
t.Run("returns current directory when destination not set", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{},
|
||||
}
|
||||
@@ -350,7 +351,7 @@ func TestGitHubReleaseGetInstallDir(t *testing.T) {
|
||||
|
||||
t.Run("returns same as destination", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/opt/bin",
|
||||
@@ -376,7 +377,7 @@ func TestGitHubReleaseCheckIsInstalled(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("myapp"),
|
||||
Name: lo.ToPtr("myapp"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"destination": tmpDir,
|
||||
@@ -395,7 +396,7 @@ func TestGitHubReleaseCheckIsInstalled(t *testing.T) {
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("nonexistent-app"),
|
||||
Name: lo.ToPtr("nonexistent-app"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"destination": tmpDir,
|
||||
@@ -411,7 +412,7 @@ func TestGitHubReleaseCheckIsInstalled(t *testing.T) {
|
||||
t.Run("uses custom check when provided", func(t *testing.T) {
|
||||
checkCmd := "true"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("myapp"),
|
||||
Name: lo.ToPtr("myapp"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
CheckInstalled: &checkCmd,
|
||||
}
|
||||
@@ -429,7 +430,7 @@ func TestGitHubReleaseCheckNeedsUpdate(t *testing.T) {
|
||||
t.Run("uses custom check when provided", func(t *testing.T) {
|
||||
checkCmd := "true" // returns success = update needed
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("myapp"),
|
||||
Name: lo.ToPtr("myapp"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
CheckHasUpdate: &checkCmd,
|
||||
}
|
||||
@@ -443,7 +444,7 @@ func TestGitHubReleaseCheckNeedsUpdate(t *testing.T) {
|
||||
t.Run("returns true when no cached tag", func(t *testing.T) {
|
||||
// Use a unique name that won't have a cache file
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("unique-no-cache-app-99999"),
|
||||
Name: lo.ToPtr("unique-no-cache-app-99999"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
Opts: &map[string]any{
|
||||
"repository": "owner/repo",
|
||||
@@ -465,7 +466,7 @@ func TestNewGitHubReleaseInstaller(t *testing.T) {
|
||||
t.Run("creates installer with config and data", func(t *testing.T) {
|
||||
cfg := &appconfig.AppConfig{}
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("test-release"),
|
||||
Name: lo.ToPtr("test-release"),
|
||||
Type: appconfig.InstallerTypeGitHubRelease,
|
||||
}
|
||||
installer := NewGitHubReleaseInstaller(cfg, data)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -23,11 +24,11 @@ func TestGroupValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid: one sub-installer
|
||||
validStep := appconfig.InstallerData{
|
||||
Name: strPtr("child-installer"),
|
||||
Name: lo.ToPtr("child-installer"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
}
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("group-valid"),
|
||||
Name: lo.ToPtr("group-valid"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
Steps: &[]appconfig.InstallerData{validStep},
|
||||
}
|
||||
@@ -35,7 +36,7 @@ func TestGroupValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Invalid: empty steps slice
|
||||
emptySteps := &appconfig.InstallerData{
|
||||
Name: strPtr("group-empty"),
|
||||
Name: lo.ToPtr("group-empty"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
Steps: &[]appconfig.InstallerData{},
|
||||
}
|
||||
@@ -43,7 +44,7 @@ func TestGroupValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Invalid: nil steps
|
||||
nilSteps := &appconfig.InstallerData{
|
||||
Name: strPtr("group-nil"),
|
||||
Name: lo.ToPtr("group-nil"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
Steps: nil,
|
||||
}
|
||||
@@ -55,7 +56,7 @@ func TestGroupGetData(t *testing.T) {
|
||||
|
||||
t.Run("returns the installer data", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("group-test"),
|
||||
Name: lo.ToPtr("group-test"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
}
|
||||
installer := newTestGroupInstaller(data)
|
||||
@@ -71,7 +72,7 @@ func TestGroupGetOpts(t *testing.T) {
|
||||
|
||||
t.Run("returns empty opts", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("group-test"),
|
||||
Name: lo.ToPtr("group-test"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
}
|
||||
installer := newTestGroupInstaller(data)
|
||||
@@ -86,7 +87,7 @@ func TestGroupGetBinName(t *testing.T) {
|
||||
|
||||
t.Run("returns name when bin_name is not set", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("my-group"),
|
||||
Name: lo.ToPtr("my-group"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
}
|
||||
installer := newTestGroupInstaller(data)
|
||||
@@ -96,7 +97,7 @@ func TestGroupGetBinName(t *testing.T) {
|
||||
t.Run("returns bin_name when set", func(t *testing.T) {
|
||||
binName := "custom-bin"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("my-group"),
|
||||
Name: lo.ToPtr("my-group"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
BinName: &binName,
|
||||
}
|
||||
@@ -107,7 +108,7 @@ func TestGroupGetBinName(t *testing.T) {
|
||||
t.Run("returns name when bin_name is empty", func(t *testing.T) {
|
||||
binName := ""
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("my-group"),
|
||||
Name: lo.ToPtr("my-group"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
BinName: &binName,
|
||||
}
|
||||
@@ -122,7 +123,7 @@ func TestGroupCheckIsInstalled(t *testing.T) {
|
||||
t.Run("runs custom check when provided", func(t *testing.T) {
|
||||
checkCmd := "true"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("group-test"),
|
||||
Name: lo.ToPtr("group-test"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
CheckInstalled: &checkCmd,
|
||||
}
|
||||
@@ -136,7 +137,7 @@ func TestGroupCheckIsInstalled(t *testing.T) {
|
||||
t.Run("runs custom check that fails", func(t *testing.T) {
|
||||
checkCmd := "false"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("group-test"),
|
||||
Name: lo.ToPtr("group-test"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
CheckInstalled: &checkCmd,
|
||||
}
|
||||
@@ -153,7 +154,7 @@ func TestGroupCheckNeedsUpdate(t *testing.T) {
|
||||
|
||||
t.Run("returns true when no custom check", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("group-test"),
|
||||
Name: lo.ToPtr("group-test"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
}
|
||||
installer := newTestGroupInstaller(data)
|
||||
@@ -166,7 +167,7 @@ func TestGroupCheckNeedsUpdate(t *testing.T) {
|
||||
t.Run("runs custom check when provided", func(t *testing.T) {
|
||||
checkCmd := "false" // Returns exit code 1, meaning no update
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("group-test"),
|
||||
Name: lo.ToPtr("group-test"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
CheckHasUpdate: &checkCmd,
|
||||
}
|
||||
@@ -184,7 +185,7 @@ func TestNewGroupInstaller(t *testing.T) {
|
||||
t.Run("creates installer with config and data", func(t *testing.T) {
|
||||
cfg := &appconfig.AppConfig{}
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("group-test"),
|
||||
Name: lo.ToPtr("group-test"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
}
|
||||
installer := NewGroupInstaller(cfg, data)
|
||||
@@ -200,15 +201,15 @@ func TestGroupValidationWithMultipleSteps(t *testing.T) {
|
||||
|
||||
t.Run("valid with multiple steps", func(t *testing.T) {
|
||||
step1 := appconfig.InstallerData{
|
||||
Name: strPtr("step1"),
|
||||
Name: lo.ToPtr("step1"),
|
||||
Type: appconfig.InstallerTypeBrew,
|
||||
}
|
||||
step2 := appconfig.InstallerData{
|
||||
Name: strPtr("step2"),
|
||||
Name: lo.ToPtr("step2"),
|
||||
Type: appconfig.InstallerTypeShell,
|
||||
}
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("group-multi"),
|
||||
Name: lo.ToPtr("group-multi"),
|
||||
Type: appconfig.InstallerTypeGroup,
|
||||
Steps: &[]appconfig.InstallerData{step1, step2},
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -32,7 +33,7 @@ func TestInstallerWithDefaults(t *testing.T) {
|
||||
func TestRunInstaller(t *testing.T) {
|
||||
config := &appconfig.AppConfig{}
|
||||
mockInstaller := &MockInstaller{
|
||||
data: &appconfig.InstallerData{Name: strPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
data: &appconfig.InstallerData{Name: lo.ToPtr("test"), Type: appconfig.InstallerTypeBrew},
|
||||
isInstalled: false,
|
||||
}
|
||||
result, err := RunInstaller(config, mockInstaller)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -23,7 +24,7 @@ func TestManifestValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-valid"),
|
||||
Name: lo.ToPtr("manifest-valid"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
Opts: &map[string]any{
|
||||
"source": "https://example.com/repo.git",
|
||||
@@ -35,7 +36,7 @@ func TestManifestValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Missing source
|
||||
missingSource := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-missing-source"),
|
||||
Name: lo.ToPtr("manifest-missing-source"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
Opts: &map[string]any{
|
||||
"path": "some/path",
|
||||
@@ -45,7 +46,7 @@ func TestManifestValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Missing path
|
||||
missingPath := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-missing-path"),
|
||||
Name: lo.ToPtr("manifest-missing-path"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
Opts: &map[string]any{
|
||||
"source": "https://example.com/repo.git",
|
||||
@@ -55,7 +56,7 @@ func TestManifestValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Empty ref (not nil, just empty)
|
||||
emptyRef := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-empty-ref"),
|
||||
Name: lo.ToPtr("manifest-empty-ref"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
Opts: &map[string]any{
|
||||
"source": "https://example.com/repo.git",
|
||||
@@ -67,7 +68,7 @@ func TestManifestValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Nil opts
|
||||
nilOpts := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-nil-opts"),
|
||||
Name: lo.ToPtr("manifest-nil-opts"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
Opts: nil,
|
||||
}
|
||||
@@ -79,7 +80,7 @@ func TestManifestGetOpts(t *testing.T) {
|
||||
|
||||
t.Run("returns all opts when set", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
Opts: &map[string]any{
|
||||
"source": "https://github.com/user/repo.git",
|
||||
@@ -100,7 +101,7 @@ func TestManifestGetOpts(t *testing.T) {
|
||||
|
||||
t.Run("returns nil fields when opts is nil", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
Opts: nil,
|
||||
}
|
||||
@@ -114,7 +115,7 @@ func TestManifestGetOpts(t *testing.T) {
|
||||
|
||||
t.Run("handles partial opts", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
Opts: &map[string]any{
|
||||
"source": "https://github.com/user/repo.git",
|
||||
@@ -131,7 +132,7 @@ func TestManifestGetOpts(t *testing.T) {
|
||||
|
||||
t.Run("handles wrong type values gracefully", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
Opts: &map[string]any{
|
||||
"source": 123, // Wrong type
|
||||
@@ -154,7 +155,7 @@ func TestManifestGetData(t *testing.T) {
|
||||
|
||||
t.Run("returns the installer data", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
}
|
||||
installer := newTestManifestInstaller(data)
|
||||
@@ -170,7 +171,7 @@ func TestManifestCheckIsInstalled(t *testing.T) {
|
||||
|
||||
t.Run("returns false when no custom check", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
}
|
||||
installer := newTestManifestInstaller(data)
|
||||
@@ -183,7 +184,7 @@ func TestManifestCheckIsInstalled(t *testing.T) {
|
||||
t.Run("runs custom check when provided", func(t *testing.T) {
|
||||
checkCmd := "true"
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
CheckInstalled: &checkCmd,
|
||||
}
|
||||
@@ -200,7 +201,7 @@ func TestManifestCheckNeedsUpdate(t *testing.T) {
|
||||
|
||||
t.Run("returns true when no custom check", func(t *testing.T) {
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
}
|
||||
installer := newTestManifestInstaller(data)
|
||||
@@ -213,7 +214,7 @@ func TestManifestCheckNeedsUpdate(t *testing.T) {
|
||||
t.Run("runs custom check when provided", func(t *testing.T) {
|
||||
checkCmd := "false" // Returns exit code 1, meaning no update
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
CheckHasUpdate: &checkCmd,
|
||||
}
|
||||
@@ -231,7 +232,7 @@ func TestNewManifestInstaller(t *testing.T) {
|
||||
t.Run("creates installer with config and data", func(t *testing.T) {
|
||||
cfg := &appconfig.AppConfig{}
|
||||
data := &appconfig.InstallerData{
|
||||
Name: strPtr("manifest-test"),
|
||||
Name: lo.ToPtr("manifest-test"),
|
||||
Type: appconfig.InstallerTypeManifest,
|
||||
}
|
||||
installer := NewManifestInstaller(cfg, data)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func newTestNpmInstaller(data *appconfig.InstallerData) *NpmInstaller {
|
||||
@@ -23,7 +24,7 @@ func TestNpmValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid npm installer
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("some-npm-package"),
|
||||
Name: lo.ToPtr("some-npm-package"),
|
||||
Type: appconfig.InstallerTypeNpm,
|
||||
}
|
||||
assertNoValidationErrors(t, newTestNpmInstaller(validData).Validate())
|
||||
@@ -41,7 +42,7 @@ func TestNpmGetOpts(t *testing.T) {
|
||||
|
||||
// Test default opts (no options set)
|
||||
defaultData := &appconfig.InstallerData{
|
||||
Name: strPtr("prettier"),
|
||||
Name: lo.ToPtr("prettier"),
|
||||
Type: appconfig.InstallerTypeNpm,
|
||||
}
|
||||
installer := newTestNpmInstaller(defaultData)
|
||||
@@ -58,7 +59,7 @@ func TestNpmGetOpts(t *testing.T) {
|
||||
|
||||
// Test with flags option
|
||||
flagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("prettier"),
|
||||
Name: lo.ToPtr("prettier"),
|
||||
Type: appconfig.InstallerTypeNpm,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--legacy-peer-deps",
|
||||
@@ -72,7 +73,7 @@ func TestNpmGetOpts(t *testing.T) {
|
||||
|
||||
// Test with install_flags option
|
||||
installFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("prettier"),
|
||||
Name: lo.ToPtr("prettier"),
|
||||
Type: appconfig.InstallerTypeNpm,
|
||||
Opts: &map[string]any{
|
||||
"install_flags": "--save-exact",
|
||||
@@ -86,7 +87,7 @@ func TestNpmGetOpts(t *testing.T) {
|
||||
|
||||
// Test with update_flags option
|
||||
updateFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("prettier"),
|
||||
Name: lo.ToPtr("prettier"),
|
||||
Type: appconfig.InstallerTypeNpm,
|
||||
Opts: &map[string]any{
|
||||
"update_flags": "--force",
|
||||
@@ -100,7 +101,7 @@ func TestNpmGetOpts(t *testing.T) {
|
||||
|
||||
// Test with all flags options combined
|
||||
allFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("prettier"),
|
||||
Name: lo.ToPtr("prettier"),
|
||||
Type: appconfig.InstallerTypeNpm,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--common",
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func newTestPacmanInstaller(data *appconfig.InstallerData) *PacmanInstaller {
|
||||
@@ -34,14 +35,14 @@ func TestPacmanValidation(t *testing.T) {
|
||||
|
||||
// Valid pacman installer
|
||||
validPacmanData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
}
|
||||
assertNoValidationErrors(t, newTestPacmanInstaller(validPacmanData).Validate())
|
||||
|
||||
// Valid yay installer
|
||||
validYayData := &appconfig.InstallerData{
|
||||
Name: strPtr("visual-studio-code-bin"),
|
||||
Name: lo.ToPtr("visual-studio-code-bin"),
|
||||
Type: appconfig.InstallerTypeYay,
|
||||
}
|
||||
assertNoValidationErrors(t, newTestYayInstaller(validYayData).Validate())
|
||||
@@ -59,7 +60,7 @@ func TestPacmanGetBinName(t *testing.T) {
|
||||
|
||||
// Test default bin name (uses package name)
|
||||
defaultBinData := &appconfig.InstallerData{
|
||||
Name: strPtr("neovim"),
|
||||
Name: lo.ToPtr("neovim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
}
|
||||
installer := newTestPacmanInstaller(defaultBinData)
|
||||
@@ -70,7 +71,7 @@ func TestPacmanGetBinName(t *testing.T) {
|
||||
// Test custom bin name
|
||||
customBinName := "nvim"
|
||||
customBinData := &appconfig.InstallerData{
|
||||
Name: strPtr("neovim"),
|
||||
Name: lo.ToPtr("neovim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
BinName: &customBinName,
|
||||
}
|
||||
@@ -85,7 +86,7 @@ func TestPacmanGetOpts(t *testing.T) {
|
||||
|
||||
// Test default opts (no options set)
|
||||
defaultData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
}
|
||||
installer := newTestPacmanInstaller(defaultData)
|
||||
@@ -105,7 +106,7 @@ func TestPacmanGetOpts(t *testing.T) {
|
||||
|
||||
// Test with needed option set to true
|
||||
neededData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
Opts: &map[string]any{
|
||||
"needed": true,
|
||||
@@ -119,7 +120,7 @@ func TestPacmanGetOpts(t *testing.T) {
|
||||
|
||||
// Test with needed option set to false
|
||||
notNeededData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
Opts: &map[string]any{
|
||||
"needed": false,
|
||||
@@ -133,7 +134,7 @@ func TestPacmanGetOpts(t *testing.T) {
|
||||
|
||||
// Test with flags option
|
||||
flagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--asdeps --overwrite '*'",
|
||||
@@ -147,7 +148,7 @@ func TestPacmanGetOpts(t *testing.T) {
|
||||
|
||||
// Test with install_flags option
|
||||
installFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
Opts: &map[string]any{
|
||||
"install_flags": "--asdeps",
|
||||
@@ -161,7 +162,7 @@ func TestPacmanGetOpts(t *testing.T) {
|
||||
|
||||
// Test with update_flags option
|
||||
updateFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
Opts: &map[string]any{
|
||||
"update_flags": "--ignore vim",
|
||||
@@ -175,7 +176,7 @@ func TestPacmanGetOpts(t *testing.T) {
|
||||
|
||||
// Test with all flags options combined
|
||||
allFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("vim"),
|
||||
Name: lo.ToPtr("vim"),
|
||||
Type: appconfig.InstallerTypePacman,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--common",
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func newTestPipxInstaller(data *appconfig.InstallerData) *PipxInstaller {
|
||||
@@ -22,7 +23,7 @@ func TestPipxValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid pipx installer
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("some-pipx-package"),
|
||||
Name: lo.ToPtr("some-pipx-package"),
|
||||
Type: appconfig.InstallerTypePipx,
|
||||
}
|
||||
assertNoValidationErrors(t, newTestPipxInstaller(validData).Validate())
|
||||
@@ -40,7 +41,7 @@ func TestPipxGetOpts(t *testing.T) {
|
||||
|
||||
// Test default opts (no options set)
|
||||
defaultData := &appconfig.InstallerData{
|
||||
Name: strPtr("black"),
|
||||
Name: lo.ToPtr("black"),
|
||||
Type: appconfig.InstallerTypePipx,
|
||||
}
|
||||
installer := newTestPipxInstaller(defaultData)
|
||||
@@ -57,7 +58,7 @@ func TestPipxGetOpts(t *testing.T) {
|
||||
|
||||
// Test with flags option
|
||||
flagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("black"),
|
||||
Name: lo.ToPtr("black"),
|
||||
Type: appconfig.InstallerTypePipx,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--verbose",
|
||||
@@ -71,7 +72,7 @@ func TestPipxGetOpts(t *testing.T) {
|
||||
|
||||
// Test with install_flags option
|
||||
installFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("black"),
|
||||
Name: lo.ToPtr("black"),
|
||||
Type: appconfig.InstallerTypePipx,
|
||||
Opts: &map[string]any{
|
||||
"install_flags": "--python python3.11",
|
||||
@@ -85,7 +86,7 @@ func TestPipxGetOpts(t *testing.T) {
|
||||
|
||||
// Test with update_flags option
|
||||
updateFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("black"),
|
||||
Name: lo.ToPtr("black"),
|
||||
Type: appconfig.InstallerTypePipx,
|
||||
Opts: &map[string]any{
|
||||
"update_flags": "--force",
|
||||
@@ -99,7 +100,7 @@ func TestPipxGetOpts(t *testing.T) {
|
||||
|
||||
// Test with all flags options combined
|
||||
allFlagsData := &appconfig.InstallerData{
|
||||
Name: strPtr("black"),
|
||||
Name: lo.ToPtr("black"),
|
||||
Type: appconfig.InstallerTypePipx,
|
||||
Opts: &map[string]any{
|
||||
"flags": "--common",
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func newTestRsyncInstaller(data *appconfig.InstallerData) *RsyncInstaller {
|
||||
@@ -22,7 +23,7 @@ func TestRsyncValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid rsync config
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("rsync-valid"),
|
||||
Name: lo.ToPtr("rsync-valid"),
|
||||
Type: appconfig.InstallerTypeRsync,
|
||||
Opts: &map[string]any{
|
||||
"source": "/path/from",
|
||||
@@ -34,7 +35,7 @@ func TestRsyncValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Missing source
|
||||
missingSource := &appconfig.InstallerData{
|
||||
Name: strPtr("rsync-missing-source"),
|
||||
Name: lo.ToPtr("rsync-missing-source"),
|
||||
Type: appconfig.InstallerTypeRsync,
|
||||
Opts: &map[string]any{
|
||||
"destination": "/path/to",
|
||||
@@ -44,7 +45,7 @@ func TestRsyncValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Missing destination
|
||||
missingDest := &appconfig.InstallerData{
|
||||
Name: strPtr("rsync-missing-destination"),
|
||||
Name: lo.ToPtr("rsync-missing-destination"),
|
||||
Type: appconfig.InstallerTypeRsync,
|
||||
Opts: &map[string]any{
|
||||
"source": "/path/from",
|
||||
@@ -55,7 +56,7 @@ func TestRsyncValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Empty flags string
|
||||
emptyFlags := &appconfig.InstallerData{
|
||||
Name: strPtr("rsync-empty-flags"),
|
||||
Name: lo.ToPtr("rsync-empty-flags"),
|
||||
Type: appconfig.InstallerTypeRsync,
|
||||
Opts: &map[string]any{
|
||||
"source": "/path/from",
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func newTestShellInstaller(data *appconfig.InstallerData) *ShellInstaller {
|
||||
@@ -20,7 +21,7 @@ func TestShellValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid shell config
|
||||
validData := &appconfig.InstallerData{
|
||||
Name: strPtr("shell-valid"),
|
||||
Name: lo.ToPtr("shell-valid"),
|
||||
Type: appconfig.InstallerTypeShell,
|
||||
Opts: &map[string]any{
|
||||
"command": "echo install",
|
||||
@@ -31,7 +32,7 @@ func TestShellValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Missing command
|
||||
missingCommand := &appconfig.InstallerData{
|
||||
Name: strPtr("shell-missing-command"),
|
||||
Name: lo.ToPtr("shell-missing-command"),
|
||||
Type: appconfig.InstallerTypeShell,
|
||||
Opts: &map[string]any{
|
||||
"update_command": "echo update",
|
||||
@@ -41,7 +42,7 @@ func TestShellValidation(t *testing.T) {
|
||||
|
||||
// 🟢 Valid - missing update_command
|
||||
missingUpdate := &appconfig.InstallerData{
|
||||
Name: strPtr("shell-missing-update"),
|
||||
Name: lo.ToPtr("shell-missing-update"),
|
||||
Type: appconfig.InstallerTypeShell,
|
||||
Opts: &map[string]any{
|
||||
"command": "echo install",
|
||||
@@ -51,7 +52,7 @@ func TestShellValidation(t *testing.T) {
|
||||
|
||||
// 🔴 Missing both
|
||||
missingBoth := &appconfig.InstallerData{
|
||||
Name: strPtr("shell-missing-both"),
|
||||
Name: lo.ToPtr("shell-missing-both"),
|
||||
Type: appconfig.InstallerTypeShell,
|
||||
Opts: &map[string]any{},
|
||||
}
|
||||
|
||||
@@ -70,11 +70,6 @@ func (m *MockInstaller) GetTemplateVars() *TemplateVars {
|
||||
return m.templateVars
|
||||
}
|
||||
|
||||
// strPtr returns a pointer to the given string.
|
||||
func strPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
// simulateBrewCheck simulates parsing output from `brew outdated --json`
|
||||
// along with handling the exit code semantics.
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package machine
|
||||
|
||||
import "github.com/samber/lo"
|
||||
|
||||
// Machines defines which machines a configuration applies to.
|
||||
type Machines struct {
|
||||
// Only specifies a list of machine IDs or aliases where the configuration should apply.
|
||||
@@ -28,20 +30,12 @@ func (m *Machines) GetShouldRunOnMachine(machineID string, aliases map[string]st
|
||||
|
||||
// containsMachineID checks if the machine ID is in the list, resolving aliases first.
|
||||
func containsMachineID(list []string, machineID string, aliases map[string]string) bool {
|
||||
for _, entry := range list {
|
||||
// First, try to resolve as an alias
|
||||
return lo.SomeBy(list, func(entry string) bool {
|
||||
if aliases != nil {
|
||||
if resolvedID, ok := aliases[entry]; ok {
|
||||
if resolvedID == machineID {
|
||||
return true
|
||||
}
|
||||
continue
|
||||
return resolvedID == machineID
|
||||
}
|
||||
}
|
||||
// Fall back to treating entry as a literal machine ID
|
||||
if entry == machineID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return entry == machineID
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"slices"
|
||||
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
var osValue string = runtime.GOOS // osValue stores the current operating system.
|
||||
@@ -179,16 +181,11 @@ func (p *Platforms) GetShouldRunOnOS(curOS Platform) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// strPtr returns a pointer to a string.
|
||||
func strPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
// DockerOSMap is a PlatformMap that defines the Docker OS for each platform.
|
||||
var DockerOSMap = PlatformMap[string]{
|
||||
MacOS: strPtr("linux"),
|
||||
Linux: strPtr("linux"),
|
||||
Windows: strPtr("windows"),
|
||||
MacOS: lo.ToPtr("linux"),
|
||||
Linux: lo.ToPtr("linux"),
|
||||
Windows: lo.ToPtr("windows"),
|
||||
}
|
||||
|
||||
// ParsePlatformSingleValue creates a new PlatformMap with the value for all platforms
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -171,16 +172,16 @@ func TestPlatformMapResolve(t *testing.T) {
|
||||
|
||||
t.Run("returns nil when platform value not set", func(t *testing.T) {
|
||||
SetOS("darwin")
|
||||
pm := &PlatformMap[string]{Linux: strPtr("linux-only")}
|
||||
pm := &PlatformMap[string]{Linux: lo.ToPtr("linux-only")}
|
||||
assert.Nil(t, pm.Resolve())
|
||||
})
|
||||
|
||||
t.Run("returns nil for unknown OS", func(t *testing.T) {
|
||||
SetOS("freebsd")
|
||||
pm := &PlatformMap[string]{
|
||||
MacOS: strPtr("mac"),
|
||||
Linux: strPtr("linux"),
|
||||
Windows: strPtr("windows"),
|
||||
MacOS: lo.ToPtr("mac"),
|
||||
Linux: lo.ToPtr("linux"),
|
||||
Windows: lo.ToPtr("windows"),
|
||||
}
|
||||
assert.Nil(t, pm.Resolve())
|
||||
})
|
||||
@@ -192,15 +193,15 @@ func TestPlatformMapResolveWithFallback(t *testing.T) {
|
||||
|
||||
t.Run("returns primary value when set", func(t *testing.T) {
|
||||
SetOS("darwin")
|
||||
primary := &PlatformMap[string]{MacOS: strPtr("primary")}
|
||||
fallback := PlatformMap[string]{MacOS: strPtr("fallback")}
|
||||
primary := &PlatformMap[string]{MacOS: lo.ToPtr("primary")}
|
||||
fallback := PlatformMap[string]{MacOS: lo.ToPtr("fallback")}
|
||||
assert.Equal(t, "primary", primary.ResolveWithFallback(fallback))
|
||||
})
|
||||
|
||||
t.Run("returns fallback value when primary not set", func(t *testing.T) {
|
||||
SetOS("darwin")
|
||||
primary := &PlatformMap[string]{Linux: strPtr("linux-only")}
|
||||
fallback := PlatformMap[string]{MacOS: strPtr("fallback")}
|
||||
primary := &PlatformMap[string]{Linux: lo.ToPtr("linux-only")}
|
||||
fallback := PlatformMap[string]{MacOS: lo.ToPtr("fallback")}
|
||||
assert.Equal(t, "fallback", primary.ResolveWithFallback(fallback))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// Action represents the action taken for an installer.
|
||||
@@ -85,12 +86,9 @@ func (s *Summary) Print() {
|
||||
|
||||
// collectByAction returns all results (including nested) that match the given action.
|
||||
func (s *Summary) collectByAction(action Action) []InstallResult {
|
||||
var results []InstallResult
|
||||
for _, r := range s.results {
|
||||
collected := collectResultsByAction(r, action)
|
||||
results = append(results, collected...)
|
||||
}
|
||||
return results
|
||||
return lo.FlatMap(s.results, func(r InstallResult, _ int) []InstallResult {
|
||||
return collectResultsByAction(r, action)
|
||||
})
|
||||
}
|
||||
|
||||
// isContainerType returns true if the installer type is a container (group/manifest).
|
||||
@@ -179,19 +177,12 @@ func filterChildrenByAction(children []InstallResult, action Action) []InstallRe
|
||||
|
||||
// hasChildrenWithAction checks if any children (recursively) match the action.
|
||||
func hasChildrenWithAction(children []InstallResult, action Action) bool {
|
||||
for _, child := range children {
|
||||
// Skip children that should be excluded from summary
|
||||
return lo.SomeBy(children, func(child InstallResult) bool {
|
||||
if shouldSkipSummary(child, action) {
|
||||
continue
|
||||
return false
|
||||
}
|
||||
if child.Action == action {
|
||||
return true
|
||||
}
|
||||
if hasChildrenWithAction(child.Children, action) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return child.Action == action || hasChildrenWithAction(child.Children, action)
|
||||
})
|
||||
}
|
||||
|
||||
// printResult prints a single result with the given indentation level.
|
||||
|
||||
10
utils/env.go
10
utils/env.go
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// ResolveEnvPaths takes one or more slices of environment variable strings (e.g., "KEY=VALUE"),
|
||||
@@ -62,11 +64,9 @@ func EnvSliceAsMap(env []string) map[string]string {
|
||||
|
||||
// EnvMapAsSlice converts a map of environment variables to a slice of "KEY=VALUE" strings.
|
||||
func EnvMapAsSlice(env map[string]string) []string {
|
||||
out := []string{}
|
||||
for k, v := range env {
|
||||
out = append(out, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
return out
|
||||
return lo.MapToSlice(env, func(k string, v string) string {
|
||||
return fmt.Sprintf("%s=%s", k, v)
|
||||
})
|
||||
}
|
||||
|
||||
// mergeEnvs helper function to merge a source slice of env strings into a target map (represented as a slice).
|
||||
|
||||
Reference in New Issue
Block a user