Skip to content

Commit 18b47ce

Browse files
committed
feat: allow updating all plugins or themes
1 parent 32faf4b commit 18b47ce

6 files changed

Lines changed: 240 additions & 12 deletions

File tree

cmd/plugins.go

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func initPluginsCmd() {
2222
func init() {
2323
initPluginsCmd()
2424
pluginsUpdateCmd.Flags().BoolP("check", "c", false, "Check for available updates without installing")
25+
pluginsUpdateCmd.Flags().BoolP("all", "a", false, "Update all installed plugins")
2526
}
2627

2728
var pluginsCmd = &cobra.Command{
@@ -135,10 +136,26 @@ var pluginsRemoveCmd = &cobra.Command{
135136
var pluginsUpdateCmd = &cobra.Command{
136137
Use: "update <name|id|url>",
137138
Short: "Update a plugin by name, ID, or URL",
138-
Args: cobra.ExactArgs(1),
139+
Args: func(cmd *cobra.Command, args []string) error {
140+
allFlag, _ := cmd.Flags().GetBool("all")
141+
if allFlag {
142+
if len(args) > 0 {
143+
return fmt.Errorf("cannot specify addon identifier when using --all flag")
144+
}
145+
return nil
146+
}
147+
return cobra.ExactArgs(1)(cmd, args)
148+
},
139149
RunE: func(cmd *cobra.Command, args []string) error {
140-
identifier := args[0]
141150
checkOnly, _ := cmd.Flags().GetBool("check")
151+
allFlag, _ := cmd.Flags().GetBool("all")
152+
153+
// Handle --all flag
154+
if allFlag {
155+
return updateAllPlugins(checkOnly)
156+
}
157+
158+
identifier := args[0]
142159

143160
// For non-URL identifiers, check if update is available
144161
if !utils.IsURL(identifier) {
@@ -191,3 +208,101 @@ var pluginsUpdateCmd = &cobra.Command{
191208
},
192209
}
193210

211+
func updateAllPlugins(checkOnly bool) error {
212+
items, err := betterdiscord.ListAddons(betterdiscord.AddonPlugin)
213+
if err != nil {
214+
return err
215+
}
216+
217+
if len(items) == 0 {
218+
output.Println("📭 No plugins installed.")
219+
return nil
220+
}
221+
222+
var toUpdate []struct {
223+
entry betterdiscord.AddonEntry
224+
localVersion string
225+
storeVersion string
226+
name string
227+
}
228+
229+
output.Println("🔍 Checking for plugin updates...")
230+
231+
// Check each plugin for updates
232+
for _, item := range items {
233+
name := item.Meta.Name
234+
if name == "" {
235+
name = item.BaseName
236+
}
237+
238+
// Try to find in store by name or ID
239+
identifier := name
240+
if identifier == "" {
241+
identifier = item.BaseName
242+
}
243+
244+
store, err := betterdiscord.FetchAddonFromStore(identifier)
245+
if err != nil {
246+
// Plugin not in store, skip
247+
continue
248+
}
249+
250+
localVersion := item.Meta.Version
251+
storeVersion := store.Version
252+
253+
if localVersion != storeVersion {
254+
toUpdate = append(toUpdate, struct {
255+
entry betterdiscord.AddonEntry
256+
localVersion string
257+
storeVersion string
258+
name string
259+
}{
260+
entry: item,
261+
localVersion: localVersion,
262+
storeVersion: storeVersion,
263+
name: name,
264+
})
265+
}
266+
}
267+
268+
if len(toUpdate) == 0 {
269+
output.Println("✅ All plugins are up to date!")
270+
return nil
271+
}
272+
273+
output.Printf("\n📦 Found %d plugin(s) with available updates:\n\n", len(toUpdate))
274+
275+
// Show what would be updated
276+
for _, item := range toUpdate {
277+
output.Printf(" • %s: v%s → v%s\n", item.name, item.localVersion, item.storeVersion)
278+
}
279+
output.Println()
280+
281+
if checkOnly {
282+
output.Println("💡 To install these updates, use: bdcli plugins update --all (without --check)")
283+
return nil
284+
}
285+
286+
// Perform updates
287+
updated := 0
288+
failed := 0
289+
290+
for _, item := range toUpdate {
291+
identifier := item.name
292+
if identifier == "" {
293+
identifier = item.entry.BaseName
294+
}
295+
296+
_, err := betterdiscord.UpdateAddon(betterdiscord.AddonPlugin, identifier)
297+
if err != nil {
298+
output.Printf("❌ Failed to update %s: %v\n", item.name, err)
299+
failed++
300+
} else {
301+
output.Printf("✅ Updated %s to v%s\n", item.name, item.storeVersion)
302+
updated++
303+
}
304+
}
305+
306+
output.Printf("\n📊 Summary: %d updated, %d failed\n", updated, failed)
307+
return nil
308+
}

cmd/themes.go

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,26 @@ var themesRemoveCmd = &cobra.Command{
124124
var themesUpdateCmd = &cobra.Command{
125125
Use: "update <name|id|url>",
126126
Short: "Update a theme by name, ID, or URL",
127-
Args: cobra.ExactArgs(1),
127+
Args: func(cmd *cobra.Command, args []string) error {
128+
allFlag, _ := cmd.Flags().GetBool("all")
129+
if allFlag {
130+
if len(args) > 0 {
131+
return fmt.Errorf("cannot specify addon identifier when using --all flag")
132+
}
133+
return nil
134+
}
135+
return cobra.ExactArgs(1)(cmd, args)
136+
},
128137
RunE: func(cmd *cobra.Command, args []string) error {
129-
identifier := args[0]
130138
checkOnly, _ := cmd.Flags().GetBool("check")
139+
allFlag, _ := cmd.Flags().GetBool("all")
140+
141+
// Handle --all flag
142+
if allFlag {
143+
return updateAllThemes(checkOnly)
144+
}
145+
146+
identifier := args[0]
131147

132148
// For non-URL identifiers, check if update is available
133149
if !utils.IsURL(identifier) {
@@ -180,6 +196,105 @@ var themesUpdateCmd = &cobra.Command{
180196
},
181197
}
182198

199+
func updateAllThemes(checkOnly bool) error {
200+
items, err := betterdiscord.ListAddons(betterdiscord.AddonTheme)
201+
if err != nil {
202+
return err
203+
}
204+
205+
if len(items) == 0 {
206+
output.Println("📭 No themes installed.")
207+
return nil
208+
}
209+
210+
var toUpdate []struct {
211+
entry betterdiscord.AddonEntry
212+
localVersion string
213+
storeVersion string
214+
name string
215+
}
216+
217+
output.Println("🔍 Checking for theme updates...")
218+
219+
// Check each theme for updates
220+
for _, item := range items {
221+
name := item.Meta.Name
222+
if name == "" {
223+
name = item.BaseName
224+
}
225+
226+
// Try to find in store by name or ID
227+
identifier := name
228+
if identifier == "" {
229+
identifier = item.BaseName
230+
}
231+
232+
store, err := betterdiscord.FetchAddonFromStore(identifier)
233+
if err != nil {
234+
// Theme not in store, skip
235+
continue
236+
}
237+
238+
localVersion := item.Meta.Version
239+
storeVersion := store.Version
240+
241+
if localVersion != storeVersion {
242+
toUpdate = append(toUpdate, struct {
243+
entry betterdiscord.AddonEntry
244+
localVersion string
245+
storeVersion string
246+
name string
247+
}{
248+
entry: item,
249+
localVersion: localVersion,
250+
storeVersion: storeVersion,
251+
name: name,
252+
})
253+
}
254+
}
255+
256+
if len(toUpdate) == 0 {
257+
output.Println("✅ All themes are up to date!")
258+
return nil
259+
}
260+
261+
output.Printf("\n📦 Found %d theme(s) with available updates:\n\n", len(toUpdate))
262+
263+
// Show what would be updated
264+
for _, item := range toUpdate {
265+
output.Printf(" • %s: v%s → v%s\n", item.name, item.localVersion, item.storeVersion)
266+
}
267+
output.Println()
268+
269+
if checkOnly {
270+
output.Println("💡 To install these updates, use: bdcli themes update --all (without --check)")
271+
return nil
272+
}
273+
274+
// Perform updates
275+
updated := 0
276+
failed := 0
277+
278+
for _, item := range toUpdate {
279+
identifier := item.name
280+
if identifier == "" {
281+
identifier = item.entry.BaseName
282+
}
283+
284+
_, err := betterdiscord.UpdateAddon(betterdiscord.AddonTheme, identifier)
285+
if err != nil {
286+
output.Printf("❌ Failed to update %s: %v\n", item.name, err)
287+
failed++
288+
} else {
289+
output.Printf("✅ Updated %s to v%s\n", item.name, item.storeVersion)
290+
updated++
291+
}
292+
}
293+
294+
output.Printf("\n📊 Summary: %d updated, %d failed\n", updated, failed)
295+
return nil
296+
}
297+
183298
func initThemesCmd() {
184299
// Parent command: themes
185300
themesCmd.AddCommand(themesListCmd)
@@ -189,4 +304,5 @@ func initThemesCmd() {
189304
themesCmd.AddCommand(themesUpdateCmd)
190305
rootCmd.AddCommand(themesCmd)
191306
themesUpdateCmd.Flags().BoolP("check", "c", false, "Check for available updates without installing")
192-
}
307+
themesUpdateCmd.Flags().BoolP("all", "a", false, "Update all installed themes")
308+
}

cmd/update.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ var updateCmd = &cobra.Command{
7979
// Returns -1 if v1 < v2, 0 if equal, 1 if v1 > v2
8080
func compareVersions(v1, v2 string) int {
8181
// Strip 'v' prefix if present
82-
v1 = "" + v1
83-
v2 = "" + v2
82+
8483
if len(v1) > 0 && v1[0] == 'v' {
8584
v1 = v1[1:]
8685
}

internal/betterdiscord/addons.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,7 @@ func UpdateAddon(kind AddonKind, identifier string) (*ResolvedAddon, error) {
183183

184184
func addonDir(kind AddonKind) (string, error) {
185185
inst := GetInstallation()
186-
// if err := inst.Prepare(); err != nil {
187-
// return "", err
188-
// }
186+
189187
switch kind {
190188
case AddonPlugin:
191189
return inst.Plugins(), nil

internal/betterdiscord/meta_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func TestParseEmptyJSDoc(t *testing.T) {
111111
/**
112112
*/
113113
`
114-
println("Testing empty JSDoc")
114+
115115
got := parseJSDoc(input)
116116

117117
if got != (Meta{}) {

internal/utils/strings.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ import "net/url"
66
func IsURL(input string) bool {
77
parsed, err := url.Parse(input)
88
return err == nil && parsed.Scheme != "" && parsed.Host != ""
9-
}
9+
}

0 commit comments

Comments
 (0)