Rework injection to shadow Discord's app.asar - #421
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors the BetterDiscord installer to use a new injection method that shadows Discord's app.asar with a custom app/ directory in the resources folder, rather than modifying index.js inside discord_desktop_core. This change updates path resolution, validation, and installation logic across Windows, macOS, and Linux (including Flatpak, while explicitly dropping Snap support due to its read-only nature). The review feedback suggests improving error handling by logging failures during rollback renames and cleaning up stale betterdiscord.app.asar files during uninjection if a fresh app.asar has already been restored.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
This PR reworks BetterDiscord injection to shadow Discord’s resources/app.asar (preserving it as betterdiscord.app.asar and inserting a resources/app/ loader), aiming to load BetterDiscord before Discord’s updater and improve survivability across versioned updates. It updates discovery/validation to anchor on resources/, renames CorePath → ResourcesPath throughout, refreshes frontend state accordingly, and expands test coverage for the new behaviors.
Changes:
- Replace the prior
discord_desktop_core-based injection with a transactionalapp.asarshadow (inject/uninject/IsInjected) plus static loader assets. - Rework install discovery/validation to resolve the
resourcesdirectory (including injected state) and preferresources/build_info.jsonfor channel/version, with per-OS default browse directories. - Update frontend + API surfaces to use
resourcePaths/ResourcesPath, and add/extend unit tests to cover reinjection/rollback and path resolution.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates Linux/Flatpak/Snap support notes and documents the new injection approach. |
| frontend/src/routes/actions/setup/[action]/+page.svelte | Renames state usage from corePaths to resourcePaths in setup flow. |
| frontend/src/routes/actions/perform/[action]/+page.svelte | Builds install path map from resourcePaths instead of corePaths. |
| frontend/src/lib/types.ts | Renames AppState.corePaths → resourcePaths. |
| frontend/src/lib/stores/state.svelte.ts | Initializes and populates resourcePaths from GetDiscordPath. |
| api/controller.go | Renames action params to resourcePaths, uses DefaultBrowseDir(), returns ResourcesPath. |
| discord/paths.go | Updates suggested/resolve logic to use ResourcesPath and improves channel parsing for macOS bundle names. |
| discord/paths_windows.go | Adds DefaultBrowseDir() and keeps Windows path discovery aligned with resources/. |
| discord/paths_linux.go | Updates Linux discovery roots for resources-based validation and adds DefaultBrowseDir() (incl. WSL). |
| discord/paths_darwin.go | Changes macOS discovery to search .app bundles and adds DefaultBrowseDir(). |
| discord/paths_common.go | Introduces resolveResources, build_info parsing, injected-state recognition, and resources-anchored validators. |
| discord/paths_test.go | Adds GetChannel tests for macOS bundle naming. |
| discord/paths_common_test.go | Replaces core.asar tests with app.asar/resources tests; adds coverage for injected resolvability and build_info preference. |
| discord/install.go | Renames CorePath → ResourcesPath and recomputes Flatpak BD data root from channel. |
| discord/install_test.go | Updates uninstall and BD-path tests for resources-based injection + Flatpak recompute logic. |
| discord/injection.go | Implements transactional app.asar shadow injection + uninject and updated IsInjected. |
| discord/injection_test.go | Adds tests for clean/idempotent injection, rollback, probe abort, repair/reinject, and post-injection resolvability. |
| discord/assets/app_index.js | New loader that loads BetterDiscord then chain-loads the preserved asar. |
| discord/assets/app_package.json | Minimal package.json for the injected resources/app/ entrypoint. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
/gemini review for the last time |
There was a problem hiding this comment.
Code Review
This pull request refactors the BetterDiscord installer to inject into Discord by shadowing app.asar with a custom app/ directory rather than modifying index.js inside discord_desktop_core. This changes path tracking from CorePath to ResourcesPath across the Go backend and Svelte frontend, updates Flatpak path resolution, and drops Snap support due to read-only mounts. Feedback on these changes highlights critical robustness issues: empty ResourcesPath or input paths can cause operations (like write probing, uninjecting, and resource resolution) to run relative to the current working directory, risking accidental data loss. Additionally, the feedback points out a regression in DefaultBrowseDir where the fallback to HOME was lost, and a potential nil pointer dereference if discord.Channel is nil.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
/gemini review the fixes |
There was a problem hiding this comment.
Code Review
This pull request refactors the installer to target Discord's resources directory instead of the discord_desktop_core module, implementing a new injection method that renames the original app.asar and uses a shadow app/ directory loader. It also updates path resolution, validation, and Flatpak configuration across platforms while dropping Snap support due to read-only mounts. The review feedback suggests wrapping the BetterDiscord require statement in a try-catch block to prevent Discord from failing to launch if the asar is corrupted, and refining the Snap path detection heuristic to avoid false positives.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
/gemini review final |
There was a problem hiding this comment.
Code Review
This pull request refactors the BetterDiscord installer to use a new injection method that shadows Discord's app.asar with a custom app/ directory containing a loader script, rather than modifying index.js inside discord_desktop_core. This change involves renaming CorePath to ResourcesPath across the codebase, updating path resolution logic to target the resources directory directly (including reading build_info.json for channel and version details), and stopping the Discord process before performing injection or uninjection to avoid file lock issues. Additionally, macOS bundle search paths and Flatpak path resolution have been updated, while Snap support has been removed due to its read-only filesystem. One review comment was evaluated and kept as it correctly identifies a potential runtime error in the injection script if process.env.HOME is undefined, offering a robust fallback using require("os").homedir().
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
/gemini review the fixes |
There was a problem hiding this comment.
Code Review
This pull request refactors the BetterDiscord installer to target Discord's resources directory directly, shadowing app.asar with a custom loader folder (app/) and renaming the original to betterdiscord.app.asar. It updates path resolution across Windows, macOS, and Linux to support this new injection method, removes Snap support due to read-only constraints, and updates Flatpak path handling. The frontend is updated to use resourcePaths instead of corePaths, and extensive unit tests are added. Review feedback highlights the need to parse channel names from right to left to avoid false positives from parent directories, and suggests adding error checks for os.UserHomeDir() and os.UserConfigDir() to prevent relative path fallbacks on macOS and Linux.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| for folder := range strings.SplitSeq(proposed, string(filepath.Separator)) { | ||
| // Normalize the segment so macOS bundle names ("Discord Canary.app") and | ||
| // flatpak channel dirs ("discord-canary") both match the channel names | ||
| // ("discordcanary"). | ||
| normalized := strings.ToLower(folder) | ||
| normalized = strings.TrimSuffix(normalized, ".app") | ||
| normalized = strings.ReplaceAll(normalized, " ", "") | ||
| normalized = strings.ReplaceAll(normalized, "-", "") | ||
| for _, channel := range types.Channels { | ||
| if strings.ToLower(folder) == strings.ReplaceAll(strings.ToLower(channel.Name()), " ", "") { | ||
| if normalized == strings.ReplaceAll(strings.ToLower(channel.Name()), " ", "") { | ||
| return channel | ||
| } | ||
| } |
There was a problem hiding this comment.
Parsing the channel by iterating through path segments from left to right can lead to false positives if any parent directory contains a channel name (for example, if the user's home directory is /home/discord or if they have a folder named discord in their path). Iterating from right to left (backwards) is much more robust because the channel identifier is always closest to the leaf of the path.
segments := strings.Split(proposed, string(filepath.Separator))
for i := len(segments) - 1; i >= 0; i-- {
folder := segments[i]
// Normalize the segment so macOS bundle names ("Discord Canary.app") and
// flatpak channel dirs ("discord-canary") both match the channel names
// ("discordcanary").
normalized := strings.ToLower(folder)
normalized = strings.TrimSuffix(normalized, ".app")
normalized = strings.ReplaceAll(normalized, " ", "")
normalized = strings.ReplaceAll(normalized, "-", "")
for _, channel := range types.Channels {
if normalized == strings.ReplaceAll(strings.ToLower(channel.Name()), " ", "") {
return channel
}
}
}|
/gemini review pls |
There was a problem hiding this comment.
Code Review
This pull request refactors the BetterDiscord installer to target the Discord resources directory (containing app.asar) instead of the nested discord_desktop_core directory, updating both the Go backend and Svelte frontend. It introduces a new injection method that renames the original app.asar to betterdiscord.app.asar and shadows it with a custom app/ loader directory. The changes also include transactional rollbacks on failure, writability probes, process lock management, and improved path resolution across Windows, macOS, and Linux (including Flatpak). One high-severity issue was identified in the BrowseForDiscord controller, where manually browsed custom installations are ignored because ResolvePath is used instead of AddCustomPath.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request refactors the BetterDiscord installer to use a new injection method that shadows Discord's app.asar with a custom app/ loader directory, transitioning from tracking CorePath to ResourcesPath. It also adds explicit rejections for Snap installs, improves Flatpak path resolution, and enhances process management by waiting for Discord to fully exit before modifying files. The review feedback highlights three key areas for improvement: resolving a compilation failure on Windows caused by using os.Geteuid() in tests, mitigating a potential bricking risk in uninject() by restoring app.asar before deleting the shadow directory, and improving cross-platform path splitting robustness by utilizing filepath.ToSlash.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
/gemini review more |
There was a problem hiding this comment.
Code Review
This pull request transitions the BetterDiscord installer from injecting into discord_desktop_core to shadowing Discord's app.asar via a shadow app/ directory. It updates path resolution across all platforms to target the resources directory, introduces robust process management to stop Discord and release file locks before modification, and explicitly rejects unsupported Snap installs. The review feedback correctly identifies a compilation failure on Windows in the test suite due to the use of os.Geteuid(), which is not supported on Windows, and suggests a cross-platform alternative using os/user.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Human Sumary
Switches the injection to the Discord resources folder to match the intended injection swap by Doggybootsy. This will in theory allow the client code to hook the updater and survive versioned updates. Also added some additional logic and handling for uninstall and reinjection cases. Added a bunch more testcases.
Important Caveats
Clanker Summary
Migrates the installer's injection method from overwriting
discord_desktop_core/index.js(a downloadable Discord module) to shadowing Discord's ownapp.asarin the install'sresources/directory.For each install we now:
resources/app.asar→resources/betterdiscord.app.asar(preserving the original),resources/app/with a staticpackage.json({"main":"./index.js"}) andindex.jsthat loads BetterDiscord and then chain-loads the preserved app.Why
The point isn't just a different location — it's when BetterDiscord's code runs. Shadowing
app.asarmakes BD load as (part of) the app entry point, before Discord's updater runs. Under the olddiscord_desktop_coreinjection, Discord's updater ran first, so BD couldn't react to updates. Now the BD client can hook the updater and re-propagate its own injection across Discord updates — so the installer is generally run once rather than after every Discord update.What changed
Discovery / validation (
discord/paths_*.go,paths_common.go)resources/app.asarvia a sharedresolveResourcesdrill that accepts a Discord root, anapp-{version}dir, aresourcesdir, or a macOS.appbundle.resources/build_info.json(with path-parsing fallback) — needed on macOS/flatpak where the path carries no version segment.GetChannelalso understands macOS bundle names (Discord Canary.app)./Applications+~/Applicationsbundles; flatpak →/var/lib/flatpak+~/.local/share/flatpakdeploymentfiles/…/resources; Windows/Linux-native/WSL roots unchanged (only the drill changed).DefaultBrowseDir()added per-OS so the "browse for Discord" dialog opens where Discord actually lives now.Injection (
discord/injection.go,discord/assets/)inject/uninject/IsInjectedoperating on theapp.asarshadow, with a writability probe before the destructive rename and rollback on any post-rename failure (transactional: fully injected or fully reverted).app.asaris always treated as authoritative: re-injecting over a Discord repair/reinstall discards a stale preserved copy and re-preserves the current app, so injection can't silently no-op.app_index.js+app_package.json(runtime resolution of the BetterDiscord folder — no templating).Data folder (
discord/install.go)~/.var/app/{id}/configfrom the channel (the deployment tree has noconfigsegment to derive from).Types / frontend
DiscordInstall.CorePath→ResourcesPath; the frontendcorePathsstore →resourcePaths.Docs
Platform support
.appbundle; no elevation/Gatekeeper issue seen (monitoring across versions).~/.config/{channel}/app-*/resources./var/lib/flatpakis root-owned; needs elevation (not implemented — see below).Known limitations / behavior changes
pkexecfor flatpak,osascriptfor non-admin macOS), which is intentionally deferred as reactive.app.asarshadow impossible.Testing
go test ./...,go vet ./...,task check, frontendbun run test, andGOOS=darwin/GOOS=windowscross-compile — all green.build_info.jsonsourcing, macOS bundle resolution, flatpak data-root recompute, and — importantly — that an injected install stays resolvable (so repair/uninstall keep working after injection).