feat: bruno app instances handling updates#6748
Conversation
WalkthroughImplements single-instance behavior and centralizes bruno:// protocol handling in the primary instance; adds window focus/restore logic for protocol opens and second-instance events; stores/defers URLs until window is ready; exposes an env toggle via Playwright to disable single-instance for tests. (41 words) Changes
Sequence Diagram(s)sequenceDiagram
participant OS
participant NewInstance
participant PrimaryInstance
participant MainWindow
OS->>NewInstance: user opens bruno:// link or launches app
NewInstance->>PrimaryInstance: attempt single-instance lock / pass argv (second-instance)
alt Lock acquired (primary)
PrimaryInstance->>PrimaryInstance: register bruno:// handler, register MIME (Linux)
PrimaryInstance->>MainWindow: create/restore window
MainWindow->>PrimaryInstance: first-finish-load event
PrimaryInstance->>PrimaryInstance: handle stored appProtocolUrl (if any)
else Lock not acquired (secondary)
NewInstance->>PrimaryInstance: send argv including protocol URL
PrimaryInstance->>PrimaryInstance: extract protocol URL, store if not ready
PrimaryInstance->>MainWindow: focus/restore window
PrimaryInstance->>MainWindow: process protocol URL (when ready)
NewInstance->>NewInstance: quit
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (1)**/*.{js,jsx,ts,tsx}📄 CodeRabbit inference engine (CODING_STANDARDS.md)
Files:
🧠 Learnings (1)📓 Common learnings⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @packages/bruno-app/src/components/Preferences/General/index.js:
- Around line 125-128: When updating the preferences, don't replace the whole
general object—merge it; update the code that sets general to spread the
existing preferences.general first and then override specific keys from
newPreferences (e.g., use general: { ...preferences.general,
defaultCollectionLocation: newPreferences.defaultCollectionLocation,
singleInstance: newPreferences.singleInstance }) so other keys like
defaultWorkspacePath are preserved.
🧹 Nitpick comments (1)
packages/bruno-electron/src/index.js (1)
122-126: Silent failure onxdg-mimecommand.The empty
catchblock swallows all errors. While this is likely intentional (non-critical registration), consider logging at debug level for troubleshooting.♻️ Suggested improvement
if (isLinux) { try { execSync('xdg-mime default bruno.desktop x-scheme-handler/bruno'); - } catch (err) {} + } catch (err) { + console.debug('Failed to set xdg-mime default:', err.message); + } }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/bruno-app/src/components/Preferences/General/index.jspackages/bruno-electron/src/index.jspackages/bruno-electron/src/store/preferences.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-electron/src/store/preferences.jspackages/bruno-electron/src/index.jspackages/bruno-app/src/components/Preferences/General/index.js
🧠 Learnings (1)
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-electron/src/store/preferences.jspackages/bruno-electron/src/index.jspackages/bruno-app/src/components/Preferences/General/index.js
🧬 Code graph analysis (2)
packages/bruno-electron/src/store/preferences.js (2)
packages/bruno-electron/src/app/collections.js (1)
Yup(4-4)packages/bruno-cli/src/runner/prepare-request.js (1)
get(1-1)
packages/bruno-electron/src/index.js (3)
packages/bruno-electron/src/utils/deeplink.js (2)
getAppProtocolUrlFromArgv(4-6)handleAppProtocolUrl(9-15)packages/bruno-electron/src/store/preferences.js (2)
process(280-280)preferencesUtil(248-306)packages/bruno-electron/src/utils/arch.js (1)
isMac(2-2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - macOS
- GitHub Check: SSL Tests - Linux
- GitHub Check: Unit Tests
- GitHub Check: Playwright E2E Tests
- GitHub Check: CLI Tests
🔇 Additional comments (11)
packages/bruno-electron/src/store/preferences.js (3)
52-56: LGTM - Default preferences extended correctly.The
singleInstance: truedefault aligns with the expected behavior where single-instance mode is enabled by default. ThedefaultWorkspacePathaddition appears to be scaffolding for a related feature.
107-111: LGTM - Schema validation is consistent.Both new fields follow the existing validation patterns. The
singleInstanceboolean anddefaultWorkspacePathstring constraints match their counterparts in the defaults.
302-306: LGTM - Utility method follows established patterns.
shouldUseSingleInstance()mirrors the structure of other preference accessors inpreferencesUtil. Default fallback totrueis consistent withdefaultPreferences.packages/bruno-electron/src/index.js (5)
63-63: LGTM - Import added for preferences utility.Clean import placement alongside other store imports.
94-102: LGTM - Helper function is clean and reusable.
focusMainWindowproperly handles the minimized state before focusing. Used in bothopen-urlandsecond-instancehandlers, reducing duplication.
128-147: LGTM - Platform-specific protocol handling.macOS
open-urland cross-platformsecond-instancehandlers are properly implemented. Both focus the main window and delegate URL handling tohandleAppProtocolUrl.
348-352: LGTM - Lock release on quit.Properly guarded with both
useSingleInstanceandgotTheLockchecks to avoid releasing a lock that was never acquired.
107-113: No issue—electron-store handles the early preference read safely.The
clearInvalidConfig: trueoption in the Store constructor automatically clears corrupted or inaccessible preference files. Even if that occurs, the code has nested fallback defaults: the store returns an empty object, which merges withdefaultPreferences, andshouldUseSingleInstance()itself provides another fallback default oftrue. The call is safe at module initialization, and no unhandled startup failure is possible.packages/bruno-app/src/components/Preferences/General/index.js (3)
63-65: LGTM - Schema validation added correctly.
singleInstance: Yup.boolean()follows the existing validation pattern for boolean preferences.
87-89: LGTM - Initial value extraction is consistent.Uses
get()with defaulttrue, matching the server-side default inpreferences.js.
304-316: LGTM - UI implementation is clean.Checkbox follows the established pattern. The "(requires restart)" note in the label clearly communicates the behavior to users.
| general: { | ||
| defaultCollectionLocation: newPreferences.defaultCollectionLocation | ||
| defaultCollectionLocation: newPreferences.defaultCollectionLocation, | ||
| singleInstance: newPreferences.singleInstance | ||
| } |
There was a problem hiding this comment.
Potential data loss: general object is replaced, not merged.
The general object is completely overwritten, which would discard any other properties (e.g., defaultWorkspacePath added in preferences.js). Spread the existing general properties to preserve them.
🐛 Proposed fix
general: {
+ ...preferences.general,
defaultCollectionLocation: newPreferences.defaultCollectionLocation,
singleInstance: newPreferences.singleInstance
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| general: { | |
| defaultCollectionLocation: newPreferences.defaultCollectionLocation | |
| defaultCollectionLocation: newPreferences.defaultCollectionLocation, | |
| singleInstance: newPreferences.singleInstance | |
| } | |
| general: { | |
| ...preferences.general, | |
| defaultCollectionLocation: newPreferences.defaultCollectionLocation, | |
| singleInstance: newPreferences.singleInstance | |
| } |
🤖 Prompt for AI Agents
In @packages/bruno-app/src/components/Preferences/General/index.js around lines
125 - 128, When updating the preferences, don't replace the whole general
object—merge it; update the code that sets general to spread the existing
preferences.general first and then override specific keys from newPreferences
(e.g., use general: { ...preferences.general, defaultCollectionLocation:
newPreferences.defaultCollectionLocation, singleInstance:
newPreferences.singleInstance }) so other keys like defaultWorkspacePath are
preserved.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In @packages/bruno-electron/src/index.js:
- Around line 122-126: The empty catch swallowing errors around the execSync
call hides failures when registering the xdg-mime handler; update the catch for
the execSync('xdg-mime default bruno.desktop x-scheme-handler/bruno') inside the
isLinux branch to log the error (e.g., console.error or the app logger) with a
clear message like "Failed to register xdg-mime handler for bruno" and include
the caught err object so failures are visible for debugging while preserving the
existing control flow.
- Line 63: The import const { preferencesUtil } =
require('./store/preferences'); is unused in this file; remove that unused
import line (or, if you intended to use it, wire up preferencesUtil where
needed—e.g., calls to preferencesUtil.get/set in the initialization logic) so
the symbol preferencesUtil is either referenced or eliminated to avoid dead code
and linter complaints.
- Around line 128-137: The open-url handler may call handleAppProtocolUrl with a
null mainWindow if the window isn't created yet; change the app.on('open-url')
callback to queue the incoming URL when mainWindow is falsy (store it in a
module-scoped pendingProtocolUrl variable) and still call focusMainWindow(),
then after mainWindow is created/ready (in the code that constructs mainWindow
or in a ready/ready-to-show handler) check pendingProtocolUrl and call
handleAppProtocolUrl(pendingProtocolUrl, mainWindow) and clear the pending
value; ensure any existing calls to focusMainWindow or handleAppProtocolUrl
check for mainWindow and use the queued URL flow so protocol URLs are processed
only once the window exists.
- Line 134: Calls to handleAppProtocolUrl are passing an extra mainWindow
argument even though the function signature handleAppProtocolUrl(url) only
accepts a single url parameter; update each call site that invokes
handleAppProtocolUrl(...) (including the calls currently passing two arguments)
to pass only the url argument, removing the unused mainWindow parameter so the
call matches the handleAppProtocolUrl function signature.
🧹 Nitpick comments (2)
packages/bruno-electron/src/index.js (2)
139-147: Same null reference risk insecond-instancehandler.While less likely,
mainWindowcould theoretically benullhere too. Add a guard for defensive coding.Proposed fix
// Handle second instance attempts - focus primary window on all platforms app.on('second-instance', (event, commandLine) => { - focusMainWindow(); // Extract and handle protocol URL from the second instance attempt const url = getAppProtocolUrlFromArgv(commandLine); - if (url) { - handleAppProtocolUrl(url, mainWindow); + if (mainWindow) { + focusMainWindow(); + if (url) { + handleAppProtocolUrl(url, mainWindow); + } + } else if (url) { + appProtocolUrl = url; } });
349-352: Redundant lock release.Electron automatically releases the single instance lock when the app terminates. This explicit release is harmless but unnecessary.
If you prefer explicit cleanup for clarity, that's fine. Otherwise, these lines can be removed.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/bruno-electron/src/index.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-electron/src/index.js
🧠 Learnings (2)
📚 Learning: 2026-01-09T18:25:23.407Z
Learnt from: kanakkholwal
Repo: usebruno/bruno PR: 6767
File: packages/bruno-app/src/components/ResponseExample/index.js:221-226
Timestamp: 2026-01-09T18:25:23.407Z
Learning: Bruno is an Electron-only application, so `window.ipcRenderer` will always be available and does not require existence checks.
Applied to files:
packages/bruno-electron/src/index.js
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-electron/src/index.js
🧬 Code graph analysis (1)
packages/bruno-electron/src/index.js (3)
packages/bruno-electron/src/utils/deeplink.js (2)
getAppProtocolUrlFromArgv(4-6)handleAppProtocolUrl(9-15)packages/bruno-electron/src/utils/arch.js (1)
isMac(2-2)packages/bruno-electron/src/ipc/network/index.js (4)
url(173-173)url(187-187)url(201-201)url(215-215)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: SSL Tests - Linux
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - macOS
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
- GitHub Check: Playwright E2E Tests
🔇 Additional comments (1)
packages/bruno-electron/src/index.js (1)
94-102: LGTM!Clean helper extraction. Properly handles the minimized state before focusing.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @packages/bruno-electron/src/index.js:
- Around line 121-125: The current isLinux block swallows errors from the
execSync('xdg-mime default bruno.desktop x-scheme-handler/bruno') call; update
the catch to log a warning with the caught error so protocol handler
registration failures are visible (e.g., in the catch for the execSync
invocation inside the isLinux conditional, call the app or console logger to
emit a warning message that includes err and context like "Failed to register
x-scheme-handler/bruno").
🧹 Nitpick comments (1)
packages/bruno-electron/src/index.js (1)
127-151: LGTM!Protocol handling logic correctly covers both macOS
open-urlevents andsecond-instancescenarios. Storing the URL when the window isn't ready prevents URL loss during app startup.Minor: Consider prefixing the unused
eventparameter with underscore on line 144 (_event) to signal intent.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/bruno-electron/src/index.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-electron/src/index.js
🧠 Learnings (3)
📚 Learning: 2026-01-09T18:25:23.407Z
Learnt from: kanakkholwal
Repo: usebruno/bruno PR: 6767
File: packages/bruno-app/src/components/ResponseExample/index.js:221-226
Timestamp: 2026-01-09T18:25:23.407Z
Learning: Bruno is an Electron-only application, so `window.ipcRenderer` will always be available and does not require existence checks.
Applied to files:
packages/bruno-electron/src/index.js
📚 Learning: 2025-12-02T07:24:50.311Z
Learnt from: bijin-bruno
Repo: usebruno/bruno PR: 6263
File: packages/bruno-requests/src/auth/oauth2-helper.ts:249-249
Timestamp: 2025-12-02T07:24:50.311Z
Learning: In OAuth2 Basic Auth headers for Bruno, clientSecret is optional and can be omitted. When constructing the Authorization header in `packages/bruno-requests/src/auth/oauth2-helper.ts`, use `clientSecret || ''` instead of `clientSecret!` to properly handle cases where only clientId is provided, per community requests.
Applied to files:
packages/bruno-electron/src/index.js
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-electron/src/index.js
🧬 Code graph analysis (1)
packages/bruno-electron/src/index.js (5)
packages/bruno-electron/src/utils/deeplink.js (2)
getAppProtocolUrlFromArgv(4-6)handleAppProtocolUrl(9-15)packages/bruno-electron/src/utils/arch.js (1)
isMac(2-2)packages/bruno-electron/src/ipc/network/interpolate-vars.js (1)
url(153-153)packages/bruno-electron/src/utils/oauth2.js (4)
url(155-155)url(382-382)url(510-510)url(645-645)packages/bruno-electron/src/ipc/network/prepare-request.js (1)
url(312-312)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Unit Tests
- GitHub Check: CLI Tests
- GitHub Check: Playwright E2E Tests
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - macOS
- GitHub Check: SSL Tests - Linux
🔇 Additional comments (4)
packages/bruno-electron/src/index.js (4)
93-101: LGTM!Clean extraction of the window focus/restore logic. Used appropriately in both the
open-urlandsecond-instancehandlers.
106-119: LGTM!Single instance lock logic is correctly implemented. The environment variable toggle provides a useful escape hatch for debugging or specific deployment scenarios.
352-356: Verify: Early lock release may cause race conditions.Releasing the single instance lock in
before-quit(before windows close and the process exits) creates a window where another instance could acquire the lock and start initializing while this instance is still shutting down.Electron automatically releases the lock when the process terminates. Is there a specific scenario requiring this early release? If not, consider removing these lines to avoid potential race conditions.
295-299: LGTM!Call signature correctly matches the
handleAppProtocolUrlfunction definition which only requires the URL parameter.
| if (isLinux) { | ||
| try { | ||
| execSync('xdg-mime default bruno.desktop x-scheme-handler/bruno'); | ||
| } catch (err) {} | ||
| } |
There was a problem hiding this comment.
Silent error swallowing hinders debugging.
The empty catch block discards errors from the xdg-mime command. While the app can continue without this registration, logging a warning would help diagnose protocol handler issues on Linux systems.
Proposed fix
if (isLinux) {
try {
execSync('xdg-mime default bruno.desktop x-scheme-handler/bruno');
- } catch (err) {}
+ } catch (err) {
+ console.warn('Failed to register xdg-mime handler for bruno://', err.message);
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (isLinux) { | |
| try { | |
| execSync('xdg-mime default bruno.desktop x-scheme-handler/bruno'); | |
| } catch (err) {} | |
| } | |
| if (isLinux) { | |
| try { | |
| execSync('xdg-mime default bruno.desktop x-scheme-handler/bruno'); | |
| } catch (err) { | |
| console.warn('Failed to register xdg-mime handler for bruno://', err.message); | |
| } | |
| } |
🤖 Prompt for AI Agents
In @packages/bruno-electron/src/index.js around lines 121 - 125, The current
isLinux block swallows errors from the execSync('xdg-mime default bruno.desktop
x-scheme-handler/bruno') call; update the catch to log a warning with the caught
error so protocol handler registration failures are visible (e.g., in the catch
for the execSync invocation inside the isLinux conditional, call the app or
console logger to emit a warning message that includes err and context like
"Failed to register x-scheme-handler/bruno").
…_handling feat: `bruno app instances` handling updates
Description
Contribution Checklist:
Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.
Publishing to New Package Managers
Please see here for more information.
Summary by CodeRabbit
New Features
Bug Fixes
Chores / Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.