Skip to content
Merged
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
31 changes: 19 additions & 12 deletions src/spec-common/variableSubstitution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function containerSubstitute<T extends object>(platform: NodeJS.Platform,
return substitute0(replaceContainerEnv.bind(undefined, isWindows, configFile, normalizeEnv(isWindows, containerEnv)), value);
}

type Replace = (match: string, variable: string, argument: string | undefined) => string;
type Replace = (match: string, variable: string, args: string[]) => string;

function substitute0(replace: Replace, value: any): any {
if (typeof value === 'string') {
Expand Down Expand Up @@ -75,21 +75,21 @@ function resolveString(replace: Replace, value: string): string {
function evaluateSingleVariable(replace: Replace, match: string, variable: string): string {

// try to separate variable arguments from variable name
let argument: string | undefined;
let args: string[] = [];
const parts = variable.split(':');
if (parts.length > 1) {
variable = parts[0];
argument = parts[1];
args = parts.slice(1);
}

return replace(match, variable, argument);
return replace(match, variable, args);
}

function replaceWithContext(isWindows: boolean, context: SubstitutionContext, match: string, variable: string, argument: string | undefined) {
function replaceWithContext(isWindows: boolean, context: SubstitutionContext, match: string, variable: string, args: string[]) {
switch (variable) {
case 'env':
case 'localEnv':
return lookupValue(isWindows, context.env, argument, match, context.configFile);
return lookupValue(isWindows, context.env, args, match, context.configFile);

case 'localWorkspaceFolder':
return context.localWorkspaceFolder !== undefined ? context.localWorkspaceFolder : match;
Expand All @@ -108,25 +108,32 @@ function replaceWithContext(isWindows: boolean, context: SubstitutionContext, ma
}
}

function replaceContainerEnv(isWindows: boolean, configFile: URI | undefined, containerEnvObj: NodeJS.ProcessEnv, match: string, variable: string, argument: string | undefined) {
function replaceContainerEnv(isWindows: boolean, configFile: URI | undefined, containerEnvObj: NodeJS.ProcessEnv, match: string, variable: string, args: string[]) {
switch (variable) {
case 'containerEnv':
return lookupValue(isWindows, containerEnvObj, argument, match, configFile);
return lookupValue(isWindows, containerEnvObj, args, match, configFile);

default:
return match;
}
}

function lookupValue(isWindows: boolean, envObj: NodeJS.ProcessEnv, argument: string | undefined, match: string, configFile: URI | undefined) {
if (argument) {
function lookupValue(isWindows: boolean, envObj: NodeJS.ProcessEnv, args: string[], match: string, configFile: URI | undefined) {
if (args.length > 0) {
let envVariableName = args[0];
if (isWindows) {
argument = argument.toLowerCase();
envVariableName = envVariableName.toLowerCase();
}
const env = envObj[argument];
const env = envObj[envVariableName];
if (typeof env === 'string') {
return env;
}

if (args.length > 1) {
const defaultValue = args[1];
return defaultValue;
}

// For `env` we should do the same as a normal shell does - evaluates missing envs to an empty string #46436
return '';
}
Expand Down
136 changes: 136 additions & 0 deletions src/test/variableSubstitution.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

import * as assert from 'assert';

import { substitute } from '../spec-common/variableSubstitution';
import { URI } from 'vscode-uri';

describe('Variable substitution', function () {

it(`environment variables`, async () => {
const raw = {
foo: 'bar${env:baz}bar'
};
const result = substitute({
platform: 'linux',
localWorkspaceFolder: '/foo/bar',
containerWorkspaceFolder: '/baz/blue',
configFile: URI.file('/foo/bar/baz.json'),
env: {
baz: 'somevalue'
},
}, raw);
assert.strictEqual(result.foo, 'barsomevaluebar');
});

it(`localWorkspaceFolder`, async () => {
const raw = {
foo: 'bar${localWorkspaceFolder}bar'
};
const result = substitute({
platform: 'linux',
localWorkspaceFolder: '/foo/bar',
containerWorkspaceFolder: '/baz/blue',
configFile: URI.file('/foo/bar/baz.json'),
env: {
baz: 'somevalue'
},
}, raw);
assert.strictEqual(result.foo, 'bar/foo/barbar');
});

it(`containerWorkspaceFolder`, async () => {
const raw = {
foo: 'bar${containerWorkspaceFolder}bar'
};
const result = substitute({
platform: 'linux',
localWorkspaceFolder: '/foo/bar',
containerWorkspaceFolder: '/baz/blue',
configFile: URI.file('/foo/bar/baz.json'),
env: {
baz: 'somevalue'
},
}, raw);
assert.strictEqual(result.foo, 'bar/baz/bluebar');
});

it(`localWorkspaceFolderBasename and containerWorkspaceFolder`, async () => {
const raw = {
foo: 'bar${containerWorkspaceFolder}bar'
};
const result = substitute({
platform: 'linux',
localWorkspaceFolder: '/foo/red',
containerWorkspaceFolder: '/baz/${localWorkspaceFolderBasename}',
configFile: URI.file('/foo/bar/baz.json'),
env: {
baz: 'somevalue'
},
}, raw);
assert.strictEqual(result.foo, 'bar/baz/redbar');
});

it(`environment variables with default value if they do not exist`, async () => {
const raw = {
foo: 'bar${localEnv:baz:default}bar'
};
const result = substitute({
platform: 'linux',
localWorkspaceFolder: '/foo/bar',
containerWorkspaceFolder: '/baz/blue',
configFile: URI.file('/foo/bar/baz.json'),
env: {
},
}, raw);
assert.strictEqual(result.foo, 'bardefaultbar');
});

it(`environment variables without default value if they do not exist`, async () => {
const raw = {
foo: 'bar${localEnv:baz}bar'
};
const result = substitute({
platform: 'linux',
localWorkspaceFolder: '/foo/bar',
containerWorkspaceFolder: '/baz/blue',
configFile: URI.file('/foo/bar/baz.json'),
env: {
},
}, raw);
assert.strictEqual(result.foo, 'barbar');
});

it(`environment variables with default value if they do not exist`, async () => {
const raw = {
foo: 'bar${localEnv:baz:default}bar'
};
const result = substitute({
platform: 'linux',
localWorkspaceFolder: '/foo/bar',
containerWorkspaceFolder: '/baz/blue',
configFile: URI.file('/foo/bar/baz.json'),
env: {
baz: 'somevalue'
},
}, raw);
assert.strictEqual(result.foo, 'barsomevaluebar');
});

it(`environment variables without default value if they do not exist`, async () => {
const raw = {
foo: 'bar${localEnv:baz:default:a:b:c}bar'
};
const result = substitute({
platform: 'linux',
localWorkspaceFolder: '/foo/bar',
containerWorkspaceFolder: '/baz/blue',
configFile: URI.file('/foo/bar/baz.json'),
env: {
},
}, raw);
assert.strictEqual(result.foo, 'bardefaultbar');
});
});