Skip to content

Commit cd3eaf8

Browse files
committed
feat: add go mod parser, better text
1 parent fe96305 commit cd3eaf8

File tree

7 files changed

+74
-20
lines changed

7 files changed

+74
-20
lines changed

Taskfile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ tasks:
1111
test:
1212
cmds:
1313
- go build -tags "fake" -o modup-test .
14-
# - defer: rm modup-test
14+
- defer: rm modup-test
1515
- ./modup-test

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@ require (
66
github.com/Masterminds/semver/v3 v3.4.0
77
github.com/charmbracelet/bubbles v0.21.0
88
github.com/charmbracelet/bubbletea v1.3.6
9-
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
9+
github.com/charmbracelet/lipgloss v1.1.1-0.20250818141516-e15c2eb5db1d
1010
github.com/spf13/pflag v1.0.7
11+
golang.org/x/mod v0.27.0
1112
)
1213

1314
require (
1415
github.com/atotto/clipboard v0.1.4 // indirect
1516
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
1617
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
1718
github.com/charmbracelet/harmonica v0.2.0 // indirect
18-
github.com/charmbracelet/x/ansi v0.9.3 // indirect
19+
github.com/charmbracelet/x/ansi v0.10.1 // indirect
1920
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
2021
github.com/charmbracelet/x/term v0.2.1 // indirect
2122
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG
1616
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
1717
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
1818
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
19+
github.com/charmbracelet/lipgloss v1.1.1-0.20250818141516-e15c2eb5db1d h1:A6IxqmPuiu9VBbF6uQAi4BkAkwfewKMc1Mvpa3ySDBY=
20+
github.com/charmbracelet/lipgloss v1.1.1-0.20250818141516-e15c2eb5db1d/go.mod h1:wKtJM5mT2ZgzEd7ldSLtl/g3j8yTcxEcxIegHNeNvt4=
1921
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
2022
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
23+
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
24+
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
2125
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
2226
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
2327
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
@@ -53,6 +57,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
5357
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
5458
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
5559
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
60+
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
61+
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
5662
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
5763
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
5864
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

internal/deps/deps.go

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/Masterminds/semver/v3"
10+
"golang.org/x/mod/modfile"
1011
)
1112

1213
// Module describes a Go module upgrade candidate
@@ -31,25 +32,53 @@ type goListModule struct {
3132
} `json:"Update"`
3233
}
3334

34-
// ListAllModulePaths returns all module paths in the current context
35+
// ListAllModulePaths returns all direct (non-indirect) module paths declared in go.mod
3536
func ListAllModulePaths() ([]string, error) {
36-
cmd := exec.Command("go", "list", "-m", "-f", "{{.Path}}", "all")
37-
cmd.Env = append(os.Environ(), "GOWORK=off")
38-
out, err := cmd.Output()
37+
gomodPath, err := getGoModPath()
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
data, err := os.ReadFile(gomodPath)
43+
if err != nil {
44+
return nil, err
45+
}
46+
47+
f, err := modfile.Parse(gomodPath, data, nil)
3948
if err != nil {
4049
return nil, err
4150
}
42-
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
43-
var paths []string
44-
for _, l := range lines {
45-
l = strings.TrimSpace(l)
46-
if l != "" {
47-
paths = append(paths, l)
51+
52+
paths := make([]string, 0, len(f.Require))
53+
seen := make(map[string]struct{})
54+
for _, req := range f.Require {
55+
if req == nil || req.Mod.Path == "" || req.Indirect {
56+
continue
57+
}
58+
if _, ok := seen[req.Mod.Path]; ok {
59+
continue
4860
}
61+
paths = append(paths, req.Mod.Path)
62+
seen[req.Mod.Path] = struct{}{}
4963
}
64+
5065
return paths, nil
5166
}
5267

68+
func getGoModPath() (string, error) {
69+
cmd := exec.Command("go", "env", "GOMOD")
70+
cmd.Env = append(os.Environ(), "GOWORK=off")
71+
out, err := cmd.Output()
72+
if err != nil {
73+
return "", err
74+
}
75+
path := strings.TrimSpace(string(out))
76+
if path == "" || path == os.DevNull {
77+
return "go.mod", nil
78+
}
79+
return path, nil
80+
}
81+
5382
func GetModuleInfo(path string) (Module, error) {
5483
cmd := exec.Command("go", "list", "-m", "-u", "-json", path)
5584
cmd.Env = append(os.Environ(), "GOWORK=off")

internal/tui/model.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func NewModel() model {
4343
progress.WithoutPercentage(),
4444
)
4545
s := spinner.New()
46+
s.Spinner = spinner.MiniDot
4647
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("63"))
4748
return model{
4849
spinner: s,

internal/tui/update.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
4242

4343
// get pkgs commands
4444
case getPackageListMsg:
45+
if msg.err != nil {
46+
return m, tea.Batch(
47+
tea.Println("list used packages:", msg.err),
48+
tea.Quit,
49+
)
50+
}
4551
m.packages = createModules(msg.packages)
4652

4753
cmds := []tea.Cmd{
@@ -74,16 +80,22 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
7480
mark = failMark
7581
}
7682

77-
if m.packages.isFinished() {
83+
if !m.packages.isFinished() {
84+
progressCmd := m.progress.SetPercent(m.packages.progressFloat())
85+
return m, tea.Batch(progressCmd, textPrint("%s %s", mark, pkg), moduleStartedCmd())
86+
}
87+
88+
if len(m.modules) == 0 {
7889
return m, tea.Sequence(
79-
textPrint("%s %s", mark, pkg),
80-
changeModeList(),
90+
stepPrint("Everything is up-to-date"),
91+
tea.Quit,
8192
)
8293
}
8394

84-
progressCmd := m.progress.SetPercent(m.packages.progressFloat())
85-
86-
return m, tea.Batch(progressCmd, textPrint("%s %s", mark, pkg), moduleStartedCmd())
95+
return m, tea.Sequence(
96+
textPrint("%s %s", mark, pkg),
97+
changeModeList(),
98+
)
8799

88100
case changeModeListMsg:
89101
l, items := m.newList()

internal/tui/update_list.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,12 @@ func newItemDelegate(keys *listKeyMap) list.DefaultDelegate {
109109
item := m.Items()[idx]
110110
newItem := listItemSetSelected(item, !listItemSelected(item))
111111
setCmd := m.SetItem(idx, newItem)
112-
statusCmd := m.NewStatusMessage(statusMessageStyle("Selected " + newItem.(listModuleItem).Module.Path))
112+
113+
selectWord := "Selected"
114+
if listItemSelected(item) {
115+
selectWord = "Deselected"
116+
}
117+
statusCmd := m.NewStatusMessage(statusMessageStyle(selectWord + " " + newItem.(listModuleItem).Module.Path))
113118
return tea.Batch(setCmd, statusCmd)
114119
}
115120

0 commit comments

Comments
 (0)