Skip to content

Commit 4a726f1

Browse files
authored
Merge pull request #71 from andmpel/Package-Selection
Package selection Interface
2 parents bb74dae + f1ded55 commit 4a726f1

File tree

6 files changed

+252
-55
lines changed

6 files changed

+252
-55
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@
3434
}
3535
}
3636
},
37-
"image": "mcr.microsoft.com/devcontainers/go:latest",
3837
"features": {
3938
"ghcr.io/gvatsal60/dev-container-features/sonarlint": "latest"
4039
},
40+
"image": "mcr.microsoft.com/devcontainers/go:latest",
4141
"runArgs": [
4242
"--rm",
4343
"--name=vsc-${localEnv:USER}-${containerWorkspaceFolderBasename}-${devcontainerId}"
Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
name: Release MacUp
2-
32
on:
43
push:
54
branches:
65
- main
76
- master
8-
97
jobs:
108
release:
119
runs-on: macos-latest
@@ -18,13 +16,13 @@ jobs:
1816
go-version: "stable"
1917
- name: Build Go project
2018
run: make all
21-
# TODO: Will do in near future, need to rethink the release process
22-
# - name: Create Release
23-
# if: success()
24-
# uses: softprops/action-gh-release@v2
25-
# with:
26-
# tag_name: ${{ github.ref_name }}
27-
# name: Release ${{ github.ref_name }}
28-
# body: "Automated release for commit ${{ github.sha }}"
29-
# env:
30-
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19+
# TODO: Will do in near future, need to rethink the release process
20+
# - name: Create Release
21+
# if: success()
22+
# uses: softprops/action-gh-release@v2
23+
# with:
24+
# tag_name: ${{ github.ref_name }}
25+
# name: Release ${{ github.ref_name }}
26+
# body: "Automated release for commit ${{ github.sha }}"
27+
# env:
28+
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

go.mod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
11
module macup
22

33
go 1.24.3
4+
5+
require (
6+
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
7+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
8+
github.com/mattn/go-colorable v0.1.2 // indirect
9+
github.com/mattn/go-isatty v0.0.8 // indirect
10+
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
11+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
12+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
13+
golang.org/x/text v0.4.0 // indirect
14+
)

go.sum

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
2+
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
3+
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
4+
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
5+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
8+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
9+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
10+
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
11+
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
12+
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
13+
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
14+
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
15+
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
16+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
17+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
18+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
19+
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
20+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
21+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
22+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
23+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
24+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
25+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
26+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
27+
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
28+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
29+
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
30+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
31+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
32+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
33+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
34+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
35+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
36+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
37+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
38+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
39+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
40+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
41+
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
42+
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
43+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
44+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
45+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
46+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
47+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
48+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

macup/macup.go

Lines changed: 142 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package macup
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"io"
67
"net/http"
78
"os"
89
"os/exec"
10+
"path/filepath"
911
"strings"
12+
"sync"
1013
"time"
14+
15+
"github.com/AlecAivazis/survey/v2"
1116
)
1217

1318
// Terminal color codes and constants
@@ -19,39 +24,50 @@ const (
1924
k_timeout = 5 * time.Second // Timeout for HTTP requests
2025
k_testURL = "https://www.google.com" // URL to test internet connection
2126
k_gemCmdPath = "/usr/bin/gem" // Path to the gem command
27+
k_configFile = ".macup.json" // Configuration file name
2228
)
2329

24-
// Print a message in green color with a newline
30+
// Update represents a single update function.
31+
type Update struct {
32+
Name string
33+
Description string
34+
Run func(writer io.Writer)
35+
}
36+
37+
// Config represents the user's selections.
38+
type Config struct {
39+
SelectedUpdates []string `json:"selected_updates"`
40+
}
41+
42+
// printlnGreen prints a message in green color with a newline.
2543
func printlnGreen(writer io.Writer, msg string) {
2644
fmt.Fprintf(writer, "\n%s%s%s\n", k_green, msg, k_clear)
2745
}
2846

29-
// Print a message in yellow color (no newline)
47+
// printlnYellow prints a message in yellow color (no newline).
3048
func printlnYellow(writer io.Writer, msg string) {
3149
fmt.Fprintf(writer, "%s%s%s", k_yellow, msg, k_clear)
3250
}
3351

34-
// Check if a command exists in `PATH`, print warning if not
52+
// checkCommand checks if a command exists in `PATH`, print warning if not.
3553
func checkCommand(writer io.Writer, cmd string) bool {
3654
_, err := exec.LookPath(cmd)
37-
3855
if err != nil {
3956
printlnYellow(writer, cmd+" is not installed.")
4057
return false
4158
}
42-
4359
return true
4460
}
4561

46-
// Run a shell command and direct its output to writer
62+
// runCommand runs a shell command and directs its output to writer.
4763
func runCommand(writer io.Writer, name string, args ...string) {
4864
cmd := exec.Command(name, args...)
4965
cmd.Stdout = writer
5066
cmd.Stderr = writer
5167
cmd.Run()
5268
}
5369

54-
// Update Homebrew formulas and perform diagnostics
70+
// UpdateBrew updates Homebrew formulas and perform diagnostics.
5571
func UpdateBrew(writer io.Writer) {
5672
printlnGreen(writer, "Updating Brew Formulas")
5773
if checkCommand(writer, "brew") {
@@ -64,15 +80,15 @@ func UpdateBrew(writer io.Writer) {
6480
}
6581
}
6682

67-
// Update VSCode extensions
83+
// UpdateVSCodeExt updates VSCode extensions.
6884
func UpdateVSCodeExt(writer io.Writer) {
6985
printlnGreen(writer, "Updating VSCode Extensions")
7086
if checkCommand(writer, "code") {
7187
runCommand(writer, "code", "--update-extensions")
7288
}
7389
}
7490

75-
// Update Ruby gems and clean up
91+
// UpdateGem updates Ruby gems and clean up.
7692
func UpdateGem(writer io.Writer) {
7793
printlnGreen(writer, "Updating Gems")
7894
gemPath, err := exec.LookPath("gem")
@@ -84,7 +100,7 @@ func UpdateGem(writer io.Writer) {
84100
runCommand(writer, "gem", "cleanup", "--user-install")
85101
}
86102

87-
// Update global Node.js, npm, and Yarn packages
103+
// UpdateNodePkg updates global Node.js, npm, and Yarn packages.
88104
func UpdateNodePkg(writer io.Writer) {
89105
printlnGreen(writer, "Updating Node Packages")
90106
if checkCommand(writer, "node") {
@@ -100,13 +116,13 @@ func UpdateNodePkg(writer io.Writer) {
100116
}
101117
}
102118

103-
// Update Rust Cargo crates by reinstalling each listed crate
119+
// UpdateCargo updates Rust Cargo crates by reinstalling each listed crate.
104120
func UpdateCargo(writer io.Writer) {
105121
printlnGreen(writer, "Updating Rust Cargo Crates")
106122
if checkCommand(writer, "cargo") {
107123
out, _ := exec.Command("cargo", "install", "--list").Output()
108-
lines := strings.SplitSeq(string(out), "\n")
109-
for line := range lines {
124+
lines := strings.Split(string(out), "\n")
125+
for _, line := range lines {
110126
if fields := strings.Fields(line); len(fields) > 0 {
111127
name := fields[0]
112128
runCommand(writer, "cargo", "install", name)
@@ -115,28 +131,27 @@ func UpdateCargo(writer io.Writer) {
115131
}
116132
}
117133

118-
// Update Mac App Store applications
134+
// UpdateAppStore updates Mac App Store applications.
119135
func UpdateAppStore(writer io.Writer) {
120136
printlnGreen(writer, "Updating App Store Applications")
121137
if checkCommand(writer, "mas") {
122138
runCommand(writer, "mas", "upgrade")
123139
}
124140
}
125141

126-
// Update macOS system software
142+
// UpdateMacOS updates macOS system software.
127143
func UpdateMacOS(writer io.Writer) {
128144
printlnGreen(writer, "Updating MacOS")
129145
runCommand(writer, "softwareupdate", "-i", "-a")
130146
}
131147

132-
// Check for internet connectivity by making an HTTP request
148+
// CheckInternet checks for internet connectivity by making an HTTP request.
133149
func CheckInternet() bool {
134150
client := http.Client{
135151
Timeout: k_timeout,
136152
}
137153

138154
resp, err := client.Get(k_testURL)
139-
140155
if err != nil {
141156
fmt.Fprintf(os.Stderr, "\n%s%s%s\n", k_red, "⚠️ No Internet Connection!!!", k_clear)
142157
return false
@@ -145,3 +160,113 @@ func CheckInternet() bool {
145160

146161
return resp.StatusCode == http.StatusOK
147162
}
163+
164+
// homeDir returns the user's home directory.
165+
func homeDir() string {
166+
home, err := os.UserHomeDir()
167+
if err != nil {
168+
fmt.Fprintf(os.Stderr, "Error getting home directory: %v\n", err)
169+
os.Exit(1)
170+
}
171+
return home
172+
}
173+
174+
// configPath returns the full path to the configuration file.
175+
func configPath() string {
176+
return filepath.Join(homeDir(), k_configFile)
177+
}
178+
179+
// LoadConfig loads the user's selections from the configuration file.
180+
func LoadConfig() (*Config, error) {
181+
path := configPath()
182+
data, err := os.ReadFile(path)
183+
if err != nil {
184+
if os.IsNotExist(err) {
185+
return &Config{}, nil
186+
}
187+
return nil, err
188+
}
189+
190+
var config Config
191+
if err := json.Unmarshal(data, &config); err != nil {
192+
return nil, err
193+
}
194+
return &config, nil
195+
}
196+
197+
// SaveConfig saves the user's selections to the configuration file.
198+
func (c *Config) SaveConfig() error {
199+
path := configPath()
200+
data, err := json.MarshalIndent(c, "", " ")
201+
if err != nil {
202+
return err
203+
}
204+
return os.WriteFile(path, data, 0644)
205+
}
206+
207+
// Updates is a list of all available update functions.
208+
var Updates = []Update{
209+
{"brew", "Update Homebrew packages", UpdateBrew},
210+
{"vscode", "Update VSCode extensions", UpdateVSCodeExt},
211+
{"gem", "Update Ruby gems", UpdateGem},
212+
{"node", "Update Node.js packages", UpdateNodePkg},
213+
{"cargo", "Update Rust packages", UpdateCargo},
214+
{"appstore", "Update Mac App Store apps", UpdateAppStore},
215+
{"macos", "Update macOS system", UpdateMacOS},
216+
}
217+
218+
// Run runs the selected update functions.
219+
func Run(writer io.Writer, selectedUpdates []string) {
220+
var wg sync.WaitGroup
221+
for _, updateName := range selectedUpdates {
222+
for _, u := range Updates {
223+
if u.Name == updateName {
224+
wg.Add(1)
225+
go func(u Update) {
226+
defer wg.Done()
227+
u.Run(writer)
228+
}(u)
229+
}
230+
}
231+
}
232+
wg.Wait()
233+
}
234+
235+
// SelectUpdates prompts the user to select which updates to run.
236+
func SelectUpdates(config *Config) ([]string, error) {
237+
options := make([]string, len(Updates))
238+
for i, u := range Updates {
239+
options[i] = u.Description
240+
}
241+
242+
defaults := []string{}
243+
for _, s := range config.SelectedUpdates {
244+
for _, u := range Updates {
245+
if u.Name == s {
246+
defaults = append(defaults, u.Description)
247+
}
248+
}
249+
}
250+
251+
prompt := &survey.MultiSelect{
252+
Message: "Select the updates you want to run:",
253+
Options: options,
254+
Default: defaults,
255+
}
256+
257+
var selectedDescriptions []string
258+
if err := survey.AskOne(prompt, &selectedDescriptions); err != nil {
259+
return nil, err
260+
}
261+
262+
selectedNames := []string{}
263+
for _, desc := range selectedDescriptions {
264+
for _, u := range Updates {
265+
if u.Description == desc {
266+
selectedNames = append(selectedNames, u.Name)
267+
}
268+
}
269+
}
270+
271+
return selectedNames, nil
272+
}

0 commit comments

Comments
 (0)