Skip to content

feat(scripting): persist environment variable changes from scripts by default#8186

Open
sanish-bruno wants to merge 12 commits into
usebruno:mainfrom
sanish-bruno:feat/persist-envs-by-default
Open

feat(scripting): persist environment variable changes from scripts by default#8186
sanish-bruno wants to merge 12 commits into
usebruno:mainfrom
sanish-bruno:feat/persist-envs-by-default

Conversation

@sanish-bruno

@sanish-bruno sanish-bruno commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Resolves BRU-3096

Summary

Makes all bru.* environment, global environment, and collection variable setter/deleter APIs persist changes to disk by default. Removes the old opt-in { persist: true } flag from bru.setEnvVar(). Enables previously disabled APIs.

APIs now persisting by default

  • bru.setEnvVar(key, value) — persistence parameter removed, always persists
  • bru.setGlobalEnvVar(key, value) — already persisted, no change
  • bru.deleteEnvVar(key) — now persists deletion
  • bru.deleteGlobalEnvVar(key) — previously disabled, now enabled and persists
  • bru.deleteAllGlobalEnvVars() — previously disabled, now enabled and persists
  • bru.setCollectionVar(key, value) — previously disabled, now enabled and persists
  • bru.deleteCollectionVar(key) — previously disabled, now enabled and persists
  • bru.deleteAllCollectionVars() — previously disabled, now enabled and persists
  • bru.getAllCollectionVars() — previously disabled, now enabled (read-only)

What was removed (dead weight)

The entire persistentEnvVariables opt-in mechanism:

  • persistentEnvVariables property on the Bru class
  • main:persistent-env-variables-update IPC event (all send locations + listener)
  • mergeAndPersistEnvironment Redux action
  • ephemeral / persistedValue overlay system in scriptEnvironmentUpdateEvent reducer
  • buildPersistedEnvVariables merge mode in environments.js

What was added

  • main:collection-variables-update IPC event — new persistence path for collection variables
  • persistActiveEnvironment thunk — saves active env from Redux state after script execution
  • persistActiveGlobalEnvironment thunk — saves active global env after script execution
  • collectionVariablesUpdateEvent thunk — syncs collection variable changes to disk
  • Simplified scriptEnvironmentUpdateEvent reducer — direct value updates, no overlay
  • globalEnvironmentsUpdateEvent now handles deletions (fixes the "UI sync issue" that kept deleteGlobalEnvVar disabled)

Architecture change

Before (two parallel IPC flows):

Script result
  ├→ main:script-environment-update      → Redux in-memory (ephemeral overlay)
  └→ main:persistent-env-variables-update → mergeAndPersistEnvironment → disk

After (single flow, always persist):

Script result
  └→ main:script-environment-update → Redux update (direct) → save to disk

Re-run safety (idempotency)

  • set* on existing key: silent overwrite
  • delete* on missing key: silent no-op
  • deleteAll* on empty scope: silent no-op

Holds across single request, collection runner, and CLI execution.

Collection variable scope

bru.setCollectionVar() / bru.deleteCollectionVar() operate on pre-request variables (request.vars.req), not post-response expression variables.

CLI behavior

The CLI has never persisted variable changes to disk — not even with the old { persist: true } flag. The persistentEnvVariables mechanism was entirely Electron/renderer-side. This PR does not change CLI behavior. CLI persistence is tracked separately in BRU-3096.

Bug fixes included

  • Runner folder test flow was missing the persistence event — fixed by the unified approach
  • deleteGlobalEnvVar / deleteAllGlobalEnvVars now properly sync with the UI (previously disabled due to "UI sync issue")

Breaking Change

  • bru.setEnvVar(key, value) now persists to disk by default
  • The { persist: true/false } options parameter is removed (extra args are silently ignored by JS)
  • Scripts that relied on non-persistent setEnvVar will now see changes saved to the environment file

Files changed (persistence-related)

Area Files
Core API packages/bruno-js/src/bru.js
QuickJS Shim packages/bruno-js/src/sandbox/quickjs/shims/bru.js
Script Results script-runtime.js, test-runtime.js, vars-runtime.js
IPC Handler packages/bruno-electron/src/ipc/network/index.js
Redux Reducer collections/index.js
Redux Actions collections/actions.js
Global Env global-environments.js
IPC Listeners useIpcEvents.js
Utils environments.js, collections/index.js
Unit Tests setEnvVar.spec.js, runtime.spec.js, collectionVar.spec.js (new)
API Tests bruno-tests/collection/scripting/api/bru/ — unskipped + added deleteEnvVar.bru
E2E Tests tests/environments/api-setEnvVar/ — updated fixtures + expectations

Test plan

  • npm run test --workspace=packages/bruno-js — 523 tests pass
  • npm run test --workspace=packages/bruno-app — 955 tests pass
  • npm run lint — 0 errors
  • Manual: bru.setEnvVar("k", "v") persists to env file after request
  • Manual: bru.deleteEnvVar("k") removes from env file
  • Manual: bru.deleteGlobalEnvVar("k") removes from global env
  • Manual: bru.setCollectionVar("k", "v") persists to collection root
  • Manual: Re-run same script twice — no errors, no spurious toasts
  • E2E: npm run test:e2e (api-setEnvVar suite)

🤖 Generated with Claude Code

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Summary by CodeRabbit

  • New Features

    • Scripting APIs: set/get/delete for collection and global variables are enabled and reflected in scripts/runs.
    • Script runs now surface collection-scoped variables alongside global environment variables.
  • Refactor

    • Simplified env API: the prior persist option was removed — script-set environment variables persist by default.
  • Tests

    • Added and updated end-to-end and unit tests/fixtures to cover variable persistence and scripting APIs.

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Refactors script-driven variable persistence: replaces per-call persistentEnvVariables with collectionVariables, simplifies persisted-variable shaping, updates IPC/runtime contracts, splits store updates from persistence side-effects, and re-enables Bru collection/global variable APIs with matching tests and fixtures.

Changes

Environment Persistence and Variable Flow Rework

Layer / File(s) Summary
Persistence utility and collection save callsites
packages/bruno-app/src/utils/environments.js, packages/bruno-app/src/utils/collections/index.js
buildPersistedEnvVariables always strips internal metadata and callsites no longer pass mode flags.
Bru class API and QuickJS shim
packages/bruno-js/src/bru.js, packages/bruno-js/src/sandbox/quickjs/shims/bru.js
setEnvVar signature simplified to (key, value); persistentEnvVariables tracking removed; global/collection var APIs (delete*, setCollectionVar, getAllCollectionVars, etc.) re-enabled.
Autocomplete hints
packages/bruno-app/src/utils/codemirror/autocomplete.js
Static bru.* API hints updated to include collection/global var methods.
Runtime result payload shape updates
packages/bruno-js/src/runtime/script-runtime.js, packages/bruno-js/src/runtime/test-runtime.js, packages/bruno-js/src/runtime/vars-runtime.js
Runtimes now include collectionVariables (cleaned) in results and remove persistentEnvVariables.
Main-process IPC emissions with collection variables
packages/bruno-electron/src/ipc/network/index.js
IPC payloads include collectionVariables; main:persistent-env-variables-update removed and replaced by main:global-environment-variables-update + main:collection-variables-update (with collectionUid).
Collection persistence actions
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
saveEnvironment uses simplified buildPersistedEnvVariables; mergeAndPersistEnvironment replaced by persistActiveEnvironment (persists current active env) and collectionVariablesUpdateEvent (reconciles and persists collection variables via renderer IPC).
Collection reducer update
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
scriptEnvironmentUpdateEvent no longer relies on ephemeral/persisted overlays; it applies script variables, creates/updates variables, and prunes enabled variables absent from script output while preserving disabled ones.
Global environment update logic
packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js
globalEnvironmentsUpdateEvent updates the store synchronously and performs validation+IPC persistence asynchronously (errors logged).
IPC listener wiring in App provider
packages/bruno-app/src/providers/App/useIpcEvents.js
Listeners updated to dispatch scriptEnvironmentUpdateEvent, globalEnvironmentsUpdateEvent, and collectionVariablesUpdateEvent; cleanup removes the new collection-variables listener.
Unit and runtime test updates
packages/bruno-js/tests/*
Tests updated to remove legacy persist-option expectations, assert non-string value support, validate deletion behavior, and confirm collectionVariables presence and persistentEnvVariables absence.
E2E tests and fixtures
tests/**, packages/bruno-tests/**
Playwright suites and .bru fixtures updated to two-argument bru.setEnvVar, expect default persistence, remove pre-request skip stubs, and add tests for safe/developer mode persistence and deletion non-throwing semantics.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • usebruno/bruno#6723: Related changes to buildPersistedEnvVariables and persisted vs ephemeral variable handling.
  • usebruno/bruno#7012: Touches the same environment persistence pipeline (saveEnvironment / utils).
  • usebruno/bruno#6703: Overlaps with scriptEnvironmentUpdateEvent reducer changes for script-deleted variable behavior.

Suggested reviewers

  • helloanoop
  • lohit-bruno
  • naman-bruno
  • bijin-bruno
  • sid-bruno

"A refactor hums where variables dance,
Old options fall; collection vars advance.
IPC and runtimes now sing the same tune,
Tests applaud beneath the PR moon."

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The PR title accurately describes the main change: transitioning environment variable persistence from opt-in to automatic by default across scripting APIs.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- Renamed the guide to "Writing Playwright Tests for Bruno" for clarity.
- Reorganized the table of contents for better navigation.
- Expanded sections on test creation, structure, and best practices.
- Added detailed instructions for using codegen and organizing test files.
- Introduced new sections on test patterns and recipes for common scenarios.
- Updated prerequisites and setup instructions for running tests.
@sanish-bruno sanish-bruno force-pushed the feat/persist-envs-by-default branch from 4d51255 to 2e4cb5c Compare June 4, 2026 18:09
@sanish-bruno sanish-bruno changed the title refactor(tests): update Playwright testing guide structure and content feat: persist environment variable changes from scripts by default Jun 4, 2026
@sanish-bruno sanish-bruno changed the title feat: persist environment variable changes from scripts by default feat(env): persist environment variable changes from scripts by default Jun 4, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/bruno-js/src/runtime/test-runtime.js (1)

62-70: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep runTests() return shape consistent in the no-tests branch.

Line 62 returns early without collectionVariables, while the main return path now includes it (Line 130). This creates a branch-dependent runtime contract and can break downstream consumers when a request has no test script.

🔧 Proposed fix
     if (!testsFile || !testsFile.length) {
       return {
         request,
-        envVariables,
-        runtimeVariables,
-        globalEnvironmentVariables,
+        envVariables: cleanJson(envVariables),
+        runtimeVariables: cleanJson(runtimeVariables),
+        collectionVariables: cleanJson(collectionVariables),
+        globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
         results: __brunoTestResults.getResults(),
         nextRequestName: bru.nextRequest
       };
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bruno-js/src/runtime/test-runtime.js` around lines 62 - 70, The
early return in runTests() when testsFile is empty returns a payload missing
collectionVariables; update the no-tests branch to include collectionVariables
(same variable used in the main return) so the function's return shape is
consistent—ensure the early return object includes request, envVariables,
runtimeVariables, globalEnvironmentVariables, collectionVariables, results:
__brunoTestResults.getResults(), and nextRequestName: bru.nextRequest.
🧹 Nitpick comments (2)
tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts (1)

34-50: ⚡ Quick win

Align test naming/path with new default-persist behavior.

Line 34 and Line 50 now validate persistence across restart, but the spec/fixture naming still says without-persist, which makes intent misleading. Please rename the spec title/path (and matching fixture names) to reflect default persistence semantics.

As per coding guidelines: "E2E tests must verify user-visible behaviour... and app-level outcomes." Clear naming should match that verified outcome.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts`
around lines 34 - 50, The test currently asserts that environment variables
persist across restart but is named "api-setEnvVar-without-persist" which is
misleading; rename the spec filename and any referenced fixture/test identifiers
(for example the visible collection name string 'api-setEnvVar-without-persist'
used in the locator and any fixture files) to a name that reflects persistence
(e.g., 'api-setEnvVar-with-persist' or similar), update any matching fixture
names and references in the test (selectors, displayed collection name, and test
title) so the spec name/path and fixture names align with the verified behavior,
and run the suite to ensure selectors like '`#sidebar-collection-name`' and the
'Environments' tab still resolve after renaming.
packages/bruno-js/tests/setEnvVar.spec.js (1)

20-30: ⚡ Quick win

Add explicit falsy-value coverage in allows non-string values.

This test is close, but it should also lock behavior for 0, false, and "" to prevent regressions around falsy handling.

Suggested additions
   test('allows non-string values', () => {
     const bru = makeBru();
     bru.setEnvVar('count', 42);
     expect(bru.envVariables.count).toBe(42);

     bru.setEnvVar('active', true);
     expect(bru.envVariables.active).toBe(true);

     bru.setEnvVar('config', { port: 3000 });
     expect(bru.envVariables.config).toEqual({ port: 3000 });
+
+    bru.setEnvVar('zero', 0);
+    expect(bru.envVariables.zero).toBe(0);
+
+    bru.setEnvVar('inactive', false);
+    expect(bru.envVariables.inactive).toBe(false);
+
+    bru.setEnvVar('empty', '');
+    expect(bru.envVariables.empty).toBe('');
   });

Based on learnings: “falsy values are legitimate environment variable values and should not be filtered out.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bruno-js/tests/setEnvVar.spec.js` around lines 20 - 30, Update the
"allows non-string values" test to include explicit assertions for falsy values
so they are not filtered out: after creating bru via makeBru() and before or
after the existing non-string checks, call bru.setEnvVar('zero', 0),
bru.setEnvVar('flag', false), and bru.setEnvVar('empty', ''), then assert
bru.envVariables.zero === 0, bru.envVariables.flag === false, and
bru.envVariables.empty === '' to lock behavior for falsy values when using
setEnvVar.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/bruno-app/src/components/Preferences/General/index.js`:
- Around line 314-326: The new checkbox input with id/name
"scripting.envVariablesPersistEnabled" used by Formik lacks a stable Playwright
selector; add a data-testid attribute to the input (e.g.,
data-testid="scripting.envVariablesPersistEnabled") so E2E tests can target it
reliably, ensuring the attribute is placed on the same input element that binds
to formik.values.scripting.envVariablesPersistEnabled and formik.handleChange.

In `@packages/bruno-app/src/providers/App/useIpcEvents.js`:
- Around line 211-215: The ipcRenderer listener
(removeGlobalEnvironmentVariablesUpdateListener callback) calls
persistActiveGlobalEnvironment() but does not handle its returned Promise, so
rejections become unhandled; update the listener to handle the Promise by
chaining .catch(...) (or using async/await with try/catch) on
persistActiveGlobalEnvironment() and in the catch block dispatch an
error-handling action or show a user-visible notification/log (e.g., dispatch a
persist failure action or call the app's toast/error logger) so schema or IPC
save failures are observed and surfaced to the user.

In `@packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js`:
- Around line 416-419: The current code mutates activeEnvironment.variables by
filtering out enabled vars not present in scriptVarNames, which causes
request-scoped deletions (from deleteEnvVar/deleteAllEnvVars after setEnvVar in
a run) to be persisted to disk; fix by introducing a separate persisted
baseline/sentinel (e.g., persistedVariables or baselineEnv) and change the
save/merge logic so activeEnvironment.variables continues to represent the
request-scoped view while persistedVariables is only updated by explicit
persistent operations — when merging, do not remove an enabled var from
persistedVariables merely because scriptVarNames lacks it; instead, persist
additions/updates from setEnvVar but ignore request-only deletes unless a new
persistent-delete API is invoked (update code paths around
activeEnvironment.variables, scriptVarNames, deleteEnvVar, deleteAllEnvVars, and
the persistence/save routine).

In `@packages/bruno-electron/src/ipc/network/index.js`:
- Around line 557-575: The IPC persistence payloads (events
'main:script-environment-update', 'main:global-environment-variables-update',
'main:collection-variables-update' and the other similar blocks) are missing the
concrete environment identifiers captured at request start, so late IPC messages
may persist to the currently active environment instead of the one used for the
request; update the payloads to include the stable IDs from the request context
(e.g., include scriptResult.environmentUid and scriptResult.globalEnvironmentUid
or whatever the request-start fields are) alongside
requestUid/collectionUid/persist so the renderer can persist against those exact
environment/global-environment IDs rather than resolving active state on
receipt.

In `@packages/bruno-js/tests/runtime.spec.js`:
- Around line 203-209: The test currently uses
expect(result.persistentEnvVariables).toBeUndefined() which still passes if the
property exists but is set to undefined; update the assertion to assert the
property is absent instead — for example, replace that line with a
property-level check such as
expect(result).not.toHaveProperty('persistentEnvVariables') (or assert that
'persistentEnvVariables' in result is false) in the test that calls
ScriptRuntime.runRequestScript so it verifies the field is not present at all.

---

Outside diff comments:
In `@packages/bruno-js/src/runtime/test-runtime.js`:
- Around line 62-70: The early return in runTests() when testsFile is empty
returns a payload missing collectionVariables; update the no-tests branch to
include collectionVariables (same variable used in the main return) so the
function's return shape is consistent—ensure the early return object includes
request, envVariables, runtimeVariables, globalEnvironmentVariables,
collectionVariables, results: __brunoTestResults.getResults(), and
nextRequestName: bru.nextRequest.

---

Nitpick comments:
In `@packages/bruno-js/tests/setEnvVar.spec.js`:
- Around line 20-30: Update the "allows non-string values" test to include
explicit assertions for falsy values so they are not filtered out: after
creating bru via makeBru() and before or after the existing non-string checks,
call bru.setEnvVar('zero', 0), bru.setEnvVar('flag', false), and
bru.setEnvVar('empty', ''), then assert bru.envVariables.zero === 0,
bru.envVariables.flag === false, and bru.envVariables.empty === '' to lock
behavior for falsy values when using setEnvVar.

In `@tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts`:
- Around line 34-50: The test currently asserts that environment variables
persist across restart but is named "api-setEnvVar-without-persist" which is
misleading; rename the spec filename and any referenced fixture/test identifiers
(for example the visible collection name string 'api-setEnvVar-without-persist'
used in the locator and any fixture files) to a name that reflects persistence
(e.g., 'api-setEnvVar-with-persist' or similar), update any matching fixture
names and references in the test (selectors, displayed collection name, and test
title) so the spec name/path and fixture names align with the verified behavior,
and run the suite to ensure selectors like '`#sidebar-collection-name`' and the
'Environments' tab still resolve after renaming.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 492640cb-64ee-43d3-b18b-91329a8ff513

📥 Commits

Reviewing files that changed from the base of the PR and between b70bfb2 and 2e4cb5c.

📒 Files selected for processing (21)
  • packages/bruno-app/src/components/Preferences/General/index.js
  • packages/bruno-app/src/providers/App/useIpcEvents.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js
  • packages/bruno-app/src/utils/collections/index.js
  • packages/bruno-app/src/utils/environments.js
  • packages/bruno-electron/src/ipc/network/index.js
  • packages/bruno-electron/src/store/preferences.js
  • packages/bruno-js/src/bru.js
  • packages/bruno-js/src/runtime/script-runtime.js
  • packages/bruno-js/src/runtime/test-runtime.js
  • packages/bruno-js/src/runtime/vars-runtime.js
  • packages/bruno-js/src/sandbox/quickjs/shims/bru.js
  • packages/bruno-js/tests/runtime.spec.js
  • packages/bruno-js/tests/setEnvVar.spec.js
  • tests/environments/api-setEnvVar/api-setEnvVar-with-persist.spec.ts
  • tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts
  • tests/environments/api-setEnvVar/fixtures/collection/api-setEnvVar-with-persist.bru
  • tests/environments/api-setEnvVar/fixtures/collection/multiple-persist-vars-folder/multiple-persist-vars-1.bru
  • tests/environments/api-setEnvVar/fixtures/collection/multiple-persist-vars-folder/multiple-persist-vars-2.bru

Comment thread packages/bruno-app/src/components/Preferences/General/index.js Outdated
Comment thread packages/bruno-app/src/providers/App/useIpcEvents.js Outdated
Comment thread packages/bruno-electron/src/ipc/network/index.js
Comment thread packages/bruno-js/tests/runtime.spec.js Outdated
- Eliminated the scripting environment persistence option from preferences and related components.
- Updated form validation and state management to reflect the removal of the scripting section.
- Adjusted IPC event handling to simplify environment updates by removing unnecessary persistence checks.
- Cleaned up related code in Redux actions and network IPC to enhance clarity and maintainability.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (3)

2263-2272: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return or handle these async persistence calls.

Both thunks fire async saves and then return undefined. That makes persistence failures from saveEnvironment() / saveCollectionRoot() invisible to the IPC listener path and can surface as unhandled rejections with no user feedback.

Minimal fix
 export const persistActiveEnvironment = (collectionUid) => (dispatch, getState) => {
   const state = getState();
   const collection = findCollectionByUid(state.collections.collections, collectionUid);
   if (!collection) return;

   const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid);
   if (!environment) return;

-  dispatch(saveEnvironment(environment.variables, environment.uid, collectionUid));
+  return dispatch(saveEnvironment(environment.variables, environment.uid, collectionUid));
 };
@@
-  dispatch(saveCollectionRoot(collectionUid));
+  return dispatch(saveCollectionRoot(collectionUid));
 };

Also applies to: 2313-2314

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js`
around lines 2263 - 2272, persistActiveEnvironment currently fires the async
thunk saveEnvironment and then returns undefined, hiding failures; change it to
return the async result so callers/IPCs can observe errors (e.g., return the
promise from dispatch(saveEnvironment(...)) or await and rethrow). Update
persistActiveEnvironment (and the similar persist function referenced around
saveCollectionRoot) to return the dispatch(...) result and/or await
dispatch(...) inside a try/catch and propagate or rethrow the error so
persistence failures are visible to the IPC listener.

2313-2313: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This request-driven path is saving the whole collection draft.

saveCollectionRoot() persists collection.draft when it exists, so a script-side setCollectionVar() can implicitly write unrelated unsaved collection settings to disk as a side effect of running a request.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js` at
line 2313, The dispatch(saveCollectionRoot(collectionUid)) call is persisting
collection.draft and thus can write unrelated unsaved settings when run from a
script-driven setCollectionVar; change this to only persist request-level
changes instead of the whole draft: either call or implement and call a
dedicated action like saveCollectionRequests(collectionUid) that writes only the
requests/vars, or add a parameter to saveCollectionRoot(collectionUid, {
persistDraft: false }) and update saveCollectionRoot to skip saving
collection.draft when that flag is set; update the caller here to use the
non-draft-saving variant so setCollectionVar() no longer has side effects.

2300-2314: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Deleted collection vars never reach Redux before you persist.

After vars = vars.filter(...), this thunk only upserts the survivors. Any enabled variable removed by the script is still present in Redux, so saveCollectionRoot(collectionUid) serializes the stale entry right back to disk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js`
around lines 2300 - 2314, After filtering out script-deleted vars you only
upsert survivors, so enabled vars removed by the script remain in Redux and get
re-saved; fix by computing the set of surviving var UIDs after vars =
vars.filter(...), read the current request vars via get(root,
'request.vars.req', []), and for each existing var (ev) that is enabled and
whose uid is not in the surviving set dispatch the matching removal action
(e.g., removeCollectionVar({ collectionUid, type: 'request', var: ev }) or the
project's remove action), then continue with the existing upsert logic using
updateCollectionVar/addCollectionVar and finally
dispatch(saveCollectionRoot(collectionUid)).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js`:
- Around line 2263-2272: persistActiveEnvironment currently fires the async
thunk saveEnvironment and then returns undefined, hiding failures; change it to
return the async result so callers/IPCs can observe errors (e.g., return the
promise from dispatch(saveEnvironment(...)) or await and rethrow). Update
persistActiveEnvironment (and the similar persist function referenced around
saveCollectionRoot) to return the dispatch(...) result and/or await
dispatch(...) inside a try/catch and propagate or rethrow the error so
persistence failures are visible to the IPC listener.
- Line 2313: The dispatch(saveCollectionRoot(collectionUid)) call is persisting
collection.draft and thus can write unrelated unsaved settings when run from a
script-driven setCollectionVar; change this to only persist request-level
changes instead of the whole draft: either call or implement and call a
dedicated action like saveCollectionRequests(collectionUid) that writes only the
requests/vars, or add a parameter to saveCollectionRoot(collectionUid, {
persistDraft: false }) and update saveCollectionRoot to skip saving
collection.draft when that flag is set; update the caller here to use the
non-draft-saving variant so setCollectionVar() no longer has side effects.
- Around line 2300-2314: After filtering out script-deleted vars you only upsert
survivors, so enabled vars removed by the script remain in Redux and get
re-saved; fix by computing the set of surviving var UIDs after vars =
vars.filter(...), read the current request vars via get(root,
'request.vars.req', []), and for each existing var (ev) that is enabled and
whose uid is not in the surviving set dispatch the matching removal action
(e.g., removeCollectionVar({ collectionUid, type: 'request', var: ev }) or the
project's remove action), then continue with the existing upsert logic using
updateCollectionVar/addCollectionVar and finally
dispatch(saveCollectionRoot(collectionUid)).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3fcbd240-ea53-4f6e-bd60-45f08eee282f

📥 Commits

Reviewing files that changed from the base of the PR and between 2e4cb5c and 0a28af1.

📒 Files selected for processing (3)
  • packages/bruno-app/src/providers/App/useIpcEvents.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
  • packages/bruno-electron/src/ipc/network/index.js

- Removed the explicit save to disk action from the collection variable update event.
- Implemented silent saving of collection state using IPC, enhancing user experience by eliminating unnecessary notifications.
- Improved code clarity by separating state update logic from persistence handling.
… APIs

- Introduced a new test suite for collection variable functionalities, covering set, get, check existence, delete, and retrieve all variables.
- Added tests to ensure proper handling of edge cases, such as empty keys and invalid characters.
- Enhanced existing test cases for delete operations to confirm no errors occur when deleting non-existent keys.
- Updated related test scripts to ensure they do not skip execution due to UI update issues.
@sanish-bruno sanish-bruno changed the title feat(env): persist environment variable changes from scripts by default feat: persist environment variable changes from scripts by default (BRU-3096) Jun 9, 2026
- Introduced a new test suite for global environment variable functionalities, covering set, get, check existence, delete, and retrieve all variables.
- Added tests to ensure proper handling of edge cases, such as empty keys and non-string values.
- Implemented tests for persistence of environment variables in both developer and safe modes.
- Created associated Bru scripts to validate the functionality of setting and retrieving global variables.
…riable deletion and persistence

- Introduced a new test suite to validate the deletion of global environment variables using the bru.deleteGlobalEnvVar() function.
- Added tests to ensure that deleted variables are no longer visible and that existing variables remain unchanged.
- Created necessary fixture files for the test collection and environment setup.
- Enhanced locators to support new test functionalities in the environment UI.
…dant tests

- Updated the baseUrl in the global environment variable configuration to point to the testbench URL.
- Removed tests verifying the existence of the baseUrl variable as it is no longer necessary.
- Cleaned up related test steps to streamline the global environment variable persistence tests.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@tests/scripting/bru-api/global-env-var-persistence/global-env-var-persistence.spec.ts`:
- Around line 27-31: The test currently calls
locators.environment.variableValue('baseUrl').textContent() and asserts on the
string which can be null or stale; instead assert directly on the locator so
Playwright can wait for stable text. Replace the textContent extraction and
expect(value).toContain(...) with an assertion on the locator returned by
locators.environment.variableValue('baseUrl') using Playwright's locator
matchers (e.g., toContainText or toHaveText/regex) to check for
'https://testbench-sanity.usebruno.com', keeping the locator expression
unchanged.

In
`@tests/scripting/bru-api/variable-persistence-safe-mode/variable-persistence-safe-mode.spec.ts`:
- Around line 30-31: The test currently calls
locators.environment.variableValue('persistedToken').textContent() and asserts
the returned string; replace this unstable DOM read with a locator-based
assertion by using Playwright's locator expect on the same locator
(locators.environment.variableValue) and assert the visible text contains
"test-value-123" (e.g., use toHaveText/toContainText on the locator) so the
assertion waits/retries and avoids null reads.
- Around line 39-40: Combine the two separate file checks into a single polled
assertion so the test waits until both persisted strings appear together: use
expect.poll(() => fs.readFileSync(envFilePath, 'utf8'), { timeout:
PERSISTENCE_TIMEOUT }).toSatisfy(text => text.includes('persistedToken') &&
text.includes('test-value-123')). Also replace any element.textContent()
followed by expect(value).toContain(...) with a direct Playwright locator
assertion (e.g. use expect(locator).toContainText('...') or
expect(locator).toHaveText(...) on the specific Locator) to avoid null/timing
races.

In `@tests/scripting/bru-api/variable-persistence/variable-persistence.spec.ts`:
- Around line 30-31: Replace the current nullable textContent() usage and string
assertion with Playwright's locator assertion: stop calling await
locators.environment.variableValue('persistedToken').textContent() and instead
assert directly on the locator using toContainText, e.g. use
locators.environment.variableValue('persistedToken') with
expect(...).toContainText('test-value-123'); this removes nullable textContent
handling and enables Playwright's auto-waiting on the locator.
- Around line 39-40: Combine the two-file assertions into a single polling
assertion: replace the pair of calls that first await expect.poll(() =>
fs.readFileSync(envFilePath, 'utf8'), { timeout: PERSISTENCE_TIMEOUT
}).toContain('persistedToken') and then immediately call
expect(fs.readFileSync(envFilePath, 'utf8')).toContain('test-value-123') with
one expect.poll that reads envFilePath and asserts the content contains both
'persistedToken' and 'test-value-123' (or polls a boolean that checks both),
using the same PERSISTENCE_TIMEOUT; remove the immediate second fs.readFileSync.
Apply the identical change to the collection.bru checks (collectionFilePath) so
each persisted *.bru file is validated with a single expect.poll that checks for
both expected substrings.

In `@tests/utils/page/locators.ts`:
- Around line 79-80: The current locator collectionEnvTab matches the substring
"Environments" inside "Global Environments"; update both locators to use
anchored regular expressions so they match the whole tab text exactly: change
collectionEnvTab to use page.locator('.request-tab').filter({ hasText:
/^Environments$/ }) and change globalEnvTab to
page.locator('.request-tab').filter({ hasText: /^Global Environments$/ }) so the
two functions (collectionEnvTab and globalEnvTab) no longer overlap and
strict-mode clicks/hover will target the correct tab.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f798985e-0c04-42d5-afad-5f37965f4018

📥 Commits

Reviewing files that changed from the base of the PR and between 3bda481 and a0d4e6a.

📒 Files selected for processing (24)
  • packages/bruno-js/tests/globalEnvVar.spec.js
  • packages/bruno-js/tests/runtime.spec.js
  • packages/bruno-tests/collection/scripting/api/bru/setGlobalEnvVar.bru
  • tests/scripting/bru-api/global-env-var-persistence/fixtures/collections/global-env-var-persistence-test/bruno.json
  • tests/scripting/bru-api/global-env-var-persistence/fixtures/collections/global-env-var-persistence-test/delete-global-env-var.bru
  • tests/scripting/bru-api/global-env-var-persistence/global-env-var-persistence.spec.ts
  • tests/scripting/bru-api/global-env-var-persistence/init-user-data/collection-security.json
  • tests/scripting/bru-api/global-env-var-persistence/init-user-data/global-environments.json
  • tests/scripting/bru-api/global-env-var-persistence/init-user-data/preferences.json
  • tests/scripting/bru-api/variable-persistence-safe-mode/fixtures/collections/variable-persistence-safe-test/bruno.json
  • tests/scripting/bru-api/variable-persistence-safe-mode/fixtures/collections/variable-persistence-safe-test/environments/Test.bru
  • tests/scripting/bru-api/variable-persistence-safe-mode/fixtures/collections/variable-persistence-safe-test/set-collection-var.bru
  • tests/scripting/bru-api/variable-persistence-safe-mode/fixtures/collections/variable-persistence-safe-test/set-env-var.bru
  • tests/scripting/bru-api/variable-persistence-safe-mode/init-user-data/collection-security.json
  • tests/scripting/bru-api/variable-persistence-safe-mode/init-user-data/preferences.json
  • tests/scripting/bru-api/variable-persistence-safe-mode/variable-persistence-safe-mode.spec.ts
  • tests/scripting/bru-api/variable-persistence/fixtures/collections/variable-persistence-test/bruno.json
  • tests/scripting/bru-api/variable-persistence/fixtures/collections/variable-persistence-test/environments/Test.bru
  • tests/scripting/bru-api/variable-persistence/fixtures/collections/variable-persistence-test/set-collection-var.bru
  • tests/scripting/bru-api/variable-persistence/fixtures/collections/variable-persistence-test/set-env-var.bru
  • tests/scripting/bru-api/variable-persistence/init-user-data/collection-security.json
  • tests/scripting/bru-api/variable-persistence/init-user-data/preferences.json
  • tests/scripting/bru-api/variable-persistence/variable-persistence.spec.ts
  • tests/utils/page/locators.ts
✅ Files skipped from review due to trivial changes (18)
  • tests/scripting/bru-api/variable-persistence-safe-mode/fixtures/collections/variable-persistence-safe-test/bruno.json
  • tests/scripting/bru-api/variable-persistence-safe-mode/fixtures/collections/variable-persistence-safe-test/environments/Test.bru
  • tests/scripting/bru-api/variable-persistence-safe-mode/init-user-data/collection-security.json
  • tests/scripting/bru-api/variable-persistence/init-user-data/collection-security.json
  • tests/scripting/bru-api/variable-persistence-safe-mode/init-user-data/preferences.json
  • tests/scripting/bru-api/variable-persistence/init-user-data/preferences.json
  • tests/scripting/bru-api/global-env-var-persistence/fixtures/collections/global-env-var-persistence-test/bruno.json
  • tests/scripting/bru-api/variable-persistence-safe-mode/fixtures/collections/variable-persistence-safe-test/set-collection-var.bru
  • tests/scripting/bru-api/global-env-var-persistence/init-user-data/collection-security.json
  • tests/scripting/bru-api/variable-persistence/fixtures/collections/variable-persistence-test/set-collection-var.bru
  • tests/scripting/bru-api/variable-persistence/fixtures/collections/variable-persistence-test/bruno.json
  • tests/scripting/bru-api/variable-persistence/fixtures/collections/variable-persistence-test/environments/Test.bru
  • tests/scripting/bru-api/global-env-var-persistence/fixtures/collections/global-env-var-persistence-test/delete-global-env-var.bru
  • packages/bruno-tests/collection/scripting/api/bru/setGlobalEnvVar.bru
  • tests/scripting/bru-api/global-env-var-persistence/init-user-data/preferences.json
  • tests/scripting/bru-api/variable-persistence-safe-mode/fixtures/collections/variable-persistence-safe-test/set-env-var.bru
  • tests/scripting/bru-api/variable-persistence/fixtures/collections/variable-persistence-test/set-env-var.bru
  • tests/scripting/bru-api/global-env-var-persistence/init-user-data/global-environments.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-js/tests/runtime.spec.js

Comment thread tests/scripting/bru-api/variable-persistence/variable-persistence.spec.ts Outdated
Comment thread tests/scripting/bru-api/variable-persistence/variable-persistence.spec.ts Outdated
Comment thread tests/utils/page/locators.ts Outdated
…ariable persistence logic

- Removed the persistActiveGlobalEnvironment function to simplify the global environment update process.
- Updated the global environment persistence logic to directly use computed variables instead of relying on Redux state.
- Enhanced error handling during the persistence operation to log failures.
- Added a new test to verify the addition of global environment variables through the bru.setGlobalEnvVar() function.
@sanish-bruno sanish-bruno changed the title feat: persist environment variable changes from scripts by default (BRU-3096) feat(scripting): persist environment variable changes from scripts by default (BRU-3096) Jun 9, 2026
@sanish-bruno sanish-bruno changed the title feat(scripting): persist environment variable changes from scripts by default (BRU-3096) feat(scripting): persist environment variable changes from scripts by default Jun 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
tests/scripting/bru-api/global-env-var-persistence/global-env-var-persistence.spec.ts (1)

52-56: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use locator text assertions instead of textContent() extraction in the new test.

This repeats the earlier flaky pattern; assert directly on the locator so Playwright handles waiting and nullability.

Suggested change
-      const value = await locators.environment.variableValue('newGlobalVar').textContent();
-      expect(value).toContain('new-global-value');
+      await expect(locators.environment.variableValue('newGlobalVar')).toContainText('new-global-value');
...
-      const value = await locators.environment.variableValue('baseUrl').textContent();
-      expect(value).toContain('https://testbench-sanity.usebruno.com');
+      await expect(locators.environment.variableValue('baseUrl')).toContainText('https://testbench-sanity.usebruno.com');

As per coding guidelines, E2E assertions should be robust and behavior-focused rather than brittle value extraction.

Also applies to: 58-62

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@tests/scripting/bru-api/global-env-var-persistence/global-env-var-persistence.spec.ts`
around lines 52 - 56, Replace the manual textContent() extraction with
Playwright locator assertions so Playwright waits and handles nullability: for
the block using locators.environment.variableRowByName('newGlobalVar') and
locators.environment.variableValue('newGlobalVar'), assert directly on the value
locator (e.g., use toHaveText/toContainText on variableValue('newGlobalVar'))
instead of calling .textContent() and then expect(value). Also update the
similar pattern in the subsequent block (the one referenced at 58-62) to use
locator-based assertions on locators.environment.variableValue(...) rather than
extracting text.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js`:
- Around line 306-323: Before dispatching _saveGlobalEnvironment({
environmentUid, variables }) capture the current variables from state (e.g. via
getState() or the environment object) so you can roll back on failure; after
calling environmentSchema.validate(...) and
ipcRenderer.invoke('renderer:save-global-environment', ...), if either promise
rejects dispatch _saveGlobalEnvironment({ environmentUid, variables:
previousVariables }) to revert the store and surface the error (e.g.
console.error and/or show a user-facing notification via ipcRenderer) so the UI
cannot silently diverge from persisted disk state.

In `@packages/bruno-app/src/utils/codemirror/autocomplete.js`:
- Around line 109-113: The autocomplete hint list was expanded to include
bru.setCollectionVar, bru.hasCollectionVar, bru.deleteCollectionVar,
bru.deleteAllCollectionVars, and bru.getAllCollectionVars but there are no
explicit unit assertions for these new hints; add explicit assertions in the
autocomplete tests to cover each new bru.* entry (and the other entries noted
around lines for 138-140) by updating the relevant test file to assert that the
autocomplete suggestions include bru.setCollectionVar(...),
bru.hasCollectionVar(...), bru.deleteCollectionVar(...),
bru.deleteAllCollectionVars(), and bru.getAllCollectionVars() (and the
additional bru.* entries mentioned) so future regressions are caught; target the
test suite that verifies CodeMirror/autocomplete hint output and add simple
presence checks for these symbol strings.

---

Duplicate comments:
In
`@tests/scripting/bru-api/global-env-var-persistence/global-env-var-persistence.spec.ts`:
- Around line 52-56: Replace the manual textContent() extraction with Playwright
locator assertions so Playwright waits and handles nullability: for the block
using locators.environment.variableRowByName('newGlobalVar') and
locators.environment.variableValue('newGlobalVar'), assert directly on the value
locator (e.g., use toHaveText/toContainText on variableValue('newGlobalVar'))
instead of calling .textContent() and then expect(value). Also update the
similar pattern in the subsequent block (the one referenced at 58-62) to use
locator-based assertions on locators.environment.variableValue(...) rather than
extracting text.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0ae03133-0c36-4189-8120-e4072583d5f9

📥 Commits

Reviewing files that changed from the base of the PR and between a0d4e6a and 8072308.

📒 Files selected for processing (6)
  • packages/bruno-app/src/providers/App/useIpcEvents.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
  • packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js
  • packages/bruno-app/src/utils/codemirror/autocomplete.js
  • tests/scripting/bru-api/global-env-var-persistence/fixtures/collections/global-env-var-persistence-test/set-global-env-var.bru
  • tests/scripting/bru-api/global-env-var-persistence/global-env-var-persistence.spec.ts
💤 Files with no reviewable changes (1)
  • packages/bruno-app/src/providers/App/useIpcEvents.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js

Comment thread packages/bruno-app/src/utils/codemirror/autocomplete.js
- Introduced setCollectionVars action to streamline the update process for collection variables.
- Removed redundant logic for updating Redux state, enhancing code clarity and performance.
- Adjusted IPC event handling to eliminate unnecessary data transmission related to collection variables.
…ty and performance

- Refactored variable persistence tests to use `toContainText` for better clarity and efficiency.
- Simplified file reading assertions in persistence tests to check for multiple expected values in a single poll.
- Removed unnecessary text content extraction steps to streamline test execution.
…ions and transformations

- Enhanced comments in the collection variable update event to clarify the synchronous nature of Redux Toolkit dispatches.
- Updated comments in the collection transformation utility to specify the exclusion of internal metadata during environment saving.
…te listeners

- Added a comment highlighting the need to implement a dirty flag for the script environment update listener to prevent unnecessary disk writes when no variables change.
- This change aims to improve performance in high-frequency runner scenarios while maintaining current functionality.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant