mirror of
https://github.com/chenasraf/sofmani.git
synced 2026-05-18 01:29:02 +00:00
feat(installer): add enabled option
This commit is contained in:
22
README.md
22
README.md
@@ -175,33 +175,24 @@ actions. Steps can be of **several types**, such as `brew`, `rsync`, `shell`, an
|
||||
|
||||
### Supported `type` of Installers
|
||||
|
||||
For a full list with all the supported options, see [the docs](./docs/installer-types.md).
|
||||
|
||||
- **`rsync`**
|
||||
|
||||
- Copy files from `source` to `destination` using rsync.
|
||||
- **Options**:
|
||||
- `opts.source`: Source directory/file.
|
||||
- `opts.destination`: Destination directory/file.
|
||||
- `opts.flags`: Additional rsync flags (e.g., `--delete`, `--exclude`).
|
||||
|
||||
- **`group`**
|
||||
|
||||
- Executes a logical group of steps in sequence.
|
||||
- Allows nesting multiple steps together.
|
||||
- **Options**:
|
||||
- `steps`: List of nested steps.
|
||||
|
||||
- **`brew`**
|
||||
|
||||
- Installs packages using Homebrew.
|
||||
- **Options**:
|
||||
- `opts.tap`: Name of the tap to install the package from.
|
||||
|
||||
- **`shell`**
|
||||
|
||||
- Executes arbitrary shell commands.
|
||||
- **Options**:
|
||||
- `opts.command`: The command to execute for installing.
|
||||
- `opts.update_command`: The command to execute for updating.
|
||||
|
||||
- **`npm`/`pnpm`/`yarn`**
|
||||
|
||||
@@ -214,9 +205,6 @@ actions. Steps can be of **several types**, such as `brew`, `rsync`, `shell`, an
|
||||
- Clones a git repository to a local directory.
|
||||
- If `name` is a full git URL (https or SSH), the repository is cloned directly. If it is a
|
||||
repository path, e.g. `chenasraf/sofmani`, GitHub is assumed.
|
||||
- **Options**:
|
||||
- `opts.destination`: The local directory to clone the repository to.
|
||||
- `opts.ref`: The branch, tag, or commit to checkout after cloning.
|
||||
|
||||
- **`manifest`**
|
||||
|
||||
@@ -225,12 +213,6 @@ actions. Steps can be of **several types**, such as `brew`, `rsync`, `shell`, an
|
||||
installers.
|
||||
- `debug` and `check_updates` will be inherited by the loaded config.
|
||||
- `env` and `defaults` will be merged into the loaded config, overriding any existing values.
|
||||
- **Options**:
|
||||
- `opts.source`: The local file, or remote git URL (https or SSH) containing the manifest.
|
||||
- `opts.path`: The path to the manifest file within the repository. If `opts.source` is a local
|
||||
file, `opts.path` will be appended to it.
|
||||
- `opts.ref`: The branch, tag, or commit to checkout after cloning if `opts.source` is a git
|
||||
URL. For local manifests, this value will be ignored.
|
||||
|
||||
- **`apt`**
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ func TestInstallerPlatformEnviron(t *testing.T) {
|
||||
assert.ElementsMatch(t, expected, data.Environ())
|
||||
}
|
||||
|
||||
func TestParseConfig(t *testing.T) {
|
||||
func TestParseJsonConfig(t *testing.T) {
|
||||
// Create a temporary config file
|
||||
file, err := os.CreateTemp("", "config.*.json")
|
||||
assert.NoError(t, err)
|
||||
@@ -78,6 +78,52 @@ func TestParseConfig(t *testing.T) {
|
||||
assert.False(t, config.CheckUpdates)
|
||||
}
|
||||
|
||||
func TestParseYamlConfig(t *testing.T) {
|
||||
// Create a temporary config file
|
||||
file, err := os.CreateTemp("", "config.*.yaml")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(file.Name())
|
||||
|
||||
_, err = file.WriteString(`
|
||||
debug: true
|
||||
check_updates: false
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
file.Close()
|
||||
|
||||
// Test parsing the config file
|
||||
overrides := AppCliConfig{ConfigFile: file.Name()}
|
||||
config, err := ParseConfig(&overrides)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, config.Debug)
|
||||
assert.False(t, config.CheckUpdates)
|
||||
}
|
||||
|
||||
func TestParseYamlConfigEnabled(t *testing.T) {
|
||||
// Create a temporary config file
|
||||
file, err := os.CreateTemp("", "config.*.yaml")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(file.Name())
|
||||
|
||||
_, err = file.WriteString(`
|
||||
debug: true
|
||||
check_updates: false
|
||||
install:
|
||||
- name: test
|
||||
type: shell
|
||||
enabled: true
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
file.Close()
|
||||
|
||||
// Test parsing the config file
|
||||
overrides := AppCliConfig{ConfigFile: file.Name()}
|
||||
config, err := ParseConfig(&overrides)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, config.Debug)
|
||||
assert.False(t, config.CheckUpdates)
|
||||
}
|
||||
|
||||
func TestFindConfigFile(t *testing.T) {
|
||||
// Create a temporary config file
|
||||
dir := t.TempDir()
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
type InstallerData struct {
|
||||
Enabled *string `json:"enabled" yaml:"enabled"`
|
||||
Name *string `json:"name" yaml:"name"`
|
||||
Type InstallerType `json:"type" yaml:"type"`
|
||||
Tags *string `json:"tags" yaml:"tags"`
|
||||
|
||||
@@ -20,6 +20,13 @@ These fields are shared by all installer types. Some fields may vary in behavior
|
||||
- **Description**: Type of the step. See [supported types](#supported-type-of-installers) for a
|
||||
comprehensive list of supported values.
|
||||
|
||||
- **`enabled`**
|
||||
|
||||
- **Type**: String or Boolean (optional)
|
||||
- **Description**: Enable or disable the step. Disabled steps are not run. This can either be a
|
||||
static boolean (`true` or `false`), or a command that returns a success status code for true, or
|
||||
a failure for false.
|
||||
|
||||
- **`tags`**
|
||||
|
||||
- **Type** String (optional)
|
||||
|
||||
@@ -3,6 +3,7 @@ package installer
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/chenasraf/sofmani/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
@@ -57,3 +58,30 @@ func isFilteredIn(installer IInstaller, filter string) bool {
|
||||
}
|
||||
return strings.Contains(*data.Name, filter)
|
||||
}
|
||||
|
||||
func InstallerIsEnabled(i IInstaller) (bool, error) {
|
||||
enabledCmd := i.GetData().Enabled
|
||||
|
||||
if enabledCmd == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if strings.ToLower(*enabledCmd) == "true" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if strings.ToLower(*enabledCmd) == "false" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
shell := utils.GetOSShell(i.GetData().EnvShell)
|
||||
args := utils.GetOSShellArgs(*enabledCmd)
|
||||
|
||||
err, success := utils.RunCmdGetSuccess(i.GetData().Environ(), shell, args...)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return success, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/chenasraf/sofmani/appconfig"
|
||||
"github.com/chenasraf/sofmani/logger"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -95,3 +96,62 @@ func TestFilterInstaller(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallerIsEnabled(t *testing.T) {
|
||||
logger.InitLogger(true)
|
||||
tests := []struct {
|
||||
name string
|
||||
installer IInstaller
|
||||
expected bool
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "Enabled is nil",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: nil},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Enabled is true",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("true")},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Enabled is false",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("false")},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Enabled is a command that succeeds",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("exit 0")},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Enabled is a command that fails",
|
||||
installer: &MockInstaller{
|
||||
data: &appconfig.InstallerData{Enabled: strPtr("exit 1")},
|
||||
},
|
||||
expected: false,
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := InstallerIsEnabled(tt.installer)
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +152,19 @@ func RunInstaller(config *appconfig.AppConfig, installer IInstaller) error {
|
||||
logger.Debug("%s is filtered, skipping", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
enabled, err := InstallerIsEnabled(installer)
|
||||
|
||||
if err != nil {
|
||||
logger.Error("Failed to check if %s is enabled: %s", name, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
logger.Debug("%s is disabled, skipping", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Debug("Checking %s (%s)", name, info.Type)
|
||||
err, installed := installer.CheckIsInstalled()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user