feat: workspace .env file support#6777
Conversation
WalkthroughAdds workspace-level .env support: Electron watches workspace Changes
Sequence DiagramsequenceDiagram
participant Electron as Electron (Main)
participant Watcher as WorkspaceWatcher
participant ProcEnv as ProcessEnvStore
participant IPC as IPC (main->renderer)
participant Redux as Redux Store
participant App as Renderer App
Electron->>Watcher: start watcher for workspace/.env
Watcher->>Watcher: parse .env on add/change/unlink
Watcher->>ProcEnv: setWorkspaceDotEnvVars / clearWorkspaceDotEnvVars
ProcEnv->>IPC: emit main:workspace-dotenv-update (workspaceUid, vars)
IPC->>Redux: dispatch workspaceDotEnvUpdateEvent
Redux->>Redux: update workspace.processEnvVariables
IPC->>Redux: dispatch workspaceEnvUpdateEvent
Redux->>Redux: update all collections.workspaceProcessEnvVariables
App->>App: getAllVariables merges workspace + collection envs
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
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
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: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js (1)
171-195: ChangeworkspacePathtoworkspaceIdin the options object passed toopenMultipleCollections.The collections reducer expects
options.workspaceId, notoptions.workspacePath(see collections/actions.js:2366, 2402). Update line 173 to pass{ workspaceId: workspacePath }for consistency with the rest of the codebase.The helper function
openCollectionsFunctioncan also be simplified toopenCollectionsfor brevity, but the option key mismatch is the critical issue.packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (1)
400-413:workspaceEnvUpdateEventapplies workspace env to all collections without workspace scoping and shares the same object reference.The reducer (lines 408–413) iterates through all collections in
state.collectionsand assigns the sameprocessEnvVariablesobject to each. In a multi-workspace setup, this pollutes collections belonging to other workspaces and creates a shared reference that can lead to unintended mutations. The action payload contains no workspace identifier to filter by, so scoping is required at the reducer level.Clone the payload per assignment and consider filtering collections by workspace identity (if available in your collection model) or passing workspace context through the action payload.
🧹 Nitpick comments (2)
packages/bruno-electron/src/ipc/collection.js (1)
823-835: Redundant require forgenerateUidBasedOnHash.
generateUidBasedOnHashis already imported at line 53. The dynamic require at line 828 is unnecessary.♻️ Suggested fix
if (options.workspacePath) { const { setCollectionWorkspace } = require('../store/process-env'); - const { generateUidBasedOnHash } = require('../utils/common'); for (const collectionPath of collectionPaths) { const collectionUid = generateUidBasedOnHash(collectionPath); setCollectionWorkspace(collectionUid, options.workspacePath); } }packages/bruno-electron/src/store/process-env.js (1)
20-28: Consider defensive fallback for workspace env vars.If
workspacePathis truthy butworkspaceDotEnvVars[workspacePath]hasn't been set yet,workspaceEnvVarswill beundefined. Spreadingundefinedworks in JS (results in{}), but this is subtle behavior. A small tweak makes intent clearer:Suggested improvement
const getProcessEnvVars = (collectionUid) => { const workspacePath = collectionWorkspaceMap[collectionUid]; - const workspaceEnvVars = workspacePath ? workspaceDotEnvVars[workspacePath] : {}; + const workspaceEnvVars = (workspacePath && workspaceDotEnvVars[workspacePath]) || {}; return { ...process.env, ...workspaceEnvVars, ...dotEnvVars[collectionUid] }; };
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
packages/bruno-app/src/providers/App/useIpcEvents.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/actions.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/index.jspackages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.jspackages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.jspackages/bruno-app/src/utils/collections/export.jspackages/bruno-app/src/utils/collections/index.jspackages/bruno-electron/src/app/workspace-watcher.jspackages/bruno-electron/src/ipc/collection.jspackages/bruno-electron/src/store/process-env.jspackages/bruno-schema/src/collections/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-app/src/utils/collections/index.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/index.jspackages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.jspackages/bruno-electron/src/ipc/collection.jspackages/bruno-schema/src/collections/index.jspackages/bruno-app/src/utils/collections/export.jspackages/bruno-electron/src/store/process-env.jspackages/bruno-app/src/providers/App/useIpcEvents.jspackages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.jspackages/bruno-electron/src/app/workspace-watcher.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
🧠 Learnings (4)
📚 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-app/src/utils/collections/index.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/index.jspackages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.jspackages/bruno-electron/src/ipc/collection.jspackages/bruno-schema/src/collections/index.jspackages/bruno-app/src/utils/collections/export.jspackages/bruno-electron/src/store/process-env.jspackages/bruno-app/src/providers/App/useIpcEvents.jspackages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.jspackages/bruno-electron/src/app/workspace-watcher.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
📚 Learning: 2026-01-09T18:25:14.640Z
Learnt from: kanakkholwal
Repo: usebruno/bruno PR: 6767
File: packages/bruno-app/src/components/ResponseExample/index.js:221-226
Timestamp: 2026-01-09T18:25:14.640Z
Learning: In the Bruno Electron renderer code (packages/bruno-app), assume window.ipcRenderer is always available and skip existence checks. Do not guard for ipcRenderer in this Electron context; use window.ipcRenderer directly (e.g., window.ipcRenderer.send(...), window.ipcRenderer.on(...)). If there are non-Electron contexts (such as test environments or non-Electron builds), add guards or mocks to avoid runtime errors there, but for the intended Electron renderer files, this pattern should be applied broadly within packages/bruno-app.
Applied to files:
packages/bruno-app/src/utils/collections/index.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/index.jspackages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.jspackages/bruno-app/src/utils/collections/export.jspackages/bruno-app/src/providers/App/useIpcEvents.jspackages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
📚 Learning: 2026-01-07T18:53:34.291Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6735
File: packages/bruno-electron/src/ipc/collection.js:936-939
Timestamp: 2026-01-07T18:53:34.291Z
Learning: In the bruno repository, the Redux reducer determines collection format by checking for the `opencollection` property in brunoConfig. The `version` property is not used for format detection, so having both `version` and `opencollection` properties together doesn't cause issues since `opencollection` takes precedence.
Applied to files:
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
📚 Learning: 2025-12-16T07:16:23.647Z
Learnt from: sanish-bruno
Repo: usebruno/bruno PR: 6090
File: tests/scripting/hooks/init-user-data/ui-state-snapshot.json:1-8
Timestamp: 2025-12-16T07:16:23.647Z
Learning: For e2e tests in the bruno repository: Collections that are shared between CLI and UI tests (comprehensive test suites testing core functionality) should be placed in `packages/bruno-tests/` to avoid duplication. The `tests/**/fixtures/collection` pattern should be used for test-specific collections that test particular UI behaviors or are specific to a single test file.
Applied to files:
packages/bruno-app/src/utils/collections/export.js
🧬 Code graph analysis (3)
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (2)
packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js (4)
action(49-49)action(62-62)action(74-74)action(82-82)packages/bruno-app/src/utils/collections/index.js (5)
collection(587-587)collection(1175-1175)collection(1177-1177)collection(1612-1612)collection(1622-1622)
packages/bruno-electron/src/ipc/collection.js (3)
packages/bruno-electron/src/app/collections.js (1)
openCollectionsByPathname(148-168)packages/bruno-app/src/utils/common/index.js (2)
generateUidBasedOnHash(187-191)generateUidBasedOnHash(187-191)packages/bruno-electron/src/store/process-env.js (3)
setCollectionWorkspace(43-45)workspacePath(21-21)clearCollectionWorkspace(47-49)
packages/bruno-electron/src/app/workspace-watcher.js (2)
packages/bruno-electron/src/store/process-env.js (3)
workspacePath(21-21)setWorkspaceDotEnvVars(35-37)clearWorkspaceDotEnvVars(39-41)packages/bruno-filestore/src/index.ts (1)
parseDotEnv(133-135)
⏰ 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 - macOS
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
- GitHub Check: Playwright E2E Tests
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
🔇 Additional comments (27)
packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js (2)
419-422: LGTM:workspaceConfigUpdatedEventnow passes{ workspacePath: workspace.pathname }to avoid ambiguous “id”.
461-473: Not a bug:createCollectionInWorkspacecorrectly passesworkspaceId.The
createCollectionfunction explicitly expects and handlesoptions.workspaceId(it even defaults to active workspace's pathname or 'default' if missing). The code at line 471 is correct. There's a naming inconsistency elsewhere in the codebase (workspacePathvsworkspaceId), but that's a separate concern from this function call.Likely an incorrect or invalid review comment.
packages/bruno-app/src/utils/collections/export.js (1)
96-116: Good: exported collections won’t persist workspace-scoped process env (workspaceProcessEnvVariables).packages/bruno-schema/src/collections/index.js (1)
598-618: Schema addition looks correct:workspaceProcessEnvVariablesdefaults to{}and allows arbitrary keys.packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (1)
3412-3434: Action export wiring looks fine (workspaceEnvUpdateEventis exported).packages/bruno-app/src/utils/collections/index.js (1)
1169-1229: The merge precedence is confirmed—collectionprocessEnvVariablesintentionally override workspace ones, as documented inpackages/bruno-electron/src/store/process-env.js("Priority: collection .env > workspace .env > OS process.env") and validated by tests infetch-gql-schema-handler.spec.js. The inline comment and implementation are both correct.packages/bruno-app/src/providers/App/useIpcEvents.js (3)
18-27: LGTM! Imports correctly added for workspace environment handling.New imports for
workspaceEnvUpdateEventandworkspaceDotEnvUpdateEventare properly sourced from their respective Redux slices.
219-222: LGTM! Workspace dotenv IPC listener correctly wired.The handler properly dispatches both
workspaceDotEnvUpdateEvent(updates workspace state) andworkspaceEnvUpdateEvent(propagates to collections). Payload structure aligns with reducer expectations.
303-303: LGTM! Cleanup properly registered.The listener removal is correctly added to the effect cleanup function.
packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js (2)
79-87: LGTM! Reducer correctly implemented.The
workspaceDotEnvUpdateEventreducer properly locates the workspace by UID and updatesprocessEnvVariables. Guard check prevents errors if workspace isn't found.
98-99: LGTM! Action export added.New action properly exported alongside existing actions.
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (5)
38-38: LGTM! Import added for collection-level workspace env propagation.
2259-2259: LGTM! Safe extraction of workspace process env variables.Optional chaining ensures graceful handling when
activeWorkspaceis undefined.
2300-2302: LGTM! Existing collection receives workspace env vars.When an existing collection is added to the current workspace, dispatching
workspaceEnvUpdateEventensures all collections receive the workspace's process env variables.
2315-2315: LGTM! New collection initialized with workspace env vars.The
workspaceProcessEnvVariablesfield is properly included in the newly created collection object.
2334-2336: LGTM! Collection-workspace mapping established via IPC.The IPC call correctly associates the collection UID with the workspace path, enabling the electron side to track which workspace a collection belongs to for env var resolution.
packages/bruno-electron/src/ipc/collection.js (2)
837-842: LGTM! New IPC handler for setting collection-workspace mapping.Handler correctly guards
workspacePathbefore invokingsetCollectionWorkspace.
853-856: LGTM! Cleanup on collection removal.
clearCollectionWorkspaceis correctly called to clean up the collection-workspace association when a collection is removed.packages/bruno-electron/src/app/workspace-watcher.js (7)
8-11: LGTM! Required imports added for .env handling.
parseDotEnvfor parsing andsetWorkspaceDotEnvVars/clearWorkspaceDotEnvVarsfor state management are correctly imported.
126-158: LGTM! Well-structured .env file handlers.Both
handleWorkspaceDotEnvFileandhandleWorkspaceDotEnvUnlinkfollow the existing patterns in this file:
- Proper error handling with try/catch
- State updates via store functions
- IPC notifications to renderer
- Clean handling of missing files via early return
164-164: LGTM! dotEnvWatchers map added to track .env file watchers.
179-181: LGTM! Existing dotEnv watcher cleanup before creating new one.Prevents potential resource leaks when
addWatcheris called multiple times for the same workspace.
189-190: LGTM! Initial .env load on watcher creation.This ensures existing .env files are loaded when a workspace is opened, not just when they change.
210-224: LGTM! dotEnv watcher properly configured.The watcher handles
add,change, andunlinkevents with the sameawaitWriteFinishoptions as other watchers in this file, ensuring consistency.
278-283: LGTM! Complete cleanup in removeWatcher.Properly closes watcher, removes from map, and clears workspace env vars to prevent stale state.
packages/bruno-electron/src/store/process-env.js (2)
35-49: Clean implementation of workspace env management.The new functions follow the established pattern and are appropriately concise. Ensure callers invoke
clearWorkspaceDotEnvVarsandclearCollectionWorkspacewhen workspaces/collections are closed to prevent memory buildup over extended sessions.
51-58: Exports are complete.All new APIs are properly exported. No trailing comma, consistent with coding guidelines.
sid-bruno
left a comment
There was a problem hiding this comment.
An overall comment
Why not stick to using the workspaceId for everything and then using that to find pathname where necessary instead of having to jump between the 2 based on usecase
Wouldn't it make it easier to keep the ipcCalls more consistent and traceable?
| ignorePermissionErrors: true, | ||
| awaitWriteFinish: { | ||
| stabilityThreshold: 80, | ||
| pollInterval: 10 |
There was a problem hiding this comment.
pollInterval can be higher, 250ms, 10 becomes too much
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/bruno-electron/src/app/workspace-watcher.js (1)
210-224: Consider adding an error handler and aligning watcher config.Two observations:
The
pollInterval: 250differs from the workspace watcher'spollInterval: 10. If intentional (e.g., .env changes less frequently), a comment would help clarify.No
errorevent handler is attached. While consistent with other watchers in this file, chokidar can emit errors on permission issues or fs failures. Consider adding a handler for robustness.♻️ Optional: Add error handling
dotEnvWatcher.on('add', () => handleWorkspaceDotEnvFile(win, workspacePath, workspaceUid)); dotEnvWatcher.on('change', () => handleWorkspaceDotEnvFile(win, workspacePath, workspaceUid)); dotEnvWatcher.on('unlink', () => handleWorkspaceDotEnvUnlink(win, workspacePath, workspaceUid)); + dotEnvWatcher.on('error', (error) => console.error('Error watching workspace .env file:', error));
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/bruno-electron/src/app/workspace-watcher.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/app/workspace-watcher.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/app/workspace-watcher.js
⏰ 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: CLI Tests
- GitHub Check: Playwright E2E Tests
🔇 Additional comments (6)
packages/bruno-electron/src/app/workspace-watcher.js (6)
8-11: LGTM!Imports are properly structured and consistent with the existing patterns in the file.
126-145: LGTM!Clean implementation that follows the established patterns in this file. The existence check before reading, parsing, and IPC dispatch all look correct.
147-158: LGTM!Proper cleanup and notification when the .env file is removed.
160-165: LGTM!Consistent with the existing watcher map pattern.
278-283: LGTM!Proper cleanup sequence: close watcher, remove reference, clear stored env vars. The renderer likely handles workspace removal separately, so not sending an IPC message here is appropriate.
189-191: LGTM!Good placement—loading the initial .env before setting up the watcher (with
ignoreInitial: true) prevents duplicate processing.
|
LGTM |
Description
Screen.Recording.2026-01-12.at.11.48.57.AM.mov
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
✏️ Tip: You can customize this high-level summary in your review settings.