Skip to content

Commit 1be909e

Browse files
feat: support excluding packages from upgrade --all (#48)
* feat: support excluding packages from upgrade --all * use binary names instead of package names * simplify stew config command * cleanup --------- Co-authored-by: Marwan Hawari <[email protected]>
1 parent 6358677 commit 1be909e

File tree

8 files changed

+68
-24
lines changed

8 files changed

+68
-24
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ stew config # Automatically updates the stew.config.json
157157
```
158158

159159
# Configuration
160-
161160
`stew` can be configured with a `stew.config.json` file. The location of this file will also depend on your OS:
162161
|Linux/macOS | Windows |
163162
| ------------ | ---------- |
@@ -166,8 +165,9 @@ stew config # Automatically updates the stew.config.json
166165
You can configure 2 aspects of `stew`:
167166
1. The `stewPath`: this is where `stew` data is stored.
168167
2. The `stewBinPath`: this is where `stew` installs binaries
168+
3. `excludeFromUpgradeAll`: this is the list of binaries that you don't want to be upgraded during `stew upgrade --all`, perhaps because they have their own built in upgrade feature or because you want to pin a specific version.
169169

170-
The default locations for these are:
170+
The default locations for the `stewPath` and `stewBinPath` are:
171171
| | Linux/macOS | Windows |
172172
| ------------ | ------------ | ---------- |
173173
| `stewPath` | `$XDG_DATA_HOME/stew` or `~/.local/share/stew` | `~/AppData/Local/stew` |
@@ -176,7 +176,7 @@ The default locations for these are:
176176
There are multiple ways to configure these:
177177
* When you first run `stew`, it will look for a `stew.config.json` file. If it cannot find one, then you will be prompted to set the configuration values.
178178
* After `stew` is installed, you can use the `stew config` command to set the configuration values.
179-
* At any time, you can manually create or edit the `stew.config.json` file. It should have values for `stewPath` and `stewBinPath`.
179+
* At any time, you can manually create or edit the `stew.config.json` file. It should have values for `stewPath`, `stewBinPath`, and `excludeFromUpgradeAll`.
180180

181181
Make sure that the installation path is in your `PATH` environment variable. Otherwise, you won't be able to use any of the binaries installed by `stew`.
182182

cmd/config.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@ func Config() {
2525

2626
config, err := stew.ReadStewConfigJSON(stewConfigFilePath)
2727
stew.CatchAndExit(err)
28-
newStewPath, newStewBinPath, err := stew.PromptConfig(config.StewPath, config.StewBinPath)
28+
systemInfo := stew.NewSystemInfo(config)
29+
installedPackages, err := stew.ReadStewLockFileContents(systemInfo.StewLockFilePath)
30+
stew.CatchAndExit(err)
31+
newStewPath, newStewBinPath, newExcludedFromUpgradeAll, err := stew.PromptConfig(config.StewPath, config.StewBinPath, installedPackages, config.ExcludedFromUpgradeAll)
2932
stew.CatchAndExit(err)
3033

31-
newStewConfig := stew.StewConfig{StewPath: newStewPath, StewBinPath: newStewBinPath}
34+
newStewConfig := stew.StewConfig{StewPath: newStewPath, StewBinPath: newStewBinPath, ExcludedFromUpgradeAll: newExcludedFromUpgradeAll}
3235
err = stew.WriteStewConfigJSON(newStewConfig, stewConfigFilePath)
3336
stew.CatchAndExit(err)
3437

cmd/upgrade.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
// Upgrade is executed when you run `stew upgrade`
1313
func Upgrade(upgradeAllCliFlag bool, binaryName string) {
1414

15-
userOS, userArch, _, systemInfo, err := stew.Initialize()
15+
userOS, userArch, stewConfig, systemInfo, err := stew.Initialize()
1616
stew.CatchAndExit(err)
1717

1818
if upgradeAllCliFlag && binaryName != "" {
@@ -38,7 +38,7 @@ func Upgrade(upgradeAllCliFlag bool, binaryName string) {
3838
}
3939

4040
if upgradeAllCliFlag {
41-
upgradeAll(userOS, userArch, lockFile, systemInfo)
41+
upgradeAll(userOS, userArch, lockFile, systemInfo, stewConfig)
4242
} else {
4343
err := upgradeOne(binaryName, userOS, userArch, lockFile, systemInfo)
4444
stew.CatchAndExit(err)
@@ -128,8 +128,12 @@ func upgradeOne(binaryName, userOS, userArch string, lockFile stew.LockFile, sys
128128
return nil
129129
}
130130

131-
func upgradeAll(userOS, userArch string, lockFile stew.LockFile, systemInfo stew.SystemInfo) {
131+
func upgradeAll(userOS, userArch string, lockFile stew.LockFile, systemInfo stew.SystemInfo, stewConfig stew.StewConfig) {
132132
for _, pkg := range lockFile.Packages {
133+
if _, packageIsExcluded := stew.Contains(stewConfig.ExcludedFromUpgradeAll, pkg.Binary); packageIsExcluded {
134+
fmt.Printf("%v (Excluded)\n", constants.YellowColor(pkg.Binary))
135+
continue
136+
}
133137
if err := upgradeOne(pkg.Binary, userOS, userArch, lockFile, systemInfo); err != nil {
134138
fmt.Fprintln(os.Stderr, err)
135139
continue

lib/config.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ func GetStewConfigFilePath(userOS string) (string, error) {
8282

8383
// StewConfig contains all the stew configuration data
8484
type StewConfig struct {
85-
StewPath string `json:"stewPath"`
86-
StewBinPath string `json:"stewBinPath"`
85+
StewPath string `json:"stewPath"`
86+
StewBinPath string `json:"stewBinPath"`
87+
ExcludedFromUpgradeAll []string `json:"excludedFromUpgradeAll"`
8788
}
8889

8990
func ReadStewConfigJSON(stewConfigFilePath string) (StewConfig, error) {
@@ -134,6 +135,7 @@ func NewStewConfig(userOS string) (StewConfig, error) {
134135
if err != nil {
135136
return StewConfig{}, err
136137
}
138+
defaultExcludedFromUpgradeAll := []string{}
137139

138140
configExists, err := PathExists(stewConfigFilePath)
139141
if err != nil {
@@ -152,13 +154,19 @@ func NewStewConfig(userOS string) (StewConfig, error) {
152154
if stewConfig.StewBinPath == "" {
153155
stewConfig.StewBinPath = defaultStewBinPath
154156
}
157+
158+
if len(stewConfig.ExcludedFromUpgradeAll) == 0 {
159+
stewConfig.ExcludedFromUpgradeAll = defaultExcludedFromUpgradeAll
160+
}
155161
} else {
156-
selectedStewPath, selectedStewBinPath, err := PromptConfig(defaultStewPath, defaultStewBinPath)
162+
defaultInstalledPackages := []PackageData{}
163+
selectedStewPath, selectedStewBinPath, excludedFromUpgradeAll, err := PromptConfig(defaultStewPath, defaultStewBinPath, defaultInstalledPackages, defaultExcludedFromUpgradeAll)
157164
if err != nil {
158165
return StewConfig{}, err
159166
}
160167
stewConfig.StewPath = selectedStewPath
161168
stewConfig.StewBinPath = selectedStewBinPath
169+
stewConfig.ExcludedFromUpgradeAll = excludedFromUpgradeAll
162170
fmt.Printf("📄 Updated %v\n", constants.GreenColor(stewConfigFilePath))
163171
}
164172

@@ -235,27 +243,38 @@ func Initialize() (string, string, StewConfig, SystemInfo, error) {
235243
return userOS, userArch, stewConfig, systemInfo, nil
236244
}
237245

238-
// PromptConfig launches an interactive UI for setting the stew config values. It returns the resolved stewPath and stewBinPath.
239-
func PromptConfig(suggestedStewPath, suggestedStewBinPath string) (string, string, error) {
246+
// PromptConfig launches an interactive UI for setting the stew config values. It returns the resolved stewPath, stewBinPath, and excludedFromUpgradeAll values.
247+
func PromptConfig(suggestedStewPath string, suggestedStewBinPath string, installedPackages []PackageData, excludedPackages []string) (string, string, []string, error) {
240248
inputStewPath, err := PromptInput("Set the stewPath. This will contain all stew data other than the binaries.", suggestedStewPath)
241249
if err != nil {
242-
return "", "", err
250+
return "", "", []string{}, err
243251
}
244252
inputStewBinPath, err := PromptInput("Set the stewBinPath. This is where the binaries will be installed by stew.", suggestedStewBinPath)
245253
if err != nil {
246-
return "", "", err
254+
return "", "", []string{}, err
255+
}
256+
excludedFromUpgradeAll := []string{}
257+
if len(installedPackages) != 0 {
258+
installedBinaryNames := []string{}
259+
for _, pkg := range installedPackages {
260+
installedBinaryNames = append(installedBinaryNames, pkg.Binary)
261+
}
262+
excludedFromUpgradeAll, err = PromptMultiSelect("Select any packages that you do not wish to be upgraded during stew upgrade --all.", installedBinaryNames, excludedPackages)
263+
if err != nil {
264+
return "", "", []string{}, err
265+
}
247266
}
248267

249268
fullStewPath, err := ResolvePath(inputStewPath)
250269
if err != nil {
251-
return "", "", err
270+
return "", "", []string{}, err
252271
}
253272
fullStewBinPath, err := ResolvePath(inputStewBinPath)
254273
if err != nil {
255-
return "", "", err
274+
return "", "", []string{}, err
256275
}
257276

258-
return fullStewPath, fullStewBinPath, nil
277+
return fullStewPath, fullStewBinPath, excludedFromUpgradeAll, nil
259278
}
260279

261280
func ValidateStewBinPath(stewBinPath, pathVariable string) bool {

lib/stewfile.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type PackageData struct {
3232
BinaryHash string `json:"binaryHash"`
3333
}
3434

35-
func readLockFileJSON(lockFilePath string) (LockFile, error) {
35+
func ReadLockFileJSON(lockFilePath string) (LockFile, error) {
3636

3737
lockFileBytes, err := os.ReadFile(lockFilePath)
3838
if err != nil {
@@ -108,7 +108,7 @@ func ReadStewfileContents(stewfilePath string) ([]PackageData, error) {
108108

109109
// ReadStewLockFileContents will read the contents of the Stewfile.lock.json
110110
func ReadStewLockFileContents(lockFilePath string) ([]PackageData, error) {
111-
lockFile, err := readLockFileJSON(lockFilePath)
111+
lockFile, err := ReadLockFileJSON(lockFilePath)
112112
if err != nil {
113113
return []PackageData{}, err
114114
}
@@ -125,7 +125,7 @@ func NewLockFile(stewLockFilePath, userOS, userArch string) (LockFile, error) {
125125
if !lockFileExists {
126126
lockFile = LockFile{Os: userOS, Arch: userArch, Packages: []PackageData{}}
127127
} else {
128-
lockFile, err = readLockFileJSON(stewLockFilePath)
128+
lockFile, err = ReadLockFileJSON(stewLockFilePath)
129129
if err != nil {
130130
return LockFile{}, err
131131
}

lib/stewfile_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func Test_readLockFileJSON(t *testing.T) {
129129
lockFilePath := filepath.Join(tempDir, "Stewfile.lock.json")
130130
WriteLockFileJSON(testLockfile, lockFilePath)
131131

132-
got, err := readLockFileJSON(lockFilePath)
132+
got, err := ReadLockFileJSON(lockFilePath)
133133
if (err != nil) != tt.wantErr {
134134
t.Errorf("readLockFileJSON() error = %v, wantErr %v", err, tt.wantErr)
135135
return
@@ -160,7 +160,7 @@ func TestWriteLockFileJSON(t *testing.T) {
160160
t.Errorf("WriteLockFileJSON() error = %v, wantErr %v", err, tt.wantErr)
161161
}
162162

163-
got, _ := readLockFileJSON(lockFilePath)
163+
got, _ := ReadLockFileJSON(lockFilePath)
164164

165165
if !reflect.DeepEqual(got, testLockfile) {
166166
t.Errorf("WriteLockFileJSON() = %v, want %v", got, testLockfile)

lib/ui.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@ func PromptSelect(message string, options []string) (string, error) {
2121
return result, nil
2222
}
2323

24+
// PromptMultiSelect launches the multiple selection UI
25+
func PromptMultiSelect(message string, options []string, defaultSelections []string) ([]string, error) {
26+
result := []string{}
27+
prompt := &survey.MultiSelect{
28+
Message: message,
29+
Options: options,
30+
Default: defaultSelections,
31+
}
32+
err := survey.AskOne(prompt, &result, survey.WithIcons(func(icons *survey.IconSet) {
33+
icons.Question.Text = "*"
34+
}))
35+
if err != nil {
36+
return []string{}, ExitUserSelectionError{Err: err}
37+
}
38+
39+
return result, nil
40+
}
41+
2442
// PromptInput launches the input UI
2543
func PromptInput(message string, defaultInput string) (string, error) {
2644
result := ""

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func main() {
9797
},
9898
{
9999
Name: "config",
100-
Usage: "Configure the stew file paths using an interactive UI. [Ex: stew config]",
100+
Usage: "Configure stew using an interactive UI. [Ex: stew config]",
101101
Action: func(c *cli.Context) error {
102102
cmd.Config()
103103
return nil

0 commit comments

Comments
 (0)