feat: support MSIX/UWP browsers on Windows (Arc, DuckDuckGo)#563
feat: support MSIX/UWP browsers on Windows (Arc, DuckDuckGo)#563
Conversation
Add glob pattern support in UserDataDir to handle MSIX/UWP browsers whose package directories contain a dynamic publisher hash suffix (e.g., "TheBrowserCompany.Arc_ttt1ap7aakyb4"). - Add resolveGlobs to expand glob patterns before browser discovery - Add Arc and DuckDuckGo browser configs for Windows - Consolidate pickFromConfigs tests into grouped table-driven structure - Add Glob integration test group covering single/multiple/no match cases
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #563 +/- ##
==========================================
+ Coverage 73.28% 73.45% +0.17%
==========================================
Files 48 48
Lines 1950 1963 +13
==========================================
+ Hits 1429 1442 +13
Misses 384 384
Partials 137 137
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds Windows support for MSIX/UWP-distributed browsers (e.g., Arc, DuckDuckGo) by allowing glob patterns in configured UserDataDir paths so dynamic package-suffix directories can be discovered during browser detection.
Changes:
- Expand
BrowserConfig.UserDataDirviafilepath.Globbefore discovery to support wildcard MSIX/UWP package paths. - Add Windows browser configs for Arc and DuckDuckGo using wildcard
Packages/<AppName>_*paths. - Refactor and extend browser discovery tests with table-driven coverage for glob scenarios.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| browser/browser.go | Adds resolveGlobs and applies it during pickFromConfigs to expand wildcard UserDataDir entries. |
| browser/browser_windows.go | Adds Arc and DuckDuckGo Windows configs using MSIX/UWP Packages/..._* wildcard directories. |
| browser/consts.go | Adds display-name constant for DuckDuckGo. |
| browser/browser_test.go | Consolidates pickFromConfigs tests and adds glob-focused test coverage, plus unit tests for resolveGlobs. |
Comments suppressed due to low confidence (1)
browser/browser.go:67
resolveGlobsis applied before theProfilePathoverride. If a browser config uses a glob (e.g. Arc on Windows) and the user targets that browser withProfilePathset, the configs get expanded to multiple entries and then each entry is overridden to the sameProfilePath, causing duplicate browser discoveries/outputs. Consider skipping glob resolution whenopts.ProfilePath != "" && name != "all", or applying the override before expanding globs (or only resolving globs after filtering to the selected browser).
configs = resolveGlobs(configs)
var browsers []Browser
for _, cfg := range configs {
if name != "all" && cfg.Key != name {
continue
}
// Override profile directory when targeting a specific browser.
if opts.ProfilePath != "" && name != "all" {
if cfg.Kind == types.Firefox {
cfg.UserDataDir = filepath.Dir(filepath.Clean(opts.ProfilePath))
} else {
cfg.UserDataDir = opts.ProfilePath
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // When a glob matches multiple directories, the config is duplicated so | ||
| // each resolved path is treated as a separate browser data directory. | ||
| func resolveGlobs(configs []types.BrowserConfig) []types.BrowserConfig { | ||
| var out []types.BrowserConfig | ||
| for _, cfg := range configs { | ||
| matches, _ := filepath.Glob(cfg.UserDataDir) |
There was a problem hiding this comment.
filepath.Glob can return an error for a malformed pattern (e.g. unmatched [), but the error is currently ignored. This can silently disable discovery for a config with an invalid UserDataDir pattern. Consider handling the error (e.g., log it and fall back to the original config, or change resolveGlobs to return an error that pickFromConfigs can surface).
| // When a glob matches multiple directories, the config is duplicated so | |
| // each resolved path is treated as a separate browser data directory. | |
| func resolveGlobs(configs []types.BrowserConfig) []types.BrowserConfig { | |
| var out []types.BrowserConfig | |
| for _, cfg := range configs { | |
| matches, _ := filepath.Glob(cfg.UserDataDir) | |
| // If a glob pattern is malformed, the error is logged and the original | |
| // config is preserved so discovery behavior remains unchanged while making | |
| // the misconfiguration visible. | |
| // | |
| // When a glob matches multiple directories, the config is duplicated so | |
| // each resolved path is treated as a separate browser data directory. | |
| func resolveGlobs(configs []types.BrowserConfig) []types.BrowserConfig { | |
| var out []types.BrowserConfig | |
| for _, cfg := range configs { | |
| matches, err := filepath.Glob(cfg.UserDataDir) | |
| if err != nil { | |
| log.Errorf("invalid browser UserDataDir glob for %q: %q: %v", cfg.Key, cfg.UserDataDir, err) | |
| out = append(out, cfg) | |
| continue | |
| } |
| name: "glob single match", | ||
| configs: []types.BrowserConfig{ | ||
| {Key: "yandex", Name: "Yandex", Kind: types.ChromiumYandex, UserDataDir: yandexDir}, | ||
| {Key: "single", UserDataDir: filepath.Join(base, "ExactBrow*", "UserData")}, | ||
| }, | ||
| wantNames: []string{"Yandex"}, | ||
| wantProfiles: []string{"Default"}, | ||
| wantDirs: []string{filepath.Join(base, "ExactBrowser", "UserData")}, | ||
| }, | ||
| { |
There was a problem hiding this comment.
Subtest name "literal path not exists preserved" is grammatically awkward; consider renaming to something clearer like "literal path does not exist (preserved)" to improve readability when failures are reported.
Summary
Closes #548
Some Windows browsers are distributed as MSIX/UWP packages (Microsoft Store apps) whose data paths contain a dynamic publisher hash suffix (e.g.,
TheBrowserCompany.Arc_ttt1ap7aakyb4). This PR adds support for these browsers by allowing glob patterns inUserDataDir.resolveGlobsto expand glob patterns in browser configs before discoveryTestPickFromConfigs_*into grouped table-drivenTestPickFromConfigsGlobintegration test group (single match, multiple matches, no match, mixed, name filter)How it works
Browser configs use glob patterns in
UserDataDir:Before browser discovery,
resolveGlobsexpands all configs viafilepath.Glob. Literal paths pass through unchanged; glob patterns expand to one config per match. The mainpickFromConfigsloop remains flat and unmodified.Verified on Windows 10
Test plan
go test -run TestPickFromConfigs/Glob ./browser/— all 5 glob cases passgo test -run TestResolveGlobs ./browser/— all 7 unit cases passgo test ./...— full suite passesgolangci-lint run— 0 issues