Skip to content

Commit 56e4f4d

Browse files
committed
fix: resolve custom paths on wsl
1 parent 9bb5633 commit 56e4f4d

7 files changed

Lines changed: 215 additions & 181 deletions

File tree

.goreleaser.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ release:
106106
107107
108108
homebrew_casks:
109-
- name: cli
109+
- name: bdcli
110110
description: "A cross-platform CLI for managing BetterDiscord."
111111
homepage: "https://betterdiscord.app/"
112112
license: "Apache-2.0"

internal/betterdiscord/install.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"sync"
77

88
"github.com/betterdiscord/cli/internal/models"
9+
"github.com/betterdiscord/cli/internal/utils"
910
)
1011

1112
type BDInstall struct {
@@ -81,8 +82,11 @@ func GetInstallation(base ...string) *BDInstall {
8182
configDir, _ := os.UserConfigDir()
8283

8384
// Handle WSL with Windows home directory
84-
if os.Getenv("WSL_DISTRO_NAME") != "" && os.Getenv("WIN_HOME") != "" {
85-
configDir = filepath.Join(os.Getenv("WIN_HOME"), "AppData", "Roaming")
85+
if utils.IsWSL() {
86+
winHome, err := utils.WindowsHome()
87+
if err == nil && winHome != "" {
88+
configDir = filepath.Join(winHome, "AppData", "Roaming")
89+
}
8690
}
8791

8892
globalInstance = GetInstallation(configDir)

internal/discord/paths_common.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//go:build darwin || linux || windows
2+
3+
package discord
4+
5+
import (
6+
"io/fs"
7+
"os"
8+
"path/filepath"
9+
"sort"
10+
"strings"
11+
12+
"github.com/betterdiscord/cli/internal/utils"
13+
)
14+
15+
// validateWindowsStyleInstall validates a Windows-style Discord installation path.
16+
// This is used for native Windows installs and WSL installs that point to Windows Discord.
17+
// Windows Discord has a nested structure: Discord/app-1.0.9002/modules/discord_desktop_core-1/discord_desktop_core
18+
func validateWindowsStyleInstall(proposed string) *DiscordInstall {
19+
var finalPath = ""
20+
var selected = filepath.Base(proposed)
21+
22+
if strings.HasPrefix(selected, "Discord") {
23+
// Get version dir like app-1.0.9002
24+
dFiles, err := os.ReadDir(proposed)
25+
if err != nil {
26+
return nil
27+
}
28+
29+
candidates := utils.Filter(dFiles, func(file fs.DirEntry) bool {
30+
return file.IsDir() && versionRegex.MatchString(file.Name())
31+
})
32+
if len(candidates) == 0 {
33+
return nil
34+
}
35+
sort.Slice(candidates, func(i, j int) bool { return candidates[i].Name() < candidates[j].Name() })
36+
versionDir := candidates[len(candidates)-1].Name()
37+
38+
// Get core wrap like discord_desktop_core-1
39+
dFiles, err = os.ReadDir(filepath.Join(proposed, versionDir, "modules"))
40+
if err != nil {
41+
return nil
42+
}
43+
candidates = utils.Filter(dFiles, func(file fs.DirEntry) bool {
44+
return file.IsDir() && strings.HasPrefix(file.Name(), "discord_desktop_core")
45+
})
46+
if len(candidates) == 0 {
47+
return nil
48+
}
49+
coreWrap := candidates[len(candidates)-1].Name()
50+
51+
finalPath = filepath.Join(proposed, versionDir, "modules", coreWrap, "discord_desktop_core")
52+
}
53+
54+
// Handle app-* directories (e.g., app-1.0.9002)
55+
if strings.HasPrefix(selected, "app-") {
56+
dFiles, err := os.ReadDir(filepath.Join(proposed, "modules"))
57+
if err != nil {
58+
return nil
59+
}
60+
61+
candidates := utils.Filter(dFiles, func(file fs.DirEntry) bool {
62+
return file.IsDir() && strings.HasPrefix(file.Name(), "discord_desktop_core")
63+
})
64+
if len(candidates) == 0 {
65+
return nil
66+
}
67+
coreWrap := candidates[len(candidates)-1].Name()
68+
finalPath = filepath.Join(proposed, "modules", coreWrap, "discord_desktop_core")
69+
}
70+
71+
if selected == "discord_desktop_core" {
72+
finalPath = proposed
73+
}
74+
75+
// Verify the path and core.asar exist
76+
if utils.Exists(finalPath) && utils.Exists(filepath.Join(finalPath, "core.asar")) {
77+
return &DiscordInstall{
78+
CorePath: finalPath,
79+
Channel: GetChannel(finalPath),
80+
Version: GetVersion(finalPath),
81+
IsFlatpak: false,
82+
IsSnap: false,
83+
}
84+
}
85+
86+
return nil
87+
}
88+
89+
// validateUnixStyleInstall validates a Unix-style Discord installation path (Linux native, macOS).
90+
// Unix Discord has a flatter structure: discord/0.0.35/modules/discord_desktop_core
91+
func validateUnixStyleInstall(proposed string, detectFlatpak bool, detectSnap bool) *DiscordInstall {
92+
var finalPath = ""
93+
var selected = filepath.Base(proposed)
94+
95+
if strings.HasPrefix(strings.ToLower(selected), "discord") {
96+
// Get version dir like 0.0.35
97+
dFiles, err := os.ReadDir(proposed)
98+
if err != nil {
99+
return nil
100+
}
101+
102+
candidates := utils.Filter(dFiles, func(file fs.DirEntry) bool {
103+
return file.IsDir() && versionRegex.MatchString(file.Name())
104+
})
105+
if len(candidates) == 0 {
106+
return nil
107+
}
108+
sort.Slice(candidates, func(i, j int) bool { return candidates[i].Name() < candidates[j].Name() })
109+
versionDir := candidates[len(candidates)-1].Name()
110+
finalPath = filepath.Join(proposed, versionDir, "modules", "discord_desktop_core")
111+
}
112+
113+
// Handle version directories (e.g., 0.0.35)
114+
if len(strings.Split(selected, ".")) == 3 {
115+
finalPath = filepath.Join(proposed, "modules", "discord_desktop_core")
116+
}
117+
118+
if selected == "modules" {
119+
finalPath = filepath.Join(proposed, "discord_desktop_core")
120+
}
121+
122+
if selected == "discord_desktop_core" {
123+
finalPath = proposed
124+
}
125+
126+
// Verify the path and core.asar exist
127+
if utils.Exists(finalPath) && utils.Exists(filepath.Join(finalPath, "core.asar")) {
128+
isFlatpak := false
129+
isSnap := false
130+
131+
if detectFlatpak {
132+
isFlatpak = strings.Contains(finalPath, "com.discordapp.")
133+
}
134+
if detectSnap {
135+
isSnap = strings.Contains(finalPath, "snap/")
136+
}
137+
138+
return &DiscordInstall{
139+
CorePath: finalPath,
140+
Channel: GetChannel(finalPath),
141+
Version: GetVersion(finalPath),
142+
IsFlatpak: isFlatpak,
143+
IsSnap: isSnap,
144+
}
145+
}
146+
147+
return nil
148+
}

internal/discord/paths_darwin.go

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
package discord
22

33
import (
4-
"io/fs"
54
"os"
65
"path/filepath"
7-
"sort"
86
"strings"
97

108
"github.com/betterdiscord/cli/internal/models"
11-
"github.com/betterdiscord/cli/internal/utils"
129
)
1310

1411
func init() {
@@ -30,53 +27,6 @@ func init() {
3027
allDiscordInstalls = GetAllInstalls()
3128
}
3229

33-
/**
34-
* Currently nearly the same as linux validation however
35-
* it is kept separate in case of future changes to
36-
* either system, it is likely that linux will require
37-
* more advanced validation for snap and flatpak.
38-
*/
3930
func Validate(proposed string) *DiscordInstall {
40-
var finalPath = ""
41-
var selected = filepath.Base(proposed)
42-
if strings.HasPrefix(selected, "discord") {
43-
// Get version dir like 1.0.9002
44-
var dFiles, err = os.ReadDir(proposed)
45-
if err != nil {
46-
return nil
47-
}
48-
49-
var candidates = utils.Filter(dFiles, func(file fs.DirEntry) bool { return file.IsDir() && versionRegex.MatchString(file.Name()) })
50-
if len(candidates) == 0 {
51-
return nil
52-
}
53-
sort.Slice(candidates, func(i, j int) bool { return candidates[i].Name() < candidates[j].Name() })
54-
var versionDir = candidates[len(candidates)-1].Name()
55-
finalPath = filepath.Join(proposed, versionDir, "modules", "discord_desktop_core")
56-
}
57-
58-
if len(strings.Split(selected, ".")) == 3 {
59-
finalPath = filepath.Join(proposed, "modules", "discord_desktop_core")
60-
}
61-
62-
if selected == "modules" {
63-
finalPath = filepath.Join(proposed, "discord_desktop_core")
64-
}
65-
66-
if selected == "discord_desktop_core" {
67-
finalPath = proposed
68-
}
69-
70-
// If the path and the asar exist, all good
71-
if utils.Exists(finalPath) && utils.Exists(filepath.Join(finalPath, "core.asar")) {
72-
return &DiscordInstall{
73-
CorePath: finalPath,
74-
Channel: GetChannel(finalPath),
75-
Version: GetVersion(finalPath),
76-
IsFlatpak: false,
77-
IsSnap: false,
78-
}
79-
}
80-
81-
return nil
31+
return validateUnixStyleInstall(proposed, false, false)
8232
}

internal/discord/paths_linux.go

Lines changed: 16 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package discord
22

33
import (
4-
"io/fs"
54
"os"
65
"path/filepath"
7-
"sort"
86
"strings"
97

108
"github.com/betterdiscord/cli/internal/models"
@@ -31,11 +29,16 @@ func init() {
3129
// Core: `snap/discord-canary/current/.config/discordcanary/0.0.90/modules/discord_desktop_core/core.asar`.
3230
// NOTE: Snap user data always exists, even when the Snap isn't mounted/running.
3331
filepath.Join(home, "snap", "{channel-}", "current", ".config", "{channel}"),
32+
}
3433

35-
// WSL. Data is stored under the Windows user's AppData folder.
36-
// Example: `/mnt/c/Users/Username/AppData/Local/DiscordCanary`.
37-
// Core: `/mnt/c/Users/Username/AppData/Local/DiscordCanary/app-1.0.9218/modules/discord_desktop_core-1/discord_desktop_core core.asar`.
38-
filepath.Join(os.Getenv("WIN_HOME"), "AppData", "Local", "{CHANNEL}"),
34+
if utils.IsWSL() {
35+
winHome, err := utils.WindowsHome()
36+
if err == nil && winHome != "" {
37+
// WSL. Data is stored under the Windows user's AppData folder.
38+
// Example: `/mnt/c/Users/Username/AppData/Local/DiscordCanary`.
39+
// Core: `/mnt/c/Users/Username/AppData/Local/DiscordCanary/app-1.0.9218/modules/discord_desktop_core-1/discord_desktop_core core.asar`.
40+
paths = append(paths, filepath.Join(winHome, "AppData", "Local", "{CHANNEL}"))
41+
}
3942
}
4043

4144
for _, channel := range models.Channels {
@@ -53,58 +56,14 @@ func init() {
5356
allDiscordInstalls = GetAllInstalls()
5457
}
5558

56-
/**
57-
* Currently nearly the same as darwin validation however
58-
* it is kept separate in case of future changes to
59-
* either system, it is likely that linux will require
60-
* more advanced validation for snap and flatpak.
61-
*/
59+
// Validate validates a Discord installation path on Linux.
60+
// For WSL environments, it uses Windows-style validation.
61+
// For native Linux, it detects Flatpak and Snap installations.
6262
func Validate(proposed string) *DiscordInstall {
63-
var finalPath = ""
64-
var selected = filepath.Base(proposed)
65-
if strings.HasPrefix(strings.ToLower(selected), "discord") {
66-
// Get version dir like 1.0.9002
67-
var dFiles, err = os.ReadDir(proposed)
68-
if err != nil {
69-
return nil
70-
}
71-
72-
var candidates = utils.Filter(dFiles, func(file fs.DirEntry) bool { return file.IsDir() && versionRegex.MatchString(file.Name()) })
73-
if len(candidates) == 0 {
74-
return nil
75-
}
76-
sort.Slice(candidates, func(i, j int) bool { return candidates[i].Name() < candidates[j].Name() })
77-
var versionDir = candidates[len(candidates)-1].Name()
78-
finalPath = filepath.Join(proposed, versionDir, "modules", "discord_desktop_core")
79-
80-
// WSL installs have an extra layer
81-
if (os.Getenv("WSL_DISTRO_NAME") != "" && os.Getenv("WIN_HOME") != "") && strings.Contains(proposed, "AppData") {
82-
finalPath = filepath.Join(proposed, versionDir, "modules", "discord_desktop_core-1", "discord_desktop_core")
83-
}
84-
}
85-
86-
if len(strings.Split(selected, ".")) == 3 {
87-
finalPath = filepath.Join(proposed, "modules", "discord_desktop_core")
88-
}
89-
90-
if selected == "modules" {
91-
finalPath = filepath.Join(proposed, "discord_desktop_core")
92-
}
93-
94-
if selected == "discord_desktop_core" {
95-
finalPath = proposed
96-
}
97-
98-
// If the path and the asar exist, all good
99-
if utils.Exists(finalPath) && utils.Exists(filepath.Join(finalPath, "core.asar")) {
100-
return &DiscordInstall{
101-
CorePath: finalPath,
102-
Channel: GetChannel(finalPath),
103-
Version: GetVersion(finalPath),
104-
IsFlatpak: strings.Contains(finalPath, "com.discordapp."),
105-
IsSnap: strings.Contains(finalPath, "snap/"),
106-
}
63+
if utils.IsWSL() {
64+
return validateWindowsStyleInstall(proposed)
10765
}
10866

109-
return nil
67+
// Native Linux validation with Flatpak and Snap detection
68+
return validateUnixStyleInstall(proposed, true, true)
11069
}

0 commit comments

Comments
 (0)