Skip to content

Commit

Permalink
Allow token replacement of .npmrc configuration with env vars (#1207)
Browse files Browse the repository at this point in the history
* test IGNORE ME

* Update npm-registry to inject env vars into npm configuration the same way npm does

* Add type annotation to config object iteration

* Update envReplace function

* Move env-replace function to a separate util module. Add unit tests

* Correct the env-replace tests

* Updates per @kittens' comments
  • Loading branch information
darthtrevino authored and Sebastian McKenzie committed Nov 14, 2016
1 parent 42dc14e commit b4e42e3
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 0 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ end_of_line = lf
[*.{js,json}]
indent_style = space
indent_size = 2

32 changes: 32 additions & 0 deletions __tests__/util/env-replace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* @flow */
import envReplace from '../../src/util/env-replace';
import assert from 'assert';

describe('environment variable replacement', () => {
it('will replace a token that exists in the environment', () => {
let result = envReplace('test ${a} replacement', {a: 'token'});
assert(result === 'test token replacement', `result: ${result}`);

result = envReplace('${a} replacement', {a: 'token'});
assert(result === 'token replacement', `result: ${result}`);

result = envReplace('${a}', {a: 'token'});
assert(result === 'token', `result: ${result}`);
});

it('will not replace a token that does not exist in the environment', () => {
let thrown = false;
try {
envReplace('${a} replacement', {b: 'token'});
} catch (err) {
thrown = true;
assert(err.message === 'Failed to replace env in config: ${a}', `error message: ${err.message}`);
}
assert(thrown);
});

it('will not replace a token when a the token-replacement mechanism is prefixed a backslash literal', () => {
const result = envReplace('\\${a} replacement', {a: 'token'});
assert(result === '\\${a} replacement', `result: ${result}`);
});
});
4 changes: 4 additions & 0 deletions src/registries/npm-registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type Config from '../config.js';
import type {ConfigRegistries} from './index.js';
import * as fs from '../util/fs.js';
import NpmResolver from '../resolvers/registries/npm-resolver.js';
import envReplace from '../util/env-replace.js';
import Registry from './base-registry.js';
import {addSuffix, removePrefix} from '../util/misc';

Expand Down Expand Up @@ -121,6 +122,9 @@ export default class NpmRegistry extends Registry {

for (const [, loc, file] of await this.getPossibleConfigLocations('.npmrc')) {
const config = Registry.normalizeConfig(ini.parse(file));
for (const key: string in config) {
config[key] = envReplace(config[key]);
}

// normalize offline mirror path relative to the current npmrc
const offlineLoc = config['yarn-offline-mirror'];
Expand Down
18 changes: 18 additions & 0 deletions src/util/env-replace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* @flow */
const ENV_EXPR = /(\\*)\$\{([^}]+)\}/g;

export default function envReplace(value: string, env: {[key: string]: ?string} = process.env): string {
if (typeof value !== 'string' || !value) {
return value;
}

return value.replace(ENV_EXPR, (match: string, esc: string, envVarName: string) => {
if (esc.length && esc.length % 2) {
return match;
}
if (undefined === env[envVarName]) {
throw new Error('Failed to replace env in config: ' + match);
}
return env[envVarName] || '';
});
}

0 comments on commit b4e42e3

Please sign in to comment.