Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@ export const selectEnvironment = (environmentUid, collectionUid) => (dispatch, g

const { ipcRenderer } = window;
ipcRenderer.invoke('renderer:update-ui-state-snapshot', { type: 'COLLECTION_ENVIRONMENT', data: { collectionPath: collection?.pathname, environmentName }});
ipcRenderer.invoke('renderer:select-environment', { collectionUid, envName: environmentName });

dispatch(_selectEnvironment({ environmentUid, collectionUid }));
resolve();
Expand Down
9 changes: 8 additions & 1 deletion packages/bruno-cli/src/commands/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,14 @@ const handler = async function (argv) {
const content = fs.readFileSync(dotEnvPath, 'utf8');
const jsonData = dotenvToJson(content);

forOwn(jsonData, (value, key) => {
const envName = envVars.__name__ || null;
const defaults = jsonData.default || {};
const envSpecific = envName && jsonData.envs ? jsonData.envs[envName] || {} : {};

forOwn(defaults, (value, key) => {
processEnvVars[key] = value;
});
forOwn(envSpecific, (value, key) => {
processEnvVars[key] = value;
});
}
Expand Down
6 changes: 3 additions & 3 deletions packages/bruno-electron/src/app/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const { dotenvToJson } = require('@usebruno/lang');
const { uuid } = require('../utils/common');
const { getRequestUid } = require('../cache/requestUids');
const { decryptString } = require('../utils/encryption');
const { setDotEnvVars } = require('../store/process-env');
const { setDotEnvVars, getProcessEnvVars } = require('../store/process-env');
const { setBrunoConfig } = require('../store/bruno-config');
const EnvironmentSecretsStore = require('../store/env-secrets');
const UiStateSnapshot = require('../store/ui-state-snapshot');
Expand Down Expand Up @@ -183,7 +183,7 @@ const add = async (win, pathname, collectionUid, collectionPath, useWorkerThread
const payload = {
collectionUid,
processEnvVariables: {
...jsonData
...getProcessEnvVars(collectionUid)
}
};
win.webContents.send('main:process-env-update', payload);
Expand Down Expand Up @@ -376,7 +376,7 @@ const change = async (win, pathname, collectionUid, collectionPath) => {
const payload = {
collectionUid,
processEnvVariables: {
...jsonData
...getProcessEnvVars(collectionUid)
}
};
win.webContents.send('main:process-env-update', payload);
Expand Down
14 changes: 13 additions & 1 deletion packages/bruno-electron/src/ipc/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const CollectionSecurityStore = require('../store/collection-security');
const UiStateSnapshotStore = require('../store/ui-state-snapshot');
const interpolateVars = require('./network/interpolate-vars');
const { getEnvVars, getTreePathFromCollectionToItem, mergeVars, parseBruFileMeta, hydrateRequestWithUuid, transformRequestToSaveToFilesystem } = require('../utils/collection');
const { getProcessEnvVars } = require('../store/process-env');
const { getProcessEnvVars, setActiveEnv } = require('../store/process-env');
const { getOAuth2TokenUsingAuthorizationCode, getOAuth2TokenUsingClientCredentials, getOAuth2TokenUsingPasswordCredentials, refreshOauth2Token } = require('../utils/oauth2');
const { getCertsAndProxyConfig } = require('./network');

Expand Down Expand Up @@ -958,6 +958,18 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
}
});

ipcMain.handle('renderer:select-environment', async (event, { collectionUid, envName }) => {
try {
setActiveEnv(collectionUid, envName);
mainWindow.webContents.send('main:process-env-update', {
collectionUid,
processEnvVariables: { ...getProcessEnvVars(collectionUid) }
});
} catch (error) {
return Promise.reject(error);
}
});

ipcMain.handle('renderer:fetch-oauth2-credentials', async (event, { itemUid, request, collection }) => {
try {
if (request.oauth2) {
Expand Down
32 changes: 23 additions & 9 deletions packages/bruno-electron/src/store/process-env.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,39 @@ const dotEnvVars = {};

// collectionUid is a hash based on the collection path
const getProcessEnvVars = (collectionUid) => {
// if there are no .env vars for this collection, return the process.env
if (!dotEnvVars[collectionUid]) {
return {
...process.env
};
const data = dotEnvVars[collectionUid];
if (!data) {
return { ...process.env };
}

// if there are .env vars for this collection, return the process.env merged with the .env vars
const defaults = data.default || {};
const envName = data.activeEnv;
const envSpecific = envName && data.envs ? data.envs[envName] || {} : {};

return {
...process.env,
...dotEnvVars[collectionUid]
...defaults,
...envSpecific
};
};

const setDotEnvVars = (collectionUid, envVars) => {
dotEnvVars[collectionUid] = envVars;
if (!dotEnvVars[collectionUid]) {
dotEnvVars[collectionUid] = { activeEnv: null };
}
dotEnvVars[collectionUid].default = envVars.default || {};
dotEnvVars[collectionUid].envs = envVars.envs || {};
};

const setActiveEnv = (collectionUid, envName) => {
if (!dotEnvVars[collectionUid]) {
dotEnvVars[collectionUid] = { default: {}, envs: {} };
}
dotEnvVars[collectionUid].activeEnv = envName;
};

module.exports = {
getProcessEnvVars,
setDotEnvVars
setDotEnvVars,
setActiveEnv
};
19 changes: 18 additions & 1 deletion packages/bruno-lang/v2/src/dotenvToJson.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,24 @@ const dotenv = require('dotenv');
const parser = (input) => {
const buf = Buffer.from(input);
const parsed = dotenv.parse(buf);
return parsed;

const result = { default: {}, envs: {} };

Object.entries(parsed).forEach(([key, value]) => {
const match = key.match(/^(.*?)\.(.+)$/);
if (match) {
const env = match[1];
const varName = match[2];
if (!result.envs[env]) {
result.envs[env] = {};
}
result.envs[env][varName] = value;
} else {
result.default[key] = value;
}
});

return result;
};

module.exports = parser;
30 changes: 23 additions & 7 deletions packages/bruno-lang/v2/tests/dotenvToJson.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const parser = require('../src/dotenvToJson');
describe('DotEnv File Parser', () => {
test('it should parse a simple key-value pair', () => {
const input = `FOO=bar`;
const expected = { FOO: 'bar' };
const expected = { default: { FOO: 'bar' }, envs: {} };
const output = parser(input);
expect(output).toEqual(expected);
});
Expand All @@ -13,7 +13,7 @@ describe('DotEnv File Parser', () => {
FOO=bar

`;
const expected = { FOO: 'bar' };
const expected = { default: { FOO: 'bar' }, envs: {} };
const output = parser(input);
expect(output).toEqual(expected);
});
Expand All @@ -25,9 +25,12 @@ BAZ=2
BEEP=false
`;
const expected = {
FOO: 'bar',
BAZ: '2',
BEEP: 'false'
default: {
FOO: 'bar',
BAZ: '2',
BEEP: 'false'
},
envs: {}
};
const output = parser(input);
expect(output).toEqual(expected);
Expand All @@ -37,7 +40,7 @@ BEEP=false
const input = `
SPACE=" value "
`;
const expected = { SPACE: ' value ' };
const expected = { default: { SPACE: ' value ' }, envs: {} };
const output = parser(input);
expect(output).toEqual(expected);
});
Expand All @@ -46,7 +49,20 @@ SPACE=" value "
const input = `
SPACE= value
`;
const expected = { SPACE: 'value' };
const expected = { default: { SPACE: 'value' }, envs: {} };
const output = parser(input);
expect(output).toEqual(expected);
});

test('it should parse environment specific variables', () => {
const input = `dev.API_URL=http://dev\nprod.API_URL=http://prod`;
const expected = {
default: {},
envs: {
dev: { API_URL: 'http://dev' },
prod: { API_URL: 'http://prod' }
}
};
const output = parser(input);
expect(output).toEqual(expected);
});
Expand Down
12 changes: 12 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ If Bruno has helped you at work and your teams, please don't forget to share you

Please see [here](publishing.md) for more information.

## .env Files

You can define variables in a `.env` file at the root of your collection. Keys prefixed with `<env>.` will only apply when that environment is selected. For example:

```env
API_KEY=default
dev.API_KEY=dev-key
prod.API_KEY=prod-key
```

When the **dev** environment is active, `process.env.API_KEY` will resolve to `dev-key`. Without any environment selected, the default value will be used.

## Stay in touch 🌐

[𝕏 (Twitter)](https://twitter.com/use_bruno) <br />
Expand Down